individui e gruppi
This commit is contained in:
@@ -25,6 +25,7 @@ App gestionale Laravel 13 con AdminLTE 4 per gestione Persone e Gruppi.
|
|||||||
- **2026-06-10**: Per-User Column Views, ColumnManager JS, editVista data-* fix, 405 AJAX fix
|
- **2026-06-10**: Per-User Column Views, ColumnManager JS, editVista data-* fix, 405 AJAX fix
|
||||||
- **2026-06-17**: Google OAuth 2.0 unificato (revert Socialite, unified OAuth per Email/Drive/Calendar, XOAUTH2 SMTP+IMAP, UI impostazioni)
|
- **2026-06-17**: Google OAuth 2.0 unificato (revert Socialite, unified OAuth per Email/Drive/Calendar, XOAUTH2 SMTP+IMAP, UI impostazioni)
|
||||||
- **2026-06-17**: Fix CalendarioConnessione — `encryptAndSetConfig()` perdeva campi sensibili in edit, `is_active` checkbox senza hidden fallback, query duplicata nella view
|
- **2026-06-17**: Fix CalendarioConnessione — `encryptAndSetConfig()` perdeva campi sensibili in edit, `is_active` checkbox senza hidden fallback, query duplicata nella view
|
||||||
|
- **2026-06-18**: Fix import CSV — `declare(strict_types=1)` mancante in IndividuoController, header check assente, Log::warning() su errori; fix freeze server (set_time_limit, session_write_close, DB::transaction, fgetcsv length illimitato) in GruppoController e IndividuoController
|
||||||
|
|
||||||
## Funzionalità Implementate
|
## Funzionalità Implementate
|
||||||
|
|
||||||
@@ -665,3 +666,42 @@ Aggiunto `if (!Schema::hasTable('table_name')) { ... }` wrapper a 28 migration f
|
|||||||
- PHP lint: OK su controller + routes
|
- PHP lint: OK su controller + routes
|
||||||
- JS brace balance: OK su index view
|
- JS brace balance: OK su index view
|
||||||
- Route list: `POST mailing-liste/mass-tag` → `MailingListController@massTag` (name: `mailing-liste.mass-tag`)
|
- Route list: `POST mailing-liste/mass-tag` → `MailingListController@massTag` (name: `mailing-liste.mass-tag`)
|
||||||
|
|
||||||
|
## 2026-06-18 — Fix import CSV: freeze server + bug IndividuoController
|
||||||
|
|
||||||
|
### Problema freeze server (Gruppi + Individui)
|
||||||
|
Importando CSV con molte righe, il server si bloccava:
|
||||||
|
1. **PHP max_execution_time (30s)** — killava il processo su import grandi
|
||||||
|
2. **Session lock** — altre richieste dello stesso utente restavano in attesa
|
||||||
|
3. **Nessuna transazione DB** — ogni `create()` era una INSERT individuale autocommit
|
||||||
|
4. **fgetcsv length=1000** — righe CSV più lunghe di 1000 caratteri venivano troncate
|
||||||
|
|
||||||
|
### Fix applicati a `GruppoController@importStore` e `IndividuoController@importStore`
|
||||||
|
```php
|
||||||
|
set_time_limit(0);
|
||||||
|
session_write_close();
|
||||||
|
$header = fgetcsv($handle, 0, ','); // length=0 = nessun limite
|
||||||
|
DB::beginTransaction();
|
||||||
|
// ... loop ...
|
||||||
|
DB::commit();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bug trovati in `IndividuoController@importStore`
|
||||||
|
| Bug | Fix |
|
||||||
|
|-----|-----|
|
||||||
|
| `declare(strict_types=1)` mancante | ✅ Aggiunto |
|
||||||
|
| Indentazione errata negli import (spazi extra su 8 righe) | ✅ Corretto |
|
||||||
|
| `use Illuminate\Support\Facades\Log` mancante | ✅ Aggiunto |
|
||||||
|
| Nessun controllo `$header === false` — CSV vuoto causava TypeError | ✅ Aggiunto |
|
||||||
|
| Nessun `array_map('trim', $header)` | ✅ Aggiunto |
|
||||||
|
| Nessun `Log::warning()` su eccezioni riga | ✅ Aggiunto |
|
||||||
|
| `fgetcsv` length=1000 (vs 0) | ✅ Portato a 0 |
|
||||||
|
| Nessun `set_time_limit(0)` / `session_write_close()` | ✅ Aggiunto |
|
||||||
|
| Nessun `DB::beginTransaction()` / `DB::commit()` | ✅ Aggiunto |
|
||||||
|
|
||||||
|
### File modificati
|
||||||
|
- `app/Http/Controllers/GruppoController.php` — aggiunti `use DB`, set_time_limit, session_write_close, fgetcsv length=0, DB::transaction
|
||||||
|
- `app/Http/Controllers/IndividuoController.php` — strict_types, Log import, DB import, header check, trim, set_time_limit, session_write_close, DB::transaction
|
||||||
|
|
||||||
|
### Verifica
|
||||||
|
- `php -l` su entrambi: nessun errore di sintassi
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use App\Models\Tag;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Pagination\Paginator;
|
use Illuminate\Pagination\Paginator;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class GruppoController extends Controller
|
class GruppoController extends Controller
|
||||||
@@ -385,9 +386,12 @@ class GruppoController extends Controller
|
|||||||
'file' => 'required|file|mimes:csv,txt',
|
'file' => 'required|file|mimes:csv,txt',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
set_time_limit(0);
|
||||||
|
session_write_close();
|
||||||
|
|
||||||
$file = $request->file('file');
|
$file = $request->file('file');
|
||||||
$handle = fopen($file->path(), 'r');
|
$handle = fopen($file->path(), 'r');
|
||||||
$header = fgetcsv($handle, 1000, ',');
|
$header = fgetcsv($handle, 0, ',');
|
||||||
|
|
||||||
if ($header === false) {
|
if ($header === false) {
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
@@ -400,7 +404,9 @@ class GruppoController extends Controller
|
|||||||
$errors = [];
|
$errors = [];
|
||||||
$rowNumber = 1;
|
$rowNumber = 1;
|
||||||
|
|
||||||
while (($row = fgetcsv($handle, 1000, ',')) !== false) {
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
while (($row = fgetcsv($handle, 0, ',')) !== false) {
|
||||||
$rowNumber++;
|
$rowNumber++;
|
||||||
|
|
||||||
if (count($row) < count($header)) {
|
if (count($row) < count($header)) {
|
||||||
@@ -444,6 +450,8 @@ class GruppoController extends Controller
|
|||||||
|
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
$message = 'Importati ' . $imported . ' gruppi.';
|
$message = 'Importati ' . $imported . ' gruppi.';
|
||||||
if ($skipped > 0) {
|
if ($skipped > 0) {
|
||||||
$message .= ' Saltate ' . $skipped . ' righe (vuote o incomplete).';
|
$message .= ' Saltate ' . $skipped . ' righe (vuote o incomplete).';
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Individuo;
|
use App\Models\Individuo;
|
||||||
use App\Models\Contatto;
|
use App\Models\Contatto;
|
||||||
use App\Models\Documento;
|
use App\Models\Documento;
|
||||||
use App\Models\Gruppo;
|
use App\Models\Gruppo;
|
||||||
use App\Models\Comune;
|
use App\Models\Comune;
|
||||||
use App\Models\Diocesi;
|
use App\Models\Diocesi;
|
||||||
use App\Models\Tag;
|
use App\Models\Tag;
|
||||||
use App\Models\VistaReport;
|
use App\Models\VistaReport;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||||
|
|
||||||
class IndividuoController extends Controller
|
class IndividuoController extends Controller
|
||||||
{
|
{
|
||||||
@@ -497,16 +501,27 @@ public function emailList(Request $request)
|
|||||||
'file' => 'required|file|mimes:csv,txt',
|
'file' => 'required|file|mimes:csv,txt',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
set_time_limit(0);
|
||||||
|
session_write_close();
|
||||||
|
|
||||||
$file = $request->file('file');
|
$file = $request->file('file');
|
||||||
$handle = fopen($file->path(), 'r');
|
$handle = fopen($file->path(), 'r');
|
||||||
$header = fgetcsv($handle, 1000, ',');
|
$header = fgetcsv($handle, 0, ',');
|
||||||
|
|
||||||
|
if ($header === false) {
|
||||||
|
fclose($handle);
|
||||||
|
return back()->with('error', 'Il file CSV non è valido.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$header = array_map('trim', $header);
|
||||||
$imported = 0;
|
$imported = 0;
|
||||||
$skipped = 0;
|
$skipped = 0;
|
||||||
$errors = [];
|
$errors = [];
|
||||||
$rowNumber = 1;
|
$rowNumber = 1;
|
||||||
|
|
||||||
while (($row = fgetcsv($handle, 1000, ',')) !== false) {
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
while (($row = fgetcsv($handle, 0, ',')) !== false) {
|
||||||
$rowNumber++;
|
$rowNumber++;
|
||||||
|
|
||||||
if (count($row) < count($header)) {
|
if (count($row) < count($header)) {
|
||||||
@@ -565,12 +580,15 @@ public function emailList(Request $request)
|
|||||||
|
|
||||||
$imported++;
|
$imported++;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
Log::warning('Errore import individuo riga ' . $rowNumber . ': ' . $e->getMessage());
|
||||||
$errors[] = 'Errore riga ' . $rowNumber . ': ' . $e->getMessage();
|
$errors[] = 'Errore riga ' . $rowNumber . ': ' . $e->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
$message = 'Importati ' . $imported . ' individui.';
|
$message = 'Importati ' . $imported . ' individui.';
|
||||||
if ($skipped > 0) {
|
if ($skipped > 0) {
|
||||||
$message .= ' Saltate ' . $skipped . ' righe (vuote o incomplete).';
|
$message .= ' Saltate ' . $skipped . ' righe (vuote o incomplete).';
|
||||||
|
|||||||
Binary file not shown.
Vendored
+1
-4
@@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) {
|
|||||||
echo $err;
|
echo $err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trigger_error(
|
throw new RuntimeException($err);
|
||||||
$err,
|
|
||||||
E_USER_ERROR
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once __DIR__ . '/composer/autoload_real.php';
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|||||||
+25
-4
@@ -26,6 +26,12 @@ use Composer\Semver\VersionParser;
|
|||||||
*/
|
*/
|
||||||
class InstalledVersions
|
class InstalledVersions
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private static $selfDir = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var mixed[]|null
|
* @var mixed[]|null
|
||||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||||
@@ -311,6 +317,18 @@ class InstalledVersions
|
|||||||
self::$installedByVendor = array();
|
self::$installedByVendor = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function getSelfDir()
|
||||||
|
{
|
||||||
|
if (self::$selfDir === null) {
|
||||||
|
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$selfDir;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array[]
|
||||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
@@ -322,6 +340,7 @@ class InstalledVersions
|
|||||||
}
|
}
|
||||||
|
|
||||||
$installed = array();
|
$installed = array();
|
||||||
|
$copiedLocalDir = false;
|
||||||
|
|
||||||
if (self::$canGetVendors) {
|
if (self::$canGetVendors) {
|
||||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||||
@@ -330,9 +349,11 @@ class InstalledVersions
|
|||||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
$required = require $vendorDir.'/composer/installed.php';
|
$required = require $vendorDir.'/composer/installed.php';
|
||||||
$installed[] = self::$installedByVendor[$vendorDir] = $required;
|
self::$installedByVendor[$vendorDir] = $required;
|
||||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
$installed[] = $required;
|
||||||
self::$installed = $installed[count($installed) - 1];
|
if (strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||||
|
self::$installed = $required;
|
||||||
|
$copiedLocalDir = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -350,7 +371,7 @@ class InstalledVersions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self::$installed !== array()) {
|
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||||
$installed[] = self::$installed;
|
$installed[] = self::$installed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+4
-4
@@ -3,7 +3,7 @@
|
|||||||
'name' => 'laravel/laravel',
|
'name' => 'laravel/laravel',
|
||||||
'pretty_version' => 'dev-main',
|
'pretty_version' => 'dev-main',
|
||||||
'version' => 'dev-main',
|
'version' => 'dev-main',
|
||||||
'reference' => '92ba47e99f4839d49d5f002915c30a88bb842c6a',
|
'reference' => 'd2084b9d07cbeccb7e289e6f31ca767f6cce07c8',
|
||||||
'type' => 'project',
|
'type' => 'project',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
@@ -427,7 +427,7 @@
|
|||||||
'laravel/laravel' => array(
|
'laravel/laravel' => array(
|
||||||
'pretty_version' => 'dev-main',
|
'pretty_version' => 'dev-main',
|
||||||
'version' => 'dev-main',
|
'version' => 'dev-main',
|
||||||
'reference' => '92ba47e99f4839d49d5f002915c30a88bb842c6a',
|
'reference' => 'd2084b9d07cbeccb7e289e6f31ca767f6cce07c8',
|
||||||
'type' => 'project',
|
'type' => 'project',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
@@ -732,8 +732,8 @@
|
|||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
'provided' => array(
|
'provided' => array(
|
||||||
0 => '1.0 || 2.0 || 3.0',
|
0 => '1.0 || 2.0 || 3.0',
|
||||||
1 => '3.0.0',
|
1 => '1.0|2.0|3.0',
|
||||||
2 => '1.0|2.0|3.0',
|
2 => '3.0.0',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'psr/simple-cache' => array(
|
'psr/simple-cache' => array(
|
||||||
|
|||||||
Reference in New Issue
Block a user