fix mailing an email
This commit is contained in:
@@ -2,10 +2,10 @@ APP_NAME=Glastree
|
||||
APP_ENV=local
|
||||
APP_KEY=base64:aWz7908H9c7s+it9uMTwb6pyUrpddyMclcuN9Kzv7Ao=
|
||||
APP_DEBUG=true
|
||||
APP_URL=https://glastree.soon.it
|
||||
APP_PROTOCOL=https
|
||||
FORCE_HTTPS=true
|
||||
TRUSTED_PROXIES=*
|
||||
APP_URL=http://glastree.soon.it
|
||||
APP_PROTOCOL=http
|
||||
FORCE_HTTPS=false
|
||||
TRUSTED_PROXIES="10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1"
|
||||
|
||||
#GOOGLE_CLIENT_ID=980774223097-o5h7a6kepvg69te34fof2otn7ibi9uha.apps.googleusercontent.com
|
||||
#GOOGLE_CLIENT_SECRET=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
|
||||
@@ -38,9 +38,11 @@ DB_DATABASE=glastree
|
||||
DB_USERNAME=glastree
|
||||
DB_PASSWORD=glastree
|
||||
|
||||
SESSION_DRIVER=file
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_SECURE_COOKIE=false
|
||||
SESSION_SAME_SITE=lax
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ DB_DATABASE=glastree
|
||||
DB_USERNAME=glastree
|
||||
DB_PASSWORD=
|
||||
|
||||
SESSION_DRIVER=file
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_SECURE_COOKIE=false
|
||||
|
||||
@@ -26,6 +26,20 @@ App gestionale Laravel 13 con AdminLTE 4 per gestione Persone e Gruppi.
|
||||
- **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-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
|
||||
- **2026-06-23**: Email compose: firma spostata dopo il corpo; Mailing list: aggiunto `sender_account_id` (mittente predefinito salvato in creazione, usato all'invio come fallback)
|
||||
- **2026-06-23**: Fix formati nome: responsabili gruppi e individui email ora mostrano "Nome Cognome"; mailing list edit: aggiunto pulsante "Rimuovi selezionati"
|
||||
- **2026-06-22**: Aggiunto supporto Email Mittente Ufficiale (from_email/from_name) per invio con indirizzo diverso dalle credenziali SMTP/IMAP
|
||||
- Migration: `from_email`, `from_name` columns su `email_settings`
|
||||
- `EmailSetting`: metodi `getEffectiveFromAddress()`, `getEffectiveFromName()`, `getEffectiveReplyTo()`
|
||||
- Tutti i metodi di invio (`sendViaImap`, `sendViaSystem`, `sendPasswordResetNotification`, `storeDraft`, `storeSentMessage`, `testSmtp`) ora usano `getEffectiveFromAddress()`/`getEffectiveFromName()` per il mittente
|
||||
- Reply-To automatico impostato su `from_email` quando disponibile
|
||||
- UI: nuovi campi "Email Mittente Ufficiale" e "Nome Mittente Ufficiale" in entrambe le pagine impostazioni email
|
||||
- **2026-06-22**: Backup system enhancements:
|
||||
- `BackupService::run()` ora accetta `$options` (include_files, include_env) per override temporanei senza persistere su DB
|
||||
- `BackupRunCommand --no-files/--no-env` non salva più permanentemente la configurazione (era un bug)
|
||||
- `manifest.json` ora include `included_components` array (database, env, files)
|
||||
- Vista backup: badge .env (grigio) e Files (giallo) nella colonna "Contenuto" basati sul manifest
|
||||
- `run()` restituisce `steps` array; mostrato in flash message (controller) e output (command)
|
||||
- **2026-06-18**: Diagnose script (`diagnose.php`), .env.example aggiornato (SESSION_SECURE_COOKIE, FORCE_HTTPS, TRUSTED_PROXIES), build-dist.sh aggiornato con passo diagnose
|
||||
|
||||
## Funzionalità Implementate
|
||||
@@ -751,3 +765,328 @@ Aggiunte variabili mancanti:
|
||||
### Verifica
|
||||
- `php -l diagnose.php`: OK
|
||||
- `php diagnose.php`: eseguito con 58/68 check superati su server locale
|
||||
|
||||
## 2026-06-19 — Fix 419: Duplicated middleware + session env vars + diagnose.php fixes
|
||||
|
||||
### Problema
|
||||
`POST /login` restituiva **419 Page Expired** su HTTP. La causa era doppia:
|
||||
|
||||
1. **Critico — Middleware duplicato in `bootstrap/app.php`**: `EncryptCookies`, `AddQueuedCookiesToResponse`, `StartSession`, `ShareErrorsFromSession` erano appesi al web group via `$middleware->web(append: [...])`. Questi middleware sono **già attivi di default** in Laravel 13. Appenderli una seconda volta causava doppia crittografia/decrittografia dei cookie di sessione → session ID corrotto → token CSRF non riconosciuto → 419.
|
||||
2. **`SESSION_SECURE_COOKIE` non impostato** nel `.env` — con `APP_URL=https` + `FORCE_HTTPS=true`, Laravel impostava il flag `Secure` sul cookie di sessione. Poiché il server serviva solo HTTP, il browser non inviava il cookie → session persa → 419.
|
||||
|
||||
### Fix apportati
|
||||
|
||||
**`bootstrap/app.php`** (righe 22-27):
|
||||
- Rimosso l'intero blocco `$middleware->web(append: [...])` che duplicava EncryptCookies, AddQueuedCookiesToResponse, StartSession, ShareErrorsFromSession.
|
||||
- Sostituito con commento esplicativo per prevenire future reintroduzioni.
|
||||
|
||||
**`.env`**:
|
||||
- Aggiunto `SESSION_SECURE_COOKIE=false` — impedisce il flag Secure su HTTP
|
||||
- Aggiunto `SESSION_SAME_SITE=lax` — esplicito per consistenza cross-protocol
|
||||
- Cambiato `APP_URL` da HTTPS a HTTP (ambiente locale senza HTTPS)
|
||||
- Cambiato `APP_PROTOCOL` da `https` a `http`
|
||||
- Cambiato `FORCE_HTTPS` da `true` a `false`
|
||||
- Cambiato `TRUSTED_PROXIES` da `*` a CIDR espliciti
|
||||
|
||||
**`diagnose.php`** — 4 bug fix:
|
||||
1. **`unlink()` senza `file_exists()`** nelle linee 863 e 900: causava PHP Warning quando curl non creava il cookie jar (es. HTTPS non raggiungibile). Aggiunto `file_exists($cookieJar) && unlink($cookieJar)`.
|
||||
2. **Regex middleware duplicato non matchava array multi-riga**: il pattern `[^\]]*` non matcha newline. Sostituito con `(.+?)` con flag `s`. Classe name extraction semplificata a `([a-zA-Z]+)::class`.
|
||||
3. **`CURLOPT_COOKIEJAR` non funzionante** in questo ambiente (curl 8.14.1 + PHP 8.4 — il file non viene creato da `curl_close()`). Riscritto `testCsrfLogin()` per parsare manualmente gli header `Set-Cookie` dalla prima risposta e reinviarli via `CURLOPT_COOKIE` nella POST. Eliminata dipendenza da `COOKIEJAR`/`COOKIEFILE`.
|
||||
4. Aggiunto verbose debug per codici HTTP POST anomali (≠ 302/200).
|
||||
|
||||
### Risultato finale
|
||||
```
|
||||
✅ Passed: 63
|
||||
⚠️ Warnings: 8 (upload/post_max_size, engine opzionali mancanti)
|
||||
❌ Failed: 1 (spazio disco 94.6% — server admin)
|
||||
```
|
||||
- Test CSRF: ✅ POST login → HTTP 302 (nessun 419)
|
||||
- Solo il disco (94.6%) è critico — non risolvibile via script.
|
||||
|
||||
### File modificati
|
||||
- `bootstrap/app.php` — rimosso middleware web duplicato
|
||||
- `.env` — aggiunte SESSION_SECURE_COOKIE, SESSION_SAME_SITE; APP_URL/APP_PROTOCOL/FORCE_HTTPS adeguati
|
||||
- `diagnose.php` — fix unlink warning, regex middleware multi-line, CSRF test manual-cookie, verbose debug
|
||||
|
||||
## 2026-06-19 — diagnose.php: storage false positivo + ForceHttps registration check
|
||||
|
||||
### Fix 1 — Storage permission false positive
|
||||
**Problema**: `diagnose.php` segnava 15 storage directory come ❌ NON SCRIVIBILE quando `php diagnose.php` veniva eseguito da CLI (utente `opencode`). Le directory sono di proprietà `www-data:www-data` con permessi `775`, quindi il web server **può** scrivere — il falso positivo generava confusione.
|
||||
|
||||
**Diagnosi**:
|
||||
- CLI esegue come `opencode` → `is_writable()` controlla i permessi "other" (5 = r-x) → `false`
|
||||
- Web server esegue come `www-data` → owner `rwx` → `true`
|
||||
- Questo è normale su server condivisi/Debian standard
|
||||
|
||||
**Fix** (`diagnose.php:659-660`):
|
||||
- Aggiunto `elseif ($owner === WEB_USER && in_array($perms, ['775', '777', '755', '770']))`
|
||||
- Se il proprietario è `www-data` e permessi sono `775`/`777`/`755`/`770`, mostra ⚠️ **warning** invece di ❌ **fail**, con messaggio "NON scrivibile da CLI ma WEB server sì"
|
||||
- Altrimenti mostra fail come prima
|
||||
|
||||
### Fix 2 — ForceHttps registration check
|
||||
**Problema**: Il controllo ForceHttps usava `str_contains($appContent, 'ForceHttps')` che matchava anche il semplice `use App\Http\Middleware\ForceHttps` (import), segnando ✅ "registrato" anche quando il middleware non era attivo in alcun gruppo.
|
||||
|
||||
**Stato attuale**: `ForceHttps::class` è **importato** ma **non registrato** in nessun middleware group (`$middleware->append()`, `->web()`, ecc.) in `bootstrap/app.php`.
|
||||
|
||||
**Fix** (`diagnose.php:871-879`):
|
||||
- Sostituito con `preg_match('/\$middleware\s*->\s*\w+\s*\(.*?ForceHttps::class/s', $appContent)`
|
||||
- 3 stati:
|
||||
1. ✅ `"registrato correttamente"` — se ForceHttps::class è argomento di una chiamata `$middleware->xxx()`
|
||||
2. ⚠️ `"IMPORTATO ma NON registrato"` — se la stringa ForceHttps esiste (use import) ma non in una chiamata
|
||||
3. ⚠️ `"NON presente"` — se ForceHttps non esiste nel file
|
||||
|
||||
### Verifica
|
||||
- `php -l diagnose.php`: No syntax errors detected
|
||||
- `php diagnose.php` su locale: ForceHttps ora segnala ⚠️ "IMPORTATO ma NON registrato" (corretto)
|
||||
|
||||
### File modificati
|
||||
- `diagnose.php` — storage false positive + ForceHttps registration check
|
||||
|
||||
## 2026-06-22 — build-dist.sh: genera post-deploy.sh per upgrade/install automatico
|
||||
|
||||
**Obiettivo**: Includere nell'archivio di distribuzione uno script `post-deploy.sh` che esegue controllo e setup dell'installazione sul server target, sia per fresh install che per upgrade.
|
||||
|
||||
### Cosa è stato fatto
|
||||
|
||||
**`build-dist.sh`** riscritto:
|
||||
- Prima del `tar`, genera `post-deploy.sh` (heredoc) e lo include nell'archivio
|
||||
- Dopo il `tar`, `post-deploy.sh` viene rimosso localmente (non sporca la working dir)
|
||||
- Istruzioni finali aggiornate: mostrano i tre comandi principali
|
||||
|
||||
**`post-deploy.sh`** (generato, non versionato):
|
||||
Tre modalità d'uso:
|
||||
|
||||
| Comando | Cosa fa |
|
||||
|---------|---------|
|
||||
| `bash post-deploy.sh check` | Diagnostica stato installazione: migration pendenti, permessi directory, symlink, cache. Nessuna modifica. |
|
||||
| `bash post-deploy.sh upgrade` | Check + upgrade esistente: crea directory, permessi, symlink, `migrate --force`, `key:generate` (se mancante), cache clear, verifica finale. Chiede conferma prima di eseguire. |
|
||||
| `bash post-deploy.sh install` | Fresh install completo: come upgrade + copia `.env.example → .env` con pausa per modifica utente. |
|
||||
| `bash post-deploy.sh upgrade --yes` | Batch mode (nessuna conferma richiesta) |
|
||||
|
||||
Fasi upgrade (8 step): directory → .gitignore → permessi → symlink → migrazioni → key → cache → diagnose
|
||||
Fasi install (9 step): come upgrade + setup .env con pausa interattiva
|
||||
|
||||
### Vantaggi
|
||||
- **Idempotente**: `migrate --force` esegue solo migration pendenti
|
||||
- **Sicuro**: modalità `check` non modifica nulla
|
||||
- **Interattivo**: chiede conferma prima di azioni distruttive
|
||||
- **Batch**: `--yes` per automazione CI/CD
|
||||
- **Compatibile**: integra `diagnose.php` già esistente (check + fix)
|
||||
- **Autopulente**: `post-deploy.sh` non resta nella working dir di build
|
||||
|
||||
### Verifica
|
||||
- `bash -n build-dist.sh`: syntax OK
|
||||
- `bash -n post-deploy.sh`: syntax OK
|
||||
- `bash post-deploy.sh check`: eseguito correttamente
|
||||
- `tar tzf glastree-*.tar.gz | grep post-deploy`: presente nell'archivio
|
||||
- `rm -f post-deploy.sh` dopo tar: pulizia OK
|
||||
|
||||
## 2026-06-22 — Estensione Gruppi: contatti propri + diocesi multiple
|
||||
|
||||
**Obiettivo**: Aggiungere contatti (email, telefono) ai gruppi e supportare assegnazione di più diocesi.
|
||||
|
||||
### Cosa è stato fatto
|
||||
|
||||
**Migration** (2 nuove tabelle):
|
||||
- `2026_06_22_000002_create_gruppo_contatti_table.php` — `gruppo_contatti` con: id, gruppo_id (FK cascade), tipo, valore, etichetta, is_primary, timestamps
|
||||
- `2026_06_22_000003_create_diocesi_gruppo_table.php` — pivot `diocesi_gruppo` con: id, gruppo_id (FK cascade), diocesi_id (FK cascade), unique(gruppo_id, diocesi_id)
|
||||
|
||||
**Model `GruppoContatto.php`** (nuovo):
|
||||
- `$table = 'gruppo_contatti'`, fillable: gruppo_id, tipo, valore, etichetta, is_primary
|
||||
- `gruppo()`: BelongsTo Gruppo
|
||||
|
||||
**Model `Gruppo.php`**:
|
||||
- `diocesi()`: cambiata da `BelongsTo` a `BelongsToMany` via `diocesi_gruppo`
|
||||
- `gruppoContatti()`: nuova `HasMany` relation
|
||||
- Accessor `getEmailPrimariaAttribute()`: primo contatto email (is_primary preferito)
|
||||
- Accessor `getTelefonoPrimarioAttribute()`: primo contatto telefono/cellulare (is_primary preferito)
|
||||
- `$appends`: già presente `email_primaria`, `telefono_primario` (invariato)
|
||||
- `$fillable`: `diocesi_id` mantenuto (nullable) per backward compat con import CSV
|
||||
|
||||
**Model `Diocesi.php`**:
|
||||
- `gruppi()`: cambiata da `HasMany` a `BelongsToMany` via `diocesi_gruppo`
|
||||
|
||||
**`GruppoController.php`**:
|
||||
- `store()`/`update()`: validazione cambiata da `diocesi_id` (nullable|exists) a `diocesi_ids` (nullable|array|exists)
|
||||
- `store()`/`update()`: nuova validazione `contatti` array con tipo (in:email,telefono,cellulare), valore, etichetta, is_primary
|
||||
- `store()`: crea contatti dopo create; sync diocesi
|
||||
- `update()`: cancella+ricrea contatti se presenti; sync diocesi (o detach se assenti)
|
||||
- `edit()`: eager-load `gruppoContatti`, passa `$selectedDiocesiIds`
|
||||
- `show()`: eager-load `gruppoContatti`
|
||||
- `index()`: eager-load `gruppoContatti`
|
||||
- `create()`: invariato (passa già `$diocesi`)
|
||||
|
||||
**`ReportController.php`**:
|
||||
- 4 occorrenze `$g->diocesi?->nome` → collection pattern `$g->diocesi->count() > 0 ? $g->diocesi->pluck('nome')->implode(', ') : '-'`
|
||||
|
||||
**View `gruppi/create.blade.php`**:
|
||||
- Select diocesi: cambiato da single `select name="diocesi_id"` a multiple `select name="diocesi_ids[]" class="select2-multi"`
|
||||
- Aggiunta card "Contatti del Gruppo" con tabella inline (tipo select, valore input, etichetta, checkbox primario, elimina)
|
||||
- Select2 CSS/JS caricati via CDN
|
||||
|
||||
**View `gruppi/edit.blade.php`**:
|
||||
- Stessa modifica diocesi → Select2 multi con `$selectedDiocesiIds` pre-selezionati
|
||||
- Aggiunta card "Contatti del Gruppo" con righe pre-popolate da `$gruppo->gruppoContatti`
|
||||
- Select2 CSS/JS caricati via CDN
|
||||
|
||||
**View `gruppi/show.blade.php`**:
|
||||
- Diocesi: da `$gruppo->diocesi?->nome` a collection implode
|
||||
- Aggiunte righe Email/Telefono (da accessor) e "Altri Contatti" nella card info
|
||||
|
||||
**View `gruppi/index.blade.php`**:
|
||||
- Diocesi: da `$gruppo->diocesi?->nome` a collection implode
|
||||
- Telefono/Email: da hardcoded `-` a `$gruppo->telefono_primario` / `$gruppo->email_primaria`
|
||||
|
||||
**View `gruppi/partials/tree-item.blade.php`**:
|
||||
- Diocesi: da `$gruppo->diocesi?->nome` a collection implode
|
||||
|
||||
**View `individui/show.blade.php` + `individui/edit.blade.php`**:
|
||||
- Diocesi nei gruppi dell'individuo: da `$gruppo->diocesi?->nome` a collection implode
|
||||
|
||||
### Backward compatibility
|
||||
- Colonna `diocesi_id` su `gruppi` mantenuta (nullable) per import CSV
|
||||
- CSV import (`importStore()`) usa ancora `diocesi_id` → singola diocesi
|
||||
- `$fillable` include ancora `diocesi_id`
|
||||
- Nessuna modifica a `Individuo` o `Contatto` esistenti
|
||||
|
||||
### Verifica
|
||||
- `php -l` su tutti i file modificati: OK
|
||||
- Migrations eseguite: ✅
|
||||
- Relazioni verificate: `diocesi()` BelongsToMany, `gruppoContatti()` HasMany, `email_primaria`/`telefono_primario` accessors funzionanti
|
||||
|
||||
## 2026-06-22 — Fix produzione: 500 pagina gruppi (migration pending + autoloader stale)
|
||||
|
||||
**Problema**: Dopo deploy su server produzione (`192.168.222.177`):
|
||||
- Migration `diocesi_gruppo` non eseguita (PENDING)
|
||||
- `GruppoContatto.php` non nell'autoloader (`composer dump-autoload` non eseguito)
|
||||
- `storage/logs/laravel.log` non scrivibile da www-data
|
||||
- 3 migration pre-esistenti non marcate come "ran" (google_oauth_connections, add_auth_method, add_google_id) — tabelle/colonne già esistenti ma migration record mancanti → bloccavano `migrate --force`
|
||||
|
||||
**Fix**:
|
||||
1. `sudo chmod -R 775 storage bootstrap/cache` — permessi
|
||||
2. `sudo usermod -a -G www-data opencode` — utente CLI nel gruppo www-data
|
||||
3. Inseriti manualmente 3 migration record mancanti in `migrations` table (già eseguite in passato ma mai registrate)
|
||||
4. `php artisan migrate --force` — eseguita `2026_06_22_000003_create_diocesi_gruppo_table`
|
||||
5. `composer dump-autoload` — autoloader rigenerato (41464 classi)
|
||||
6. `php artisan view:clear && php artisan route:clear && php artisan config:clear`
|
||||
|
||||
**Risultato**:
|
||||
- `class_exists(App\Models\GruppoContatto)` → ✅
|
||||
- `gruppi` page HTTP 200 (after login redirect 302) — 500 risolto
|
||||
- Tutte le migration marked as Ran (batch 40-42)
|
||||
- Views/routes/config cache pulite
|
||||
|
||||
**Lezione**: `build-dist.sh` fa `composer install --no-scripts` che non rigenera autoloader ottimizzato per il target. `post-deploy.sh upgrade` dovrebbe includere `composer dump-autoload` (o almeno `composer install --no-dev --optimize-autoloader`).
|
||||
|
||||
## 2026-06-22 — Fix: Select2 search field visibile sotto le diocesi selezionate
|
||||
|
||||
**Problema**: In create/edit gruppi, il `<textarea class="select2-search__field">` creato da Select2 in modalità multiple era visibile sotto i tag delle diocesi selezionate, con altezza 65px (ereditata da Bootstrap form-control).
|
||||
|
||||
**Causa**:
|
||||
1. `allowClear: true` in Select2 multiple mode non serve (ogni tag ha già la X per rimuoverlo) e causa conflitti di rendering
|
||||
2. Bootstrap 4 applica `height: 65px` al textarea del search field
|
||||
3. Nessun CSS specifico per normalizzare il search field inline
|
||||
|
||||
**Fix**:
|
||||
1. Rimosso `allowClear: true` da entrambi i file (`create.blade.php`, `edit.blade.php`)
|
||||
2. Aggiunto CSS per normalizzare `height: 28px`, `border: none`, `background: transparent`, `width: auto` con `min-width: 30px`
|
||||
|
||||
**File modificati**:
|
||||
- `resources/views/gruppi/create.blade.php` — rimosso allowClear, aggiunto CSS search field
|
||||
- `resources/views/gruppi/edit.blade.php` — rimosso allowClear, aggiunto CSS search field
|
||||
|
||||
## 2026-06-23 — Fix: Select2 counter rimosso (utente vuole vedere tutti i nomi)
|
||||
|
||||
**Problema**: Il `templateSelection` con counter mostrava "5 diocesi selezionate" invece dei nomi.
|
||||
|
||||
**Fix**: Rimosso l'intero blocco `templateSelection` da entrambi create/edit. Ogni diocesi selezionata mostra ora il proprio nome.
|
||||
|
||||
**File modificati**:
|
||||
- `resources/views/gruppi/create.blade.php` — rimosso templateSelection
|
||||
- `resources/views/gruppi/edit.blade.php` — rimosso templateSelection
|
||||
|
||||
## 2026-06-23 — Fix: DiocesiSeeder + build-dist.sh per deploy
|
||||
|
||||
**Problema**: La tabella `diocesi` con 225 record importati da ODS non veniva popolata sul server target dopo deploy. Il seeder `DiocesiSeeder.php` aveva solo ~100 nomi obsoleti/inaccurati (es. "Mongolia", "Donegal", "Tirana").
|
||||
|
||||
**Fix**:
|
||||
1. **`DiocesiSeeder.php`** riscritto completamente:
|
||||
- 225 nomi corretti (Arcidiocesi/Diocesi/Sede/Patriarcato/Abbazia/Eparachia)
|
||||
- Encoding: 7 nomi con mojibake da ODS fixati via `where('id', ...)->update()` (Trinità, Cefalù, Città, Forlì, Mondovì, Nardò, Perugia-Città)
|
||||
- Apostrofo: "Val d Elsa" → "Val d'Elsa"
|
||||
- `declare(strict_types=1)` aggiunto
|
||||
- `firstOrCreate` per idempotenza
|
||||
2. **`build-dist.sh`** (`post-deploy.sh` generato):
|
||||
- Upgrade: aggiunto step `[6/10] Seed diocesi` dopo migrazioni (rinumerati da [1-5/9] → [1-10/10])
|
||||
- Install: aggiunto step `[9/11] Seed diocesi` dopo migrazioni (rinumerato da [1-8/10] → [1-11/11])
|
||||
- Usa `--force` per bypassare conferma in produzione
|
||||
|
||||
**File modificati**:
|
||||
- `database/seeders/DiocesiSeeder.php` — riscritto con 225 nomi + fix encoding
|
||||
- `build-dist.sh` — aggiunto step seed diocesi in upgrade/install
|
||||
|
||||
## 2026-06-23 — Fix 419: SESSION_DRIVER=database + diagnose CSRF su IP locale
|
||||
|
||||
**Problema**: Accesso via IP di rete locale (`http://192.168.222.174`) poteva causare 419 Page Expired. Sessione su file vulnerabile a permessi/LOCK del filesystem.
|
||||
|
||||
**Fix**:
|
||||
1. **`SESSION_DRIVER=database`**: cambiato da `file` a `database`. La sessione su DB non soffre di:
|
||||
- Permessi filesystem errati
|
||||
- Lock concorrente su file
|
||||
- Pulizia sessioni scadute lottery-based
|
||||
2. **Migration `2026_06_23_000001_create_sessions_table.php`**: con guard `Schema::hasTable` per idempotenza cross-DB
|
||||
3. **`diagnose.php`**: aggiunto test CSRF su IP locale automatico. Rileva `hostname -I` ed esegue POST login → verifica 419 su `http://<lan-ip>/login`
|
||||
4. **`.env` e `.env.example`**: `SESSION_DRIVER=file` → `SESSION_DRIVER=database`
|
||||
5. Vecchi file di sessione in `storage/framework/sessions/` eliminati
|
||||
|
||||
**Risultato**:
|
||||
- diagnose.php: `✅ HTTP: POST login → HTTP 302 (nessun 419)`
|
||||
- diagnose.php: `✅ LOCAL IP: POST login → HTTP 302 (nessun 419)`
|
||||
- Sessioni persistono in tabella `sessions` (DB) invece di file
|
||||
|
||||
**File modificati**:
|
||||
- `database/migrations/2026_06_23_000001_create_sessions_table.php` (nuovo)
|
||||
- `.env` — SESSION_DRIVER=database
|
||||
- `.env.example` — SESSION_DRIVER=database
|
||||
- `diagnose.php` — test CSRF su IP locale
|
||||
|
||||
## 2026-06-23 — Email compose: firma dopo corpo + Mailing list: mittente e firma
|
||||
|
||||
**Obiettivo**: Spostare la select firma dopo il corpo email nella pagina di composizione email. Aggiungere mittente predefinito (`sender_account_id`) alle mailing list (salvato in creazione/edit, usato come fallback all'invio).
|
||||
|
||||
### Cosa è stato fatto
|
||||
|
||||
**Migration** (`2026_06_23_000002_add_sender_account_id_to_mailing_lists_table.php`):
|
||||
- Aggiunta colonna `sender_account_id` (FK → sender_accounts, nullOnDelete) a `mailing_lists`
|
||||
|
||||
**Model `MailingList.php`**:
|
||||
- `sender_account_id` aggiunto a `$fillable`
|
||||
- `senderAccount()`: nuova relazione BelongsTo → `SenderAccount`
|
||||
|
||||
**Controller `MailingListController.php`**:
|
||||
- `create()`: passa `$senderAccounts` (SenderAccount::active()->get()) alla view
|
||||
- `edit()`: passa `$senderAccounts` alla view, eager-load `senderAccount`
|
||||
- `store()`: validazione `sender_account_id` nullable|exists, salvato in create
|
||||
- `update()`: validazione `sender_account_id` nullable|exists, salvato in update
|
||||
|
||||
**View `mailing-liste/create.blade.php`**:
|
||||
- Aggiunto select per `sender_account_id` dopo il select firma
|
||||
|
||||
**View `mailing-liste/edit.blade.php`**:
|
||||
- Aggiunto select per `sender_account_id` dopo il select firma, con `selected` se uguale a `$mailingList->sender_account_id`
|
||||
|
||||
**View `email/compose.blade.php`**:
|
||||
- Spostato blocco firma (prima del body) → dopo il textarea body (prima della sezione allegati)
|
||||
|
||||
**Controller `MailingController.php`**:
|
||||
- `invia()`: fallback a `$lista->senderAccount` se `mittente_id` non fornito esplicitamente (stesso pattern del firma_id fallback già esistente)
|
||||
- `invioElabora()`: fallback a `mittente_id`/`firma_id` dalla mailing list se non forniti esplicitamente (solo quando una singola lista è selezionata)
|
||||
|
||||
**File modificati**:
|
||||
- `database/migrations/2026_06_23_000002_add_sender_account_id_to_mailing_lists_table.php` (nuovo)
|
||||
- `app/Models/MailingList.php` — fillable + senderAccount relazione
|
||||
- `app/Http/Controllers/MailingListController.php` — sender_account in create/edit/store/update
|
||||
- `app/Http/Controllers/MailingController.php` — fallback mittente/firma da mailing list
|
||||
- `resources/views/email/compose.blade.php` — firma spostata dopo il body
|
||||
- `resources/views/mailing-liste/create.blade.php` — select mittente aggiunto
|
||||
- `resources/views/mailing-liste/edit.blade.php` — select mittente aggiunto
|
||||
|
||||
@@ -28,16 +28,17 @@ class BackupRunCommand extends Command
|
||||
{
|
||||
$this->info('Avvio backup...');
|
||||
|
||||
$options = [];
|
||||
if ($this->option('no-files')) {
|
||||
$this->backupService->saveConfig(['backup_include_files' => false]);
|
||||
$options['include_files'] = false;
|
||||
$this->warn('Files esclusi dal backup.');
|
||||
}
|
||||
if ($this->option('no-env')) {
|
||||
$this->backupService->saveConfig(['backup_include_env' => false]);
|
||||
$options['include_env'] = false;
|
||||
$this->warn('.env escluso dal backup.');
|
||||
}
|
||||
|
||||
$result = $this->backupService->run();
|
||||
$result = $this->backupService->run($options);
|
||||
|
||||
if ($result['success']) {
|
||||
$this->info(' Backup completato con successo!');
|
||||
@@ -45,6 +46,13 @@ class BackupRunCommand extends Command
|
||||
$this->line('Dimensione: ' . $result['size_formatted']);
|
||||
$this->line('Percorso: ' . storage_path('app/backups/' . $result['filename']));
|
||||
|
||||
if (!empty($result['steps'])) {
|
||||
$this->line('Contenuto:');
|
||||
foreach ($result['steps'] as $step) {
|
||||
$this->line(' - ' . $step);
|
||||
}
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,9 @@ class BackupController extends Controller
|
||||
$result = $this->backupService->run();
|
||||
|
||||
if ($result['success']) {
|
||||
$steps = !empty($result['steps']) ? ' — ' . implode(', ', $result['steps']) : '';
|
||||
return redirect()->route('admin.backup.index')
|
||||
->with('success', 'Backup completato: ' . ($result['filename'] ?? '') . ' (' . ($result['size_formatted'] ?? '') . ')');
|
||||
->with('success', 'Backup completato: ' . ($result['filename'] ?? '') . ' (' . ($result['size_formatted'] ?? '') . ')' . $steps);
|
||||
}
|
||||
|
||||
return redirect()->route('admin.backup.index')
|
||||
|
||||
@@ -7,12 +7,15 @@ use App\Http\Controllers\Controller;
|
||||
use App\Models\EmailAttachment;
|
||||
use App\Models\EmailFolder;
|
||||
use App\Models\EmailSetting;
|
||||
use App\Models\Firma;
|
||||
use App\Models\SenderAccount;
|
||||
use App\Services\GoogleOAuthService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\Mime\Email;
|
||||
|
||||
class EmailSettingsController extends Controller
|
||||
{
|
||||
@@ -22,7 +25,8 @@ class EmailSettingsController extends Controller
|
||||
$settings = EmailSetting::first() ?? new EmailSetting();
|
||||
$senderAccounts = SenderAccount::orderBy('email_address')->get();
|
||||
$googleStatus = app(GoogleOAuthService::class)->getConnectionStatus();
|
||||
return view('admin.email-settings.index', compact('settings', 'senderAccounts', 'googleStatus'));
|
||||
$firme = $settings->exists ? $settings->firme : collect();
|
||||
return view('admin.email-settings.index', compact('settings', 'senderAccounts', 'googleStatus', 'firme'));
|
||||
}
|
||||
|
||||
public function save(Request $request)
|
||||
@@ -41,6 +45,8 @@ class EmailSettingsController extends Controller
|
||||
'smtp_password' => 'nullable|string',
|
||||
'email_address' => 'required|email',
|
||||
'email_name' => 'nullable|string|max:255',
|
||||
'from_email' => 'nullable|email|max:255',
|
||||
'from_name' => 'nullable|string|max:255',
|
||||
'reply_to' => 'nullable|email',
|
||||
'sync_interval_minutes' => 'nullable|integer|min:1|max:60',
|
||||
'is_active' => 'boolean',
|
||||
@@ -128,25 +134,20 @@ class EmailSettingsController extends Controller
|
||||
return response()->json(['success' => false, 'message' => 'Impossibile creare il mailer SMTP. Verifica la configurazione.']);
|
||||
}
|
||||
|
||||
$fromName = $settings->email_name ?? '';
|
||||
if (filter_var($fromName, FILTER_VALIDATE_EMAIL)) {
|
||||
$fromName = 'Glastree';
|
||||
} else {
|
||||
$fromName = preg_replace('/[^\p{L}\p{N}\s]/u', '', $fromName);
|
||||
$fromName = trim($fromName);
|
||||
if (empty($fromName) || strlen($fromName) > 50) {
|
||||
$fromName = 'Glastree';
|
||||
}
|
||||
}
|
||||
$smtpUser = $settings->smtp_username ?: $settings->email_address;
|
||||
$fromName = $settings->getEffectiveFromName();
|
||||
$replyToEmail = $settings->from_email ?: $smtpUser;
|
||||
|
||||
$fromAddress = \Symfony\Component\Mime\Address::create($settings->email_address, $fromName);
|
||||
|
||||
$email = (new \Symfony\Component\Mime\Email())
|
||||
->from($fromAddress)
|
||||
$email = (new Email())
|
||||
->from(Address::create($smtpUser, $fromName))
|
||||
->to($request->email)
|
||||
->subject('Test Configurazione SMTP - Glastree')
|
||||
->text('Test email da Glastree - Configurazione SMTP funziona!');
|
||||
|
||||
if ($replyToEmail !== $smtpUser) {
|
||||
$email->replyTo(Address::create($replyToEmail, $fromName));
|
||||
}
|
||||
|
||||
$mailer->send($email);
|
||||
|
||||
\Illuminate\Support\Facades\Log::info('Test SMTP ok', ['to' => $request->email, 'from' => $settings->email_address]);
|
||||
@@ -294,4 +295,66 @@ class EmailSettingsController extends Controller
|
||||
return response()->json(['success' => false, 'message' => 'Errore invio: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function firmaStore(Request $request)
|
||||
{
|
||||
$this->authorizeWrite('settings');
|
||||
$data = $request->validate([
|
||||
'nome' => 'required|string|max:255',
|
||||
'contenuto_html' => 'nullable|string',
|
||||
'is_default' => 'boolean',
|
||||
]);
|
||||
|
||||
$settings = EmailSetting::getActive();
|
||||
if (!$settings) {
|
||||
return redirect('/impostazioni/email')->with('error', 'Account email non configurato.');
|
||||
}
|
||||
|
||||
$firma = $settings->firme()->create($data);
|
||||
|
||||
if ($data['is_default'] ?? false) {
|
||||
$settings->firme()->where('id', '!=', $firma->id)->update(['is_default' => false]);
|
||||
}
|
||||
|
||||
return redirect('/impostazioni/email#firme')->with('success', 'Firma "' . $firma->nome . '" creata.');
|
||||
}
|
||||
|
||||
public function firmaUpdate(Request $request, int $id)
|
||||
{
|
||||
$this->authorizeWrite('settings');
|
||||
$data = $request->validate([
|
||||
'nome' => 'required|string|max:255',
|
||||
'contenuto_html' => 'nullable|string',
|
||||
'is_default' => 'boolean',
|
||||
]);
|
||||
|
||||
$firma = Firma::findOrFail($id);
|
||||
$firma->update($data);
|
||||
|
||||
if ($data['is_default'] ?? false) {
|
||||
Firma::where('email_setting_id', $firma->email_setting_id)
|
||||
->where('id', '!=', $firma->id)
|
||||
->update(['is_default' => false]);
|
||||
}
|
||||
|
||||
return redirect('/impostazioni/email#firme')->with('success', 'Firma "' . $firma->nome . '" aggiornata.');
|
||||
}
|
||||
|
||||
public function firmaSetDefault(int $id)
|
||||
{
|
||||
$this->authorizeWrite('settings');
|
||||
$firma = Firma::findOrFail($id);
|
||||
Firma::where('email_setting_id', $firma->email_setting_id)->update(['is_default' => false]);
|
||||
$firma->update(['is_default' => true]);
|
||||
return redirect('/impostazioni/email#firme')->with('success', 'Firma "' . $firma->nome . '" impostata come predefinita.');
|
||||
}
|
||||
|
||||
public function firmaDestroy(int $id)
|
||||
{
|
||||
$this->authorizeWrite('settings');
|
||||
$firma = Firma::findOrFail($id);
|
||||
$nome = $firma->nome;
|
||||
$firma->delete();
|
||||
return redirect('/impostazioni/email#firme')->with('success', 'Firma "' . $nome . '" eliminata.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Models\MailingList;
|
||||
use App\Models\Gruppo;
|
||||
use App\Models\Individuo;
|
||||
use App\Models\Documento;
|
||||
use App\Models\Firma;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -96,6 +97,9 @@ class EmailController extends Controller
|
||||
|
||||
$documenti = Documento::orderBy('nome_file')->get();
|
||||
|
||||
$settings = EmailSetting::getActive();
|
||||
$firme = $settings?->firme ?? collect();
|
||||
|
||||
$prefill = [];
|
||||
if ($request->reply_to) {
|
||||
$original = EmailMessage::find($request->reply_to);
|
||||
@@ -113,7 +117,7 @@ class EmailController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
return view('email.compose', compact('folders', 'mailingLists', 'gruppi', 'individui', 'documenti', 'prefill', 'senderAccounts'));
|
||||
return view('email.compose', compact('folders', 'mailingLists', 'gruppi', 'individui', 'documenti', 'prefill', 'senderAccounts', 'firme'));
|
||||
}
|
||||
|
||||
public function send(Request $request)
|
||||
@@ -128,6 +132,7 @@ class EmailController extends Controller
|
||||
$rules['to'] = 'required';
|
||||
}
|
||||
|
||||
$rules['firma_id'] = 'nullable|integer|exists:firme,id';
|
||||
$validated = $request->validate($rules);
|
||||
|
||||
$recipients = $this->resolveRecipients($request);
|
||||
@@ -159,9 +164,11 @@ class EmailController extends Controller
|
||||
$imapError = null;
|
||||
$attachments = $this->processAttachments($request);
|
||||
|
||||
$firmaId = $request->firma_id;
|
||||
|
||||
foreach ($recipients as $recipient) {
|
||||
try {
|
||||
$bodyWithSig = $this->appendSignature($validated['body'], $settings->getSignature(), $settings->signature_enabled ?? false);
|
||||
$bodyWithSig = $this->appendSignature($validated['body'], $firmaId, $settings);
|
||||
$this->sendViaImap($settings, $recipient, $validated['subject'], $bodyWithSig, $request->cc, $attachments);
|
||||
$imapSuccess = true;
|
||||
} catch (\Exception $e) {
|
||||
@@ -171,7 +178,7 @@ class EmailController extends Controller
|
||||
}
|
||||
|
||||
try {
|
||||
$bodyWithSig = $this->appendSignature($validated['body'], $settings->getSignature(), $settings->signature_enabled ?? false);
|
||||
$bodyWithSig = $this->appendSignature($validated['body'], $firmaId, $settings);
|
||||
$this->storeSentMessage($settings, $recipients, $validated['subject'], $bodyWithSig, $request->cc);
|
||||
} catch (\Exception $e) {
|
||||
\Illuminate\Support\Facades\Log::error('Store sent message error', ['error' => $e->getMessage()]);
|
||||
@@ -189,12 +196,16 @@ class EmailController extends Controller
|
||||
{
|
||||
$sender = SenderAccount::findOrFail($request->mittente_id);
|
||||
$attachmentPaths = $this->resolveAttachmentPaths($request);
|
||||
|
||||
$settings = EmailSetting::getActive();
|
||||
$body = $this->appendSignature($validated['body'], $request->firma_id, $settings);
|
||||
|
||||
$ok = 0;
|
||||
$errors = [];
|
||||
|
||||
foreach ($recipients as $recipient) {
|
||||
try {
|
||||
$sender->sendEmail($recipient, $validated['subject'], $validated['body'], $attachmentPaths);
|
||||
$sender->sendEmail($recipient, $validated['subject'], $body, $attachmentPaths);
|
||||
$ok++;
|
||||
} catch (\Exception $e) {
|
||||
$errors[] = $recipient . ': ' . $e->getMessage();
|
||||
@@ -237,8 +248,8 @@ class EmailController extends Controller
|
||||
'body_text' => $request->body,
|
||||
'to_email' => $request->to,
|
||||
'is_draft' => true,
|
||||
'from_email' => $settings->email_address ?? '',
|
||||
'from_name' => $settings->email_name ?? '',
|
||||
'from_email' => $settings->getEffectiveFromAddress(),
|
||||
'from_name' => $settings->getEffectiveFromName(),
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true, 'draft_id' => $message->id]);
|
||||
@@ -496,12 +507,20 @@ class EmailController extends Controller
|
||||
return array_filter(array_unique($recipients));
|
||||
}
|
||||
|
||||
private function appendSignature(string $body, ?string $signature, bool $enabled): string
|
||||
private function appendSignature(string $body, int|string|null $firmaId = null, ?EmailSetting $settings = null): string
|
||||
{
|
||||
if (empty($signature) || !$enabled) {
|
||||
return $body;
|
||||
if ($firmaId) {
|
||||
$firma = Firma::find($firmaId);
|
||||
if ($firma && $firma->contenuto_html) {
|
||||
return $body . "\n\n---\n" . $firma->contenuto_html;
|
||||
}
|
||||
}
|
||||
return $body . "\n\n---\n" . $signature;
|
||||
|
||||
if ($settings && $settings->signature && ($settings->signature_enabled ?? false)) {
|
||||
return $body . "\n\n---\n" . $settings->signature;
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
private function sendViaImap($settings, $to, $subject, $body, $cc = null, array $attachmentPaths = [])
|
||||
@@ -527,18 +546,10 @@ class EmailController extends Controller
|
||||
$transport = \Symfony\Component\Mailer\Transport::fromDsn($dsn);
|
||||
$mailer = new \Symfony\Component\Mailer\Mailer($transport);
|
||||
|
||||
$fromName = $settings->email_name ?? '';
|
||||
if (filter_var($fromName, FILTER_VALIDATE_EMAIL)) {
|
||||
$fromName = 'Glastree';
|
||||
} else {
|
||||
$fromName = preg_replace('/[^\p{L}\p{N}\s]/u', '', $fromName);
|
||||
$fromName = trim($fromName);
|
||||
if (empty($fromName)) {
|
||||
$fromName = 'Glastree';
|
||||
}
|
||||
}
|
||||
|
||||
$fromAddress = \Symfony\Component\Mime\Address::create($settings->email_address, $fromName);
|
||||
$fromAddress = \Symfony\Component\Mime\Address::create(
|
||||
$settings->getEffectiveFromAddress(),
|
||||
$settings->getEffectiveFromName()
|
||||
);
|
||||
|
||||
$email = (new \Symfony\Component\Mime\Email())
|
||||
->from($fromAddress)
|
||||
@@ -546,6 +557,11 @@ class EmailController extends Controller
|
||||
->subject($subject)
|
||||
->text($body);
|
||||
|
||||
$replyTo = $settings->getEffectiveReplyTo();
|
||||
if ($replyTo) {
|
||||
$email->replyTo($replyTo);
|
||||
}
|
||||
|
||||
if ($cc) {
|
||||
$email->cc($cc);
|
||||
}
|
||||
@@ -619,14 +635,14 @@ class EmailController extends Controller
|
||||
private function storeSentMessage($settings, $recipients, $subject, $body, $cc = null)
|
||||
{
|
||||
$sentFolder = EmailFolder::where('type', 'sent')->first();
|
||||
$bodyWithSignature = $this->appendSignature($body, $settings->getSignature(), $settings->signature_enabled ?? false);
|
||||
$bodyWithSignature = $body;
|
||||
|
||||
$message = EmailMessage::create([
|
||||
'email_folder_id' => $sentFolder->id,
|
||||
'subject' => $subject,
|
||||
'body_text' => $bodyWithSignature,
|
||||
'from_email' => $settings->email_address,
|
||||
'from_name' => $settings->email_name,
|
||||
'from_email' => $settings->getEffectiveFromAddress(),
|
||||
'from_name' => $settings->getEffectiveFromName(),
|
||||
'to_email' => implode(', ', $recipients),
|
||||
'cc' => $cc,
|
||||
'is_sent' => true,
|
||||
|
||||
@@ -66,7 +66,7 @@ class GruppoController extends Controller
|
||||
$entityType = 'gruppi';
|
||||
$columnWidths = $vista && $vista->colonne_larghezze ? $vista->colonne_larghezze : [];
|
||||
|
||||
$gruppiQuery = Gruppo::with(['diocesi', 'parent', 'children', 'individui', 'avatar', 'tags']);
|
||||
$gruppiQuery = Gruppo::with(['diocesi', 'gruppoContatti', 'parent', 'children', 'individui', 'avatar', 'tags']);
|
||||
|
||||
if (request()->filled('tag')) {
|
||||
$tagSlugs = (array) request()->input('tag');
|
||||
@@ -142,7 +142,7 @@ class GruppoController extends Controller
|
||||
public function create(Request $request)
|
||||
{
|
||||
$this->authorizeWrite('gruppi');
|
||||
$diocesi = Diocesi::orderBy('nome')->get();
|
||||
$diocesi = Diocesi::orderBy('id')->get();
|
||||
$allGruppi = Gruppo::orderBy('nome')->get();
|
||||
$selectedParent = $request->query('parent_id') ? Gruppo::find($request->query('parent_id')) : null;
|
||||
$individui = Individuo::orderBy('cognome')->orderBy('nome')->get();
|
||||
@@ -157,7 +157,8 @@ class GruppoController extends Controller
|
||||
'nome' => 'required|string|max:255',
|
||||
'descrizione' => 'nullable|string',
|
||||
'parent_id' => 'nullable|exists:gruppi,id',
|
||||
'diocesi_id' => 'nullable|exists:diocesi,id',
|
||||
'diocesi_ids' => 'nullable|array',
|
||||
'diocesi_ids.*' => 'exists:diocesi,id',
|
||||
'responsabile_ids' => 'nullable|array',
|
||||
'responsabile_ids.*' => 'exists:individui,id',
|
||||
'indirizzo_incontro' => 'nullable|string|max:500',
|
||||
@@ -165,6 +166,11 @@ class GruppoController extends Controller
|
||||
'città_incontro' => 'nullable|string|max:255',
|
||||
'sigla_provincia_incontro' => 'nullable|string|max:2',
|
||||
'mappa_posizione' => 'nullable|string',
|
||||
'contatti' => 'nullable|array',
|
||||
'contatti.*.tipo' => 'required|string|in:email,telefono,cellulare',
|
||||
'contatti.*.valore' => 'required|string|max:255',
|
||||
'contatti.*.etichetta' => 'nullable|string|max:255',
|
||||
'contatti.*.is_primary' => 'nullable|boolean',
|
||||
'tags' => 'nullable|array',
|
||||
'tags.*' => 'exists:tags,id',
|
||||
]);
|
||||
@@ -175,6 +181,21 @@ class GruppoController extends Controller
|
||||
|
||||
$gruppo = Gruppo::create($data);
|
||||
|
||||
if (!empty($data['diocesi_ids'])) {
|
||||
$gruppo->diocesi()->sync($data['diocesi_ids']);
|
||||
}
|
||||
|
||||
if (!empty($data['contatti'])) {
|
||||
foreach ($data['contatti'] as $contattoData) {
|
||||
$gruppo->gruppoContatti()->create([
|
||||
'tipo' => $contattoData['tipo'],
|
||||
'valore' => $contattoData['valore'],
|
||||
'etichetta' => $contattoData['etichetta'] ?? null,
|
||||
'is_primary' => $contattoData['is_primary'] ?? false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->has('tags')) {
|
||||
$gruppo->tags()->sync($request->tags);
|
||||
}
|
||||
@@ -196,7 +217,7 @@ class GruppoController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorizeRead('gruppi');
|
||||
$gruppo = Gruppo::with(['parent', 'children', 'diocesi', 'individui.contatti', 'individui.documenti', 'individui' => function ($q) {
|
||||
$gruppo = Gruppo::with(['parent', 'children', 'diocesi', 'gruppoContatti', 'individui.contatti', 'individui.documenti', 'individui' => function ($q) {
|
||||
$q->withPivot('ruolo_ids', 'ruolo_nel_gruppo', 'data_adesione');
|
||||
}, 'avatar', 'tags', 'eventi' => function ($q) {
|
||||
$q->where('is_incontro_gruppo', true);
|
||||
@@ -207,13 +228,14 @@ class GruppoController extends Controller
|
||||
public function edit($id)
|
||||
{
|
||||
$this->authorizeWrite('gruppi');
|
||||
$gruppo = Gruppo::with(['individui.contatti', 'individui.documenti', 'avatar', 'tags'])->findOrFail($id);
|
||||
$diocesi = Diocesi::orderBy('nome')->get();
|
||||
$gruppo = Gruppo::with(['individui.contatti', 'individui.documenti', 'gruppoContatti', 'avatar', 'tags'])->findOrFail($id);
|
||||
$diocesi = Diocesi::orderBy('id')->get();
|
||||
$gruppi = Gruppo::where('id', '!=', $gruppo->id)->orderBy('nome')->get();
|
||||
$membri = $gruppo->individui()->withPivot('ruolo_ids', 'ruolo_nel_gruppo', 'data_adesione')->orderBy('cognome')->orderBy('nome')->get();
|
||||
$tags = Tag::orderBy('name')->get();
|
||||
$selectedTags = $gruppo->tags->pluck('id')->toArray();
|
||||
return view('gruppi.edit', compact('gruppo', 'diocesi', 'gruppi', 'membri', 'tags', 'selectedTags'));
|
||||
$selectedDiocesiIds = $gruppo->diocesi()->pluck('diocesi.id')->toArray();
|
||||
return view('gruppi.edit', compact('gruppo', 'diocesi', 'gruppi', 'membri', 'tags', 'selectedTags', 'selectedDiocesiIds'));
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
@@ -224,7 +246,8 @@ class GruppoController extends Controller
|
||||
'nome' => 'required|string|max:255',
|
||||
'descrizione' => 'nullable|string',
|
||||
'parent_id' => 'nullable|exists:gruppi,id',
|
||||
'diocesi_id' => 'nullable|exists:diocesi,id',
|
||||
'diocesi_ids' => 'nullable|array',
|
||||
'diocesi_ids.*' => 'exists:diocesi,id',
|
||||
'responsabile_ids' => 'nullable|array',
|
||||
'responsabile_ids.*' => 'exists:individui,id',
|
||||
'indirizzo_incontro' => 'nullable|string|max:500',
|
||||
@@ -232,6 +255,11 @@ class GruppoController extends Controller
|
||||
'città_incontro' => 'nullable|string|max:255',
|
||||
'sigla_provincia_incontro' => 'nullable|string|max:2',
|
||||
'mappa_posizione' => 'nullable|string',
|
||||
'contatti' => 'nullable|array',
|
||||
'contatti.*.tipo' => 'required|string|in:email,telefono,cellulare',
|
||||
'contatti.*.valore' => 'required|string|max:255',
|
||||
'contatti.*.etichetta' => 'nullable|string|max:255',
|
||||
'contatti.*.is_primary' => 'nullable|boolean',
|
||||
'tags' => 'nullable|array',
|
||||
'tags.*' => 'exists:tags,id',
|
||||
]);
|
||||
@@ -244,6 +272,24 @@ class GruppoController extends Controller
|
||||
|
||||
$gruppo->update($data);
|
||||
|
||||
if ($request->has('diocesi_ids')) {
|
||||
$gruppo->diocesi()->sync($data['diocesi_ids'] ?? []);
|
||||
} else {
|
||||
$gruppo->diocesi()->detach();
|
||||
}
|
||||
|
||||
if ($request->has('contatti')) {
|
||||
$gruppo->gruppoContatti()->delete();
|
||||
foreach ($data['contatti'] as $contattoData) {
|
||||
$gruppo->gruppoContatti()->create([
|
||||
'tipo' => $contattoData['tipo'],
|
||||
'valore' => $contattoData['valore'],
|
||||
'etichetta' => $contattoData['etichetta'] ?? null,
|
||||
'is_primary' => $contattoData['is_primary'] ?? false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->has('tags')) {
|
||||
$gruppo->tags()->sync($request->tags);
|
||||
} else {
|
||||
|
||||
@@ -152,7 +152,7 @@ public function create()
|
||||
{
|
||||
$this->authorizeWrite('individui');
|
||||
$comuni = Comune::orderBy('nome')->get();
|
||||
$diocesi = Diocesi::orderBy('nome')->get();
|
||||
$diocesi = Diocesi::orderBy('id')->get();
|
||||
$tags = Tag::orderBy('name')->get();
|
||||
return view('individui.create', compact('comuni', 'diocesi', 'tags'));
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ use App\Models\MailingMessaggio;
|
||||
use App\Models\SenderAccount;
|
||||
use App\Models\Individuo;
|
||||
use App\Models\Documento;
|
||||
use App\Models\EmailSetting;
|
||||
use App\Models\Firma;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -30,6 +32,8 @@ class MailingController extends Controller
|
||||
$liste = MailingList::all();
|
||||
$documenti = Documento::orderBy('nome_file')->get();
|
||||
$senderAccounts = SenderAccount::active()->get();
|
||||
$settings = EmailSetting::getActive();
|
||||
$firme = $settings?->firme ?? collect();
|
||||
|
||||
$documentiSelezionati = collect();
|
||||
if ($request->has('documenti_selezionati')) {
|
||||
@@ -37,7 +41,7 @@ class MailingController extends Controller
|
||||
$documentiSelezionati = Documento::whereIn('id', $docIds)->get();
|
||||
}
|
||||
|
||||
return view('mailing.nuovo', compact('individui', 'liste', 'documenti', 'documentiSelezionati', 'senderAccounts'));
|
||||
return view('mailing.nuovo', compact('individui', 'liste', 'documenti', 'documentiSelezionati', 'senderAccounts', 'firme'));
|
||||
}
|
||||
|
||||
public function invia(Request $request)
|
||||
@@ -46,16 +50,22 @@ class MailingController extends Controller
|
||||
$data = $request->validate([
|
||||
'oggetto' => 'required|string|max:255',
|
||||
'corpo' => 'required',
|
||||
'lista_id' => 'nullable|exists:mailing_liste,id',
|
||||
'lista_id' => 'nullable|exists:mailing_lists,id',
|
||||
'destinatari_ids' => 'nullable|string',
|
||||
'documenti_selezionati' => 'nullable|string',
|
||||
'allegato' => 'nullable|file|max:10240',
|
||||
'mittente_id' => 'nullable|integer|exists:sender_accounts,id',
|
||||
'firma_id' => 'nullable|integer|exists:firme,id',
|
||||
]);
|
||||
|
||||
$sender = null;
|
||||
if (!empty($data['mittente_id'])) {
|
||||
$sender = SenderAccount::findOrFail($data['mittente_id']);
|
||||
} elseif (!empty($data['lista_id'])) {
|
||||
$lista = MailingList::with('senderAccount')->find($data['lista_id']);
|
||||
if ($lista && $lista->senderAccount) {
|
||||
$sender = $lista->senderAccount;
|
||||
}
|
||||
}
|
||||
|
||||
$documentiAllegati = [];
|
||||
@@ -112,6 +122,20 @@ class MailingController extends Controller
|
||||
|
||||
$attachmentPaths = $this->resolveMailingAttachmentPaths($documentiAllegati);
|
||||
|
||||
$firmaId = $data['firma_id'] ?? null;
|
||||
if (!$firmaId && !empty($data['lista_id'])) {
|
||||
$lista = MailingList::with('firma')->find($data['lista_id']);
|
||||
$firmaId = $lista?->firma?->id;
|
||||
}
|
||||
|
||||
$corpoConFirma = $data['corpo'];
|
||||
if ($firmaId) {
|
||||
$firma = Firma::find($firmaId);
|
||||
if ($firma && $firma->contenuto_html) {
|
||||
$corpoConFirma .= "\n\n---\n" . $firma->contenuto_html;
|
||||
}
|
||||
}
|
||||
|
||||
$messaggio = MailingMessaggio::create([
|
||||
'mailing_list_id' => $data['lista_id'] ?? null,
|
||||
'user_id' => auth()->id(),
|
||||
@@ -121,6 +145,7 @@ class MailingController extends Controller
|
||||
'totale_destinatari' => $emails->count(),
|
||||
'mittente_nome' => $sender?->email_name,
|
||||
'mittente_email' => $sender?->email_address,
|
||||
'firma_id' => $firmaId,
|
||||
]);
|
||||
|
||||
$ok = 0;
|
||||
@@ -130,9 +155,9 @@ class MailingController extends Controller
|
||||
foreach ($emails as $email) {
|
||||
try {
|
||||
if ($sender) {
|
||||
$sender->sendEmail($email, $data['oggetto'], $data['corpo'], $attachmentPaths);
|
||||
$sender->sendEmail($email, $data['oggetto'], $corpoConFirma, $attachmentPaths);
|
||||
} else {
|
||||
$this->sendViaSystem($email, $data['oggetto'], $data['corpo'], $attachmentPaths);
|
||||
$this->sendViaSystem($email, $data['oggetto'], $corpoConFirma, $attachmentPaths);
|
||||
}
|
||||
$ok++;
|
||||
} catch (\Throwable $e) {
|
||||
@@ -181,11 +206,13 @@ class MailingController extends Controller
|
||||
public function invio(Request $request)
|
||||
{
|
||||
$this->authorizeWrite('mailing');
|
||||
$liste = MailingList::where('attiva', true)->orderBy('nome')->get();
|
||||
$liste = MailingList::with('firma')->where('attiva', true)->orderBy('nome')->get();
|
||||
$documenti = Documento::orderBy('nome_file')->get();
|
||||
$senderAccounts = SenderAccount::active()->get();
|
||||
$settings = EmailSetting::getActive();
|
||||
$firme = $settings?->firme ?? collect();
|
||||
|
||||
return view('mailing.invio', compact('liste', 'documenti', 'senderAccounts'));
|
||||
return view('mailing.invio', compact('liste', 'documenti', 'senderAccounts', 'firme'));
|
||||
}
|
||||
|
||||
public function invioElabora(Request $request)
|
||||
@@ -193,18 +220,23 @@ class MailingController extends Controller
|
||||
$this->authorizeWrite('mailing');
|
||||
$data = $request->validate([
|
||||
'liste' => 'required|array',
|
||||
'liste.*' => 'exists:mailing_liste,id',
|
||||
'liste.*' => 'exists:mailing_lists,id',
|
||||
'oggetto' => 'required|string|max:255',
|
||||
'corpo' => 'required',
|
||||
'documenti_selezionati' => 'nullable|array',
|
||||
'documenti_selezionati.*' => 'exists:documenti,id',
|
||||
'allegato' => 'nullable|file|max:10240',
|
||||
'mittente_id' => 'nullable|integer|exists:sender_accounts,id',
|
||||
'firma_id' => 'nullable|integer|exists:firme,id',
|
||||
]);
|
||||
|
||||
$listeModels = MailingList::with('firma', 'senderAccount')->whereIn('id', $data['liste'])->get();
|
||||
|
||||
$sender = null;
|
||||
if (!empty($data['mittente_id'])) {
|
||||
$sender = SenderAccount::findOrFail($data['mittente_id']);
|
||||
} elseif ($listeModels->count() === 1) {
|
||||
$sender = $listeModels->first()->senderAccount;
|
||||
}
|
||||
|
||||
$documentiAllegati = [];
|
||||
@@ -254,7 +286,20 @@ class MailingController extends Controller
|
||||
|
||||
$attachmentPaths = $this->resolveMailingAttachmentPaths($documentiAllegati);
|
||||
|
||||
$listeNomi = MailingList::whereIn('id', $data['liste'])->pluck('nome')->implode(', ');
|
||||
$firmaId = $data['firma_id'] ?? null;
|
||||
if (!$firmaId && $listeModels->count() === 1) {
|
||||
$firmaId = $listeModels->first()->firma_id;
|
||||
}
|
||||
|
||||
$corpoConFirma = $data['corpo'];
|
||||
if ($firmaId) {
|
||||
$firma = Firma::find($firmaId);
|
||||
if ($firma && $firma->contenuto_html) {
|
||||
$corpoConFirma .= "\n\n---\n" . $firma->contenuto_html;
|
||||
}
|
||||
}
|
||||
|
||||
$listeNomi = $listeModels->pluck('nome')->implode(', ');
|
||||
|
||||
$messaggio = MailingMessaggio::create([
|
||||
'mailing_list_id' => null,
|
||||
@@ -265,6 +310,7 @@ class MailingController extends Controller
|
||||
'totale_destinatari' => $emails->count(),
|
||||
'mittente_nome' => $sender?->email_name,
|
||||
'mittente_email' => $sender?->email_address,
|
||||
'firma_id' => $firmaId,
|
||||
]);
|
||||
|
||||
$ok = 0;
|
||||
@@ -274,9 +320,9 @@ class MailingController extends Controller
|
||||
foreach ($emails as $email) {
|
||||
try {
|
||||
if ($sender) {
|
||||
$sender->sendEmail($email, $data['oggetto'], $data['corpo'], $attachmentPaths);
|
||||
$sender->sendEmail($email, $data['oggetto'], $corpoConFirma, $attachmentPaths);
|
||||
} else {
|
||||
$this->sendViaSystem($email, $data['oggetto'], $data['corpo'], $attachmentPaths);
|
||||
$this->sendViaSystem($email, $data['oggetto'], $corpoConFirma, $attachmentPaths);
|
||||
}
|
||||
$ok++;
|
||||
} catch (\Throwable $e) {
|
||||
@@ -347,18 +393,10 @@ class MailingController extends Controller
|
||||
$transport = \Symfony\Component\Mailer\Transport::fromDsn($dsn);
|
||||
$mailer = new \Symfony\Component\Mailer\Mailer($transport);
|
||||
|
||||
$fromName = $settings->email_name ?? '';
|
||||
if (filter_var($fromName, FILTER_VALIDATE_EMAIL)) {
|
||||
$fromName = 'Glastree';
|
||||
} else {
|
||||
$fromName = preg_replace('/[^\p{L}\p{N}\s]/u', '', $fromName);
|
||||
$fromName = trim($fromName);
|
||||
if (empty($fromName) || strlen($fromName) > 50) {
|
||||
$fromName = 'Glastree';
|
||||
}
|
||||
}
|
||||
|
||||
$fromAddress = \Symfony\Component\Mime\Address::create($settings->email_address, $fromName);
|
||||
$fromAddress = \Symfony\Component\Mime\Address::create(
|
||||
$settings->getEffectiveFromAddress(),
|
||||
$settings->getEffectiveFromName()
|
||||
);
|
||||
|
||||
$email = (new \Symfony\Component\Mime\Email())
|
||||
->from($fromAddress)
|
||||
@@ -366,6 +404,11 @@ class MailingController extends Controller
|
||||
->subject($subject)
|
||||
->text($body);
|
||||
|
||||
$replyTo = $settings->getEffectiveReplyTo();
|
||||
if ($replyTo) {
|
||||
$email->replyTo($replyTo);
|
||||
}
|
||||
|
||||
foreach ($attachmentPaths as $path) {
|
||||
$email->attachFromPath($path);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\MailingList;
|
||||
use App\Models\SenderAccount;
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@@ -60,7 +61,10 @@ class MailingListController extends Controller
|
||||
{
|
||||
$this->authorizeWrite('mailing');
|
||||
$tags = Tag::orderBy('name')->get();
|
||||
return view('mailing-liste.create', compact('tags'));
|
||||
$settings = \App\Models\EmailSetting::getActive();
|
||||
$firme = $settings?->firme ?? collect();
|
||||
$senderAccounts = SenderAccount::active()->get();
|
||||
return view('mailing-liste.create', compact('tags', 'firme', 'senderAccounts'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
@@ -73,6 +77,8 @@ class MailingListController extends Controller
|
||||
'contatti_json' => 'nullable|string',
|
||||
'tags' => 'nullable|array',
|
||||
'tags.*' => 'exists:tags,id',
|
||||
'firma_id' => 'nullable|integer|exists:firme,id',
|
||||
'sender_account_id' => 'nullable|integer|exists:sender_accounts,id',
|
||||
]);
|
||||
|
||||
$data['user_id'] = auth()->id();
|
||||
@@ -83,6 +89,8 @@ class MailingListController extends Controller
|
||||
'descrizione' => $data['descrizione'] ?? null,
|
||||
'attiva' => $data['attiva'],
|
||||
'user_id' => $data['user_id'],
|
||||
'firma_id' => $data['firma_id'] ?? null,
|
||||
'sender_account_id' => $data['sender_account_id'] ?? null,
|
||||
]);
|
||||
|
||||
if ($request->has('tags')) {
|
||||
@@ -117,10 +125,13 @@ class MailingListController extends Controller
|
||||
public function edit($mailingList)
|
||||
{
|
||||
$this->authorizeWrite('mailing');
|
||||
$mailingList = MailingList::with(['contatti.individuo.contatti', 'tags'])->findOrFail($mailingList);
|
||||
$mailingList = MailingList::with(['contatti.individuo.contatti', 'tags', 'firma', 'senderAccount'])->findOrFail($mailingList);
|
||||
$tags = Tag::orderBy('name')->get();
|
||||
$selectedTags = $mailingList->tags->pluck('id')->toArray();
|
||||
return view('mailing-liste.edit', compact('mailingList', 'tags', 'selectedTags'));
|
||||
$settings = \App\Models\EmailSetting::getActive();
|
||||
$firme = $settings?->firme ?? collect();
|
||||
$senderAccounts = SenderAccount::active()->get();
|
||||
return view('mailing-liste.edit', compact('mailingList', 'tags', 'selectedTags', 'firme', 'senderAccounts'));
|
||||
}
|
||||
|
||||
public function update(Request $request, $mailingList)
|
||||
@@ -135,6 +146,8 @@ class MailingListController extends Controller
|
||||
'contatti_json' => 'nullable|string',
|
||||
'tags' => 'nullable|array',
|
||||
'tags.*' => 'exists:tags,id',
|
||||
'firma_id' => 'nullable|integer|exists:firme,id',
|
||||
'sender_account_id' => 'nullable|integer|exists:sender_accounts,id',
|
||||
]);
|
||||
|
||||
$data['attiva'] = $data['attiva'] ?? false;
|
||||
@@ -143,6 +156,8 @@ class MailingListController extends Controller
|
||||
'nome' => $data['nome'],
|
||||
'descrizione' => $data['descrizione'] ?? null,
|
||||
'attiva' => $data['attiva'],
|
||||
'firma_id' => $data['firma_id'] ?? null,
|
||||
'sender_account_id' => $data['sender_account_id'] ?? null,
|
||||
]);
|
||||
|
||||
if ($request->has('tags')) {
|
||||
|
||||
@@ -515,7 +515,7 @@ class ReportController extends Controller
|
||||
$rows[] = [
|
||||
'livello' => 0,
|
||||
'nome' => $gruppo->nome,
|
||||
'diocesi' => $gruppo->diocesi?->nome ?? '-',
|
||||
'diocesi' => $gruppo->diocesi->count() > 0 ? $gruppo->diocesi->pluck('nome')->implode(', ') : '-',
|
||||
'membri' => $gruppo->individui()->count(),
|
||||
'padre' => '-',
|
||||
];
|
||||
@@ -538,7 +538,7 @@ class ReportController extends Controller
|
||||
$rows[] = [
|
||||
'livello' => $level,
|
||||
'nome' => str_repeat(' ', $level) . $child->nome,
|
||||
'diocesi' => $child->diocesi?->nome ?? '-',
|
||||
'diocesi' => $child->diocesi->count() > 0 ? $child->diocesi->pluck('nome')->implode(', ') : '-',
|
||||
'membri' => $child->individui()->count(),
|
||||
'padre' => $parent?->nome ?? '-',
|
||||
];
|
||||
@@ -554,7 +554,7 @@ class ReportController extends Controller
|
||||
$parent = $g->parent;
|
||||
return [
|
||||
'nome' => $g->full_path,
|
||||
'diocesi' => $g->diocesi?->nome ?? '-',
|
||||
'diocesi' => $g->diocesi->count() > 0 ? $g->diocesi->pluck('nome')->implode(', ') : '-',
|
||||
'membri' => $g->individui_count,
|
||||
'padre' => $parent?->nome ?? '-',
|
||||
'responsabili' => $g->getResponsabili()->pluck('cognome')->implode(', ') ?: '-',
|
||||
@@ -895,7 +895,7 @@ class ReportController extends Controller
|
||||
return [
|
||||
'nome' => $g->nome,
|
||||
'descrizione' => $g->descrizione ?? '-',
|
||||
'diocesi' => $g->diocesi?->nome ?? '-',
|
||||
'diocesi' => $g->diocesi->count() > 0 ? $g->diocesi->pluck('nome')->implode(', ') : '-',
|
||||
'livello' => $g->parent_id ? (count($g->getAncestors()) + 1) : 0,
|
||||
'membri' => $g->individui_count,
|
||||
'padre' => $parent?->nome ?? '-',
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class Diocesi extends Model
|
||||
{
|
||||
protected $table = 'diocesi';
|
||||
protected $fillable = ['nome', 'regione'];
|
||||
|
||||
public function gruppi(): HasMany
|
||||
public function gruppi(): BelongsToMany
|
||||
{
|
||||
return $this->hasMany(Gruppo::class);
|
||||
return $this->belongsToMany(Gruppo::class, 'diocesi_gruppo');
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,8 @@ class EmailSetting extends Model
|
||||
protected $fillable = [
|
||||
'imap_host', 'imap_port', 'imap_encryption', 'imap_username', 'imap_password',
|
||||
'smtp_host', 'smtp_port', 'smtp_encryption', 'smtp_username', 'smtp_password',
|
||||
'email_address', 'email_name', 'reply_to', 'sync_interval_minutes',
|
||||
'email_address', 'email_name', 'from_email', 'from_name', 'reply_to',
|
||||
'sync_interval_minutes',
|
||||
'last_sync_at', 'is_active', 'signature', 'signature_enabled',
|
||||
'auth_method', 'google_oauth_connection_id',
|
||||
];
|
||||
@@ -247,4 +248,38 @@ class EmailSetting extends Model
|
||||
{
|
||||
return $this->signature;
|
||||
}
|
||||
|
||||
public function getEffectiveFromAddress(): string
|
||||
{
|
||||
return $this->from_email ?: $this->email_address;
|
||||
}
|
||||
|
||||
public function getEffectiveFromName(): string
|
||||
{
|
||||
$name = $this->from_name ?: $this->email_name;
|
||||
if (filter_var($name, FILTER_VALIDATE_EMAIL)) {
|
||||
return 'Glastree';
|
||||
}
|
||||
$name = preg_replace('/[^\p{L}\p{N}\s]/u', '', $name);
|
||||
$name = trim($name);
|
||||
return empty($name) ? 'Glastree' : $name;
|
||||
}
|
||||
|
||||
public function getEffectiveReplyTo(): ?string
|
||||
{
|
||||
if ($this->from_email) {
|
||||
return $this->from_email;
|
||||
}
|
||||
return $this->reply_to ?: null;
|
||||
}
|
||||
|
||||
public function firme(): HasMany
|
||||
{
|
||||
return $this->hasMany(Firma::class);
|
||||
}
|
||||
|
||||
public function firmaPredefinita(): ?Firma
|
||||
{
|
||||
return $this->firme()->where('is_default', true)->first() ?? $this->firme()->first();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Firma extends Model
|
||||
{
|
||||
protected $table = 'firme';
|
||||
|
||||
protected $fillable = [
|
||||
'email_setting_id',
|
||||
'nome',
|
||||
'contenuto_html',
|
||||
'is_default',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_default' => 'boolean',
|
||||
];
|
||||
|
||||
public function emailSetting(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(EmailSetting::class);
|
||||
}
|
||||
}
|
||||
+21
-2
@@ -24,6 +24,8 @@ class Gruppo extends Model
|
||||
'responsabile_ids' => 'array',
|
||||
];
|
||||
|
||||
protected $appends = ['email_primaria', 'telefono_primario'];
|
||||
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
@@ -39,9 +41,14 @@ class Gruppo extends Model
|
||||
return $this->hasMany(Gruppo::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function diocesi(): BelongsTo
|
||||
public function diocesi(): BelongsToMany
|
||||
{
|
||||
return $this->belongsTo(Diocesi::class);
|
||||
return $this->belongsToMany(Diocesi::class, 'diocesi_gruppo');
|
||||
}
|
||||
|
||||
public function gruppoContatti(): HasMany
|
||||
{
|
||||
return $this->hasMany(GruppoContatto::class, 'gruppo_id');
|
||||
}
|
||||
|
||||
public function individui(): BelongsToMany
|
||||
@@ -66,6 +73,18 @@ class Gruppo extends Model
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getEmailPrimariaAttribute(): ?string
|
||||
{
|
||||
return $this->gruppoContatti()->where('tipo', 'email')->where('is_primary', true)->first()?->valore
|
||||
?? $this->gruppoContatti()->where('tipo', 'email')->first()?->valore;
|
||||
}
|
||||
|
||||
public function getTelefonoPrimarioAttribute(): ?string
|
||||
{
|
||||
return $this->gruppoContatti()->whereIn('tipo', ['telefono', 'cellulare'])->where('is_primary', true)->first()?->valore
|
||||
?? $this->gruppoContatti()->whereIn('tipo', ['telefono', 'cellulare'])->first()?->valore;
|
||||
}
|
||||
|
||||
public function getResponsabili()
|
||||
{
|
||||
if (empty($this->responsabile_ids)) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class GruppoContatto extends Model
|
||||
{
|
||||
protected $table = 'gruppo_contatti';
|
||||
protected $fillable = ['gruppo_id', 'tipo', 'valore', 'etichetta', 'is_primary'];
|
||||
|
||||
protected $casts = [
|
||||
'is_primary' => 'boolean',
|
||||
];
|
||||
|
||||
public function gruppo(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Gruppo::class);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ class MailingList extends Model
|
||||
use HasTagsLight;
|
||||
|
||||
protected $table = 'mailing_lists';
|
||||
protected $fillable = ['tenant_id', 'user_id', 'nome', 'descrizione', 'attiva'];
|
||||
protected $fillable = ['tenant_id', 'user_id', 'nome', 'descrizione', 'attiva', 'firma_id', 'sender_account_id'];
|
||||
|
||||
protected $casts = ['attiva' => 'boolean'];
|
||||
|
||||
@@ -36,6 +36,16 @@ class MailingList extends Model
|
||||
return $this->hasMany(MailingMessaggio::class);
|
||||
}
|
||||
|
||||
public function firma(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Firma::class);
|
||||
}
|
||||
|
||||
public function senderAccount(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(SenderAccount::class);
|
||||
}
|
||||
|
||||
public function getIndividui()
|
||||
{
|
||||
return Individuo::whereHas('mailingContacts', function ($q) {
|
||||
|
||||
@@ -11,7 +11,7 @@ class MailingMessaggio extends Model
|
||||
protected $fillable = [
|
||||
'tenant_id', 'mailing_list_id', 'user_id', 'oggetto', 'corpo',
|
||||
'canale', 'stato', 'inviato_al', 'totale_destinatari', 'invii_ok', 'invii_falliti',
|
||||
'log_falliti', 'mittente_nome', 'mittente_email',
|
||||
'log_falliti', 'mittente_nome', 'mittente_email', 'firma_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
|
||||
+5
-4
@@ -197,12 +197,13 @@ class User extends Authenticatable implements CanResetPasswordContract
|
||||
|
||||
$resetUrl = url('/password/reset/' . $token);
|
||||
|
||||
$fromName = $settings->email_name ?? config('app.name');
|
||||
$fromName = preg_replace('/[^\p{L}\p{N}\s]/u', '', $fromName);
|
||||
$fromName = trim($fromName) ?: config('app.name');
|
||||
$fromAddress = new Address(
|
||||
$settings->getEffectiveFromAddress(),
|
||||
$settings->getEffectiveFromName()
|
||||
);
|
||||
|
||||
$email = (new Email())
|
||||
->from(new Address($settings->email_address, $fromName))
|
||||
->from($fromAddress)
|
||||
->to($this->email)
|
||||
->subject('Reset della password - ' . config('app.name'))
|
||||
->html(view('auth.emails.reset-password', [
|
||||
|
||||
@@ -60,7 +60,7 @@ class BackupService
|
||||
return $backups;
|
||||
}
|
||||
|
||||
public function run(): array
|
||||
public function run(array $options = []): array
|
||||
{
|
||||
$result = ['success' => true, 'message' => '', 'filename' => ''];
|
||||
|
||||
@@ -74,12 +74,28 @@ class BackupService
|
||||
$filename = $appName . '_' . $timestamp . '.zip';
|
||||
$filepath = $this->backupDir . '/' . $filename;
|
||||
|
||||
$includeFiles = $options['include_files'] ?? (bool) AppSetting::getSetting('backup_include_files', true);
|
||||
$includeEnv = $options['include_env'] ?? (bool) AppSetting::getSetting('backup_include_env', true);
|
||||
|
||||
$steps = [];
|
||||
$includedComponents = [];
|
||||
|
||||
$steps[] = $this->backupDatabase();
|
||||
$steps[] = $this->backupEnv();
|
||||
$steps[] = $this->backupFiles();
|
||||
$this->writeManifest($timestamp);
|
||||
$includedComponents[] = 'database';
|
||||
|
||||
$stepEnv = $this->backupEnv($includeEnv);
|
||||
$steps[] = $stepEnv;
|
||||
if ($includeEnv) {
|
||||
$includedComponents[] = 'env';
|
||||
}
|
||||
|
||||
$stepFiles = $this->backupFiles($includeFiles);
|
||||
$steps[] = $stepFiles;
|
||||
if ($includeFiles) {
|
||||
$includedComponents[] = 'files';
|
||||
}
|
||||
|
||||
$this->writeManifest($timestamp, $includedComponents);
|
||||
|
||||
$zipResult = $this->createZip($filepath);
|
||||
if (!$zipResult) {
|
||||
@@ -90,7 +106,7 @@ class BackupService
|
||||
|
||||
$size = file_exists($filepath) ? filesize($filepath) : 0;
|
||||
|
||||
Log::info("Backup completato: {$filename} (" . $this->formatBytes($size) . ")");
|
||||
Log::info("Backup completato: {$filename} (" . $this->formatBytes($size) . ')');
|
||||
|
||||
$this->cleanupOldBackups();
|
||||
|
||||
@@ -100,6 +116,7 @@ class BackupService
|
||||
'filename' => $filename,
|
||||
'size' => $size,
|
||||
'size_formatted' => $this->formatBytes($size),
|
||||
'steps' => $steps,
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
$this->cleanTemp();
|
||||
@@ -109,6 +126,7 @@ class BackupService
|
||||
'success' => false,
|
||||
'message' => 'Backup fallito: ' . $e->getMessage(),
|
||||
'filename' => '',
|
||||
'steps' => [],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -181,9 +199,9 @@ class BackupService
|
||||
return 'database.sql (' . $this->formatBytes(filesize($sqlFile)) . ')';
|
||||
}
|
||||
|
||||
private function backupEnv(): string
|
||||
private function backupEnv(bool $includeEnv = true): string
|
||||
{
|
||||
if (!AppSetting::getSetting('backup_include_env', true)) {
|
||||
if (!$includeEnv) {
|
||||
return '.env (escluso)';
|
||||
}
|
||||
|
||||
@@ -200,9 +218,9 @@ class BackupService
|
||||
return '.env copiato';
|
||||
}
|
||||
|
||||
private function backupFiles(): string
|
||||
private function backupFiles(bool $includeFiles = true): string
|
||||
{
|
||||
if (!AppSetting::getSetting('backup_include_files', true)) {
|
||||
if (!$includeFiles) {
|
||||
return 'files (esclusi)';
|
||||
}
|
||||
|
||||
@@ -220,7 +238,7 @@ class BackupService
|
||||
return 'files copiati';
|
||||
}
|
||||
|
||||
private function writeManifest(string $timestamp): void
|
||||
private function writeManifest(string $timestamp, array $includedComponents = ['database']): void
|
||||
{
|
||||
$manifest = [
|
||||
'created_at' => $timestamp,
|
||||
@@ -230,6 +248,7 @@ class BackupService
|
||||
'db_name' => config('database.connections.mysql.database'),
|
||||
'php_version' => PHP_VERSION,
|
||||
'laravel_version' => app()->version(),
|
||||
'included_components' => $includedComponents,
|
||||
];
|
||||
|
||||
File::put($this->tempDir . '/manifest.json', json_encode($manifest, JSON_PRETTY_PRINT));
|
||||
|
||||
+3
-6
@@ -19,12 +19,9 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
'permission' => CheckPermission::class,
|
||||
]);
|
||||
$middleware->trustProxies(at: '*');
|
||||
$middleware->web(append: [
|
||||
\Illuminate\Cookie\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
]);
|
||||
// Web middleware defaults are already applied by the framework.
|
||||
// Do NOT append EncryptCookies, StartSession, etc. here — they
|
||||
// would run twice and corrupt session cookies, causing 419 errors.
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
//
|
||||
|
||||
+227
-27
@@ -20,6 +20,223 @@ echo "==> Pulizia cache..."
|
||||
rm -rf bootstrap/cache/*.php storage/framework/cache/data/* storage/framework/sessions/* storage/framework/views/* storage/logs/* 2>/dev/null || true
|
||||
echo " OK"
|
||||
|
||||
echo "==> Generazione post-deploy.sh..."
|
||||
cat > post-deploy.sh << 'POSTDEPLOY'
|
||||
#!/bin/bash
|
||||
# post-deploy.sh — Generated by build-dist.sh
|
||||
# Usage: bash post-deploy.sh {check|upgrade|install} [--yes]
|
||||
set -u
|
||||
APP_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ARTISAN="php ${APP_DIR}/artisan"
|
||||
DIAGNOSE="php ${APP_DIR}/diagnose.php"
|
||||
RED='\033[31m'; GREEN='\033[32m'; YELLOW='\033[33m'
|
||||
CYAN='\033[36m'; BOLD='\033[1m'; NC='\033[0m'
|
||||
|
||||
MODE="${1:-check}"
|
||||
BATCH=false; [ "${2:-}" = "--yes" ] && BATCH=true
|
||||
|
||||
echo -e "${CYAN}${BOLD}============================================${NC}"
|
||||
echo -e "${CYAN}${BOLD} Glastree Post-Deploy — ${MODE}${NC}"
|
||||
echo -e "${CYAN}${BOLD}============================================${NC}"
|
||||
echo ""
|
||||
|
||||
IS_FRESH=false; [ ! -f "${APP_DIR}/.env" ] && IS_FRESH=true
|
||||
|
||||
log() { echo -e " ${BOLD}$1${NC}"; }
|
||||
ok() { echo -e " ${GREEN}✅ $1${NC}"; }
|
||||
warn() { echo -e " ${YELLOW}⚠ $1${NC}"; }
|
||||
fail() { echo -e " ${RED}❌ $1${NC}"; }
|
||||
|
||||
check_artisan() {
|
||||
if [ ! -f "${APP_DIR}/artisan" ]; then
|
||||
fail "artisan non trovato. Esegui da root dell'applicazione."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
run_step() {
|
||||
local step="$1" desc="$2"; shift 2
|
||||
log "${step} ${desc}"
|
||||
if "$@" 2>&1 | sed 's/^/ /'; then
|
||||
ok "${desc}"
|
||||
return 0
|
||||
else
|
||||
local rc=$?
|
||||
fail "${desc} — exit code ${rc}"
|
||||
return ${rc}
|
||||
fi
|
||||
}
|
||||
|
||||
run_diagnose() {
|
||||
if ${DIAGNOSE} 2>&1 | tail -5; then
|
||||
ok "Diagnostica OK"
|
||||
else
|
||||
warn "Diagnostica ha rilevato problemi (non bloccante)"
|
||||
fi
|
||||
}
|
||||
|
||||
run_check() {
|
||||
echo -e "${YELLOW}═══════════ CONTROLLO ═══════════${NC}" ; echo ""
|
||||
|
||||
echo -e "${BOLD}[0] Diagnostica sistema${NC}"
|
||||
run_diagnose ; echo ""
|
||||
|
||||
echo -e "${BOLD}[1] Ambiente${NC}"
|
||||
if $IS_FRESH; then warn ".env non trovato — fresh install necessario"
|
||||
else ok ".env presente"
|
||||
fi ; echo ""
|
||||
|
||||
echo -e "${BOLD}[2] Migration pendenti${NC}"
|
||||
if [ -f "${APP_DIR}/artisan" ]; then
|
||||
$ARTISAN migrate:status 2>&1 | grep -E '(Pending|Ran)' | head -5 || true
|
||||
PENDING=$(php -r "echo trim(shell_exec('${ARTISAN} migrate:status 2>&1 | grep -c Pending || true'));" 2>/dev/null || echo "0")
|
||||
if [ "${PENDING:-0}" -gt 0 ] 2>/dev/null; then
|
||||
warn "${PENDING} migration da eseguire"
|
||||
else
|
||||
ok "Nessuna migration pendente"
|
||||
fi
|
||||
else
|
||||
warn "artisan non disponibile — skip migration check"
|
||||
fi ; echo ""
|
||||
|
||||
echo -e "${BOLD}[3] Storage & Permessi${NC}"
|
||||
for d in storage/framework/cache/data storage/framework/sessions \
|
||||
storage/framework/views storage/logs bootstrap/cache \
|
||||
storage/app/public storage/app/backups; do
|
||||
p="${APP_DIR}/${d}"
|
||||
if [ -d "$p" ]; then
|
||||
[ -w "$p" ] && ok "${d}" || warn "${d} — non scrivibile"
|
||||
else
|
||||
warn "${d} — mancante"
|
||||
fi
|
||||
done ; echo ""
|
||||
|
||||
echo -e "${BOLD}[4] Symlink storage${NC}"
|
||||
if [ -L "${APP_DIR}/public/storage" ]; then
|
||||
ok "public/storage → $(readlink "${APP_DIR}/public/storage")"
|
||||
else
|
||||
warn "public/storage non presente"
|
||||
fi ; echo ""
|
||||
|
||||
echo -e "${BOLD}[5] Cache${NC}"
|
||||
for d in bootstrap/cache storage/framework/cache/data \
|
||||
storage/framework/sessions storage/framework/views; do
|
||||
c=$(find "${APP_DIR}/${d}" -type f 2>/dev/null | wc -l)
|
||||
[ "$c" -gt 0 ] && warn "${d}: ${c} file" || ok "${d}: vuoto"
|
||||
done
|
||||
echo "" ; echo -e "${YELLOW}═══════════ FINE CONTROLLO ═══════════${NC}"
|
||||
}
|
||||
|
||||
run_upgrade() {
|
||||
echo -e "${YELLOW}═══════════ UPGRADE ═══════════${NC}" ; check_artisan
|
||||
|
||||
run_step "[1/9]" "Directory storage" mkdir -p \
|
||||
"${APP_DIR}/storage/framework/cache/data" \
|
||||
"${APP_DIR}/storage/framework/sessions" \
|
||||
"${APP_DIR}/storage/framework/views" \
|
||||
"${APP_DIR}/storage/logs" "${APP_DIR}/bootstrap/cache" \
|
||||
"${APP_DIR}/storage/app/public" \
|
||||
"${APP_DIR}/storage/app/public/logos" \
|
||||
"${APP_DIR}/storage/app/public/documenti/eventi" \
|
||||
"${APP_DIR}/storage/app/backups" \
|
||||
"${APP_DIR}/storage/app/documenti" \
|
||||
"${APP_DIR}/storage/app/private/avatars/gruppi" || true
|
||||
|
||||
run_step "[2/9]" ".gitignore storage" bash -c \
|
||||
"echo '*'>'${APP_DIR}/storage/app/public/.gitignore'; echo '!.gitignore'>>'${APP_DIR}/storage/app/public/.gitignore'" || true
|
||||
|
||||
run_step "[3/9]" "Permessi" chmod -R 775 \
|
||||
"${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache" || true
|
||||
|
||||
run_step "[4/9]" "Symlink storage" bash -c \
|
||||
"rm -f '${APP_DIR}/public/storage'; ln -sf '../storage/app/public' '${APP_DIR}/public/storage'" || true
|
||||
|
||||
run_step "[5/10]" "Migrazioni DB" $ARTISAN migrate --force || true
|
||||
|
||||
run_step "[6/10]" "Seed diocesi" $ARTISAN db:seed --class=DiocesiSeeder --force || true
|
||||
|
||||
run_step "[7/10]" "Dump autoload" composer dump-autoload --no-dev --optimize || true
|
||||
|
||||
run_step "[8/10]" "Key generate" $ARTISAN key:generate --force || true
|
||||
|
||||
run_step "[9/10]" "Pulizia cache" bash -c \
|
||||
"$ARTISAN config:clear && $ARTISAN route:clear && $ARTISAN view:clear" || true
|
||||
|
||||
run_step "[10/10]" "Verifica finale" $DIAGNOSE || true
|
||||
|
||||
echo "" ; echo -e "${GREEN}${BOLD}✅ Upgrade completato.${NC}"
|
||||
}
|
||||
|
||||
run_install() {
|
||||
echo -e "${YELLOW}═══════════ INSTALL (fresh) ═══════════${NC}" ; check_artisan
|
||||
|
||||
run_step "[1/10]" "Directory storage" mkdir -p \
|
||||
"${APP_DIR}/storage/framework/cache/data" \
|
||||
"${APP_DIR}/storage/framework/sessions" \
|
||||
"${APP_DIR}/storage/framework/views" \
|
||||
"${APP_DIR}/storage/logs" "${APP_DIR}/bootstrap/cache" \
|
||||
"${APP_DIR}/storage/app/public" \
|
||||
"${APP_DIR}/storage/app/public/logos" \
|
||||
"${APP_DIR}/storage/app/public/documenti/eventi" \
|
||||
"${APP_DIR}/storage/app/backups" \
|
||||
"${APP_DIR}/storage/app/documenti" \
|
||||
"${APP_DIR}/storage/app/private/avatars/gruppi" || true
|
||||
|
||||
run_step "[2/10]" ".gitignore storage" bash -c \
|
||||
"echo '*'>'${APP_DIR}/storage/app/public/.gitignore'; echo '!.gitignore'>>'${APP_DIR}/storage/app/public/.gitignore'" || true
|
||||
|
||||
run_step "[3/10]" "Permessi" chmod -R 775 \
|
||||
"${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache" || true
|
||||
|
||||
run_step "[4/10]" "File .env" cp "${APP_DIR}/.env.example" "${APP_DIR}/.env" || true
|
||||
warn "MODIFICA .env con i tuoi dati DB/URL prima di proseguire"
|
||||
if ! $BATCH; then read -p " Fatto? (Invio per continuare) " _; fi
|
||||
|
||||
run_step "[5/10]" "Symlink storage" bash -c \
|
||||
"rm -f '${APP_DIR}/public/storage'; ln -sf '../storage/app/public' '${APP_DIR}/public/storage'" || true
|
||||
|
||||
run_step "[6/10]" "Dump autoload" composer dump-autoload --no-dev --optimize || true
|
||||
|
||||
run_step "[7/10]" "Key generate" $ARTISAN key:generate --force || true
|
||||
|
||||
run_step "[8/11]" "Migrazioni DB" $ARTISAN migrate --force || true
|
||||
|
||||
run_step "[9/11]" "Seed diocesi" $ARTISAN db:seed --class=DiocesiSeeder --force || true
|
||||
|
||||
run_step "[10/11]" "Pulizia cache" bash -c \
|
||||
"$ARTISAN config:clear && $ARTISAN route:clear && $ARTISAN view:clear" || true
|
||||
|
||||
run_step "[11/11]" "Verifica finale" $DIAGNOSE || true
|
||||
|
||||
echo "" ; echo -e "${GREEN}${BOLD}✅ Installazione completata.${NC}"
|
||||
}
|
||||
|
||||
case "$MODE" in
|
||||
check) run_check ;;
|
||||
upgrade)
|
||||
run_check ; echo ""
|
||||
if $BATCH; then run_upgrade
|
||||
else
|
||||
read -p "Procedere con l'upgrade? (s/N) " c
|
||||
[ "$c" = "s" ] || [ "$c" = "S" ] && run_upgrade || echo "Annullato."
|
||||
fi
|
||||
;;
|
||||
install) run_install ;;
|
||||
*)
|
||||
echo "Uso: bash post-deploy.sh {check|upgrade|install} [--yes]"
|
||||
echo " check — diagnostica (nessuna modifica)"
|
||||
echo " upgrade — aggiorna installazione esistente"
|
||||
echo " install — fresh install"
|
||||
echo " --yes — batch (nessuna conferma)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
POSTDEPLOY
|
||||
|
||||
chmod +x post-deploy.sh
|
||||
|
||||
echo " OK"
|
||||
|
||||
echo "==> Creazione archivio: ${ARCHIVE}"
|
||||
|
||||
tar czf "${ARCHIVE}" \
|
||||
@@ -60,6 +277,8 @@ tar czf "${ARCHIVE}" \
|
||||
--warning=no-file-changed \
|
||||
.
|
||||
|
||||
rm -f post-deploy.sh
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " Archivio creato: ${ARCHIVE}"
|
||||
@@ -70,33 +289,14 @@ echo "Per estrarre sul server di destinazione:"
|
||||
echo " tar xzf ${ARCHIVE}"
|
||||
echo ""
|
||||
echo "Poi esegui:"
|
||||
echo " # 0. Diagnostica preliminare"
|
||||
echo " php diagnose.php"
|
||||
echo " # Upgrade di un'installazione esistente:"
|
||||
echo " bash post-deploy.sh upgrade"
|
||||
echo ""
|
||||
echo " # 1. Crea directory necessarie (se non esistono già)"
|
||||
echo ' mkdir -p storage/framework/cache/data storage/framework/sessions storage/framework/views storage/logs bootstrap/cache storage/app/public storage/app/public/logos storage/app/public/documenti/eventi storage/app/backups storage/app/documenti storage/app/private/avatars/gruppi'
|
||||
echo " # Fresh install:"
|
||||
echo " bash post-deploy.sh install"
|
||||
echo ""
|
||||
echo " # 2. Crea file .gitignore in storage/app/public (serve a Laravel)"
|
||||
echo ' echo "*" > storage/app/public/.gitignore'
|
||||
echo ' echo "!.gitignore" >> storage/app/public/.gitignore'
|
||||
echo " # Solo controllo (nessuna modifica):"
|
||||
echo " bash post-deploy.sh check"
|
||||
echo ""
|
||||
echo " # 3. Permessi (web server = www-data)"
|
||||
echo ' chmod -R 775 storage bootstrap/cache'
|
||||
echo ' chown -R www-data:www-data storage bootstrap/cache'
|
||||
echo ""
|
||||
echo " # 4. Configura ambiente"
|
||||
echo " cp .env.example .env # modifica DB, APP_URL, etc."
|
||||
echo " php artisan key:generate"
|
||||
echo ""
|
||||
echo " # 5. Pulisci cache e ricrea symlink storage (relativo per portabilità)"
|
||||
echo ' rm -f public/storage'
|
||||
echo ' ln -sf ../storage/app/public public/storage'
|
||||
echo " php artisan config:clear"
|
||||
echo " php artisan route:clear"
|
||||
echo " php artisan view:clear"
|
||||
echo ""
|
||||
echo " # 6. Avvia installazione MySQL"
|
||||
echo " php install.php"
|
||||
echo ""
|
||||
echo " # 7. Verifica finale"
|
||||
echo " php diagnose.php"
|
||||
echo " # Batch mode (nessuna conferma):"
|
||||
echo " bash post-deploy.sh upgrade --yes"
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (!Schema::hasTable('email_settings')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Schema::hasColumn('email_settings', 'from_email')) {
|
||||
Schema::table('email_settings', function (Blueprint $table) {
|
||||
$table->string('from_email')->nullable()->after('email_name');
|
||||
});
|
||||
}
|
||||
|
||||
if (!Schema::hasColumn('email_settings', 'from_name')) {
|
||||
Schema::table('email_settings', function (Blueprint $table) {
|
||||
$table->string('from_name')->nullable()->after('from_email');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('email_settings', function (Blueprint $table) {
|
||||
$table->dropColumn(['from_email', 'from_name']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (!Schema::hasTable('gruppo_contatti')) {
|
||||
Schema::create('gruppo_contatti', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('gruppo_id')->constrained('gruppi')->onDelete('cascade');
|
||||
$table->string('tipo');
|
||||
$table->string('valore');
|
||||
$table->string('etichetta')->nullable();
|
||||
$table->boolean('is_primary')->default(false);
|
||||
$table->timestamps();
|
||||
$table->index('gruppo_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('gruppo_contatti');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (!Schema::hasTable('diocesi_gruppo')) {
|
||||
Schema::create('diocesi_gruppo', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('gruppo_id')->constrained('gruppi')->onDelete('cascade');
|
||||
$table->foreignId('diocesi_id')->constrained('diocesi')->onDelete('cascade');
|
||||
$table->unique(['gruppo_id', 'diocesi_id']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('diocesi_gruppo');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('firme', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('email_setting_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('nome', 255);
|
||||
$table->longText('contenuto_html')->nullable();
|
||||
$table->boolean('is_default')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('firme');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('mailing_lists', function (Blueprint $table) {
|
||||
$table->foreignId('firma_id')->nullable()->constrained('firme')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('mailing_lists', function (Blueprint $table) {
|
||||
$table->dropForeign(['firma_id']);
|
||||
$table->dropColumn('firma_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('mailing_messaggi', function (Blueprint $table) {
|
||||
$table->foreignId('firma_id')->nullable()->constrained('firme')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('mailing_messaggi', function (Blueprint $table) {
|
||||
$table->dropForeign(['firma_id']);
|
||||
$table->dropColumn('firma_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (!Schema::hasTable('sessions')) {
|
||||
Schema::create('sessions', function (Blueprint $table): void {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->longText('payload');
|
||||
$table->integer('last_activity')->index();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sessions');
|
||||
}
|
||||
};
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('mailing_lists', function (Blueprint $table) {
|
||||
$table->foreignId('sender_account_id')->nullable()->constrained('sender_accounts')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('mailing_lists', function (Blueprint $table) {
|
||||
$table->dropForeign(['sender_account_id']);
|
||||
$table->dropColumn('sender_account_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
+243
-138
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
@@ -9,147 +11,250 @@ class DiocesiSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$fixes = [
|
||||
50 => 'Abbazia territoriale della Santissima Trinità di Cava de Tirreni',
|
||||
51 => 'Diocesi di Cefalù',
|
||||
58 => 'Diocesi di Città di Castello',
|
||||
79 => 'Diocesi di Forlì-Bertinoro',
|
||||
121 => 'Diocesi di Mondovì',
|
||||
128 => 'Diocesi di Nardò-Gallipoli',
|
||||
148 => 'Arcidiocesi di Perugia-Città della Pieve',
|
||||
185 => "Arcidiocesi di Siena-Colle di Val d'Elsa-Montalcino",
|
||||
];
|
||||
foreach ($fixes as $id => $correctNome) {
|
||||
Diocesi::where('id', $id)->update(['nome' => $correctNome]);
|
||||
}
|
||||
|
||||
$diocesi = [
|
||||
['nome' => 'Acqui Terme', 'regione' => 'Piemonte'],
|
||||
['nome' => 'Afragola', 'regione' => 'Campania'],
|
||||
['nome' => 'Agrigento', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Alba', 'regione' => 'Piemonte'],
|
||||
['nome' => 'Alessandria', 'regione' => 'Piemonte'],
|
||||
['nome' => 'Ancona', 'regione' => 'Marche'],
|
||||
['nome' => 'Aosta', 'regione' => 'Valle d\'Aosta'],
|
||||
['nome' => 'Aquila', 'regione' => 'Abruzzo'],
|
||||
['nome' => 'Arezzo', 'regione' => 'Toscana'],
|
||||
['nome' => 'Ascoli Piceno', 'regione' => 'Marche'],
|
||||
['nome' => 'Assisi', 'regione' => 'Umbria'],
|
||||
['nome' => 'Asti', 'regione' => 'Piemonte'],
|
||||
['nome' => 'Avellino', 'regione' => 'Campania'],
|
||||
['nome' => 'Bari', 'regione' => 'Puglia'],
|
||||
['nome' => 'Barletta', 'regione' => 'Puglia'],
|
||||
['nome' => 'Belluno', 'regione' => 'Veneto'],
|
||||
['nome' => 'Benevento', 'regione' => 'Campania'],
|
||||
['nome' => 'Bergamo', 'regione' => 'Lombardia'],
|
||||
['nome' => 'Biella', 'regione' => 'Piemonte'],
|
||||
['nome' => 'Bologna', 'regione' => 'Emilia-Romagna'],
|
||||
['nome' => 'Bolzano', 'regione' => 'Trentino-Alto Adige'],
|
||||
['nome' => 'Brescia', 'regione' => 'Lombardia'],
|
||||
['nome' => 'Brindisi', 'regione' => 'Puglia'],
|
||||
['nome' => 'Cagliari', 'regione' => 'Sardegna'],
|
||||
['nome' => 'Caltanissetta', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Campobasso', 'regione' => 'Molise'],
|
||||
['nome' => 'Caserta', 'regione' => 'Campania'],
|
||||
['nome' => 'Cassano allo Ionio', 'regione' => 'Calabria'],
|
||||
['nome' => 'Castelvetrano', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Catania', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Catanzaro', 'regione' => 'Calabria'],
|
||||
['nome' => 'Cefalù', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Cerro al Lambro', 'regione' => 'Lombardia'],
|
||||
['nome' => 'Cesena', 'regione' => 'Emilia-Romagna'],
|
||||
['nome' => 'Chieti', 'regione' => 'Abruzzo'],
|
||||
['nome' => 'Civitavecchia', 'regione' => 'Lazio'],
|
||||
['nome' => 'Cosenza', 'regione' => 'Calabria'],
|
||||
['nome' => 'Cremona', 'regione' => 'Lombardia'],
|
||||
['nome' => 'Crotone', 'regione' => 'Calabria'],
|
||||
['nome' => 'Cuneo', 'regione' => 'Piemonte'],
|
||||
['nome' => 'Donegal', 'regione' => 'Irlanda'],
|
||||
['nome' => 'Enna', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Fano', 'regione' => 'Marche'],
|
||||
['nome' => 'Fermo', 'regione' => 'Marche'],
|
||||
['nome' => 'Ferrara', 'regione' => 'Emilia-Romagna'],
|
||||
['nome' => 'Firenze', 'regione' => 'Toscana'],
|
||||
['nome' => 'Foggia', 'regione' => 'Puglia'],
|
||||
['nome' => 'Foligno', 'regione' => 'Umbria'],
|
||||
['nome' => 'Forlì', 'regione' => 'Emilia-Romagna'],
|
||||
['nome' => 'Frosinone', 'regione' => 'Lazio'],
|
||||
['nome' => 'Genova', 'regione' => 'Liguria'],
|
||||
['nome' => 'Gorizia', 'regione' => 'Friuli-Venezia Giulia'],
|
||||
['nome' => 'Grosseto', 'regione' => 'Toscana'],
|
||||
['nome' => 'Gubbio', 'regione' => 'Umbria'],
|
||||
['nome' => 'Imola', 'regione' => 'Emilia-Romagna'],
|
||||
['nome' => 'Isernia', 'regione' => 'Molise'],
|
||||
['nome' => 'L\'Aquila', 'regione' => 'Abruzzo'],
|
||||
['nome' => 'La Spezia', 'regione' => 'Liguria'],
|
||||
['nome' => 'Lamezia Terme', 'regione' => 'Calabria'],
|
||||
['nome' => 'Latina', 'regione' => 'Lazio'],
|
||||
['nome' => 'Lecce', 'regione' => 'Puglia'],
|
||||
['nome' => 'Lecco', 'regione' => 'Lombardia'],
|
||||
['nome' => 'Livorno', 'regione' => 'Toscana'],
|
||||
['nome' => 'Lodi', 'regione' => 'Lombardia'],
|
||||
['nome' => 'Lucca', 'regione' => 'Toscana'],
|
||||
['nome' => 'Lunedì', 'regione' => 'Marche'],
|
||||
['nome' => 'Macerata', 'regione' => 'Marche'],
|
||||
['nome' => 'Mantova', 'regione' => 'Lombardia'],
|
||||
['nome' => 'Massa', 'regione' => 'Toscana'],
|
||||
['nome' => 'Matera', 'regione' => 'Basilicata'],
|
||||
['nome' => 'Messina', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Milano', 'regione' => 'Lombardia'],
|
||||
['nome' => 'Modena', 'regione' => 'Emilia-Romagna'],
|
||||
['nome' => 'Monaco', 'regione' => 'Germania'],
|
||||
['nome' => 'Mongolia', 'regione' => 'Asia'],
|
||||
['nome' => 'Monreale', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Montefeltro', 'regione' => 'Marche'],
|
||||
['nome' => 'Montepulciano', 'regione' => 'Toscana'],
|
||||
['nome' => 'Monza', 'regione' => 'Lombardia'],
|
||||
['nome' => 'Napoli', 'regione' => 'Campania'],
|
||||
['nome' => 'Novara', 'regione' => 'Piemonte'],
|
||||
['nome' => 'Nuoro', 'regione' => 'Sardegna'],
|
||||
['nome' => 'Oristano', 'regione' => 'Sardegna'],
|
||||
['nome' => 'Otranto', 'regione' => 'Puglia'],
|
||||
['nome' => 'Padova', 'regione' => 'Veneto'],
|
||||
['nome' => 'Palermo', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Parma', 'regione' => 'Emilia-Romagna'],
|
||||
['nome' => 'Pavia', 'regione' => 'Lombardia'],
|
||||
['nome' => 'Perugia', 'regione' => 'Umbria'],
|
||||
['nome' => 'Pesaro', 'regione' => 'Marche'],
|
||||
['nome' => 'Piacenza', 'regione' => 'Emilia-Romagna'],
|
||||
['nome' => 'Pisa', 'regione' => 'Toscana'],
|
||||
['nome' => 'Pistoia', 'regione' => 'Toscana'],
|
||||
['nome' => 'Pordenone', 'regione' => 'Friuli-Venezia Giulia'],
|
||||
['nome' => 'Potenza', 'regione' => 'Basilicata'],
|
||||
['nome' => 'Prato', 'regione' => 'Toscana'],
|
||||
['nome' => 'Ragusa', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Ravenna', 'regione' => 'Emilia-Romagna'],
|
||||
['nome' => 'Reggio Calabria', 'regione' => 'Calabria'],
|
||||
['nome' => 'Reggio Emilia', 'regione' => 'Emilia-Romagna'],
|
||||
['nome' => 'Rieti', 'regione' => 'Lazio'],
|
||||
['nome' => 'Rimini', 'regione' => 'Emilia-Romagna'],
|
||||
['nome' => 'Roma', 'regione' => 'Lazio'],
|
||||
['nome' => 'Rosario', 'regione' => 'Argentina'],
|
||||
['nome' => 'Rovigo', 'regione' => 'Veneto'],
|
||||
['nome' => 'Salerno', 'regione' => 'Campania'],
|
||||
['nome' => 'San Severo', 'regione' => 'Puglia'],
|
||||
['nome' => 'Sassari', 'regione' => 'Sardegna'],
|
||||
['nome' => 'Savona', 'regione' => 'Liguria'],
|
||||
['nome' => 'Siena', 'regione' => 'Toscana'],
|
||||
['nome' => 'Siracusa', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Sondrio', 'regione' => 'Lombardia'],
|
||||
['nome' => 'Spezia', 'regione' => 'Liguria'],
|
||||
['nome' => 'Spoleto', 'regione' => 'Umbria'],
|
||||
['nome' => 'Syracuse', 'regione' => 'USA'],
|
||||
['nome' => 'Taranto', 'regione' => 'Puglia'],
|
||||
['nome' => 'Tempio Ampurias', 'regione' => 'Sardegna'],
|
||||
['nome' => 'Teramo', 'regione' => 'Abruzzo'],
|
||||
['nome' => 'Terni', 'regione' => 'Umbria'],
|
||||
['nome' => 'Tirana', 'regione' => 'Albania'],
|
||||
['nome' => 'Torino', 'regione' => 'Piemonte'],
|
||||
['nome' => 'Tortona', 'regione' => 'Piemonte'],
|
||||
['nome' => 'Trani', 'regione' => 'Puglia'],
|
||||
['nome' => 'Trapani', 'regione' => 'Sicilia'],
|
||||
['nome' => 'Trento', 'regione' => 'Trentino-Alto Adige'],
|
||||
['nome' => 'Treviso', 'regione' => 'Veneto'],
|
||||
['nome' => 'Trieste', 'regione' => 'Friuli-Venezia Giulia'],
|
||||
['nome' => 'Udine', 'regione' => 'Friuli-Venezia Giulia'],
|
||||
['nome' => 'Urbino', 'regione' => 'Marche'],
|
||||
['nome' => 'Vaticano', 'regione' => 'Lazio'],
|
||||
['nome' => 'Vercelli', 'regione' => 'Piemonte'],
|
||||
['nome' => 'Verona', 'regione' => 'Veneto'],
|
||||
['nome' => 'Vibo Valentia', 'regione' => 'Calabria'],
|
||||
['nome' => 'Vicenza', 'regione' => 'Veneto'],
|
||||
['nome' => 'Viterbo', 'regione' => 'Lazio'],
|
||||
['nome' => 'Vittorio Veneto', 'regione' => 'Veneto'],
|
||||
'Arcidiocesi di Acerenza',
|
||||
'Diocesi di Acerra',
|
||||
'Diocesi di Acireale',
|
||||
'Diocesi di Acqui',
|
||||
'Diocesi di Adria-Rovigo',
|
||||
'Arcidiocesi di Agrigento',
|
||||
'Diocesi di Alba',
|
||||
'Sede suburbicaria di Albano',
|
||||
'Diocesi di Albenga-Imperia',
|
||||
'Diocesi di Ales-Terralba',
|
||||
'Diocesi di Alessandria',
|
||||
'Diocesi di Alghero-Bosa',
|
||||
'Diocesi di Alife-Caiazzo',
|
||||
'Diocesi di Altamura-Gravina-Acquaviva delle Fonti',
|
||||
'Arcidiocesi di Amalfi-Cava dei Tirreni',
|
||||
'Diocesi di Anagni-Alatri',
|
||||
'Arcidiocesi di Ancona-Osimo',
|
||||
'Diocesi di Andria',
|
||||
'Diocesi di Aosta',
|
||||
'Diocesi di Arezzo-Cortona-Sansepolcro',
|
||||
'Diocesi di Ariano Irpino-Lacedonia',
|
||||
'Diocesi di Ascoli Piceno',
|
||||
'Diocesi di Assisi-Nocera Umbra-Gualdo Tadino',
|
||||
'Diocesi di Asti',
|
||||
'Diocesi di Avellino',
|
||||
'Diocesi di Aversa',
|
||||
'Diocesi di Avezzano',
|
||||
'Arcidiocesi di Bari-Bitonto',
|
||||
'Diocesi di Belluno-Feltre',
|
||||
'Arcidiocesi di Benevento',
|
||||
'Diocesi di Bergamo',
|
||||
'Diocesi di Biella',
|
||||
'Arcidiocesi di Bologna',
|
||||
'Diocesi di Bolzano-Bressanone',
|
||||
'Diocesi di Brescia',
|
||||
'Arcidiocesi di Brindisi-Ostuni',
|
||||
'Arcidiocesi di Cagliari',
|
||||
'Diocesi di Caltagirone',
|
||||
'Diocesi di Caltanissetta',
|
||||
'Arcidiocesi di Camerino-San Severino Marche',
|
||||
'Arcidiocesi di Campobasso-Boiano',
|
||||
'Arcidiocesi di Capua',
|
||||
'Diocesi di Carpi',
|
||||
'Diocesi di Casale Monferrato',
|
||||
'Diocesi di Caserta',
|
||||
'Diocesi di Cassano allo Ionio',
|
||||
'Diocesi di Castellaneta',
|
||||
'Arcidiocesi di Catania',
|
||||
'Arcidiocesi di Catanzaro-Squillace',
|
||||
'Abbazia territoriale della Santissima Trinità di Cava de Tirreni',
|
||||
'Diocesi di Cefalù',
|
||||
'Diocesi di Cerignola-Ascoli Satriano',
|
||||
'Diocesi di Cerreto Sannita-Telese-Sant\'Agata de Goti',
|
||||
'Diocesi di Cesena-Sarsina',
|
||||
'Diocesi di Chiavari',
|
||||
'Arcidiocesi di Chieti-Vasto',
|
||||
'Diocesi di Chioggia',
|
||||
'Diocesi di Città di Castello',
|
||||
'Diocesi di Civita Castellana',
|
||||
'Diocesi di Civitavecchia-Tarquinia',
|
||||
'Diocesi di Como',
|
||||
'Diocesi di Concordia-Pordenone',
|
||||
'Diocesi di Conversano-Monopoli',
|
||||
'Arcidiocesi di Cosenza-Bisignano',
|
||||
'Diocesi di Crema',
|
||||
'Diocesi di Cremona',
|
||||
'Arcidiocesi di Crotone-Santa Severina',
|
||||
'Diocesi di Cuneo',
|
||||
'Diocesi di Fabriano-Matelica',
|
||||
'Diocesi di Faenza-Modigliana',
|
||||
'Diocesi di Fano-Fossombrone-Cagli-Pergola',
|
||||
'Arcidiocesi di Fermo',
|
||||
'Arcidiocesi di Ferrara-Comacchio',
|
||||
'Diocesi di Fidenza',
|
||||
'Diocesi di Fiesole',
|
||||
'Arcidiocesi di Firenze',
|
||||
'Arcidiocesi di Foggia-Bovino',
|
||||
'Diocesi di Foligno',
|
||||
'Diocesi di Forlì-Bertinoro',
|
||||
'Diocesi di Fossano',
|
||||
'Sede suburbicaria di Frascati',
|
||||
'Diocesi di Frosinone-Veroli-Ferentino',
|
||||
'Arcidiocesi di Gaeta',
|
||||
'Arcidiocesi di Genova',
|
||||
'Arcidiocesi di Gorizia',
|
||||
'Diocesi di Grosseto',
|
||||
'Diocesi di Gubbio',
|
||||
'Diocesi di Iglesias',
|
||||
'Diocesi di Imola',
|
||||
'Diocesi di Ischia',
|
||||
'Diocesi di Isernia-Venafro',
|
||||
'Diocesi di Ivrea',
|
||||
'Diocesi di Jesi',
|
||||
'Diocesi della Spezia-Sarzana-Brugnato',
|
||||
'Diocesi di Lamezia Terme',
|
||||
'Arcidiocesi di Lanciano-Ortona',
|
||||
'Diocesi di Lanusei',
|
||||
'Arcidiocesi dell\'Aquila',
|
||||
'Diocesi di Latina-Terracina-Sezze-Priverno',
|
||||
'Arcidiocesi di Lecce',
|
||||
'Diocesi di Livorno',
|
||||
'Diocesi di Locri-Gerace',
|
||||
'Diocesi di Lodi',
|
||||
'Prelatura territoriale di Loreto',
|
||||
'Arcidiocesi di Lucca',
|
||||
'Diocesi di Lucera-Troia',
|
||||
'Eparchia di Lungro',
|
||||
'Diocesi di Macerata-Tolentino-Recanati-Cingoli-Treia',
|
||||
'Arcidiocesi di Manfredonia-Vieste-San Giovanni Rotondo',
|
||||
'Diocesi di Mantova',
|
||||
'Diocesi di Massa Carrara-Pontremoli',
|
||||
'Diocesi di Massa Marittima-Piombino',
|
||||
'Arcidiocesi di Matera-Irsina',
|
||||
'Diocesi di Mazara del Vallo',
|
||||
'Diocesi di Melfi-Rapolla-Venosa',
|
||||
'Arcidiocesi di Messina-Lipari-Santa Lucia del Mela',
|
||||
'Arcidiocesi di Milano',
|
||||
'Diocesi di Mileto-Nicotera-Tropea',
|
||||
'Arcidiocesi di Modena-Nonantola',
|
||||
'Diocesi di Molfetta-Ruvo-Giovinazzo-Terlizzi',
|
||||
'Diocesi di Mondovì',
|
||||
'Arcidiocesi di Monreale',
|
||||
'Abbazia territoriale di Montecassino',
|
||||
'Abbazia territoriale di Monte Oliveto Maggiore',
|
||||
'Diocesi di Montepulciano-Chiusi-Pienza',
|
||||
'Abbazia territoriale di Montevergine',
|
||||
'Arcidiocesi di Napoli',
|
||||
'Diocesi di Nardò-Gallipoli',
|
||||
'Diocesi di Nicosia',
|
||||
'Diocesi di Nocera Inferiore-Sarno',
|
||||
'Diocesi di Nola',
|
||||
'Diocesi di Noto',
|
||||
'Diocesi di Novara',
|
||||
'Diocesi di Nuoro',
|
||||
'Diocesi di Oppido Mamertina-Palmi',
|
||||
'Diocesi di Oria',
|
||||
'Arcidiocesi di Oristano',
|
||||
'Diocesi di Orvieto-Todi',
|
||||
'Sede suburbicaria di Ostia',
|
||||
'Arcidiocesi di Otranto',
|
||||
'Diocesi di Ozieri',
|
||||
'Diocesi di Padova',
|
||||
'Arcidiocesi di Palermo',
|
||||
'Sede suburbicaria di Palestrina',
|
||||
'Diocesi di Parma',
|
||||
'Diocesi di Patti',
|
||||
'Diocesi di Pavia',
|
||||
'Arcidiocesi di Perugia-Città della Pieve',
|
||||
'Arcidiocesi di Pesaro',
|
||||
'Arcidiocesi di Pescara-Penne',
|
||||
'Diocesi di Pescia',
|
||||
'Eparchia di Piana degli Albanesi',
|
||||
'Diocesi di Piazza Armerina',
|
||||
'Diocesi di Pinerolo',
|
||||
'Arcidiocesi di Pisa',
|
||||
'Diocesi di Pistoia',
|
||||
'Diocesi di Pitigliano-Sovana-Orbetello',
|
||||
'Prelatura territoriale di Pompei',
|
||||
'Sede suburbicaria di Porto-Santa Rufina',
|
||||
'Arcidiocesi di Potenza-Muro Lucano-Marsico Nuovo',
|
||||
'Diocesi di Pozzuoli',
|
||||
'Diocesi di Prato',
|
||||
'Diocesi di Ragusa',
|
||||
'Arcidiocesi di Ravenna-Cervia',
|
||||
'Arcidiocesi di Reggio Calabria-Bova',
|
||||
'Diocesi di Reggio Emilia-Guastalla',
|
||||
'Diocesi di Rieti',
|
||||
'Diocesi di Rimini',
|
||||
'Diocesi di Roma',
|
||||
'Arcidiocesi di Rossano-Cariati',
|
||||
'Sede suburbicaria di Sabina-Poggio Mirteto',
|
||||
'Arcidiocesi di Salerno-Campagna-Acerno',
|
||||
'Diocesi di Saluzzo',
|
||||
'Diocesi di San Benedetto del Tronto-Ripatransone-Montalto',
|
||||
'Diocesi di San Marco Argentano-Scalea',
|
||||
'Diocesi di San Marino-Montefeltro',
|
||||
'Diocesi di San Miniato',
|
||||
'Diocesi di San Severo',
|
||||
'Abbazia territoriale di Santa Maria di Grottaferrata',
|
||||
'Arcidiocesi di Sant\'Angelo dei Lombardi-Conza-Nusco-Bisaccia',
|
||||
'Arcidiocesi di Sassari',
|
||||
'Diocesi di Savona-Noli',
|
||||
'Diocesi di Senigallia',
|
||||
'Diocesi di Sessa Aurunca',
|
||||
'Arcidiocesi di Siena-Colle di Val d\'Elsa-Montalcino',
|
||||
'Arcidiocesi di Siracusa',
|
||||
'Diocesi di Sora-Cassino-Aquino-Pontecorvo',
|
||||
'Arcidiocesi di Sorrento-Castellammare di Stabia',
|
||||
'Arcidiocesi di Spoleto-Norcia',
|
||||
'Abbazia territoriale di Subiaco',
|
||||
'Diocesi di Sulmona-Valva',
|
||||
'Diocesi di Susa',
|
||||
'Arcidiocesi di Taranto',
|
||||
'Diocesi di Teano-Calvi',
|
||||
'Diocesi di Teggiano-Policastro',
|
||||
'Diocesi di Tempio-Ampurias',
|
||||
'Diocesi di Teramo-Atri',
|
||||
'Diocesi di Termoli-Larino',
|
||||
'Diocesi di Terni-Narni-Amelia',
|
||||
'Diocesi di Tivoli',
|
||||
'Arcidiocesi di Torino',
|
||||
'Diocesi di Tortona',
|
||||
'Arcidiocesi di Trani-Barletta-Bisceglie-Nazareth',
|
||||
'Diocesi di Trapani',
|
||||
'Arcidiocesi di Trento',
|
||||
'Diocesi di Treviso',
|
||||
'Diocesi di Tricarico',
|
||||
'Diocesi di Trieste',
|
||||
'Diocesi di Trivento',
|
||||
'Diocesi di Tursi-Lagonegro',
|
||||
'Arcidiocesi di Udine',
|
||||
'Diocesi di Ugento-Santa Maria di Leuca',
|
||||
'Arcidiocesi di Urbino-Urbania-Sant\'Angelo in Vado',
|
||||
'Diocesi di Vallo della Lucania',
|
||||
'Sede suburbicaria di Velletri-Segni',
|
||||
'Patriarcato di Venezia',
|
||||
'Diocesi di Ventimiglia-Sanremo',
|
||||
'Arcidiocesi di Vercelli',
|
||||
'Diocesi di Verona',
|
||||
'Diocesi di Vicenza',
|
||||
'Diocesi di Vigevano',
|
||||
'Diocesi di Viterbo',
|
||||
'Diocesi di Vittorio Veneto',
|
||||
'Diocesi di Volterra',
|
||||
'Diocesi di Piacenza-Bobbio',
|
||||
];
|
||||
|
||||
foreach ($diocesi as $d) {
|
||||
Diocesi::firstOrCreate(['nome' => $d['nome']], $d);
|
||||
foreach ($diocesi as $nome) {
|
||||
Diocesi::firstOrCreate(['nome' => $nome]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+686
-321
File diff suppressed because it is too large
Load Diff
Executable
+209
@@ -0,0 +1,209 @@
|
||||
#!/bin/bash
|
||||
# post-deploy.sh — Generated by build-dist.sh
|
||||
# Usage: bash post-deploy.sh {check|upgrade|install} [--yes]
|
||||
set -u
|
||||
APP_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ARTISAN="php ${APP_DIR}/artisan"
|
||||
DIAGNOSE="php ${APP_DIR}/diagnose.php"
|
||||
RED='\033[31m'; GREEN='\033[32m'; YELLOW='\033[33m'
|
||||
CYAN='\033[36m'; BOLD='\033[1m'; NC='\033[0m'
|
||||
|
||||
MODE="${1:-check}"
|
||||
BATCH=false; [ "${2:-}" = "--yes" ] && BATCH=true
|
||||
|
||||
echo -e "${CYAN}${BOLD}============================================${NC}"
|
||||
echo -e "${CYAN}${BOLD} Glastree Post-Deploy — ${MODE}${NC}"
|
||||
echo -e "${CYAN}${BOLD}============================================${NC}"
|
||||
echo ""
|
||||
|
||||
IS_FRESH=false; [ ! -f "${APP_DIR}/.env" ] && IS_FRESH=true
|
||||
|
||||
log() { echo -e " ${BOLD}$1${NC}"; }
|
||||
ok() { echo -e " ${GREEN}✅ $1${NC}"; }
|
||||
warn() { echo -e " ${YELLOW}⚠ $1${NC}"; }
|
||||
fail() { echo -e " ${RED}❌ $1${NC}"; }
|
||||
|
||||
check_artisan() {
|
||||
if [ ! -f "${APP_DIR}/artisan" ]; then
|
||||
fail "artisan non trovato. Esegui da root dell'applicazione."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
run_step() {
|
||||
local step="$1" desc="$2"; shift 2
|
||||
log "${step} ${desc}"
|
||||
if "$@" 2>&1 | sed 's/^/ /'; then
|
||||
ok "${desc}"
|
||||
return 0
|
||||
else
|
||||
local rc=$?
|
||||
fail "${desc} — exit code ${rc}"
|
||||
return ${rc}
|
||||
fi
|
||||
}
|
||||
|
||||
run_diagnose() {
|
||||
if ${DIAGNOSE} 2>&1 | tail -5; then
|
||||
ok "Diagnostica OK"
|
||||
else
|
||||
warn "Diagnostica ha rilevato problemi (non bloccante)"
|
||||
fi
|
||||
}
|
||||
|
||||
run_check() {
|
||||
echo -e "${YELLOW}═══════════ CONTROLLO ═══════════${NC}" ; echo ""
|
||||
|
||||
echo -e "${BOLD}[0] Diagnostica sistema${NC}"
|
||||
run_diagnose ; echo ""
|
||||
|
||||
echo -e "${BOLD}[1] Ambiente${NC}"
|
||||
if $IS_FRESH; then warn ".env non trovato — fresh install necessario"
|
||||
else ok ".env presente"
|
||||
fi ; echo ""
|
||||
|
||||
echo -e "${BOLD}[2] Migration pendenti${NC}"
|
||||
if [ -f "${APP_DIR}/artisan" ]; then
|
||||
$ARTISAN migrate:status 2>&1 | grep -E '(Pending|Ran)' | head -5 || true
|
||||
PENDING=$(php -r "echo trim(shell_exec('${ARTISAN} migrate:status 2>&1 | grep -c Pending || true'));" 2>/dev/null || echo "0")
|
||||
if [ "${PENDING:-0}" -gt 0 ] 2>/dev/null; then
|
||||
warn "${PENDING} migration da eseguire"
|
||||
else
|
||||
ok "Nessuna migration pendente"
|
||||
fi
|
||||
else
|
||||
warn "artisan non disponibile — skip migration check"
|
||||
fi ; echo ""
|
||||
|
||||
echo -e "${BOLD}[3] Storage & Permessi${NC}"
|
||||
for d in storage/framework/cache/data storage/framework/sessions \
|
||||
storage/framework/views storage/logs bootstrap/cache \
|
||||
storage/app/public storage/app/backups; do
|
||||
p="${APP_DIR}/${d}"
|
||||
if [ -d "$p" ]; then
|
||||
[ -w "$p" ] && ok "${d}" || warn "${d} — non scrivibile"
|
||||
else
|
||||
warn "${d} — mancante"
|
||||
fi
|
||||
done ; echo ""
|
||||
|
||||
echo -e "${BOLD}[4] Symlink storage${NC}"
|
||||
if [ -L "${APP_DIR}/public/storage" ]; then
|
||||
ok "public/storage → $(readlink "${APP_DIR}/public/storage")"
|
||||
else
|
||||
warn "public/storage non presente"
|
||||
fi ; echo ""
|
||||
|
||||
echo -e "${BOLD}[5] Cache${NC}"
|
||||
for d in bootstrap/cache storage/framework/cache/data \
|
||||
storage/framework/sessions storage/framework/views; do
|
||||
c=$(find "${APP_DIR}/${d}" -type f 2>/dev/null | wc -l)
|
||||
[ "$c" -gt 0 ] && warn "${d}: ${c} file" || ok "${d}: vuoto"
|
||||
done
|
||||
echo "" ; echo -e "${YELLOW}═══════════ FINE CONTROLLO ═══════════${NC}"
|
||||
}
|
||||
|
||||
run_upgrade() {
|
||||
echo -e "${YELLOW}═══════════ UPGRADE ═══════════${NC}" ; check_artisan
|
||||
|
||||
run_step "[1/9]" "Directory storage" mkdir -p \
|
||||
"${APP_DIR}/storage/framework/cache/data" \
|
||||
"${APP_DIR}/storage/framework/sessions" \
|
||||
"${APP_DIR}/storage/framework/views" \
|
||||
"${APP_DIR}/storage/logs" "${APP_DIR}/bootstrap/cache" \
|
||||
"${APP_DIR}/storage/app/public" \
|
||||
"${APP_DIR}/storage/app/public/logos" \
|
||||
"${APP_DIR}/storage/app/public/documenti/eventi" \
|
||||
"${APP_DIR}/storage/app/backups" \
|
||||
"${APP_DIR}/storage/app/documenti" \
|
||||
"${APP_DIR}/storage/app/private/avatars/gruppi" || true
|
||||
|
||||
run_step "[2/9]" ".gitignore storage" bash -c \
|
||||
"echo '*'>'${APP_DIR}/storage/app/public/.gitignore'; echo '!.gitignore'>>'${APP_DIR}/storage/app/public/.gitignore'" || true
|
||||
|
||||
run_step "[3/9]" "Permessi" chmod -R 775 \
|
||||
"${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache" || true
|
||||
|
||||
run_step "[4/9]" "Symlink storage" bash -c \
|
||||
"rm -f '${APP_DIR}/public/storage'; ln -sf '../storage/app/public' '${APP_DIR}/public/storage'" || true
|
||||
|
||||
run_step "[5/10]" "Migrazioni DB" $ARTISAN migrate --force || true
|
||||
|
||||
run_step "[6/10]" "Seed diocesi" $ARTISAN db:seed --class=DiocesiSeeder --force || true
|
||||
|
||||
run_step "[7/10]" "Dump autoload" composer dump-autoload --no-dev --optimize || true
|
||||
|
||||
run_step "[8/10]" "Key generate" $ARTISAN key:generate --force || true
|
||||
|
||||
run_step "[9/10]" "Pulizia cache" bash -c \
|
||||
"$ARTISAN config:clear && $ARTISAN route:clear && $ARTISAN view:clear" || true
|
||||
|
||||
run_step "[10/10]" "Verifica finale" $DIAGNOSE || true
|
||||
|
||||
echo "" ; echo -e "${GREEN}${BOLD}✅ Upgrade completato.${NC}"
|
||||
}
|
||||
|
||||
run_install() {
|
||||
echo -e "${YELLOW}═══════════ INSTALL (fresh) ═══════════${NC}" ; check_artisan
|
||||
|
||||
run_step "[1/10]" "Directory storage" mkdir -p \
|
||||
"${APP_DIR}/storage/framework/cache/data" \
|
||||
"${APP_DIR}/storage/framework/sessions" \
|
||||
"${APP_DIR}/storage/framework/views" \
|
||||
"${APP_DIR}/storage/logs" "${APP_DIR}/bootstrap/cache" \
|
||||
"${APP_DIR}/storage/app/public" \
|
||||
"${APP_DIR}/storage/app/public/logos" \
|
||||
"${APP_DIR}/storage/app/public/documenti/eventi" \
|
||||
"${APP_DIR}/storage/app/backups" \
|
||||
"${APP_DIR}/storage/app/documenti" \
|
||||
"${APP_DIR}/storage/app/private/avatars/gruppi" || true
|
||||
|
||||
run_step "[2/10]" ".gitignore storage" bash -c \
|
||||
"echo '*'>'${APP_DIR}/storage/app/public/.gitignore'; echo '!.gitignore'>>'${APP_DIR}/storage/app/public/.gitignore'" || true
|
||||
|
||||
run_step "[3/10]" "Permessi" chmod -R 775 \
|
||||
"${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache" || true
|
||||
|
||||
run_step "[4/10]" "File .env" cp "${APP_DIR}/.env.example" "${APP_DIR}/.env" || true
|
||||
warn "MODIFICA .env con i tuoi dati DB/URL prima di proseguire"
|
||||
if ! $BATCH; then read -p " Fatto? (Invio per continuare) " _; fi
|
||||
|
||||
run_step "[5/10]" "Symlink storage" bash -c \
|
||||
"rm -f '${APP_DIR}/public/storage'; ln -sf '../storage/app/public' '${APP_DIR}/public/storage'" || true
|
||||
|
||||
run_step "[6/10]" "Dump autoload" composer dump-autoload --no-dev --optimize || true
|
||||
|
||||
run_step "[7/10]" "Key generate" $ARTISAN key:generate --force || true
|
||||
|
||||
run_step "[8/11]" "Migrazioni DB" $ARTISAN migrate --force || true
|
||||
|
||||
run_step "[9/11]" "Seed diocesi" $ARTISAN db:seed --class=DiocesiSeeder --force || true
|
||||
|
||||
run_step "[10/11]" "Pulizia cache" bash -c \
|
||||
"$ARTISAN config:clear && $ARTISAN route:clear && $ARTISAN view:clear" || true
|
||||
|
||||
run_step "[11/11]" "Verifica finale" $DIAGNOSE || true
|
||||
|
||||
echo "" ; echo -e "${GREEN}${BOLD}✅ Installazione completata.${NC}"
|
||||
}
|
||||
|
||||
case "$MODE" in
|
||||
check) run_check ;;
|
||||
upgrade)
|
||||
run_check ; echo ""
|
||||
if $BATCH; then run_upgrade
|
||||
else
|
||||
read -p "Procedere con l'upgrade? (s/N) " c
|
||||
[ "$c" = "s" ] || [ "$c" = "S" ] && run_upgrade || echo "Annullato."
|
||||
fi
|
||||
;;
|
||||
install) run_install ;;
|
||||
*)
|
||||
echo "Uso: bash post-deploy.sh {check|upgrade|install} [--yes]"
|
||||
echo " check — diagnostica (nessuna modifica)"
|
||||
echo " upgrade — aggiorna installazione esistente"
|
||||
echo " install — fresh install"
|
||||
echo " --yes — batch (nessuna conferma)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -46,9 +46,20 @@
|
||||
<td>{{ $backup['size_formatted'] }}</td>
|
||||
<td>
|
||||
@if($backup['manifest'])
|
||||
@php $components = $backup['manifest']['included_components'] ?? ['database']; @endphp
|
||||
<span class="badge badge-info" title="PHP {{ $backup['manifest']['php_version'] ?? '?' }}, Laravel {{ $backup['manifest']['laravel_version'] ?? '?' }}">
|
||||
<i class="fas fa-database mr-1"></i>DB
|
||||
</span>
|
||||
@if(in_array('env', $components))
|
||||
<span class="badge badge-secondary" title="Configurazione .env">
|
||||
<i class="fas fa-cog mr-1"></i>.env
|
||||
</span>
|
||||
@endif
|
||||
@if(in_array('files', $components))
|
||||
<span class="badge badge-warning" title="File caricati">
|
||||
<i class="fas fa-file mr-1"></i>Files
|
||||
</span>
|
||||
@endif
|
||||
@if(isset($backup['manifest']['db_name']))
|
||||
<small class="text-muted">{{ $backup['manifest']['db_name'] }}</small>
|
||||
@endif
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
<a href="#signature" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-signature"></i> Firma
|
||||
</a>
|
||||
<a href="#firme" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-pen-fancy"></i> Firme Multiple
|
||||
</a>
|
||||
<a href="#mittenti" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-user-plus"></i> Mittenti Aggiuntivi
|
||||
</a>
|
||||
@@ -197,12 +200,32 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="from_email">Email Mittente Ufficiale <small class="text-muted">(se diversa dall'indirizzo IMAP)</small></label>
|
||||
<input type="email" name="from_email" id="from_email" class="form-control"
|
||||
value="{{ $settings->from_email ?? '' }}"
|
||||
placeholder="es. ufficiale@esempio.it">
|
||||
<small class="text-muted">Se impostata, viene usata come mittente e Reply-To nelle email in uscita</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="from_name">Nome Mittente Ufficiale</label>
|
||||
<input type="text" name="from_name" id="from_name" class="form-control"
|
||||
value="{{ $settings->from_name ?? '' }}"
|
||||
placeholder="es. Nome Ufficiale">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="reply_to">Email Risposta (Reply-To)</label>
|
||||
<input type="email" name="reply_to" id="reply_to" class="form-control"
|
||||
value="{{ $settings->reply_to ?? '' }}">
|
||||
<small class="text-muted">Usato solo se "Email Mittente Ufficiale" non è impostata</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -284,6 +307,64 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="firme">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Firme Multiple</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-sm btn-success" data-toggle="modal" data-target="#firmaModal" onclick="resetFirmaForm()">
|
||||
<i class="fas fa-plus mr-1"></i> Nuova Firma
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($firme->count() > 0)
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Anteprima</th>
|
||||
<th>Predefinita</th>
|
||||
<th style="width: 140px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($firme as $firma)
|
||||
<tr>
|
||||
<td><strong>{{ $firma->nome }}</strong></td>
|
||||
<td><small class="text-muted">{!! Str::limit(strip_tags($firma->contenuto_html), 80) !!}</small></td>
|
||||
<td>
|
||||
@if($firma->is_default)
|
||||
<span class="badge badge-success"><i class="fas fa-check"></i> Default</span>
|
||||
@else
|
||||
<form action="{{ route('impostazioni.firma.default', $firma->id) }}" method="POST" class="d-inline" onsubmit="return confirm('Impostare questa firma come predefinita?')">
|
||||
@csrf @method('PUT')
|
||||
<button type="submit" class="btn btn-xs btn-outline-secondary">Imposta default</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-info" onclick="editFirma({{ $firma->id }}, '{{ addslashes($firma->nome) }}', '{{ addslashes($firma->contenuto_html ?? '') }}')">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<form action="{{ route('impostazioni.firma.destroy', $firma->id) }}" method="POST" class="d-inline" onsubmit="return confirm('Eliminare questa firma?')">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger"><i class="fas fa-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted">Nessuna firma multipla configurata. La firma predefinita nella scheda "Firma" verrà usata come fallback.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="mittenti">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
@@ -513,9 +594,47 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="firmaModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<form id="firmaForm" method="POST" action="{{ route('impostazioni.firma.store') }}">
|
||||
@csrf
|
||||
<input type="hidden" name="_method" id="firmaMethod" value="POST">
|
||||
<input type="hidden" name="id" id="firmaId" value="">
|
||||
<div class="modal-header bg-primary">
|
||||
<h5 class="modal-title" id="firmaModalTitle"><i class="fas fa-pen-fancy mr-2"></i>Nuova Firma</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="firma_nome">Nome *</label>
|
||||
<input type="text" name="nome" id="firma_nome" class="form-control" required placeholder="Es: Default, Personale, Ufficio">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Contenuto Firma (HTML)</label>
|
||||
<div id="firma-quill-editor" style="height: 200px;"></div>
|
||||
<input type="hidden" name="contenuto_html" id="firma_contenuto_input" value="">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="firma_is_default" name="is_default" value="1">
|
||||
<label class="custom-control-label" for="firma_is_default">Imposta come predefinita</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annulla</button>
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-save mr-1"></i> Salva Firma</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
let senderAccounts = @json($senderAccounts);
|
||||
let firmaQuillInstance = null;
|
||||
|
||||
function resetSenderForm() {
|
||||
document.getElementById('senderForm').action = '{{ route('impostazioni.sender.store') }}';
|
||||
@@ -696,13 +815,89 @@ function createEditor() {
|
||||
quillEditorInstance = quill;
|
||||
}
|
||||
|
||||
function resetFirmaForm() {
|
||||
document.getElementById('firmaForm').action = '{{ route('impostazioni.firma.store') }}';
|
||||
document.getElementById('firmaMethod').value = 'POST';
|
||||
document.getElementById('firmaModalTitle').textContent = 'Nuova Firma';
|
||||
document.getElementById('firmaForm').reset();
|
||||
document.getElementById('firmaId').value = '';
|
||||
if (firmaQuillInstance) {
|
||||
firmaQuillInstance.root.innerHTML = '';
|
||||
}
|
||||
document.getElementById('firma_contenuto_input').value = '';
|
||||
}
|
||||
|
||||
function editFirma(id, nome, contenuto) {
|
||||
document.getElementById('firmaForm').action = '{{ route('impostazioni.firma.update', '__ID__') }}'.replace('__ID__', id);
|
||||
document.getElementById('firmaMethod').value = 'PUT';
|
||||
document.getElementById('firmaModalTitle').textContent = 'Modifica Firma - ' + nome;
|
||||
document.getElementById('firmaId').value = id;
|
||||
document.getElementById('firma_nome').value = nome;
|
||||
if (firmaQuillInstance && contenuto) {
|
||||
firmaQuillInstance.root.innerHTML = contenuto;
|
||||
document.getElementById('firma_contenuto_input').value = contenuto;
|
||||
}
|
||||
$('#firmaModal').modal('show');
|
||||
}
|
||||
|
||||
function initFirmaQuill() {
|
||||
var container = document.querySelector('#firma-quill-editor');
|
||||
if (!container || firmaQuillInstance) return;
|
||||
|
||||
var quill = new Quill('#firma-quill-editor', {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline'],
|
||||
[{ 'color': [] }, { 'background': [] }],
|
||||
[{ 'header': 1 }, { 'header': 2 }, { 'header': 3 }],
|
||||
[{ 'align': [] }],
|
||||
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
|
||||
['link', 'image'],
|
||||
['clean']
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
var input = document.getElementById('firma_contenuto_input');
|
||||
|
||||
quill.on('text-change', function() {
|
||||
input.value = quill.root.innerHTML;
|
||||
});
|
||||
|
||||
firmaQuillInstance = quill;
|
||||
}
|
||||
|
||||
$('#firmaModal').on('shown.bs.modal', function () {
|
||||
if (typeof Quill === 'undefined') {
|
||||
var script = document.createElement('script');
|
||||
script.src = 'https://cdn.quilljs.com/1.3.7/quill.min.js';
|
||||
script.onload = function() {
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'https://cdn.quilljs.com/1.3.7/quill.snow.css';
|
||||
document.head.appendChild(link);
|
||||
setTimeout(initFirmaQuill, 200);
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
setTimeout(initFirmaQuill, 100);
|
||||
}
|
||||
});
|
||||
|
||||
function initSignatureEditor() {
|
||||
if (quillEditorInstance) return;
|
||||
|
||||
if (typeof Quill === 'undefined') {
|
||||
var script = document.createElement('script');
|
||||
script.src = 'https://cdn.quilljs.com/1.3.7/quill.min.js';
|
||||
script.onload = createEditor;
|
||||
script.onload = function() {
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'https://cdn.quilljs.com/1.3.7/quill.snow.css';
|
||||
document.head.appendChild(link);
|
||||
setTimeout(createEditor, 200);
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
|
||||
var link = document.createElement('link');
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="form-group">
|
||||
<label for="mittente_id">Da (mittente)</label>
|
||||
<select name="mittente_id" id="mittente_id" class="form-control">
|
||||
<option value="0">Sistema — {{ optional(\App\Models\EmailSetting::getActive())->email_address ?? 'Configura email' }}</option>
|
||||
<option value="0">Sistema — {{ optional(\App\Models\EmailSetting::getActive())->getEffectiveFromAddress() ?? 'Configura email' }}</option>
|
||||
@foreach($senderAccounts as $sender)
|
||||
<option value="{{ $sender->id }}">{{ $sender->email_name ?: $sender->email_address }} <{{ $sender->email_address }}></option>
|
||||
@endforeach
|
||||
@@ -45,7 +45,7 @@
|
||||
<div id="individui-input" class="destinatari-section" style="display: none;">
|
||||
<select name="individui[]" id="individui-select" class="form-control" multiple size="5">
|
||||
@foreach($individui as $ind)
|
||||
<option value="{{ $ind->id }}">{{ $ind->cognito }} {{ $ind->nome }} ({{ $ind->email_primaria ?? 'senza email' }})</option>
|
||||
<option value="{{ $ind->id }}">{{ $ind->nome }} {{ $ind->cognome }} ({{ $ind->email_primaria ?? 'senza email' }})</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Tieni premuto Ctrl per selezionare più individui</small>
|
||||
@@ -84,6 +84,19 @@
|
||||
<textarea name="body" id="body" class="form-control" rows="15" required>{{ $prefill['body'] ?? '' }}</textarea>
|
||||
</div>
|
||||
|
||||
@if($firme->count() > 0)
|
||||
<div class="form-group">
|
||||
<label for="firma_id">Firma</label>
|
||||
<select name="firma_id" id="firma_id" class="form-control">
|
||||
<option value="">Nessuna</option>
|
||||
@foreach($firme as $firma)
|
||||
<option value="{{ $firma->id }}">{{ $firma->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Seleziona la firma da appender al messaggio.</small>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="form-group">
|
||||
<label>Allegati</label>
|
||||
<div class="border rounded p-3 bg-light">
|
||||
|
||||
@@ -2,6 +2,26 @@
|
||||
@section('title', 'Nuovo Gruppo')
|
||||
@section('page_title', 'Nuovo Gruppo')
|
||||
|
||||
@push('styles')
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2-bootstrap4-theme@1.0.0/dist/select2-bootstrap4.min.css" rel="stylesheet" />
|
||||
<style>
|
||||
.select2-container--bootstrap4 .select2-selection--multiple .select2-selection__choice {
|
||||
color: #333;
|
||||
}
|
||||
.select2-container--bootstrap4 .select2-selection--multiple .select2-search__field {
|
||||
height: 28px !important;
|
||||
min-height: 28px !important;
|
||||
width: auto !important;
|
||||
min-width: 30px;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
margin: 0;
|
||||
padding: 0 4px;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<form action="/gruppi" method="POST">
|
||||
@csrf
|
||||
@@ -37,10 +57,9 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Diocesi</label>
|
||||
<select name="diocesi_id" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<select name="diocesi_ids[]" class="form-control select2-multi" multiple style="width: 100%;">
|
||||
@foreach($diocesi as $d)
|
||||
<option value="{{ $d->id }}" {{ old('diocesi_id') == $d->id ? 'selected' : '' }}>{{ $d->nome }}</option>
|
||||
<option value="{{ $d->id }}" {{ in_array($d->id, old('diocesi_ids', [])) ? 'selected' : '' }}>{{ $d->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@@ -89,6 +108,33 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-address-book mr-2"></i>Contatti del Gruppo</h3>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="aggiungiContatto()">
|
||||
<i class="fas fa-plus mr-1"></i> Aggiungi Contatto
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-bordered mb-0" id="contatti-table">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 20%;">Tipo</th>
|
||||
<th style="width: 40%;">Valore</th>
|
||||
<th style="width: 25%;">Etichetta</th>
|
||||
<th style="width: 80px;">Primario</th>
|
||||
<th style="width: 50px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="contatti-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="no-contatti-msg" class="text-center text-muted py-4">
|
||||
<p class="mb-0">Nessun contatto. Clicca su "Aggiungi Contatto" per iniziare.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-users mr-2"></i>Membri</h3>
|
||||
@@ -138,10 +184,69 @@
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('.select2-multi').select2({
|
||||
theme: 'bootstrap4',
|
||||
placeholder: 'Cerca diocesi...',
|
||||
width: '100%',
|
||||
sorter: function(data) {
|
||||
return data.sort(function(a, b) {
|
||||
if (a.selected && !b.selected) return -1;
|
||||
if (!a.selected && b.selected) return 1;
|
||||
return a.text.localeCompare(b.text, 'it');
|
||||
});
|
||||
},
|
||||
language: {
|
||||
noResults: function() { return 'Nessuna diocesi trovata'; },
|
||||
searching: function() { return 'Ricerca...'; }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let contattiIndex = 0;
|
||||
let membriIndex = 0;
|
||||
const membriResponsabiliMap = new Map();
|
||||
|
||||
function aggiungiContatto() {
|
||||
const tbody = document.getElementById('contatti-tbody');
|
||||
const noMsg = document.getElementById('no-contatti-msg');
|
||||
if (noMsg) noMsg.style.display = 'none';
|
||||
|
||||
const row = document.createElement('tr');
|
||||
const idx = contattiIndex;
|
||||
row.innerHTML = `
|
||||
<td>
|
||||
<select name="contatti[${idx}][tipo]" class="form-control" required>
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="email">Email</option>
|
||||
<option value="telefono">Telefono</option>
|
||||
<option value="cellulare">Cellulare</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" name="contatti[${idx}][valore]" class="form-control" placeholder="Es. info@example.com" required></td>
|
||||
<td><input type="text" name="contatti[${idx}][etichetta]" class="form-control" placeholder="Es. Ufficio"></td>
|
||||
<td class="text-center">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="contatto-primary-${idx}" name="contatti[${idx}][is_primary]" value="1">
|
||||
<label class="custom-control-label" for="contatto-primary-${idx}"></label>
|
||||
</div>
|
||||
</td>
|
||||
<td><button type="button" class="btn btn-danger btn-xs" onclick="rimuoviContatto(this)"><i class="fas fa-trash"></i></button></td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
contattiIndex++;
|
||||
}
|
||||
|
||||
function rimuoviContatto(btn) {
|
||||
const row = btn.closest('tr');
|
||||
row.remove();
|
||||
if (document.getElementById('contatti-tbody').children.length === 0) {
|
||||
document.getElementById('no-contatti-msg').style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
function aggiungiMembro() {
|
||||
const tbody = document.getElementById('membri-tbody');
|
||||
const noMsg = document.getElementById('no-membri-msg');
|
||||
|
||||
@@ -2,6 +2,26 @@
|
||||
@section('title', 'Modifica Gruppo')
|
||||
@section('page_title', 'Modifica Gruppo')
|
||||
|
||||
@push('styles')
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2-bootstrap4-theme@1.0.0/dist/select2-bootstrap4.min.css" rel="stylesheet" />
|
||||
<style>
|
||||
.select2-container--bootstrap4 .select2-selection--multiple .select2-selection__choice {
|
||||
color: #333;
|
||||
}
|
||||
.select2-container--bootstrap4 .select2-selection--multiple .select2-search__field {
|
||||
height: 28px !important;
|
||||
min-height: 28px !important;
|
||||
width: auto !important;
|
||||
min-width: 30px;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
margin: 0;
|
||||
padding: 0 4px;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<form action="/gruppi/{{ $gruppo->id }}" method="POST">
|
||||
@csrf @method('PUT')
|
||||
@@ -31,10 +51,9 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Diocesi</label>
|
||||
<select name="diocesi_id" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<select name="diocesi_ids[]" class="form-control select2-multi" multiple style="width: 100%;">
|
||||
@foreach($diocesi as $d)
|
||||
<option value="{{ $d->id }}" {{ old('diocesi_id', $gruppo->diocesi_id) == $d->id ? 'selected' : '' }}>{{ $d->nome }}</option>
|
||||
<option value="{{ $d->id }}" {{ in_array($d->id, old('diocesi_ids', $selectedDiocesiIds ?? [])) ? 'selected' : '' }}>{{ $d->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@@ -91,6 +110,55 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-address-book mr-2"></i>Contatti del Gruppo</h3>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="aggiungiContatto()">
|
||||
<i class="fas fa-plus mr-1"></i> Aggiungi Contatto
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-bordered mb-0" id="contatti-table">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 20%;">Tipo</th>
|
||||
<th style="width: 40%;">Valore</th>
|
||||
<th style="width: 25%;">Etichetta</th>
|
||||
<th style="width: 80px;">Primario</th>
|
||||
<th style="width: 50px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="contatti-tbody">
|
||||
@forelse(old('contatti', $gruppo->gruppoContatti) as $idx => $contatto)
|
||||
@php $contattoVal = is_array($contatto) ? $contatto : $contatto->toArray(); @endphp
|
||||
<tr>
|
||||
<td>
|
||||
<select name="contatti[{{ $idx }}][tipo]" class="form-control" required>
|
||||
<option value="email" {{ ($contattoVal['tipo'] ?? '') === 'email' ? 'selected' : '' }}>Email</option>
|
||||
<option value="telefono" {{ ($contattoVal['tipo'] ?? '') === 'telefono' ? 'selected' : '' }}>Telefono</option>
|
||||
<option value="cellulare" {{ ($contattoVal['tipo'] ?? '') === 'cellulare' ? 'selected' : '' }}>Cellulare</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" name="contatti[{{ $idx }}][valore]" class="form-control" value="{{ $contattoVal['valore'] ?? '' }}" required></td>
|
||||
<td><input type="text" name="contatti[{{ $idx }}][etichetta]" class="form-control" value="{{ $contattoVal['etichetta'] ?? '' }}" placeholder="Es. Ufficio"></td>
|
||||
<td class="text-center">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="contatto-primary-{{ $idx }}" name="contatti[{{ $idx }}][is_primary]" value="1" {{ !empty($contattoVal['is_primary']) ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="contatto-primary-{{ $idx }}"></label>
|
||||
</div>
|
||||
</td>
|
||||
<td><button type="button" class="btn btn-danger btn-xs" onclick="rimuoviContatto(this)"><i class="fas fa-trash"></i></button></td>
|
||||
</tr>
|
||||
@empty
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="no-contatti-msg" class="text-center text-muted py-4" style="{{ $gruppo->gruppoContatti->count() > 0 ? 'display:none;' : '' }}">
|
||||
<p class="mb-0">Nessun contatto. Clicca su "Aggiungi Contatto" per iniziare.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-users mr-2"></i>Membri</h3>
|
||||
@@ -352,7 +420,67 @@
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('.select2-multi').select2({
|
||||
theme: 'bootstrap4',
|
||||
placeholder: 'Cerca diocesi...',
|
||||
width: '100%',
|
||||
sorter: function(data) {
|
||||
return data.sort(function(a, b) {
|
||||
if (a.selected && !b.selected) return -1;
|
||||
if (!a.selected && b.selected) return 1;
|
||||
return a.text.localeCompare(b.text, 'it');
|
||||
});
|
||||
},
|
||||
language: {
|
||||
noResults: function() { return 'Nessuna diocesi trovata'; },
|
||||
searching: function() { return 'Ricerca...'; }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let contattiIndex = {{ old('contatti') ? count(old('contatti')) : $gruppo->gruppoContatti->count() }};
|
||||
|
||||
function aggiungiContatto() {
|
||||
const tbody = document.getElementById('contatti-tbody');
|
||||
const noMsg = document.getElementById('no-contatti-msg');
|
||||
if (noMsg) noMsg.style.display = 'none';
|
||||
|
||||
const row = document.createElement('tr');
|
||||
const idx = contattiIndex;
|
||||
row.innerHTML = `
|
||||
<td>
|
||||
<select name="contatti[${idx}][tipo]" class="form-control" required>
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="email">Email</option>
|
||||
<option value="telefono">Telefono</option>
|
||||
<option value="cellulare">Cellulare</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" name="contatti[${idx}][valore]" class="form-control" placeholder="Es. info@example.com" required></td>
|
||||
<td><input type="text" name="contatti[${idx}][etichetta]" class="form-control" placeholder="Es. Ufficio"></td>
|
||||
<td class="text-center">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="contatto-primary-${idx}" name="contatti[${idx}][is_primary]" value="1">
|
||||
<label class="custom-control-label" for="contatto-primary-${idx}"></label>
|
||||
</div>
|
||||
</td>
|
||||
<td><button type="button" class="btn btn-danger btn-xs" onclick="rimuoviContatto(this)"><i class="fas fa-trash"></i></button></td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
contattiIndex++;
|
||||
}
|
||||
|
||||
function rimuoviContatto(btn) {
|
||||
const row = btn.closest('tr');
|
||||
row.remove();
|
||||
if (document.getElementById('contatti-tbody').children.length === 0) {
|
||||
document.getElementById('no-contatti-msg').style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
function showMembroForm() {
|
||||
console.log('showMembroForm called');
|
||||
var card = document.getElementById('membro-card');
|
||||
|
||||
@@ -161,7 +161,7 @@ $entityType = $entityType ?? 'gruppi';
|
||||
<td>{{ Str::limit($gruppo->descrizione, 50) ?: '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('diocesi', $visibleColumns))
|
||||
<td>{{ $gruppo->diocesi?->nome ?? '-' }}</td>
|
||||
<td>{{ $gruppo->diocesi->count() > 0 ? $gruppo->diocesi->pluck('nome')->implode(', ') : '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('livello', $visibleColumns))
|
||||
<td><span class="badge badge-{{ ($gruppo->depth ?? 0) == 0 ? 'primary' : 'secondary' }}">{{ $gruppo->depth ?? 0 }}</span></td>
|
||||
@@ -176,7 +176,7 @@ $entityType = $entityType ?? 'gruppi';
|
||||
<td>
|
||||
@php $resp = $gruppo->getResponsabili() @endphp
|
||||
@if($resp->count() > 0)
|
||||
{{ $resp->pluck('cognome')->implode(', ') }}
|
||||
{{ $resp->map(fn($r) => $r->nome . ' ' . $r->cognome)->implode(', ') }}
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
@@ -189,10 +189,10 @@ $entityType = $entityType ?? 'gruppi';
|
||||
<td>{{ $gruppo->città_incontro ?: '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('telefono', $visibleColumns))
|
||||
<td>-</td>
|
||||
<td>{{ $gruppo->telefono_primario ?? '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('email', $visibleColumns))
|
||||
<td>-</td>
|
||||
<td>{{ $gruppo->email_primaria ?? '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('tag', $visibleColumns))
|
||||
<td>
|
||||
|
||||
@@ -20,7 +20,7 @@ $responsabili = $gruppo->getResponsabili();
|
||||
</a>
|
||||
<span class="badge badge-{{ ($gruppo->depth ?? 0) == 0 ? 'primary' : 'secondary' }} badge-level">{{ $gruppo->depth ?? 0 }}</span>
|
||||
<small class="text-muted ml-2">
|
||||
{{ $gruppo->diocesi?->nome ?? '' }}
|
||||
{{ $gruppo->diocesi->count() > 0 ? $gruppo->diocesi->pluck('nome')->implode(', ') : '' }}
|
||||
@if($responsabili->count() > 0)
|
||||
· <i class="fas fa-user text-info"></i> {{ $responsabili->pluck('cognome')->implode(', ') }}
|
||||
@endif
|
||||
|
||||
@@ -26,7 +26,7 @@ $canDeleteGruppi = Auth::user()->canDelete('gruppi');
|
||||
</div>
|
||||
@endif
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-12">
|
||||
<div class="card card-success">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
@@ -35,52 +35,113 @@ $canDeleteGruppi = Auth::user()->canDelete('gruppi');
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm table-borderless">
|
||||
<tr>
|
||||
<td style="width: 130px;"><strong>Path:</strong></td>
|
||||
<td class="text-muted small">{{ $gruppo->full_path }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Gruppo Padre:</strong></td>
|
||||
<td>
|
||||
@if($gruppo->parent)
|
||||
<a href="{{ route('gruppi.show', $gruppo->parent->id) }}">{{ $gruppo->parent->nome }}</a>
|
||||
@else
|
||||
<span class="badge badge-success">Gruppo radice</span>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr>
|
||||
<td style="width: 130px;"><strong>Path:</strong></td>
|
||||
<td class="text-muted small">{{ $gruppo->full_path }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Gruppo Padre:</strong></td>
|
||||
<td>
|
||||
@if($gruppo->parent)
|
||||
<a href="{{ route('gruppi.show', $gruppo->parent->id) }}">{{ $gruppo->parent->nome }}</a>
|
||||
@else
|
||||
<span class="badge badge-success">Gruppo radice</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Diocesi:</strong></td>
|
||||
<td>
|
||||
@if($gruppo->diocesi->count() > 0)
|
||||
@foreach($gruppo->diocesi as $diocesi)
|
||||
<span class="badge badge-secondary mr-1">{{ $diocesi->nome }}</span>
|
||||
@endforeach
|
||||
@else
|
||||
<span class="text-muted">-</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr>
|
||||
<td style="width: 130px;"><strong>Responsabili:</strong></td>
|
||||
<td>
|
||||
@php $responsabili = $gruppo->getResponsabili() @endphp
|
||||
@if($responsabili->count() > 0)
|
||||
@foreach($responsabili as $resp)
|
||||
<a href="{{ route('individui.show', $resp->id) }}">
|
||||
<i class="fas fa-user text-info mr-1"></i>
|
||||
{{ $resp->nome_completo }}
|
||||
</a><br>
|
||||
@endforeach
|
||||
@else
|
||||
<span class="text-muted">-</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Email:</strong></td>
|
||||
<td>{{ $gruppo->email_primaria ?? '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Telefono:</strong></td>
|
||||
<td>{{ $gruppo->telefono_primario ?? '-' }}</td>
|
||||
</tr>
|
||||
@if($gruppo->gruppoContatti->count() > 0)
|
||||
<tr>
|
||||
<td><strong>Altri Contatti:</strong></td>
|
||||
<td>
|
||||
@foreach($gruppo->gruppoContatti as $contatto)
|
||||
@if(!$contatto->is_primary || ($contatto->tipo !== 'email' && $contatto->tipo !== 'telefono' && $contatto->tipo !== 'cellulare'))
|
||||
<div>
|
||||
@if($contatto->etichetta)<small class="text-muted">{{ $contatto->etichetta }}:</small>@endif
|
||||
<span>{{ $contatto->valore }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Diocesi:</strong></td>
|
||||
<td>{{ $gruppo->diocesi?->nome ?? '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Responsabili:</strong></td>
|
||||
<td>
|
||||
@php $responsabili = $gruppo->getResponsabili() @endphp
|
||||
@if($responsabili->count() > 0)
|
||||
@foreach($responsabili as $resp)
|
||||
<a href="{{ route('individui.show', $resp->id) }}">
|
||||
<i class="fas fa-user text-info mr-1"></i>
|
||||
{{ $resp->nome_completo }}
|
||||
</a><br>
|
||||
@endforeach
|
||||
@else
|
||||
<span class="text-muted">-</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
</div>
|
||||
|
||||
@php $incontroEvento = $gruppo->eventi->where('is_incontro_gruppo', true)->first(); @endphp
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-chart-pie mr-2"></i>Statistiche</h3></div>
|
||||
<div class="card-body d-flex align-items-center">
|
||||
<div class="row text-center w-100">
|
||||
<div class="col-6">
|
||||
<h2 class="mb-0 text-primary">{{ $gruppo->individui->count() }}</h2>
|
||||
<small class="text-muted">Membri</small>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h2 class="mb-0 text-warning">{{ $gruppo->children->count() }}</h2>
|
||||
<small class="text-muted">Sottogruppi</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-map-marker-alt mr-2"></i>Luogo Incontro</h3></div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr>
|
||||
<td style="width: 100px;"><strong>Indirizzo:</strong></td>
|
||||
<td style="width: 80px;"><strong>Indirizzo:</strong></td>
|
||||
<td>{{ $gruppo->indirizzo_incontro ?: '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -94,34 +155,78 @@ $canDeleteGruppi = Auth::user()->canDelete('gruppi');
|
||||
</table>
|
||||
@if($gruppo->mappa_posizione)
|
||||
<a href="{{ $gruppo->mappa_posizione }}" target="_blank" class="btn btn-sm btn-outline-primary mt-2">
|
||||
<i class="fas fa-external-link-alt mr-1"></i> Visualizza su mappa
|
||||
<i class="fas fa-external-link-alt mr-1"></i> Mappa
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-chart-pie mr-2"></i>Statistiche</h3></div>
|
||||
@if($incontroEvento)
|
||||
<div class="col-md-3">
|
||||
<div class="card card-success h-100">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-calendar-check mr-2"></i>
|
||||
Giorno di Incontro
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row text-center">
|
||||
<div class="col-6">
|
||||
<div class="border-right">
|
||||
<h2 class="mb-0 text-primary">{{ $gruppo->individui->count() }}</h2>
|
||||
<small class="text-muted">Membri</small>
|
||||
</div>
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr>
|
||||
<td style="width: 70px;"><strong>Evento:</strong></td>
|
||||
<td>{{ $incontroEvento->nome_evento }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Quando:</strong></td>
|
||||
<td>
|
||||
@if($incontroEvento->tipo_recorrenza === 'settimanale')
|
||||
{{ $incontroEvento->giorno_settimana_label }}
|
||||
<span class="badge badge-info ml-1">Sett.</span>
|
||||
@elseif($incontroEvento->tipo_recorrenza === 'mensile')
|
||||
{{ $incontroEvento->occorrenza_mensile_label }}
|
||||
<span class="badge badge-info ml-1">Mens.</span>
|
||||
@elseif($incontroEvento->tipo_recorrenza === 'annuale')
|
||||
{{ $incontroEvento->mese_annuale_label }}
|
||||
<span class="badge badge-info ml-1">Ann.</span>
|
||||
@elseif($incontroEvento->tipo_recorrenza === 'altro')
|
||||
{{ $incontroEvento->giorno_settimana_label }}
|
||||
<span class="badge badge-info ml-1">Altro</span>
|
||||
@else
|
||||
{{ $incontroEvento->data_specifica?->format('d/m/Y') ?: '-' }}
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@if($incontroEvento->ora_inizio)
|
||||
<tr>
|
||||
<td><strong>Ora:</strong></td>
|
||||
<td>ore {{ $incontroEvento->ora_inizio->format('H:i') }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
</table>
|
||||
@if($incontroEvento->responsabili->count() > 0)
|
||||
<hr>
|
||||
<small class="text-muted"><strong>Resp.:</strong></small>
|
||||
@foreach($incontroEvento->responsabili as $resp)
|
||||
<div class="mt-1">
|
||||
<i class="fas fa-user text-info mr-1"></i>
|
||||
<a href="/individui/{{ $resp->id }}">{{ $resp->cognome }} {{ $resp->nome }}</a>
|
||||
@if($resp->telefono_primario)
|
||||
<br><small class="text-success ml-3">{{ $resp->telefono_primario }}</small>
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h2 class="mb-0 text-warning">{{ $gruppo->children->count() }}</h2>
|
||||
<small class="text-muted">Sottogruppi</small>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
<div class="mt-2">
|
||||
<a href="/eventi/{{ $incontroEvento->id }}" class="btn btn-xs btn-outline-primary">
|
||||
<i class="fas fa-eye mr-1"></i> Vedi
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
@endif
|
||||
<div class="col-md-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-camera mr-2"></i>Avatar</h3>
|
||||
</div>
|
||||
@@ -130,113 +235,38 @@ $canDeleteGruppi = Auth::user()->canDelete('gruppi');
|
||||
<img src="{{ $gruppo->avatar_url }}"
|
||||
alt="{{ $gruppo->nome }}"
|
||||
class="img-thumbnail mb-2"
|
||||
style="max-width: 120px; max-height: 120px; object-fit: cover; border-radius: 50%;">
|
||||
style="max-width: 100px; max-height: 100px; object-fit: cover; border-radius: 50%;">
|
||||
<br>
|
||||
<form id="remove-avatar-form" class="d-inline">
|
||||
<button type="button" class="btn btn-danger btn-sm" onclick="removeAvatar()">
|
||||
<i class="fas fa-trash mr-1"></i>Rimuovi
|
||||
<i class="fas fa-trash mr-1"></i>
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<div class="bg-secondary d-flex align-items-center justify-content-center mb-3"
|
||||
style="width: 120px; height: 120px; margin: 0 auto; border-radius: 50%;">
|
||||
<i class="fas fa-folder fa-4x text-white"></i>
|
||||
<div class="bg-secondary d-flex align-items-center justify-content-center mx-auto mb-3"
|
||||
style="width: 100px; height: 100px; border-radius: 50%;">
|
||||
<i class="fas fa-folder fa-3x text-white"></i>
|
||||
</div>
|
||||
@endif
|
||||
<form id="avatar-upload-form" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<div class="custom-file mb-2">
|
||||
<input type="file" class="custom-file-input" id="avatar-input" name="avatar" accept="image/jpeg,image/png,image/gif,image/webp">
|
||||
<label class="custom-file-label" for="avatar-input">Scegli immagine...</label>
|
||||
<label class="custom-file-label" for="avatar-input">Scegli...</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="uploadAvatar()">
|
||||
<i class="fas fa-upload mr-1"></i>Carica
|
||||
<i class="fas fa-upload mr-1"></i> Carica
|
||||
</button>
|
||||
</form>
|
||||
<div id="avatar-message" class="mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$incontroEvento = $gruppo->eventi->where('is_incontro_gruppo', true)->first();
|
||||
@endphp
|
||||
@if($incontroEvento)
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card card-success">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-calendar-check mr-2"></i>
|
||||
Giorno di Incontro
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr>
|
||||
<td style="width: 100px;"><strong>Evento:</strong></td>
|
||||
<td>{{ $incontroEvento->nome_evento }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Quando:</strong></td>
|
||||
<td>
|
||||
@if($incontroEvento->tipo_recorrenza === 'settimanale')
|
||||
{{ $incontroEvento->giorno_settimana_label }}
|
||||
<span class="badge badge-info ml-1">Settimanale</span>
|
||||
@elseif($incontroEvento->tipo_recorrenza === 'mensile')
|
||||
{{ $incontroEvento->occorrenza_mensile_label }}
|
||||
@if($incontroEvento->mesi_recorrenza)
|
||||
<small class="text-muted">({{ $incontroEvento->mesi_recorrenza_label }})</small>
|
||||
@endif
|
||||
<span class="badge badge-info ml-1">Mensile</span>
|
||||
@elseif($incontroEvento->tipo_recorrenza === 'annuale')
|
||||
{{ $incontroEvento->mese_annuale_label }}
|
||||
@if($incontroEvento->giorno_mese)
|
||||
, giorno {{ $incontroEvento->giorno_mese }}
|
||||
@endif
|
||||
<span class="badge badge-info ml-1">Annuale</span>
|
||||
@elseif($incontroEvento->tipo_recorrenza === 'altro')
|
||||
{{ $incontroEvento->giorno_settimana_label }}
|
||||
<span class="badge badge-info ml-1">Altro</span>
|
||||
@else
|
||||
{{ $incontroEvento->data_specifica?->format('d/m/Y') ?: '-' }}
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@if($incontroEvento->ora_inizio)
|
||||
<tr>
|
||||
<td><strong>Ora:</strong></td>
|
||||
<td>ore {{ $incontroEvento->ora_inizio->format('H:i') }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
</table>
|
||||
@if($incontroEvento->responsabili->count() > 0)
|
||||
<hr>
|
||||
<small class="text-muted"><strong>Responsabili:</strong></small>
|
||||
@foreach($incontroEvento->responsabili as $resp)
|
||||
<div class="mt-1">
|
||||
<i class="fas fa-user text-info mr-1"></i>
|
||||
<a href="/individui/{{ $resp->id }}">{{ $resp->cognome }} {{ $resp->nome }}</a>
|
||||
@if($resp->telefono_primario)
|
||||
<br><small class="text-success ml-3">{{ $resp->telefono_primario }}</small>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
<div class="mt-2">
|
||||
<a href="/eventi/{{ $incontroEvento->id }}" class="btn btn-xs btn-outline-primary">
|
||||
<i class="fas fa-eye mr-1"></i> Vedi evento
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($gruppo->descrizione)
|
||||
@if($gruppo->descrizione)
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-8">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-align-left mr-2"></i>Descrizione</h3></div>
|
||||
<div class="card-body">
|
||||
@@ -413,7 +443,7 @@ $canDeleteGruppi = Auth::user()->canDelete('gruppi');
|
||||
{{ $child->nome }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ $child->diocesi?->nome ?? '-' }}</td>
|
||||
<td>@if($child->diocesi->count() > 0)@foreach($child->diocesi as $dio)<span class="badge badge-secondary mr-1">{{ $dio->nome }}</span>@endforeach @else <span class="text-muted">-</span>@endif</td>
|
||||
<td>{{ $child->getResponsabili()->isNotEmpty() ? $child->getResponsabili()->first()->nome_completo : '-' }}</td>
|
||||
<td class="text-center">
|
||||
@if($child->individui()->count() > 0)
|
||||
|
||||
@@ -384,6 +384,7 @@
|
||||
<li class="nav-item"><a href="#email-account" class="nav-link" data-toggle="tab">Account</a></li>
|
||||
<li class="nav-item"><a href="#email-sync" class="nav-link" data-toggle="tab">Sincronizzazione</a></li>
|
||||
<li class="nav-item"><a href="#email-firma" class="nav-link" data-toggle="tab">Firma</a></li>
|
||||
<li class="nav-item"><a href="#email-firme" class="nav-link" data-toggle="tab">Firme Multiple</a></li>
|
||||
<li class="nav-item"><a href="#email-mittenti" class="nav-link" data-toggle="tab">Mittenti Aggiuntivi</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -538,12 +539,32 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="from_email">Email Mittente Ufficiale <small class="text-muted">(se diversa dall'indirizzo IMAP)</small></label>
|
||||
<input type="email" name="from_email" id="from_email" class="form-control"
|
||||
value="{{ $emailSettings->from_email ?? '' }}"
|
||||
placeholder="es. ufficiale@esempio.it">
|
||||
<small class="text-muted">Se impostata, viene usata come mittente e Reply-To nelle email in uscita</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="from_name">Nome Mittente Ufficiale</label>
|
||||
<input type="text" name="from_name" id="from_name" class="form-control"
|
||||
value="{{ $emailSettings->from_name ?? '' }}"
|
||||
placeholder="es. Nome Ufficiale">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="reply_to">Email Risposta (Reply-To)</label>
|
||||
<input type="email" name="reply_to" id="reply_to" class="form-control"
|
||||
value="{{ $emailSettings->reply_to ?? '' }}">
|
||||
<small class="text-muted">Usato solo se "Email Mittente Ufficiale" non è impostata</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -625,6 +646,47 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="email-firme">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Firme Multiple</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-sm btn-success" onclick="window.location.href='/impostazioni/email#firme'">
|
||||
<i class="fas fa-plus mr-1"></i> Gestisci Firme
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@php $firme = optional(\App\Models\EmailSetting::getActive())->firme ?? collect(); @endphp
|
||||
@if($firme->count() > 0)
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Anteprima</th>
|
||||
<th>Predefinita</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($firme as $firma)
|
||||
<tr>
|
||||
<td><strong>{{ $firma->nome }}</strong></td>
|
||||
<td><small class="text-muted">{!! Str::limit(strip_tags($firma->contenuto_html), 80) !!}</small></td>
|
||||
<td>@if($firma->is_default)<span class="badge badge-success"><i class="fas fa-check"></i> Default</span>@endif</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted">Nessuna firma multipla configurata. <a href="/impostazioni/email#firme">Gestisci firme nelle impostazioni avanzate.</a></p>
|
||||
@endif
|
||||
<p class="mt-2"><a href="/impostazioni/email#firme" class="btn btn-sm btn-primary"><i class="fas fa-cog mr-1"></i> Gestione completa firme</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="email-mittenti">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
|
||||
@@ -226,7 +226,7 @@
|
||||
<i class="fas fa-folder text-warning mr-1"></i>
|
||||
<a href="/gruppi/{{ $gruppo->id }}">{{ $gruppo->nome }}</a>
|
||||
</td>
|
||||
<td>{{ $gruppo->diocesi?->nome ?: '-' }}</td>
|
||||
<td>{{ $gruppo->diocesi->count() > 0 ? $gruppo->diocesi->pluck('nome')->implode(', ') : '-' }}</td>
|
||||
<td>
|
||||
@php $ruoliIndividuo = $individuo->getRuoliForGruppo($gruppo->id) @endphp
|
||||
@if($ruoliIndividuo->count() > 0)
|
||||
|
||||
@@ -256,7 +256,7 @@ if (in_array($contatto->tipo, ['web', 'telegram'])) {
|
||||
<i class="fas fa-folder text-warning mr-1"></i>
|
||||
<a href="{{ url('/gruppi/' . $gruppo->id) }}">{{ $gruppo->nome }}</a>
|
||||
</td>
|
||||
<td>{{ $gruppo->diocesi?->nome ?: '-' }}</td>
|
||||
<td>{{ $gruppo->diocesi->count() > 0 ? $gruppo->diocesi->pluck('nome')->implode(', ') : '-' }}</td>
|
||||
<td>{{ $gruppo->getResponsabili()->isNotEmpty() ? $gruppo->getResponsabili()->first()->nome_completo : '-' }}</td>
|
||||
<td>
|
||||
@php
|
||||
|
||||
@@ -34,6 +34,32 @@
|
||||
<hr>
|
||||
@include('partials._tag-selector', ['allTags' => $tags ?? collect(), 'selectedTags' => old('tags', []), 'label' => 'Tag'])
|
||||
|
||||
@if(isset($firme) && $firme->count() > 0)
|
||||
<div class="form-group">
|
||||
<label for="firma_id">Firma predefinita</label>
|
||||
<select name="firma_id" id="firma_id" class="form-control">
|
||||
<option value="">Nessuna</option>
|
||||
@foreach($firme as $firma)
|
||||
<option value="{{ $firma->id }}">{{ $firma->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Firma da appender automaticamente quando invii email da questa lista.</small>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(isset($senderAccounts) && $senderAccounts->count() > 0)
|
||||
<div class="form-group">
|
||||
<label for="sender_account_id">Mittente predefinito</label>
|
||||
<select name="sender_account_id" id="sender_account_id" class="form-control">
|
||||
<option value="">Sistema predefinito</option>
|
||||
@foreach($senderAccounts as $sender)
|
||||
<option value="{{ $sender->id }}">{{ $sender->email_name ?: $sender->email_address }} <{{ $sender->email_address }}></option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Mittente da usare automaticamente quando invii email da questa lista.</small>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
<strong>Come aggiungere contatti:</strong> vai su <em>Individui</em>, seleziona le righe desired, e usa il menu <em>Azioni > Crea Mailing List</em> per creare una lista dai contatti selezionati.
|
||||
|
||||
@@ -34,6 +34,32 @@
|
||||
|
||||
@include('partials._tag-selector', ['allTags' => $tags ?? collect(), 'selectedTags' => $selectedTags ?? [], 'label' => 'Tag'])
|
||||
|
||||
@if(isset($firme) && $firme->count() > 0)
|
||||
<div class="form-group">
|
||||
<label for="firma_id">Firma predefinita</label>
|
||||
<select name="firma_id" id="firma_id" class="form-control">
|
||||
<option value="">Nessuna</option>
|
||||
@foreach($firme as $firma)
|
||||
<option value="{{ $firma->id }}" {{ $mailingList->firma_id == $firma->id ? 'selected' : '' }}>{{ $firma->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Firma da appender automaticamente quando invii email da questa lista.</small>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(isset($senderAccounts) && $senderAccounts->count() > 0)
|
||||
<div class="form-group">
|
||||
<label for="sender_account_id">Mittente predefinito</label>
|
||||
<select name="sender_account_id" id="sender_account_id" class="form-control">
|
||||
<option value="">Sistema predefinito</option>
|
||||
@foreach($senderAccounts as $sender)
|
||||
<option value="{{ $sender->id }}" {{ $mailingList->sender_account_id == $sender->id ? 'selected' : '' }}>{{ $sender->email_name ?: $sender->email_address }} <{{ $sender->email_address }}></option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Mittente da usare automaticamente quando invii email da questa lista.</small>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
@@ -65,6 +91,9 @@
|
||||
<button type="button" class="btn btn-warning" onclick="deselezionaTutti()">
|
||||
<i class="fas fa-square mr-1"></i> Deseleziona tutti
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" onclick="rimuoviSelezionati()">
|
||||
<i class="fas fa-trash mr-1"></i> Rimuovi selezionati
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="no-email-warning" class="alert alert-warning alert-dismissible" role="alert" style="display:none;">
|
||||
@@ -313,9 +342,26 @@ function renderContattiTable() {
|
||||
}
|
||||
|
||||
function rimuoviContatto(btn) {
|
||||
btn.closest('tr').remove();
|
||||
const count = document.querySelectorAll('.contatto-checkbox').length;
|
||||
document.getElementById('contatti-count').textContent = count;
|
||||
const row = btn.closest('tr');
|
||||
const cb = row.querySelector('.contatto-checkbox');
|
||||
const id = parseInt(cb.value);
|
||||
contattiCaricati = contattiCaricati.filter(function(c) { return c.individuo_id !== id; });
|
||||
row.remove();
|
||||
document.getElementById('contatti-count').textContent = contattiCaricati.length;
|
||||
}
|
||||
|
||||
function rimuoviSelezionati() {
|
||||
const checkboxes = document.querySelectorAll('.contatto-checkbox:checked');
|
||||
if (checkboxes.length === 0) {
|
||||
alert('Seleziona almeno un contatto da rimuovere.');
|
||||
return;
|
||||
}
|
||||
checkboxes.forEach(function(cb) {
|
||||
const id = parseInt(cb.value);
|
||||
contattiCaricati = contattiCaricati.filter(function(c) { return c.individuo_id !== id; });
|
||||
cb.closest('tr').remove();
|
||||
});
|
||||
document.getElementById('contatti-count').textContent = contattiCaricati.length;
|
||||
}
|
||||
|
||||
function toggleSelectAll(source) {
|
||||
|
||||
@@ -26,6 +26,19 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(isset($firme) && $firme->count() > 0)
|
||||
<div class="form-group">
|
||||
<label for="firma_id">Firma</label>
|
||||
<select name="firma_id" id="firma_id" class="form-control">
|
||||
<option value="">Nessuna</option>
|
||||
@foreach($firme as $firma)
|
||||
<option value="{{ $firma->id }}">{{ $firma->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Seleziona la firma da appender al messaggio.</small>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="form-group">
|
||||
<label>Liste Destinatari *</label>
|
||||
<select name="liste[]" id="liste" class="form-control" multiple required size="4">
|
||||
|
||||
@@ -27,6 +27,19 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(isset($firme) && $firme->count() > 0)
|
||||
<div class="form-group">
|
||||
<label for="firma_id">Firma</label>
|
||||
<select name="firma_id" id="firma_id" class="form-control">
|
||||
<option value="">Nessuna</option>
|
||||
@foreach($firme as $firma)
|
||||
<option value="{{ $firma->id }}">{{ $firma->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Seleziona la firma da appender al messaggio.</small>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($individui->count() > 0)
|
||||
<div class="form-group" id="destinatari-form-group">
|
||||
<label>Destinatari selezionati</label>
|
||||
|
||||
@@ -211,6 +211,11 @@ Route::put('/impostazioni/sender/{id}', [EmailSettingsController::class, 'sender
|
||||
Route::delete('/impostazioni/sender/{id}', [EmailSettingsController::class, 'senderDestroy'])->middleware('auth')->name('impostazioni.sender.destroy');
|
||||
Route::post('/impostazioni/sender/{id}/test', [EmailSettingsController::class, 'senderTestSmtp'])->middleware('auth')->name('impostazioni.sender.test');
|
||||
|
||||
Route::post('/impostazioni/firma', [EmailSettingsController::class, 'firmaStore'])->middleware('auth')->name('impostazioni.firma.store');
|
||||
Route::put('/impostazioni/firma/{id}', [EmailSettingsController::class, 'firmaUpdate'])->middleware('auth')->name('impostazioni.firma.update');
|
||||
Route::put('/impostazioni/firma/{id}/default', [EmailSettingsController::class, 'firmaSetDefault'])->middleware('auth')->name('impostazioni.firma.default');
|
||||
Route::delete('/impostazioni/firma/{id}', [EmailSettingsController::class, 'firmaDestroy'])->middleware('auth')->name('impostazioni.firma.destroy');
|
||||
|
||||
// Calendar Connections (CalDAV / Google Calendar)
|
||||
Route::post('/calendario-connessioni', [App\Http\Controllers\CalendarioConnessioneController::class, 'store'])->middleware('auth')->name('calendario-connessioni.store');
|
||||
Route::put('/calendario-connessioni/{id}', [App\Http\Controllers\CalendarioConnessioneController::class, 'update'])->middleware('auth')->name('calendario-connessioni.update');
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Proxy PHP file generated by Composer
|
||||
*
|
||||
* This file includes the referenced bin path (../phpunit/phpunit/phpunit)
|
||||
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||
*
|
||||
* @generated
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||
$GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'] = $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'] = array(realpath(__DIR__ . '/..'.'/phpunit/phpunit/phpunit'));
|
||||
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BinProxyWrapper
|
||||
{
|
||||
private $handle;
|
||||
private $position;
|
||||
private $realpath;
|
||||
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||
$opened_path = substr($path, 17);
|
||||
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||
$opened_path = 'phpvfscomposer://'.$this->realpath;
|
||||
$this->handle = fopen($this->realpath, $mode);
|
||||
$this->position = 0;
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
$data = fread($this->handle, $count);
|
||||
|
||||
if ($this->position === 0) {
|
||||
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||
}
|
||||
$data = str_replace('__DIR__', var_export(dirname($this->realpath), true), $data);
|
||||
$data = str_replace('__FILE__', var_export($this->realpath, true), $data);
|
||||
|
||||
$this->position += strlen($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function stream_cast($castAs)
|
||||
{
|
||||
return $this->handle;
|
||||
}
|
||||
|
||||
public function stream_close()
|
||||
{
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
public function stream_lock($operation)
|
||||
{
|
||||
return $operation ? flock($this->handle, $operation) : true;
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||
$this->position = ftell($this->handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
return feof($this->handle);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function stream_set_option($option, $arg1, $arg2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function url_stat($path, $flags)
|
||||
{
|
||||
$path = substr($path, 17);
|
||||
if (file_exists($path)) {
|
||||
return stat($path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||
) {
|
||||
return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpunit/phpunit/phpunit');
|
||||
}
|
||||
}
|
||||
|
||||
return include __DIR__ . '/..'.'/phpunit/phpunit/phpunit';
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Proxy PHP file generated by Composer
|
||||
*
|
||||
* This file includes the referenced bin path (../laravel/pint/builds/pint)
|
||||
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||
*
|
||||
* @generated
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BinProxyWrapper
|
||||
{
|
||||
private $handle;
|
||||
private $position;
|
||||
private $realpath;
|
||||
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||
$opened_path = substr($path, 17);
|
||||
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||
$opened_path = $this->realpath;
|
||||
$this->handle = fopen($this->realpath, $mode);
|
||||
$this->position = 0;
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
$data = fread($this->handle, $count);
|
||||
|
||||
if ($this->position === 0) {
|
||||
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||
}
|
||||
|
||||
$this->position += strlen($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function stream_cast($castAs)
|
||||
{
|
||||
return $this->handle;
|
||||
}
|
||||
|
||||
public function stream_close()
|
||||
{
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
public function stream_lock($operation)
|
||||
{
|
||||
return $operation ? flock($this->handle, $operation) : true;
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||
$this->position = ftell($this->handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
return feof($this->handle);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function stream_set_option($option, $arg1, $arg2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function url_stat($path, $flags)
|
||||
{
|
||||
$path = substr($path, 17);
|
||||
if (file_exists($path)) {
|
||||
return stat($path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||
) {
|
||||
return include("phpvfscomposer://" . __DIR__ . '/..'.'/laravel/pint/builds/pint');
|
||||
}
|
||||
}
|
||||
|
||||
return include __DIR__ . '/..'.'/laravel/pint/builds/pint';
|
||||
+2096
File diff suppressed because it is too large
Load Diff
Vendored
+11
-4
@@ -11,11 +11,11 @@ return array(
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'383eaff206634a77a1be54e64e6459c7' => $vendorDir . '/sabre/uri/lib/functions.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
|
||||
'383eaff206634a77a1be54e64e6459c7' => $vendorDir . '/sabre/uri/lib/functions.php',
|
||||
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
@@ -27,13 +27,15 @@ return array(
|
||||
'2203a247e6fda86070a5e4e07aed533a' => $vendorDir . '/symfony/clock/Resources/now.php',
|
||||
'606a39d89246991a373564698c2d8383' => $vendorDir . '/symfony/polyfill-php85/bootstrap.php',
|
||||
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
|
||||
'35a6ad97d21e794e7e22a17d806652e4' => $vendorDir . '/nunomaduro/termwind/src/Functions.php',
|
||||
'b33e3d135e5d9e47d845c576147bda89' => $vendorDir . '/php-di/php-di/src/functions.php',
|
||||
'1f87db08236948d07391152dccb70f04' => $vendorDir . '/google/apiclient-services/autoload.php',
|
||||
'ebdb698ed4152ae445614b69b5e4bb6a' => $vendorDir . '/sabre/http/lib/functions.php',
|
||||
'09f6b20656683369174dd6fa83b7e5fb' => $vendorDir . '/symfony/polyfill-uuid/bootstrap.php',
|
||||
'a8d3953fd9959404dd22d3dfcd0a79f0' => $vendorDir . '/google/apiclient/src/aliases.php',
|
||||
'23dd7ece5822da3d0100ef3deb0ef55f' => $vendorDir . '/laravel/agent-detector/src/functions.php',
|
||||
'47e1160838b5e5a10346ac4084b58c23' => $vendorDir . '/laravel/prompts/src/helpers.php',
|
||||
'35a6ad97d21e794e7e22a17d806652e4' => $vendorDir . '/nunomaduro/termwind/src/Functions.php',
|
||||
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
|
||||
'801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php',
|
||||
'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
|
||||
'9d2b9fc6db0f153a0a149fefb182415e' => $vendorDir . '/symfony/polyfill-php84/bootstrap.php',
|
||||
@@ -47,5 +49,10 @@ return array(
|
||||
'91892b814db86b8442ad76273bb7aec5' => $vendorDir . '/laravel/framework/src/Illuminate/Reflection/helpers.php',
|
||||
'493c6aea52f6009bab023b26c21a386a' => $vendorDir . '/laravel/framework/src/Illuminate/Support/functions.php',
|
||||
'58571171fd5812e6e447dce228f52f4d' => $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php',
|
||||
'309cd39bb536ff667b25a3a76938cb83' => $vendorDir . '/laravel/pao/src/Autoload.php',
|
||||
'c72349b1fe8d0deeedd3a52e8aa814d8' => $vendorDir . '/mockery/mockery/library/helpers.php',
|
||||
'ce9671a430e4846b44e1c68c7611f9f5' => $vendorDir . '/mockery/mockery/library/Mockery.php',
|
||||
'a1cfe24d14977df6878b9bf804af2d1c' => $vendorDir . '/nunomaduro/collision/src/Adapters/Phpunit/Autoload.php',
|
||||
'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
|
||||
'377b22b161c09ed6e5152de788ca020a' => $vendorDir . '/spatie/laravel-permission/src/helpers.php',
|
||||
);
|
||||
|
||||
Vendored
+12
-3
@@ -10,8 +10,10 @@ return array(
|
||||
'ZBateson\\StreamDecorators\\' => array($vendorDir . '/zbateson/stream-decorators/src'),
|
||||
'ZBateson\\MbWrapper\\' => array($vendorDir . '/zbateson/mb-wrapper/src'),
|
||||
'ZBateson\\MailMimeParser\\' => array($vendorDir . '/zbateson/mail-mime-parser/src'),
|
||||
'Whoops\\' => array($vendorDir . '/filp/whoops/src/Whoops'),
|
||||
'Webklex\\PHPIMAP\\' => array($vendorDir . '/webklex/php-imap/src'),
|
||||
'TijsVerkoyen\\CssToInlineStyles\\' => array($vendorDir . '/tijsverkoyen/css-to-inline-styles/src'),
|
||||
'Tests\\' => array($baseDir . '/tests'),
|
||||
'Termwind\\' => array($vendorDir . '/nunomaduro/termwind/src'),
|
||||
'Symfony\\Polyfill\\Uuid\\' => array($vendorDir . '/symfony/polyfill-uuid'),
|
||||
'Symfony\\Polyfill\\Php86\\' => array($vendorDir . '/symfony/polyfill-php86'),
|
||||
@@ -64,8 +66,10 @@ return array(
|
||||
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
|
||||
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
|
||||
'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'),
|
||||
'NunoMaduro\\Collision\\' => array($vendorDir . '/nunomaduro/collision/src'),
|
||||
'Nette\\' => array($vendorDir . '/nette/schema/src', $vendorDir . '/nette/utils/src'),
|
||||
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
|
||||
'Mockery\\' => array($vendorDir . '/mockery/mockery/library/Mockery'),
|
||||
'League\\Uri\\' => array($vendorDir . '/league/uri', $vendorDir . '/league/uri-interfaces'),
|
||||
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
|
||||
'League\\Flysystem\\WebDAV\\' => array($vendorDir . '/league/flysystem-webdav'),
|
||||
@@ -76,6 +80,9 @@ return array(
|
||||
'Laravel\\Tinker\\' => array($vendorDir . '/laravel/tinker/src'),
|
||||
'Laravel\\SerializableClosure\\' => array($vendorDir . '/laravel/serializable-closure/src'),
|
||||
'Laravel\\Prompts\\' => array($vendorDir . '/laravel/prompts/src'),
|
||||
'Laravel\\Pao\\' => array($vendorDir . '/laravel/pao/src'),
|
||||
'Laravel\\Pail\\' => array($vendorDir . '/laravel/pail/src'),
|
||||
'Laravel\\AgentDetector\\' => array($vendorDir . '/laravel/agent-detector/src'),
|
||||
'Invoker\\' => array($vendorDir . '/php-di/invoker/src'),
|
||||
'Illuminate\\Support\\' => array($vendorDir . '/laravel/framework/src/Illuminate/Macroable', $vendorDir . '/laravel/framework/src/Illuminate/Collections', $vendorDir . '/laravel/framework/src/Illuminate/Conditionable', $vendorDir . '/laravel/framework/src/Illuminate/Reflection'),
|
||||
'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'),
|
||||
@@ -89,6 +96,7 @@ return array(
|
||||
'Google\\' => array($vendorDir . '/google/apiclient/src'),
|
||||
'Fruitcake\\Cors\\' => array($vendorDir . '/fruitcake/php-cors/src'),
|
||||
'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
|
||||
'Faker\\' => array($vendorDir . '/fakerphp/faker/src/Faker'),
|
||||
'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'),
|
||||
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
|
||||
'Doctrine\\Inflector\\' => array($vendorDir . '/doctrine/inflector/src'),
|
||||
@@ -96,8 +104,9 @@ return array(
|
||||
'DirectoryTree\\ImapEngine\\Laravel\\' => array($vendorDir . '/directorytree/imapengine-laravel/src'),
|
||||
'DirectoryTree\\ImapEngine\\' => array($vendorDir . '/directorytree/imapengine/src'),
|
||||
'Dflydev\\DotAccessData\\' => array($vendorDir . '/dflydev/dot-access-data/src'),
|
||||
'Database\\Seeders\\' => array($baseDir . '/database/seeders'),
|
||||
'Database\\Factories\\' => array($baseDir . '/database/factories'),
|
||||
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
|
||||
'Database\\Seeders\\' => array($baseDir . '/database/seeders', $vendorDir . '/laravel/pint/database/seeders'),
|
||||
'Database\\Factories\\' => array($baseDir . '/database/factories', $vendorDir . '/laravel/pint/database/factories'),
|
||||
'DI\\' => array($vendorDir . '/php-di/php-di/src'),
|
||||
'Cron\\' => array($vendorDir . '/dragonmantank/cron-expression/src/Cron'),
|
||||
'Carbon\\Doctrine\\' => array($vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine'),
|
||||
@@ -105,5 +114,5 @@ return array(
|
||||
'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
|
||||
'As247\\Flysystem\\GoogleDrive\\' => array($vendorDir . '/as247/flysystem-google-drive/src'),
|
||||
'As247\\CloudStorages\\' => array($vendorDir . '/as247/cloud-storages/src'),
|
||||
'App\\' => array($baseDir . '/app'),
|
||||
'App\\' => array($baseDir . '/app', $vendorDir . '/laravel/pint/app'),
|
||||
);
|
||||
|
||||
Vendored
+2155
-4
File diff suppressed because it is too large
Load Diff
Vendored
+2405
-2
File diff suppressed because it is too large
Load Diff
Vendored
+318
-3
@@ -3,11 +3,11 @@
|
||||
'name' => 'laravel/laravel',
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => 'baea18115984a1c2506c8af908424d45498b0b11',
|
||||
'reference' => 'c5127da750123ed4b569cc18681360a1ba1d04af',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev' => false,
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'as247/cloud-storages' => array(
|
||||
@@ -46,6 +46,18 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'cordoval/hamcrest-php' => array(
|
||||
'dev_requirement' => true,
|
||||
'replaced' => array(
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'davedevelopment/hamcrest-php' => array(
|
||||
'dev_requirement' => true,
|
||||
'replaced' => array(
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'dflydev/dot-access-data' => array(
|
||||
'pretty_version' => 'v3.0.3',
|
||||
'version' => '3.0.3.0',
|
||||
@@ -109,6 +121,24 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'fakerphp/faker' => array(
|
||||
'pretty_version' => 'v1.24.1',
|
||||
'version' => '1.24.1.0',
|
||||
'reference' => 'e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../fakerphp/faker',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'filp/whoops' => array(
|
||||
'pretty_version' => '2.18.4',
|
||||
'version' => '2.18.4.0',
|
||||
'reference' => 'd2102955e48b9fd9ab24280a7ad12ed552752c4d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../filp/whoops',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'firebase/php-jwt' => array(
|
||||
'pretty_version' => 'v7.0.5',
|
||||
'version' => '7.0.5.0',
|
||||
@@ -199,6 +229,15 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'hamcrest/hamcrest-php' => array(
|
||||
'pretty_version' => 'v2.1.1',
|
||||
'version' => '2.1.1.0',
|
||||
'reference' => 'f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../hamcrest/hamcrest-php',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'illuminate/auth' => array(
|
||||
'dev_requirement' => false,
|
||||
'replaced' => array(
|
||||
@@ -415,6 +454,21 @@
|
||||
0 => 'v13.8.0',
|
||||
),
|
||||
),
|
||||
'kodova/hamcrest-php' => array(
|
||||
'dev_requirement' => true,
|
||||
'replaced' => array(
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'laravel/agent-detector' => array(
|
||||
'pretty_version' => 'v2.0.2',
|
||||
'version' => '2.0.2.0',
|
||||
'reference' => '90694b9256099591cf9e55d08c18ba7a00bf099f',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../laravel/agent-detector',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'laravel/framework' => array(
|
||||
'pretty_version' => 'v13.8.0',
|
||||
'version' => '13.8.0.0',
|
||||
@@ -427,12 +481,39 @@
|
||||
'laravel/laravel' => array(
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => 'baea18115984a1c2506c8af908424d45498b0b11',
|
||||
'reference' => 'c5127da750123ed4b569cc18681360a1ba1d04af',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'laravel/pail' => array(
|
||||
'pretty_version' => 'v1.2.6',
|
||||
'version' => '1.2.6.0',
|
||||
'reference' => 'aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../laravel/pail',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'laravel/pao' => array(
|
||||
'pretty_version' => 'v1.0.6',
|
||||
'version' => '1.0.6.0',
|
||||
'reference' => '02f62a64c2b60af44a418ee490fee193590d8269',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../laravel/pao',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'laravel/pint' => array(
|
||||
'pretty_version' => 'v1.29.1',
|
||||
'version' => '1.29.1.0',
|
||||
'reference' => '0770e9b7fafd50d4586881d456d6eb41c9247a80',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../laravel/pint',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'laravel/prompts' => array(
|
||||
'pretty_version' => 'v0.3.17',
|
||||
'version' => '0.3.17.0',
|
||||
@@ -532,6 +613,15 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'mockery/mockery' => array(
|
||||
'pretty_version' => '1.6.12',
|
||||
'version' => '1.6.12.0',
|
||||
'reference' => '1f4efdd7d3beafe9807b08156dfcb176d18f1699',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../mockery/mockery',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'monolog/monolog' => array(
|
||||
'pretty_version' => '3.10.0',
|
||||
'version' => '3.10.0.0',
|
||||
@@ -547,6 +637,15 @@
|
||||
0 => '^1.0',
|
||||
),
|
||||
),
|
||||
'myclabs/deep-copy' => array(
|
||||
'pretty_version' => '1.13.4',
|
||||
'version' => '1.13.4.0',
|
||||
'reference' => '07d290f0c47959fd5eed98c95ee5602db07e0b6a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../myclabs/deep-copy',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'nesbot/carbon' => array(
|
||||
'pretty_version' => '3.11.4',
|
||||
'version' => '3.11.4.0',
|
||||
@@ -583,6 +682,15 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'nunomaduro/collision' => array(
|
||||
'pretty_version' => 'v8.9.4',
|
||||
'version' => '8.9.4.0',
|
||||
'reference' => '716af8f95a470e9094cfca09ed897b023be191a5',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../nunomaduro/collision',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'nunomaduro/termwind' => array(
|
||||
'pretty_version' => 'v2.4.0',
|
||||
'version' => '2.4.0.0',
|
||||
@@ -592,6 +700,24 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'phar-io/manifest' => array(
|
||||
'pretty_version' => '2.0.4',
|
||||
'version' => '2.0.4.0',
|
||||
'reference' => '54750ef60c58e43759730615a392c31c80e23176',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phar-io/manifest',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phar-io/version' => array(
|
||||
'pretty_version' => '3.2.1',
|
||||
'version' => '3.2.1.0',
|
||||
'reference' => '4f7fd7836c6f332bb2933569e566a0d6c4cbed74',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phar-io/version',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'php-di/invoker' => array(
|
||||
'pretty_version' => '2.3.7',
|
||||
'version' => '2.3.7.0',
|
||||
@@ -619,6 +745,60 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'phpunit/php-code-coverage' => array(
|
||||
'pretty_version' => '12.5.6',
|
||||
'version' => '12.5.6.0',
|
||||
'reference' => '876099a072646c7745f673d7aeab5382c4439691',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-code-coverage',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-file-iterator' => array(
|
||||
'pretty_version' => '6.0.1',
|
||||
'version' => '6.0.1.0',
|
||||
'reference' => '3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-file-iterator',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-invoker' => array(
|
||||
'pretty_version' => '6.0.0',
|
||||
'version' => '6.0.0.0',
|
||||
'reference' => '12b54e689b07a25a9b41e57736dfab6ec9ae5406',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-invoker',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-text-template' => array(
|
||||
'pretty_version' => '5.0.0',
|
||||
'version' => '5.0.0.0',
|
||||
'reference' => 'e1367a453f0eda562eedb4f659e13aa900d66c53',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-text-template',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-timer' => array(
|
||||
'pretty_version' => '8.0.0',
|
||||
'version' => '8.0.0.0',
|
||||
'reference' => 'f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-timer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/phpunit' => array(
|
||||
'pretty_version' => '12.5.24',
|
||||
'version' => '12.5.24.0',
|
||||
'reference' => 'd75dd30597caa80e72fad2ef7904601a30ef1046',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/phpunit',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'psr/cache' => array(
|
||||
'pretty_version' => '3.0.0',
|
||||
'version' => '3.0.0.0',
|
||||
@@ -847,6 +1027,123 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'sebastian/cli-parser' => array(
|
||||
'pretty_version' => '4.2.0',
|
||||
'version' => '4.2.0.0',
|
||||
'reference' => '90f41072d220e5c40df6e8635f5dafba2d9d4d04',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/cli-parser',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/comparator' => array(
|
||||
'pretty_version' => '7.1.6',
|
||||
'version' => '7.1.6.0',
|
||||
'reference' => 'c769009dee98f494e0edc3fd4f4087501688f11e',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/comparator',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/complexity' => array(
|
||||
'pretty_version' => '5.0.0',
|
||||
'version' => '5.0.0.0',
|
||||
'reference' => 'bad4316aba5303d0221f43f8cee37eb58d384bbb',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/complexity',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/diff' => array(
|
||||
'pretty_version' => '7.0.0',
|
||||
'version' => '7.0.0.0',
|
||||
'reference' => '7ab1ea946c012266ca32390913653d844ecd085f',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/diff',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/environment' => array(
|
||||
'pretty_version' => '8.1.0',
|
||||
'version' => '8.1.0.0',
|
||||
'reference' => 'b121608b28a13f721e76ffbbd386d08eff58f3f6',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/environment',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/exporter' => array(
|
||||
'pretty_version' => '7.0.2',
|
||||
'version' => '7.0.2.0',
|
||||
'reference' => '016951ae10980765e4e7aee491eb288c64e505b7',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/exporter',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/global-state' => array(
|
||||
'pretty_version' => '8.0.2',
|
||||
'version' => '8.0.2.0',
|
||||
'reference' => 'ef1377171613d09edd25b7816f05be8313f9115d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/global-state',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/lines-of-code' => array(
|
||||
'pretty_version' => '4.0.0',
|
||||
'version' => '4.0.0.0',
|
||||
'reference' => '97ffee3bcfb5805568d6af7f0f893678fc076d2f',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/lines-of-code',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/object-enumerator' => array(
|
||||
'pretty_version' => '7.0.0',
|
||||
'version' => '7.0.0.0',
|
||||
'reference' => '1effe8e9b8e068e9ae228e542d5d11b5d16db894',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/object-enumerator',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/object-reflector' => array(
|
||||
'pretty_version' => '5.0.0',
|
||||
'version' => '5.0.0.0',
|
||||
'reference' => '4bfa827c969c98be1e527abd576533293c634f6a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/object-reflector',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/recursion-context' => array(
|
||||
'pretty_version' => '7.0.1',
|
||||
'version' => '7.0.1.0',
|
||||
'reference' => '0b01998a7d5b1f122911a66bebcb8d46f0c82d8c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/recursion-context',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/type' => array(
|
||||
'pretty_version' => '6.0.3',
|
||||
'version' => '6.0.3.0',
|
||||
'reference' => 'e549163b9760b8f71f191651d22acf32d56d6d4d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/type',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/version' => array(
|
||||
'pretty_version' => '6.0.0',
|
||||
'version' => '6.0.0.0',
|
||||
'reference' => '3e6ccf7657d4f0a59200564b08cead899313b53c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/version',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'spatie/laravel-package-tools' => array(
|
||||
'pretty_version' => '1.93.0',
|
||||
'version' => '1.93.0.0',
|
||||
@@ -871,6 +1168,15 @@
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'staabm/side-effects-detector' => array(
|
||||
'pretty_version' => '1.0.5',
|
||||
'version' => '1.0.5.0',
|
||||
'reference' => 'd8334211a140ce329c13726d4a715adbddd0a163',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../staabm/side-effects-detector',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'symfony/clock' => array(
|
||||
'pretty_version' => 'v8.0.8',
|
||||
'version' => '8.0.8.0',
|
||||
@@ -1162,6 +1468,15 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'theseer/tokenizer' => array(
|
||||
'pretty_version' => '2.0.1',
|
||||
'version' => '2.0.1.0',
|
||||
'reference' => '7989e43bf381af0eac72e4f0ca5bcbfa81658be4',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../theseer/tokenizer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'tijsverkoyen/css-to-inline-styles' => array(
|
||||
'pretty_version' => 'v2.4.0',
|
||||
'version' => '2.4.0.0',
|
||||
|
||||
Vendored
+209
@@ -0,0 +1,209 @@
|
||||
# CHANGELOG
|
||||
|
||||
## [Unreleased](https://github.com/FakerPHP/Faker/compare/v1.24.0...1.24.1)
|
||||
|
||||
- Removed domain `gmail.com.au` from `Provider\en_AU\Internet` (#886)
|
||||
|
||||
## [2024-11-09, v1.24.0](https://github.com/FakerPHP/Faker/compare/v1.23.1..v1.24.0)
|
||||
|
||||
- Fix internal deprecations in Doctrine's populator by @gnutix in (#889)
|
||||
- Fix mobile phone number pattern for France by @ker0x in (#859)
|
||||
- PHP 8.4 Support by @Jubeki in (#904)
|
||||
|
||||
- Added support for PHP 8.4 (#904)
|
||||
|
||||
## [2023-09-29, v1.23.1](https://github.com/FakerPHP/Faker/compare/v1.23.0..v1.23.1)
|
||||
|
||||
- Fixed double `а` female lastName in `ru_RU/Person::name()` (#832)
|
||||
- Fixed polish license plates (#685)
|
||||
- Stopped using `static` in callables in `Provider\pt_BR\PhoneNumber` (#785)
|
||||
- Fixed incorrect female name (#794)
|
||||
- Stopped using the deprecated `MT_RAND_PHP` constant to seed the random generator on PHP 8.3 (#844)
|
||||
|
||||
## [2023-06-12, v1.23.0](https://github.com/FakerPHP/Faker/compare/v1.22.0..v1.23.0)
|
||||
|
||||
- Update `randomElements` to return random number of elements when no count is provided (#658)
|
||||
|
||||
## [2023-05-14, v1.22.0](https://github.com/FakerPHP/Faker/compare/v1.21.0..v1.22.0)
|
||||
|
||||
- Fixed `randomElements()` to accept empty iterator (#605)
|
||||
- Added support for passing an `Enum` to `randomElement()` and `randomElements()` (#620)
|
||||
- Started rejecting invalid arguments passed to `randomElement()` and `randomElements()` (#642)
|
||||
|
||||
## [2022-12-13, v1.21.0](https://github.com/FakerPHP/Faker/compare/v1.20.0..v1.21.0)
|
||||
|
||||
- Dropped support for PHP 7.1, 7.2, and 7.3 (#543)
|
||||
- Added support for PHP 8.2 (#528)
|
||||
|
||||
## [2022-07-20, v1.20.0](https://github.com/FakerPHP/Faker/compare/v1.19.0..v1.20.0)
|
||||
|
||||
- Fixed typo in French phone number (#452)
|
||||
- Fixed some Hungarian naming bugs (#451)
|
||||
- Fixed bug where the NL-BE VAT generation was incorrect (#455)
|
||||
- Improve Turkish phone numbers for E164 and added landline support (#460)
|
||||
- Add Microsoft Edge User Agent (#464)
|
||||
- Added option to set image formats on Faker\Provider\Image (#473)
|
||||
- Added support for French color translations (#466)
|
||||
- Support filtering timezones by country code (#480)
|
||||
- Fixed typo in some greek names (#490)
|
||||
- Marked the Faker\Provider\Image as deprecated
|
||||
|
||||
## [2022-02-02, v1.19.0](https://github.com/FakerPHP/Faker/compare/v1.18.0..v1.19.0)
|
||||
|
||||
- Added color extension to core (#442)
|
||||
- Added conflict with `doctrine/persistence` below version `1.4`
|
||||
- Fix for support on different Doctrine ORM versions (#414)
|
||||
- Fix usage of `Doctrine\Persistence` dependency
|
||||
- Fix CZ Person birthNumber docblock return type (#437)
|
||||
- Fix is_IS Person docbock types (#439)
|
||||
- Fix is_IS Address docbock type (#438)
|
||||
- Fix regexify escape backslash in character class (#434)
|
||||
- Removed UUID from Generator to be able to extend it (#441)
|
||||
|
||||
## [2022-01-23, v1.18.0](https://github.com/FakerPHP/Faker/compare/v1.17.0..v1.18.0)
|
||||
|
||||
- Deprecated UUID, use uuid3 to specify version (#427)
|
||||
- Reset formatters when adding a new provider (#366)
|
||||
- Helper methods to use our custom generators (#155)
|
||||
- Set allow-plugins for Composer 2.2 (#405)
|
||||
- Fix kk_KZ\Person::individualIdentificationNumber generation (#411)
|
||||
- Allow for -> syntax to be used in parsing (#423)
|
||||
- Person->name was missing string return type (#424)
|
||||
- Generate a valid BE TAX number (#415)
|
||||
- Added the UUID extension to Core (#427)
|
||||
|
||||
## [2021-12-05, v1.17.0](https://github.com/FakerPHP/Faker/compare/v1.16.0..v1.17.0)
|
||||
|
||||
- Partial PHP 8.1 compatibility (#373)
|
||||
- Add payment provider for `ne_NP` locale (#375)
|
||||
- Add Egyptian Arabic `ar_EG` locale (#377)
|
||||
- Updated list of South African TLDs (#383)
|
||||
- Fixed formatting of E.164 numbers (#380)
|
||||
- Allow `symfony/deprecation-contracts` `^3.0` (#397)
|
||||
|
||||
## [2021-09-06, v1.16.0](https://github.com/FakerPHP/Faker/compare/v1.15.0..v1.16.0)
|
||||
|
||||
- Add Company extension
|
||||
- Add Address extension
|
||||
- Add Person extension
|
||||
- Add PhoneNumber extension
|
||||
- Add VersionExtension (#350)
|
||||
- Stricter types in Extension\Container and Extension\GeneratorAwareExtension (#345)
|
||||
- Fix deprecated property access in `nl_NL` (#348)
|
||||
- Add support for `psr/container` >= 2.0 (#354)
|
||||
- Add missing union types in Faker\Generator (#352)
|
||||
|
||||
## [2021-07-06, v1.15.0](https://github.com/FakerPHP/Faker/compare/v1.14.1..v1.15.0)
|
||||
|
||||
- Updated the generator phpdoc to help identify magic methods (#307)
|
||||
- Prevent direct access and triggered deprecation warning for "word" (#302)
|
||||
- Updated length on all global e164 numbers (#301)
|
||||
- Updated last names from different source (#312)
|
||||
- Don't generate birth number of '000' for Swedish personal identity (#306)
|
||||
- Add job list for localization id_ID (#339)
|
||||
|
||||
## [2021-03-30, v1.14.1](https://github.com/FakerPHP/Faker/compare/v1.14.0..v1.14.1)
|
||||
|
||||
- Fix where randomNumber and randomFloat would return a 0 value (#291 / #292)
|
||||
|
||||
## [2021-03-29, v1.14.0](https://github.com/FakerPHP/Faker/compare/v1.13.0..v1.14.0)
|
||||
|
||||
- Fix for realText to ensure the text keeps closer to its boundaries (#152)
|
||||
- Fix where regexify produces a random character instead of a literal dot (#135
|
||||
- Deprecate zh_TW methods that only call base methods (#122)
|
||||
- Add used extensions to composer.json as suggestion (#120)
|
||||
- Moved TCNo and INN from calculator to localized providers (#108)
|
||||
- Fix regex dot/backslash issue where a dot is replaced with a backslash as escape character (#206)
|
||||
- Deprecate direct property access (#164)
|
||||
- Added test to assert unique() behaviour (#233)
|
||||
- Added RUC for the es_PE locale (#244)
|
||||
- Test IBAN formats for Latin America (AR/PE/VE) (#260)
|
||||
- Added VAT number for en_GB (#255)
|
||||
- Added new districts for the ne_NP locale (#258)
|
||||
- Fix for U.S. Area Code Generation (#261)
|
||||
- Fix in numerify where a better random numeric value is guaranteed (#256)
|
||||
- Fix e164PhoneNumber to only generate valid phone numbers with valid country codes (#264)
|
||||
- Extract fixtures into separate classes (#234)
|
||||
- Remove french domains that no longer exists (#277)
|
||||
- Fix error that occurs when getting a polish title (#279)
|
||||
- Use valid area codes for North America E164 phone numbers (#280)
|
||||
|
||||
- Adding support for extensions and PSR-11 (#154)
|
||||
- Adding trait for GeneratorAwareExtension (#165)
|
||||
- Added helper class for extension (#162)
|
||||
- Added blood extension to core (#232)
|
||||
- Added barcode extension to core (#252)
|
||||
- Added number extension (#257)
|
||||
|
||||
- Various code style updates
|
||||
- Added a note about our breaking change promise (#273)
|
||||
|
||||
## [2020-12-18, v1.13.0](https://github.com/FakerPHP/Faker/compare/v1.12.1..v1.13.0)
|
||||
|
||||
Several fixes and new additions in this release. A lot of cleanup has been done
|
||||
on the codebase on both tests and consistency.
|
||||
|
||||
- Feature/pl pl license plate (#62)
|
||||
- Fix greek phone numbers (#16)
|
||||
- Move AT payment provider logic to de_AT (#72)
|
||||
- Fix wiktionary links (#73)
|
||||
- Fix AT person links (#74)
|
||||
- Fix AT cities (#75)
|
||||
- Deprecate at_AT providers (#78)
|
||||
- Add Austrian `ssn()` to `Person` provider (#79)
|
||||
- Fix typos in id_ID Address (#83)
|
||||
- Austrian post codes (#86)
|
||||
- Updated Polish data (#70)
|
||||
- Improve Austrian social security number generation (#88)
|
||||
- Move US phone numbers with extension to own method (#91)
|
||||
- Add UK National Insurance number generator (#89)
|
||||
- Fix en_SG phone number generator (#100)
|
||||
- Remove usage of mt_rand (#87)
|
||||
- Remove whitespace from beginning of el_GR phone numbers (#105)
|
||||
- Building numbers can not be 0, 00, 000 (#107)
|
||||
- Add 172.16/12 local IPv4 block (#121)
|
||||
- Add JCB credit card type (#124)
|
||||
- Remove json_decode from emoji generation (#123)
|
||||
- Remove ro street address (#146)
|
||||
|
||||
## [2020-12-11, v1.12.1](https://github.com/FakerPHP/Faker/compare/v1.12.0..v1.12.1)
|
||||
|
||||
This is a security release that prevents a hacker to execute code on the server.
|
||||
|
||||
## [2020-11-23, v1.12.0](https://github.com/FakerPHP/Faker/compare/v1.11.0..v1.12.0)
|
||||
|
||||
- Fix ro_RO first and last day of year calculation offset (#65)
|
||||
- Fix en_NG locale test namespaces that did not match PSR-4 (#57)
|
||||
- Added Singapore NRIC/FIN provider (#56)
|
||||
- Added provider for Lithuanian municipalities (#58)
|
||||
- Added blood types provider (#61)
|
||||
|
||||
## [2020-11-15, v1.11.0](https://github.com/FakerPHP/Faker/compare/v1.10.1..v1.11.0)
|
||||
|
||||
- Added Provider for Swedish Municipalities
|
||||
- Updates to person names in pt_BR
|
||||
- Many code style changes
|
||||
|
||||
## [2020-10-28, v1.10.1](https://github.com/FakerPHP/Faker/compare/v1.10.0..v1.10.1)
|
||||
|
||||
- Updates the Danish addresses in dk_DK
|
||||
- Removed offense company names in nl_NL
|
||||
- Clarify changelog with original fork
|
||||
- Standin replacement for LoremPixel to Placeholder.com (#11)
|
||||
|
||||
## [2020-10-27, v1.10.0](https://github.com/FakerPHP/Faker/compare/v1.9.1..v1.10.0)
|
||||
|
||||
- Support PHP 7.1-8.0
|
||||
- Fix typo in de_DE Company Provider
|
||||
- Fix dateTimeThisYear method
|
||||
- Fix typo in de_DE jobTitleFormat
|
||||
- Fix IBAN generation for CR
|
||||
- Fix typos in greek first names
|
||||
- Fix US job title typo
|
||||
- Do not clear entity manager for doctrine orm populator
|
||||
- Remove persian rude words
|
||||
- Corrections to RU names
|
||||
|
||||
## 2020-10-27, v1.9.1
|
||||
|
||||
- Initial version. Same as `fzaninotto/Faker:v1.9.1`.
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2011 François Zaninotto
|
||||
Portions Copyright (c) 2008 Caius Durling
|
||||
Portions Copyright (c) 2008 Adam Royle
|
||||
Portions Copyright (c) 2008 Fiona Burrows
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Vendored
+114
@@ -0,0 +1,114 @@
|
||||
<p style="text-align: center"><img src="https://github.com/FakerPHP/Artwork/raw/main/src/socialcard.png" alt="Social card of FakerPHP"></p>
|
||||
|
||||
# Faker
|
||||
|
||||
[](https://packagist.org/packages/fakerphp/faker)
|
||||
[](https://github.com/FakerPHP/Faker/actions)
|
||||
[](https://shepherd.dev/github/FakerPHP/Faker)
|
||||
[](https://codecov.io/gh/FakerPHP/Faker)
|
||||
|
||||
Faker is a PHP library that generates fake data for you. Whether you need to bootstrap your database, create good-looking XML documents, fill-in your persistence to stress test it, or anonymize data taken from a production service, Faker is for you.
|
||||
|
||||
It's heavily inspired by Perl's [Data::Faker](https://metacpan.org/pod/Data::Faker), and by Ruby's [Faker](https://rubygems.org/gems/faker).
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
Faker requires PHP >= 7.4.
|
||||
|
||||
```shell
|
||||
composer require fakerphp/faker
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
Full documentation can be found over on [fakerphp.github.io](https://fakerphp.github.io).
|
||||
|
||||
### Basic Usage
|
||||
|
||||
Use `Faker\Factory::create()` to create and initialize a Faker generator, which can generate data by accessing methods named after the type of data you want.
|
||||
|
||||
```php
|
||||
<?php
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
// use the factory to create a Faker\Generator instance
|
||||
$faker = Faker\Factory::create();
|
||||
// generate data by calling methods
|
||||
echo $faker->name();
|
||||
// 'Vince Sporer'
|
||||
echo $faker->email();
|
||||
// 'walter.sophia@hotmail.com'
|
||||
echo $faker->text();
|
||||
// 'Numquam ut mollitia at consequuntur inventore dolorem.'
|
||||
```
|
||||
|
||||
Each call to `$faker->name()` yields a different (random) result. This is because Faker uses `__call()` magic, and forwards `Faker\Generator->$method()` calls to `Faker\Generator->format($method, $attributes)`.
|
||||
|
||||
```php
|
||||
<?php
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
echo $faker->name() . "\n";
|
||||
}
|
||||
|
||||
// 'Cyrus Boyle'
|
||||
// 'Alena Cummerata'
|
||||
// 'Orlo Bergstrom'
|
||||
```
|
||||
|
||||
## Automated refactoring
|
||||
|
||||
If you already used this library with its properties, they are now deprecated and needs to be replaced by their equivalent methods.
|
||||
|
||||
You can use the provided [Rector](https://github.com/rectorphp/rector) config file to automate the work.
|
||||
|
||||
Run
|
||||
|
||||
```bash
|
||||
composer require --dev rector/rector
|
||||
```
|
||||
|
||||
to install `rector/rector`.
|
||||
|
||||
Run
|
||||
|
||||
```bash
|
||||
vendor/bin/rector process src/ --config vendor/fakerphp/faker/rector-migrate.php
|
||||
```
|
||||
|
||||
to run `rector/rector`.
|
||||
|
||||
*Note:* do not forget to replace `src/` with the path to your source directory.
|
||||
|
||||
Alternatively, import the configuration in your `rector.php` file:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Config;
|
||||
|
||||
return static function (Config\RectorConfig $rectorConfig): void {
|
||||
$rectorConfig->import('vendor/fakerphp/faker/rector-migrate.php');
|
||||
};
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Faker is released under the MIT License. See [`LICENSE`](LICENSE) for details.
|
||||
|
||||
## Backward compatibility promise
|
||||
|
||||
Faker is using [Semver](https://semver.org/). This means that versions are tagged
|
||||
with MAJOR.MINOR.PATCH. Only a new major version will be allowed to break backward
|
||||
compatibility (BC).
|
||||
|
||||
Classes marked as `@experimental` or `@internal` are not included in our backward compatibility promise.
|
||||
You are also not guaranteed that the value returned from a method is always the
|
||||
same. You are guaranteed that the data type will not change.
|
||||
|
||||
PHP 8 introduced [named arguments](https://wiki.php.net/rfc/named_params), which
|
||||
increased the cost and reduces flexibility for package maintainers. The names of the
|
||||
arguments for methods in Faker is not included in our BC promise.
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "fakerphp/faker",
|
||||
"type": "library",
|
||||
"description": "Faker is a PHP library that generates fake data for you.",
|
||||
"keywords": [
|
||||
"faker",
|
||||
"fixtures",
|
||||
"data"
|
||||
],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "François Zaninotto"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.4 || ^8.0",
|
||||
"psr/container": "^1.0 || ^2.0",
|
||||
"symfony/deprecation-contracts": "^2.2 || ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-intl": "*",
|
||||
"bamarni/composer-bin-plugin": "^1.4.1",
|
||||
"doctrine/persistence": "^1.3 || ^2.0",
|
||||
"phpunit/phpunit": "^9.5.26",
|
||||
"symfony/phpunit-bridge": "^5.4.16"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Faker\\": "src/Faker/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Faker\\Test\\": "test/Faker/",
|
||||
"Faker\\Test\\Fixture\\": "test/Fixture/"
|
||||
}
|
||||
},
|
||||
"conflict": {
|
||||
"fzaninotto/faker": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Required by Faker\\Provider\\Image to download images.",
|
||||
"ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.",
|
||||
"ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.",
|
||||
"ext-mbstring": "Required for multibyte Unicode string functionality.",
|
||||
"doctrine/orm": "Required to use Faker\\ORM\\Doctrine"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"bamarni/composer-bin-plugin": true,
|
||||
"composer/package-versions-deprecated": true
|
||||
},
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Faker\Generator;
|
||||
use Rector\Config;
|
||||
use Rector\Transform;
|
||||
|
||||
// This file configures rector/rector to replace all deprecated property usages with their equivalent functions.
|
||||
return static function (Config\RectorConfig $rectorConfig): void {
|
||||
$properties = [
|
||||
'address',
|
||||
'amPm',
|
||||
'asciify',
|
||||
'biasedNumberBetween',
|
||||
'boolean',
|
||||
'bothify',
|
||||
'buildingNumber',
|
||||
'century',
|
||||
'chrome',
|
||||
'city',
|
||||
'citySuffix',
|
||||
'colorName',
|
||||
'company',
|
||||
'companyEmail',
|
||||
'companySuffix',
|
||||
'country',
|
||||
'countryCode',
|
||||
'countryISOAlpha3',
|
||||
'creditCardDetails',
|
||||
'creditCardExpirationDate',
|
||||
'creditCardExpirationDateString',
|
||||
'creditCardNumber',
|
||||
'creditCardType',
|
||||
'currencyCode',
|
||||
'date',
|
||||
'dateTime',
|
||||
'dateTimeAD',
|
||||
'dateTimeBetween',
|
||||
'dateTimeInInterval',
|
||||
'dateTimeThisCentury',
|
||||
'dateTimeThisDecade',
|
||||
'dateTimeThisMonth',
|
||||
'dateTimeThisYear',
|
||||
'dayOfMonth',
|
||||
'dayOfWeek',
|
||||
'domainName',
|
||||
'domainWord',
|
||||
'e164PhoneNumber',
|
||||
'email',
|
||||
'emoji',
|
||||
'file',
|
||||
'firefox',
|
||||
'firstName',
|
||||
'firstNameFemale',
|
||||
'firstNameMale',
|
||||
'freeEmail',
|
||||
'freeEmailDomain',
|
||||
'getDefaultTimezone',
|
||||
'hexColor',
|
||||
'hslColor',
|
||||
'hslColorAsArray',
|
||||
'iban',
|
||||
'image',
|
||||
'imageUrl',
|
||||
'imei',
|
||||
'internetExplorer',
|
||||
'iosMobileToken',
|
||||
'ipv4',
|
||||
'ipv6',
|
||||
'iso8601',
|
||||
'jobTitle',
|
||||
'languageCode',
|
||||
'lastName',
|
||||
'latitude',
|
||||
'lexify',
|
||||
'linuxPlatformToken',
|
||||
'linuxProcessor',
|
||||
'localCoordinates',
|
||||
'localIpv4',
|
||||
'locale',
|
||||
'longitude',
|
||||
'macAddress',
|
||||
'macPlatformToken',
|
||||
'macProcessor',
|
||||
'md5',
|
||||
'month',
|
||||
'monthName',
|
||||
'msedge',
|
||||
'name',
|
||||
'numerify',
|
||||
'opera',
|
||||
'paragraph',
|
||||
'paragraphs',
|
||||
'passthrough',
|
||||
'password',
|
||||
'phoneNumber',
|
||||
'postcode',
|
||||
'randomAscii',
|
||||
'randomDigitNotNull',
|
||||
'randomElement',
|
||||
'randomElements',
|
||||
'randomHtml',
|
||||
'randomKey',
|
||||
'randomLetter',
|
||||
'realText',
|
||||
'realTextBetween',
|
||||
'regexify',
|
||||
'rgbColor',
|
||||
'rgbColorAsArray',
|
||||
'rgbCssColor',
|
||||
'rgbaCssColor',
|
||||
'safari',
|
||||
'safeColorName',
|
||||
'safeEmail',
|
||||
'safeEmailDomain',
|
||||
'safeHexColor',
|
||||
'sentence',
|
||||
'sentences',
|
||||
'setDefaultTimezone',
|
||||
'sha1',
|
||||
'sha256',
|
||||
'shuffle',
|
||||
'shuffleArray',
|
||||
'shuffleString',
|
||||
'slug',
|
||||
'streetAddress',
|
||||
'streetName',
|
||||
'streetSuffix',
|
||||
'swiftBicNumber',
|
||||
'text',
|
||||
'time',
|
||||
'timezone',
|
||||
'title',
|
||||
'titleFemale',
|
||||
'titleMale',
|
||||
'tld',
|
||||
'toLower',
|
||||
'toUpper',
|
||||
'unixTime',
|
||||
'url',
|
||||
'userAgent',
|
||||
'userName',
|
||||
'uuid',
|
||||
'windowsPlatformToken',
|
||||
'word',
|
||||
'words',
|
||||
'year',
|
||||
];
|
||||
|
||||
$rectorConfig->ruleWithConfiguration(
|
||||
Transform\Rector\Assign\PropertyFetchToMethodCallRector::class,
|
||||
array_map(static function (string $property): Transform\ValueObject\PropertyFetchToMethodCall {
|
||||
return new Transform\ValueObject\PropertyFetchToMethodCall(
|
||||
Generator::class,
|
||||
$property,
|
||||
$property,
|
||||
);
|
||||
}, $properties),
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Calculator;
|
||||
|
||||
/**
|
||||
* Utility class for validating EAN-8 and EAN-13 numbers
|
||||
*/
|
||||
class Ean
|
||||
{
|
||||
/**
|
||||
* @var string EAN validation pattern
|
||||
*/
|
||||
public const PATTERN = '/^(?:\d{8}|\d{13})$/';
|
||||
|
||||
/**
|
||||
* Computes the checksum of an EAN number.
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/International_Article_Number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function checksum(string $digits)
|
||||
{
|
||||
$sequence = (strlen($digits) + 1) === 8 ? [3, 1] : [1, 3];
|
||||
$sums = 0;
|
||||
|
||||
foreach (str_split($digits) as $n => $digit) {
|
||||
$sums += ((int) $digit) * $sequence[$n % 2];
|
||||
}
|
||||
|
||||
return (10 - $sums % 10) % 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the provided number is an EAN compliant number and that
|
||||
* the checksum is correct.
|
||||
*
|
||||
* @param string $ean An EAN number
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid(string $ean)
|
||||
{
|
||||
if (!preg_match(self::PATTERN, $ean)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::checksum(substr($ean, 0, -1)) === (int) substr($ean, -1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Calculator;
|
||||
|
||||
class Iban
|
||||
{
|
||||
/**
|
||||
* Generates IBAN Checksum
|
||||
*
|
||||
* @return string Checksum (numeric string)
|
||||
*/
|
||||
public static function checksum(string $iban)
|
||||
{
|
||||
// Move first four digits to end and set checksum to '00'
|
||||
$checkString = substr($iban, 4) . substr($iban, 0, 2) . '00';
|
||||
|
||||
// Replace all letters with their number equivalents
|
||||
$checkString = preg_replace_callback(
|
||||
'/[A-Z]/',
|
||||
static function (array $matches): string {
|
||||
return (string) self::alphaToNumber($matches[0]);
|
||||
},
|
||||
$checkString,
|
||||
);
|
||||
|
||||
// Perform mod 97 and subtract from 98
|
||||
$checksum = 98 - self::mod97($checkString);
|
||||
|
||||
return str_pad($checksum, 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts letter to number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function alphaToNumber(string $char)
|
||||
{
|
||||
return ord($char) - 55;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates mod97 on a numeric string
|
||||
*
|
||||
* @param string $number Numeric string
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function mod97(string $number)
|
||||
{
|
||||
$checksum = (int) $number[0];
|
||||
|
||||
for ($i = 1, $size = strlen($number); $i < $size; ++$i) {
|
||||
$checksum = (10 * $checksum + (int) $number[$i]) % 97;
|
||||
}
|
||||
|
||||
return $checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an IBAN has a valid checksum
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid(string $iban)
|
||||
{
|
||||
return self::checksum($iban) === substr($iban, 2, 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Calculator;
|
||||
|
||||
/**
|
||||
* @deprecated moved to ru_RU\Company, use {@link \Faker\Provider\ru_RU\Company}.
|
||||
* @see \Faker\Provider\ru_RU\Company
|
||||
*/
|
||||
class Inn
|
||||
{
|
||||
/**
|
||||
* Generates INN Checksum
|
||||
*
|
||||
* https://ru.wikipedia.org/wiki/%D0%98%D0%B4%D0%B5%D0%BD%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%BE%D0%BC%D0%B5%D1%80_%D0%BD%D0%B0%D0%BB%D0%BE%D0%B3%D0%BE%D0%BF%D0%BB%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D1%89%D0%B8%D0%BA%D0%B0
|
||||
*
|
||||
* @param string $inn
|
||||
*
|
||||
* @return string Checksum (one digit)
|
||||
*
|
||||
* @deprecated use {@link \Faker\Provider\ru_RU\Company::inn10Checksum()} instead
|
||||
* @see \Faker\Provider\ru_RU\Company::inn10Checksum()
|
||||
*/
|
||||
public static function checksum($inn)
|
||||
{
|
||||
return \Faker\Provider\ru_RU\Company::inn10Checksum($inn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an INN has a valid checksum
|
||||
*
|
||||
* @param string $inn
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @deprecated use {@link \Faker\Provider\ru_RU\Company::inn10IsValid()} instead
|
||||
* @see \Faker\Provider\ru_RU\Company::inn10IsValid()
|
||||
*/
|
||||
public static function isValid($inn)
|
||||
{
|
||||
return \Faker\Provider\ru_RU\Company::inn10IsValid($inn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Calculator;
|
||||
|
||||
/**
|
||||
* Utility class for validating ISBN-10
|
||||
*/
|
||||
class Isbn
|
||||
{
|
||||
/**
|
||||
* @var string ISBN-10 validation pattern
|
||||
*/
|
||||
public const PATTERN = '/^\d{9}[0-9X]$/';
|
||||
|
||||
/**
|
||||
* ISBN-10 check digit
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digits
|
||||
*
|
||||
* @param string $input ISBN without check-digit
|
||||
*
|
||||
* @throws \LengthException When wrong input length passed
|
||||
*/
|
||||
public static function checksum(string $input): string
|
||||
{
|
||||
// We're calculating check digit for ISBN-10
|
||||
// so, the length of the input should be 9
|
||||
$length = 9;
|
||||
|
||||
if (strlen($input) !== $length) {
|
||||
throw new \LengthException(sprintf('Input length should be equal to %d', $length));
|
||||
}
|
||||
|
||||
$digits = str_split($input);
|
||||
array_walk(
|
||||
$digits,
|
||||
static function (&$digit, $position): void {
|
||||
$digit = (10 - $position) * $digit;
|
||||
},
|
||||
);
|
||||
$result = (11 - array_sum($digits) % 11) % 11;
|
||||
|
||||
// 10 is replaced by X
|
||||
return ($result < 10) ? (string) $result : 'X';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the provided number is a valid ISBN-10 number
|
||||
*
|
||||
* @param string $isbn ISBN to check
|
||||
*/
|
||||
public static function isValid(string $isbn): bool
|
||||
{
|
||||
if (!preg_match(self::PATTERN, $isbn)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::checksum(substr($isbn, 0, -1)) === substr($isbn, -1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Calculator;
|
||||
|
||||
/**
|
||||
* Utility class for generating and validating Luhn numbers.
|
||||
*
|
||||
* Luhn algorithm is used to validate credit card numbers, IMEI numbers, and
|
||||
* National Provider Identifier numbers.
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/Luhn_algorithm
|
||||
*/
|
||||
class Luhn
|
||||
{
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
private static function checksum(string $number)
|
||||
{
|
||||
$number = (string) $number;
|
||||
$length = strlen($number);
|
||||
$sum = 0;
|
||||
|
||||
for ($i = $length - 1; $i >= 0; $i -= 2) {
|
||||
$sum += $number[$i];
|
||||
}
|
||||
|
||||
for ($i = $length - 2; $i >= 0; $i -= 2) {
|
||||
$sum += array_sum(str_split($number[$i] * 2));
|
||||
}
|
||||
|
||||
return $sum % 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function computeCheckDigit(string $partialNumber)
|
||||
{
|
||||
$checkDigit = self::checksum($partialNumber . '0');
|
||||
|
||||
if ($checkDigit === 0) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
return (string) (10 - $checkDigit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a number (partial number + check digit) is Luhn compliant
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid(string $number)
|
||||
{
|
||||
return self::checksum($number) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Luhn compliant number.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generateLuhnNumber(string $partialValue)
|
||||
{
|
||||
if (!preg_match('/^\d+$/', $partialValue)) {
|
||||
throw new \InvalidArgumentException('Argument should be an integer.');
|
||||
}
|
||||
|
||||
return $partialValue . Luhn::computeCheckDigit($partialValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Calculator;
|
||||
|
||||
/**
|
||||
* @deprecated moved to tr_TR\Person, use {@link \Faker\Provider\tr_TR\Person}.
|
||||
* @see \Faker\Provider\tr_TR\Person
|
||||
*/
|
||||
class TCNo
|
||||
{
|
||||
/**
|
||||
* Generates Turkish Identity Number Checksum
|
||||
* Gets first 9 digit as prefix and calculates checksum
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Turkish_Identification_Number
|
||||
*
|
||||
* @param string $identityPrefix
|
||||
*
|
||||
* @return string Checksum (two digit)
|
||||
*
|
||||
* @deprecated use {@link \Faker\Provider\tr_TR\Person::tcNoChecksum()} instead
|
||||
* @see \Faker\Provider\tr_TR\Person::tcNoChecksum()
|
||||
*/
|
||||
public static function checksum($identityPrefix)
|
||||
{
|
||||
return \Faker\Provider\tr_TR\Person::tcNoChecksum($identityPrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a TCNo has a valid checksum
|
||||
*
|
||||
* @param string $tcNo
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @deprecated use {@link \Faker\Provider\tr_TR\Person::tcNoIsValid()} instead
|
||||
* @see \Faker\Provider\tr_TR\Person::tcNoIsValid()
|
||||
*/
|
||||
public static function isValid($tcNo)
|
||||
{
|
||||
return \Faker\Provider\tr_TR\Person::tcNoIsValid($tcNo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Faker;
|
||||
|
||||
use Faker\Extension\Extension;
|
||||
|
||||
/**
|
||||
* This generator returns a default value for all called properties
|
||||
* and methods. It works with Faker\Generator::optional().
|
||||
*
|
||||
* @mixin Generator
|
||||
*/
|
||||
class ChanceGenerator
|
||||
{
|
||||
private $generator;
|
||||
private $weight;
|
||||
protected $default;
|
||||
|
||||
/**
|
||||
* @param Extension|Generator $generator
|
||||
*/
|
||||
public function __construct($generator, float $weight, $default = null)
|
||||
{
|
||||
$this->default = $default;
|
||||
$this->generator = $generator;
|
||||
$this->weight = $weight;
|
||||
}
|
||||
|
||||
public function ext(string $id)
|
||||
{
|
||||
return new self($this->generator->ext($id), $this->weight, $this->default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch and proxy all generator calls but return only valid values
|
||||
*
|
||||
* @param string $attribute
|
||||
*
|
||||
* @deprecated Use a method instead.
|
||||
*/
|
||||
public function __get($attribute)
|
||||
{
|
||||
trigger_deprecation('fakerphp/faker', '1.14', 'Accessing property "%s" is deprecated, use "%s()" instead.', $attribute, $attribute);
|
||||
|
||||
return $this->__call($attribute, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if (mt_rand(1, 100) <= (100 * $this->weight)) {
|
||||
return call_user_func_array([$this->generator, $name], $arguments);
|
||||
}
|
||||
|
||||
return $this->default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Container;
|
||||
|
||||
use Faker\Extension\Extension;
|
||||
|
||||
/**
|
||||
* A simple implementation of a container.
|
||||
*
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class Container implements ContainerInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string, callable|object|string>
|
||||
*/
|
||||
private array $definitions;
|
||||
|
||||
private array $services = [];
|
||||
|
||||
/**
|
||||
* Create a container object with a set of definitions. The array value MUST
|
||||
* produce an object that implements Extension.
|
||||
*
|
||||
* @param array<string, callable|object|string> $definitions
|
||||
*/
|
||||
public function __construct(array $definitions)
|
||||
{
|
||||
$this->definitions = $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a definition from the container.
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \RuntimeException
|
||||
* @throws ContainerException
|
||||
* @throws NotInContainerException
|
||||
*/
|
||||
public function get($id): Extension
|
||||
{
|
||||
if (!is_string($id)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'First argument of %s::get() must be string',
|
||||
self::class,
|
||||
));
|
||||
}
|
||||
|
||||
if (array_key_exists($id, $this->services)) {
|
||||
return $this->services[$id];
|
||||
}
|
||||
|
||||
if (!$this->has($id)) {
|
||||
throw new NotInContainerException(sprintf(
|
||||
'There is not service with id "%s" in the container.',
|
||||
$id,
|
||||
));
|
||||
}
|
||||
|
||||
$definition = $this->definitions[$id];
|
||||
|
||||
$service = $this->getService($id, $definition);
|
||||
|
||||
if (!$service instanceof Extension) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Service resolved for identifier "%s" does not implement the %s" interface.',
|
||||
$id,
|
||||
Extension::class,
|
||||
));
|
||||
}
|
||||
|
||||
$this->services[$id] = $service;
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the service from a definition.
|
||||
*
|
||||
* @param callable|object|string $definition
|
||||
*/
|
||||
private function getService(string $id, $definition)
|
||||
{
|
||||
if (is_callable($definition)) {
|
||||
try {
|
||||
return $definition();
|
||||
} catch (\Throwable $e) {
|
||||
throw new ContainerException(
|
||||
sprintf('Error while invoking callable for "%s"', $id),
|
||||
0,
|
||||
$e,
|
||||
);
|
||||
}
|
||||
} elseif (is_object($definition)) {
|
||||
return $definition;
|
||||
} elseif (is_string($definition)) {
|
||||
if (class_exists($definition)) {
|
||||
try {
|
||||
return new $definition();
|
||||
} catch (\Throwable $e) {
|
||||
throw new ContainerException(sprintf('Could not instantiate class "%s"', $id), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new ContainerException(sprintf(
|
||||
'Could not instantiate class "%s". Class was not found.',
|
||||
$id,
|
||||
));
|
||||
} else {
|
||||
throw new ContainerException(sprintf(
|
||||
'Invalid type for definition with id "%s"',
|
||||
$id,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the container contains a given identifier.
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function has($id): bool
|
||||
{
|
||||
if (!is_string($id)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'First argument of %s::get() must be string',
|
||||
self::class,
|
||||
));
|
||||
}
|
||||
|
||||
return array_key_exists($id, $this->definitions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Container;
|
||||
|
||||
use Faker\Core;
|
||||
use Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class ContainerBuilder
|
||||
{
|
||||
/**
|
||||
* @var array<string, callable|object|string>
|
||||
*/
|
||||
private array $definitions = [];
|
||||
|
||||
/**
|
||||
* @param callable|object|string $definition
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function add(string $id, $definition): self
|
||||
{
|
||||
if (!is_string($definition) && !is_callable($definition) && !is_object($definition)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'First argument to "%s::add()" must be a string, callable or object.',
|
||||
self::class,
|
||||
));
|
||||
}
|
||||
|
||||
$this->definitions[$id] = $definition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function build(): ContainerInterface
|
||||
{
|
||||
return new Container($this->definitions);
|
||||
}
|
||||
|
||||
private static function defaultExtensions(): array
|
||||
{
|
||||
return [
|
||||
Extension\BarcodeExtension::class => Core\Barcode::class,
|
||||
Extension\BloodExtension::class => Core\Blood::class,
|
||||
Extension\ColorExtension::class => Core\Color::class,
|
||||
Extension\DateTimeExtension::class => Core\DateTime::class,
|
||||
Extension\FileExtension::class => Core\File::class,
|
||||
Extension\NumberExtension::class => Core\Number::class,
|
||||
Extension\UuidExtension::class => Core\Uuid::class,
|
||||
Extension\VersionExtension::class => Core\Version::class,
|
||||
];
|
||||
}
|
||||
|
||||
public static function withDefaultExtensions(): self
|
||||
{
|
||||
$instance = new self();
|
||||
|
||||
foreach (self::defaultExtensions() as $id => $definition) {
|
||||
$instance->add($id, $definition);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Container;
|
||||
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class ContainerException extends \RuntimeException implements ContainerExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Container;
|
||||
|
||||
use Psr\Container\ContainerInterface as BaseContainerInterface;
|
||||
|
||||
interface ContainerInterface extends BaseContainerInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Container;
|
||||
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class NotInContainerException extends \RuntimeException implements NotFoundExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Core;
|
||||
|
||||
use Faker\Calculator;
|
||||
use Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class Barcode implements Extension\BarcodeExtension
|
||||
{
|
||||
private Extension\NumberExtension $numberExtension;
|
||||
|
||||
public function __construct(?Extension\NumberExtension $numberExtension = null)
|
||||
{
|
||||
$this->numberExtension = $numberExtension ?: new Number();
|
||||
}
|
||||
|
||||
private function ean(int $length = 13): string
|
||||
{
|
||||
$code = Extension\Helper::numerify(str_repeat('#', $length - 1));
|
||||
|
||||
return sprintf('%s%s', $code, Calculator\Ean::checksum($code));
|
||||
}
|
||||
|
||||
public function ean13(): string
|
||||
{
|
||||
return $this->ean();
|
||||
}
|
||||
|
||||
public function ean8(): string
|
||||
{
|
||||
return $this->ean(8);
|
||||
}
|
||||
|
||||
public function isbn10(): string
|
||||
{
|
||||
$code = Extension\Helper::numerify(str_repeat('#', 9));
|
||||
|
||||
return sprintf('%s%s', $code, Calculator\Isbn::checksum($code));
|
||||
}
|
||||
|
||||
public function isbn13(): string
|
||||
{
|
||||
$code = '97' . $this->numberExtension->numberBetween(8, 9) . Extension\Helper::numerify(str_repeat('#', 9));
|
||||
|
||||
return sprintf('%s%s', $code, Calculator\Ean::checksum($code));
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Core;
|
||||
|
||||
use Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class Blood implements Extension\BloodExtension
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $bloodTypes = ['A', 'AB', 'B', 'O'];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $bloodRhFactors = ['+', '-'];
|
||||
|
||||
public function bloodType(): string
|
||||
{
|
||||
return Extension\Helper::randomElement($this->bloodTypes);
|
||||
}
|
||||
|
||||
public function bloodRh(): string
|
||||
{
|
||||
return Extension\Helper::randomElement($this->bloodRhFactors);
|
||||
}
|
||||
|
||||
public function bloodGroup(): string
|
||||
{
|
||||
return sprintf(
|
||||
'%s%s',
|
||||
$this->bloodType(),
|
||||
$this->bloodRh(),
|
||||
);
|
||||
}
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Core;
|
||||
|
||||
use Faker\Extension;
|
||||
use Faker\Extension\Helper;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class Color implements Extension\ColorExtension
|
||||
{
|
||||
private Extension\NumberExtension $numberExtension;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $safeColorNames = [
|
||||
'black', 'maroon', 'green', 'navy', 'olive',
|
||||
'purple', 'teal', 'lime', 'blue', 'silver',
|
||||
'gray', 'yellow', 'fuchsia', 'aqua', 'white',
|
||||
];
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $allColorNames = [
|
||||
'AliceBlue', 'AntiqueWhite', 'Aqua', 'Aquamarine',
|
||||
'Azure', 'Beige', 'Bisque', 'Black', 'BlanchedAlmond',
|
||||
'Blue', 'BlueViolet', 'Brown', 'BurlyWood', 'CadetBlue',
|
||||
'Chartreuse', 'Chocolate', 'Coral', 'CornflowerBlue',
|
||||
'Cornsilk', 'Crimson', 'Cyan', 'DarkBlue', 'DarkCyan',
|
||||
'DarkGoldenRod', 'DarkGray', 'DarkGreen', 'DarkKhaki',
|
||||
'DarkMagenta', 'DarkOliveGreen', 'Darkorange', 'DarkOrchid',
|
||||
'DarkRed', 'DarkSalmon', 'DarkSeaGreen', 'DarkSlateBlue',
|
||||
'DarkSlateGray', 'DarkTurquoise', 'DarkViolet', 'DeepPink',
|
||||
'DeepSkyBlue', 'DimGray', 'DimGrey', 'DodgerBlue', 'FireBrick',
|
||||
'FloralWhite', 'ForestGreen', 'Fuchsia', 'Gainsboro', 'GhostWhite',
|
||||
'Gold', 'GoldenRod', 'Gray', 'Green', 'GreenYellow', 'HoneyDew',
|
||||
'HotPink', 'IndianRed', 'Indigo', 'Ivory', 'Khaki', 'Lavender',
|
||||
'LavenderBlush', 'LawnGreen', 'LemonChiffon', 'LightBlue', 'LightCoral',
|
||||
'LightCyan', 'LightGoldenRodYellow', 'LightGray', 'LightGreen', 'LightPink',
|
||||
'LightSalmon', 'LightSeaGreen', 'LightSkyBlue', 'LightSlateGray', 'LightSteelBlue',
|
||||
'LightYellow', 'Lime', 'LimeGreen', 'Linen', 'Magenta', 'Maroon', 'MediumAquaMarine',
|
||||
'MediumBlue', 'MediumOrchid', 'MediumPurple', 'MediumSeaGreen', 'MediumSlateBlue',
|
||||
'MediumSpringGreen', 'MediumTurquoise', 'MediumVioletRed', 'MidnightBlue',
|
||||
'MintCream', 'MistyRose', 'Moccasin', 'NavajoWhite', 'Navy', 'OldLace', 'Olive',
|
||||
'OliveDrab', 'Orange', 'OrangeRed', 'Orchid', 'PaleGoldenRod', 'PaleGreen',
|
||||
'PaleTurquoise', 'PaleVioletRed', 'PapayaWhip', 'PeachPuff', 'Peru', 'Pink', 'Plum',
|
||||
'PowderBlue', 'Purple', 'Red', 'RosyBrown', 'RoyalBlue', 'SaddleBrown', 'Salmon',
|
||||
'SandyBrown', 'SeaGreen', 'SeaShell', 'Sienna', 'Silver', 'SkyBlue', 'SlateBlue',
|
||||
'SlateGray', 'Snow', 'SpringGreen', 'SteelBlue', 'Tan', 'Teal', 'Thistle', 'Tomato',
|
||||
'Turquoise', 'Violet', 'Wheat', 'White', 'WhiteSmoke', 'Yellow', 'YellowGreen',
|
||||
];
|
||||
|
||||
public function __construct(?Extension\NumberExtension $numberExtension = null)
|
||||
{
|
||||
$this->numberExtension = $numberExtension ?: new Number();
|
||||
}
|
||||
|
||||
/**
|
||||
* @example '#fa3cc2'
|
||||
*/
|
||||
public function hexColor(): string
|
||||
{
|
||||
return '#' . str_pad(dechex($this->numberExtension->numberBetween(1, 16777215)), 6, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example '#ff0044'
|
||||
*/
|
||||
public function safeHexColor(): string
|
||||
{
|
||||
$color = str_pad(dechex($this->numberExtension->numberBetween(0, 255)), 3, '0', STR_PAD_LEFT);
|
||||
|
||||
return sprintf(
|
||||
'#%s%s%s%s%s%s',
|
||||
$color[0],
|
||||
$color[0],
|
||||
$color[1],
|
||||
$color[1],
|
||||
$color[2],
|
||||
$color[2],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example 'array(0,255,122)'
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function rgbColorAsArray(): array
|
||||
{
|
||||
$color = $this->hexColor();
|
||||
|
||||
return [
|
||||
hexdec(substr($color, 1, 2)),
|
||||
hexdec(substr($color, 3, 2)),
|
||||
hexdec(substr($color, 5, 2)),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @example '0,255,122'
|
||||
*/
|
||||
public function rgbColor(): string
|
||||
{
|
||||
return implode(',', $this->rgbColorAsArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @example 'rgb(0,255,122)'
|
||||
*/
|
||||
public function rgbCssColor(): string
|
||||
{
|
||||
return sprintf(
|
||||
'rgb(%s)',
|
||||
$this->rgbColor(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example 'rgba(0,255,122,0.8)'
|
||||
*/
|
||||
public function rgbaCssColor(): string
|
||||
{
|
||||
return sprintf(
|
||||
'rgba(%s,%s)',
|
||||
$this->rgbColor(),
|
||||
$this->numberExtension->randomFloat(1, 0, 1),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example 'blue'
|
||||
*/
|
||||
public function safeColorName(): string
|
||||
{
|
||||
return Helper::randomElement($this->safeColorNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example 'NavajoWhite'
|
||||
*/
|
||||
public function colorName(): string
|
||||
{
|
||||
return Helper::randomElement($this->allColorNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example '340,50,20'
|
||||
*/
|
||||
public function hslColor(): string
|
||||
{
|
||||
return sprintf(
|
||||
'%s,%s,%s',
|
||||
$this->numberExtension->numberBetween(0, 360),
|
||||
$this->numberExtension->numberBetween(0, 100),
|
||||
$this->numberExtension->numberBetween(0, 100),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example array(340, 50, 20)
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function hslColorAsArray(): array
|
||||
{
|
||||
return [
|
||||
$this->numberExtension->numberBetween(0, 360),
|
||||
$this->numberExtension->numberBetween(0, 100),
|
||||
$this->numberExtension->numberBetween(0, 100),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Core;
|
||||
|
||||
use Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class Coordinates implements Extension\Extension
|
||||
{
|
||||
private Extension\NumberExtension $numberExtension;
|
||||
|
||||
public function __construct(?Extension\NumberExtension $numberExtension = null)
|
||||
{
|
||||
$this->numberExtension = $numberExtension ?: new Number();
|
||||
}
|
||||
|
||||
/**
|
||||
* @example '77.147489'
|
||||
*
|
||||
* @return float Uses signed degrees format (returns a float number between -90 and 90)
|
||||
*/
|
||||
public function latitude(float $min = -90.0, float $max = 90.0): float
|
||||
{
|
||||
if ($min < -90 || $max < -90) {
|
||||
throw new \LogicException('Latitude cannot be less that -90.0');
|
||||
}
|
||||
|
||||
if ($min > 90 || $max > 90) {
|
||||
throw new \LogicException('Latitude cannot be greater that 90.0');
|
||||
}
|
||||
|
||||
return $this->randomFloat(6, $min, $max);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example '86.211205'
|
||||
*
|
||||
* @return float Uses signed degrees format (returns a float number between -180 and 180)
|
||||
*/
|
||||
public function longitude(float $min = -180.0, float $max = 180.0): float
|
||||
{
|
||||
if ($min < -180 || $max < -180) {
|
||||
throw new \LogicException('Longitude cannot be less that -180.0');
|
||||
}
|
||||
|
||||
if ($min > 180 || $max > 180) {
|
||||
throw new \LogicException('Longitude cannot be greater that 180.0');
|
||||
}
|
||||
|
||||
return $this->randomFloat(6, $min, $max);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example array('77.147489', '86.211205')
|
||||
*
|
||||
* @return array{latitude: float, longitude: float}
|
||||
*/
|
||||
public function localCoordinates(): array
|
||||
{
|
||||
return [
|
||||
'latitude' => $this->latitude(),
|
||||
'longitude' => $this->longitude(),
|
||||
];
|
||||
}
|
||||
|
||||
private function randomFloat(int $nbMaxDecimals, float $min, float $max): float
|
||||
{
|
||||
if ($min > $max) {
|
||||
throw new \LogicException('Invalid coordinates boundaries');
|
||||
}
|
||||
|
||||
return $this->numberExtension->randomFloat($nbMaxDecimals, $min, $max);
|
||||
}
|
||||
}
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Core;
|
||||
|
||||
use Faker\Extension\DateTimeExtension;
|
||||
use Faker\Extension\GeneratorAwareExtension;
|
||||
use Faker\Extension\GeneratorAwareExtensionTrait;
|
||||
use Faker\Extension\Helper;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*
|
||||
* @since 1.20.0
|
||||
*/
|
||||
final class DateTime implements DateTimeExtension, GeneratorAwareExtension
|
||||
{
|
||||
use GeneratorAwareExtensionTrait;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $centuries = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI'];
|
||||
|
||||
private ?string $defaultTimezone = null;
|
||||
|
||||
/**
|
||||
* Get the POSIX-timestamp of a DateTime, int or string.
|
||||
*
|
||||
* @param \DateTime|float|int|string $until
|
||||
*
|
||||
* @return false|int
|
||||
*/
|
||||
private function getTimestamp($until = 'now')
|
||||
{
|
||||
if (is_numeric($until)) {
|
||||
return (int) $until;
|
||||
}
|
||||
|
||||
if ($until instanceof \DateTime) {
|
||||
return $until->getTimestamp();
|
||||
}
|
||||
|
||||
return strtotime(empty($until) ? 'now' : $until);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a DateTime created based on a POSIX-timestamp.
|
||||
*
|
||||
* @param int $timestamp the UNIX / POSIX-compatible timestamp
|
||||
*/
|
||||
private function getTimestampDateTime(int $timestamp): \DateTime
|
||||
{
|
||||
return new \DateTime('@' . $timestamp);
|
||||
}
|
||||
|
||||
private function resolveTimezone(?string $timezone): string
|
||||
{
|
||||
if ($timezone !== null) {
|
||||
return $timezone;
|
||||
}
|
||||
|
||||
return null === $this->defaultTimezone ? date_default_timezone_get() : $this->defaultTimezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to set the timezone on a DateTime object.
|
||||
*/
|
||||
private function setTimezone(\DateTime $dateTime, ?string $timezone): \DateTime
|
||||
{
|
||||
$timezone = $this->resolveTimezone($timezone);
|
||||
|
||||
return $dateTime->setTimezone(new \DateTimeZone($timezone));
|
||||
}
|
||||
|
||||
public function dateTime($until = 'now', ?string $timezone = null): \DateTime
|
||||
{
|
||||
return $this->setTimezone(
|
||||
$this->getTimestampDateTime($this->unixTime($until)),
|
||||
$timezone,
|
||||
);
|
||||
}
|
||||
|
||||
public function dateTimeAD($until = 'now', ?string $timezone = null): \DateTime
|
||||
{
|
||||
$min = (PHP_INT_SIZE > 4) ? -62135597361 : -PHP_INT_MAX;
|
||||
|
||||
return $this->setTimezone(
|
||||
$this->getTimestampDateTime($this->generator->numberBetween($min, $this->getTimestamp($until))),
|
||||
$timezone,
|
||||
);
|
||||
}
|
||||
|
||||
public function dateTimeBetween($from = '-30 years', $until = 'now', ?string $timezone = null): \DateTime
|
||||
{
|
||||
$start = $this->getTimestamp($from);
|
||||
$end = $this->getTimestamp($until);
|
||||
|
||||
if ($start > $end) {
|
||||
throw new \InvalidArgumentException('"$from" must be anterior to "$until".');
|
||||
}
|
||||
|
||||
$timestamp = $this->generator->numberBetween($start, $end);
|
||||
|
||||
return $this->setTimezone(
|
||||
$this->getTimestampDateTime($timestamp),
|
||||
$timezone,
|
||||
);
|
||||
}
|
||||
|
||||
public function dateTimeInInterval($from = '-30 years', string $interval = '+5 days', ?string $timezone = null): \DateTime
|
||||
{
|
||||
$intervalObject = \DateInterval::createFromDateString($interval);
|
||||
$datetime = $from instanceof \DateTime ? $from : new \DateTime($from);
|
||||
|
||||
$other = (clone $datetime)->add($intervalObject);
|
||||
|
||||
$begin = min($datetime, $other);
|
||||
$end = $datetime === $begin ? $other : $datetime;
|
||||
|
||||
return $this->dateTimeBetween($begin, $end, $timezone);
|
||||
}
|
||||
|
||||
public function dateTimeThisWeek($until = 'sunday this week', ?string $timezone = null): \DateTime
|
||||
{
|
||||
return $this->dateTimeBetween('monday this week', $until, $timezone);
|
||||
}
|
||||
|
||||
public function dateTimeThisMonth($until = 'last day of this month', ?string $timezone = null): \DateTime
|
||||
{
|
||||
return $this->dateTimeBetween('first day of this month', $until, $timezone);
|
||||
}
|
||||
|
||||
public function dateTimeThisYear($until = 'last day of december', ?string $timezone = null): \DateTime
|
||||
{
|
||||
return $this->dateTimeBetween('first day of january', $until, $timezone);
|
||||
}
|
||||
|
||||
public function dateTimeThisDecade($until = 'now', ?string $timezone = null): \DateTime
|
||||
{
|
||||
$year = floor(date('Y') / 10) * 10;
|
||||
|
||||
return $this->dateTimeBetween("first day of january $year", $until, $timezone);
|
||||
}
|
||||
|
||||
public function dateTimeThisCentury($until = 'now', ?string $timezone = null): \DateTime
|
||||
{
|
||||
$year = floor(date('Y') / 100) * 100;
|
||||
|
||||
return $this->dateTimeBetween("first day of january $year", $until, $timezone);
|
||||
}
|
||||
|
||||
public function date(string $format = 'Y-m-d', $until = 'now'): string
|
||||
{
|
||||
return $this->dateTime($until)->format($format);
|
||||
}
|
||||
|
||||
public function time(string $format = 'H:i:s', $until = 'now'): string
|
||||
{
|
||||
return $this->date($format, $until);
|
||||
}
|
||||
|
||||
public function unixTime($until = 'now'): int
|
||||
{
|
||||
return $this->generator->numberBetween(0, $this->getTimestamp($until));
|
||||
}
|
||||
|
||||
public function iso8601($until = 'now'): string
|
||||
{
|
||||
return $this->date(\DateTime::ISO8601, $until);
|
||||
}
|
||||
|
||||
public function amPm($until = 'now'): string
|
||||
{
|
||||
return $this->date('a', $until);
|
||||
}
|
||||
|
||||
public function dayOfMonth($until = 'now'): string
|
||||
{
|
||||
return $this->date('d', $until);
|
||||
}
|
||||
|
||||
public function dayOfWeek($until = 'now'): string
|
||||
{
|
||||
return $this->date('l', $until);
|
||||
}
|
||||
|
||||
public function month($until = 'now'): string
|
||||
{
|
||||
return $this->date('m', $until);
|
||||
}
|
||||
|
||||
public function monthName($until = 'now'): string
|
||||
{
|
||||
return $this->date('F', $until);
|
||||
}
|
||||
|
||||
public function year($until = 'now'): string
|
||||
{
|
||||
return $this->date('Y', $until);
|
||||
}
|
||||
|
||||
public function century(): string
|
||||
{
|
||||
return Helper::randomElement($this->centuries);
|
||||
}
|
||||
|
||||
public function timezone(?string $countryCode = null): string
|
||||
{
|
||||
if ($countryCode) {
|
||||
$timezones = \DateTimeZone::listIdentifiers(\DateTimeZone::PER_COUNTRY, $countryCode);
|
||||
} else {
|
||||
$timezones = \DateTimeZone::listIdentifiers();
|
||||
}
|
||||
|
||||
return Helper::randomElement($timezones);
|
||||
}
|
||||
}
|
||||
+564
@@ -0,0 +1,564 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Core;
|
||||
|
||||
use Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class File implements Extension\FileExtension
|
||||
{
|
||||
/**
|
||||
* MIME types from the apache.org file. Some types are truncated.
|
||||
*
|
||||
* @var array Map of MIME types => file extension(s)
|
||||
*
|
||||
* @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
||||
*/
|
||||
private array $mimeTypes = [
|
||||
'application/atom+xml' => 'atom',
|
||||
'application/ecmascript' => 'ecma',
|
||||
'application/emma+xml' => 'emma',
|
||||
'application/epub+zip' => 'epub',
|
||||
'application/java-archive' => 'jar',
|
||||
'application/java-vm' => 'class',
|
||||
'application/javascript' => 'js',
|
||||
'application/json' => 'json',
|
||||
'application/jsonml+json' => 'jsonml',
|
||||
'application/lost+xml' => 'lostxml',
|
||||
'application/mathml+xml' => 'mathml',
|
||||
'application/mets+xml' => 'mets',
|
||||
'application/mods+xml' => 'mods',
|
||||
'application/mp4' => 'mp4s',
|
||||
'application/msword' => ['doc', 'dot'],
|
||||
'application/octet-stream' => [
|
||||
'bin',
|
||||
'dms',
|
||||
'lrf',
|
||||
'mar',
|
||||
'so',
|
||||
'dist',
|
||||
'distz',
|
||||
'pkg',
|
||||
'bpk',
|
||||
'dump',
|
||||
'elc',
|
||||
'deploy',
|
||||
],
|
||||
'application/ogg' => 'ogx',
|
||||
'application/omdoc+xml' => 'omdoc',
|
||||
'application/pdf' => 'pdf',
|
||||
'application/pgp-encrypted' => 'pgp',
|
||||
'application/pgp-signature' => ['asc', 'sig'],
|
||||
'application/pkix-pkipath' => 'pkipath',
|
||||
'application/pkixcmp' => 'pki',
|
||||
'application/pls+xml' => 'pls',
|
||||
'application/postscript' => ['ai', 'eps', 'ps'],
|
||||
'application/pskc+xml' => 'pskcxml',
|
||||
'application/rdf+xml' => 'rdf',
|
||||
'application/reginfo+xml' => 'rif',
|
||||
'application/rss+xml' => 'rss',
|
||||
'application/rtf' => 'rtf',
|
||||
'application/sbml+xml' => 'sbml',
|
||||
'application/vnd.adobe.air-application-installer-package+zip' => 'air',
|
||||
'application/vnd.adobe.xdp+xml' => 'xdp',
|
||||
'application/vnd.adobe.xfdf' => 'xfdf',
|
||||
'application/vnd.ahead.space' => 'ahead',
|
||||
'application/vnd.dart' => 'dart',
|
||||
'application/vnd.data-vision.rdz' => 'rdz',
|
||||
'application/vnd.dece.data' => ['uvf', 'uvvf', 'uvd', 'uvvd'],
|
||||
'application/vnd.dece.ttml+xml' => ['uvt', 'uvvt'],
|
||||
'application/vnd.dece.unspecified' => ['uvx', 'uvvx'],
|
||||
'application/vnd.dece.zip' => ['uvz', 'uvvz'],
|
||||
'application/vnd.denovo.fcselayout-link' => 'fe_launch',
|
||||
'application/vnd.dna' => 'dna',
|
||||
'application/vnd.dolby.mlp' => 'mlp',
|
||||
'application/vnd.dpgraph' => 'dpg',
|
||||
'application/vnd.dreamfactory' => 'dfac',
|
||||
'application/vnd.ds-keypoint' => 'kpxx',
|
||||
'application/vnd.dvb.ait' => 'ait',
|
||||
'application/vnd.dvb.service' => 'svc',
|
||||
'application/vnd.dynageo' => 'geo',
|
||||
'application/vnd.ecowin.chart' => 'mag',
|
||||
'application/vnd.enliven' => 'nml',
|
||||
'application/vnd.epson.esf' => 'esf',
|
||||
'application/vnd.epson.msf' => 'msf',
|
||||
'application/vnd.epson.quickanime' => 'qam',
|
||||
'application/vnd.epson.salt' => 'slt',
|
||||
'application/vnd.epson.ssf' => 'ssf',
|
||||
'application/vnd.ezpix-album' => 'ez2',
|
||||
'application/vnd.ezpix-package' => 'ez3',
|
||||
'application/vnd.fdf' => 'fdf',
|
||||
'application/vnd.fdsn.mseed' => 'mseed',
|
||||
'application/vnd.fdsn.seed' => ['seed', 'dataless'],
|
||||
'application/vnd.flographit' => 'gph',
|
||||
'application/vnd.fluxtime.clip' => 'ftc',
|
||||
'application/vnd.hal+xml' => 'hal',
|
||||
'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx',
|
||||
'application/vnd.ibm.minipay' => 'mpy',
|
||||
'application/vnd.ibm.secure-container' => 'sc',
|
||||
'application/vnd.iccprofile' => ['icc', 'icm'],
|
||||
'application/vnd.igloader' => 'igl',
|
||||
'application/vnd.immervision-ivp' => 'ivp',
|
||||
'application/vnd.kde.karbon' => 'karbon',
|
||||
'application/vnd.kde.kchart' => 'chrt',
|
||||
'application/vnd.kde.kformula' => 'kfo',
|
||||
'application/vnd.kde.kivio' => 'flw',
|
||||
'application/vnd.kde.kontour' => 'kon',
|
||||
'application/vnd.kde.kpresenter' => ['kpr', 'kpt'],
|
||||
'application/vnd.kde.kspread' => 'ksp',
|
||||
'application/vnd.kde.kword' => ['kwd', 'kwt'],
|
||||
'application/vnd.kenameaapp' => 'htke',
|
||||
'application/vnd.kidspiration' => 'kia',
|
||||
'application/vnd.kinar' => ['kne', 'knp'],
|
||||
'application/vnd.koan' => ['skp', 'skd', 'skt', 'skm'],
|
||||
'application/vnd.kodak-descriptor' => 'sse',
|
||||
'application/vnd.las.las+xml' => 'lasxml',
|
||||
'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',
|
||||
'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',
|
||||
'application/vnd.lotus-1-2-3' => '123',
|
||||
'application/vnd.lotus-approach' => 'apr',
|
||||
'application/vnd.lotus-freelance' => 'pre',
|
||||
'application/vnd.lotus-notes' => 'nsf',
|
||||
'application/vnd.lotus-organizer' => 'org',
|
||||
'application/vnd.lotus-screencam' => 'scm',
|
||||
'application/vnd.mozilla.xul+xml' => 'xul',
|
||||
'application/vnd.ms-artgalry' => 'cil',
|
||||
'application/vnd.ms-cab-compressed' => 'cab',
|
||||
'application/vnd.ms-excel' => [
|
||||
'xls',
|
||||
'xlm',
|
||||
'xla',
|
||||
'xlc',
|
||||
'xlt',
|
||||
'xlw',
|
||||
],
|
||||
'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam',
|
||||
'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb',
|
||||
'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',
|
||||
'application/vnd.ms-excel.template.macroenabled.12' => 'xltm',
|
||||
'application/vnd.ms-fontobject' => 'eot',
|
||||
'application/vnd.ms-htmlhelp' => 'chm',
|
||||
'application/vnd.ms-ims' => 'ims',
|
||||
'application/vnd.ms-lrm' => 'lrm',
|
||||
'application/vnd.ms-officetheme' => 'thmx',
|
||||
'application/vnd.ms-pki.seccat' => 'cat',
|
||||
'application/vnd.ms-pki.stl' => 'stl',
|
||||
'application/vnd.ms-powerpoint' => ['ppt', 'pps', 'pot'],
|
||||
'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam',
|
||||
'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm',
|
||||
'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm',
|
||||
'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm',
|
||||
'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm',
|
||||
'application/vnd.ms-project' => ['mpp', 'mpt'],
|
||||
'application/vnd.ms-word.document.macroenabled.12' => 'docm',
|
||||
'application/vnd.ms-word.template.macroenabled.12' => 'dotm',
|
||||
'application/vnd.ms-works' => ['wps', 'wks', 'wcm', 'wdb'],
|
||||
'application/vnd.ms-wpl' => 'wpl',
|
||||
'application/vnd.ms-xpsdocument' => 'xps',
|
||||
'application/vnd.mseq' => 'mseq',
|
||||
'application/vnd.musician' => 'mus',
|
||||
'application/vnd.oasis.opendocument.chart' => 'odc',
|
||||
'application/vnd.oasis.opendocument.chart-template' => 'otc',
|
||||
'application/vnd.oasis.opendocument.database' => 'odb',
|
||||
'application/vnd.oasis.opendocument.formula' => 'odf',
|
||||
'application/vnd.oasis.opendocument.formula-template' => 'odft',
|
||||
'application/vnd.oasis.opendocument.graphics' => 'odg',
|
||||
'application/vnd.oasis.opendocument.graphics-template' => 'otg',
|
||||
'application/vnd.oasis.opendocument.image' => 'odi',
|
||||
'application/vnd.oasis.opendocument.image-template' => 'oti',
|
||||
'application/vnd.oasis.opendocument.presentation' => 'odp',
|
||||
'application/vnd.oasis.opendocument.presentation-template' => 'otp',
|
||||
'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
|
||||
'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
|
||||
'application/vnd.oasis.opendocument.text' => 'odt',
|
||||
'application/vnd.oasis.opendocument.text-master' => 'odm',
|
||||
'application/vnd.oasis.opendocument.text-template' => 'ott',
|
||||
'application/vnd.oasis.opendocument.text-web' => 'oth',
|
||||
'application/vnd.olpc-sugar' => 'xo',
|
||||
'application/vnd.oma.dd2+xml' => 'dd2',
|
||||
'application/vnd.openofficeorg.extension' => 'oxt',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
|
||||
'application/vnd.pvi.ptid1' => 'ptid',
|
||||
'application/vnd.quark.quarkxpress' => [
|
||||
'qxd',
|
||||
'qxt',
|
||||
'qwd',
|
||||
'qwt',
|
||||
'qxl',
|
||||
'qxb',
|
||||
],
|
||||
'application/vnd.realvnc.bed' => 'bed',
|
||||
'application/vnd.recordare.musicxml' => 'mxl',
|
||||
'application/vnd.recordare.musicxml+xml' => 'musicxml',
|
||||
'application/vnd.rig.cryptonote' => 'cryptonote',
|
||||
'application/vnd.rim.cod' => 'cod',
|
||||
'application/vnd.rn-realmedia' => 'rm',
|
||||
'application/vnd.rn-realmedia-vbr' => 'rmvb',
|
||||
'application/vnd.route66.link66+xml' => 'link66',
|
||||
'application/vnd.sailingtracker.track' => 'st',
|
||||
'application/vnd.seemail' => 'see',
|
||||
'application/vnd.sema' => 'sema',
|
||||
'application/vnd.semd' => 'semd',
|
||||
'application/vnd.semf' => 'semf',
|
||||
'application/vnd.shana.informed.formdata' => 'ifm',
|
||||
'application/vnd.shana.informed.formtemplate' => 'itp',
|
||||
'application/vnd.shana.informed.interchange' => 'iif',
|
||||
'application/vnd.shana.informed.package' => 'ipk',
|
||||
'application/vnd.simtech-mindmapper' => ['twd', 'twds'],
|
||||
'application/vnd.smaf' => 'mmf',
|
||||
'application/vnd.stepmania.stepchart' => 'sm',
|
||||
'application/vnd.sun.xml.calc' => 'sxc',
|
||||
'application/vnd.sun.xml.calc.template' => 'stc',
|
||||
'application/vnd.sun.xml.draw' => 'sxd',
|
||||
'application/vnd.sun.xml.draw.template' => 'std',
|
||||
'application/vnd.sun.xml.impress' => 'sxi',
|
||||
'application/vnd.sun.xml.impress.template' => 'sti',
|
||||
'application/vnd.sun.xml.math' => 'sxm',
|
||||
'application/vnd.sun.xml.writer' => 'sxw',
|
||||
'application/vnd.sun.xml.writer.global' => 'sxg',
|
||||
'application/vnd.sun.xml.writer.template' => 'stw',
|
||||
'application/vnd.sus-calendar' => ['sus', 'susp'],
|
||||
'application/vnd.svd' => 'svd',
|
||||
'application/vnd.symbian.install' => ['sis', 'sisx'],
|
||||
'application/vnd.syncml+xml' => 'xsm',
|
||||
'application/vnd.syncml.dm+wbxml' => 'bdm',
|
||||
'application/vnd.syncml.dm+xml' => 'xdm',
|
||||
'application/vnd.tao.intent-module-archive' => 'tao',
|
||||
'application/vnd.tcpdump.pcap' => ['pcap', 'cap', 'dmp'],
|
||||
'application/vnd.tmobile-livetv' => 'tmo',
|
||||
'application/vnd.trid.tpt' => 'tpt',
|
||||
'application/vnd.triscape.mxs' => 'mxs',
|
||||
'application/vnd.trueapp' => 'tra',
|
||||
'application/vnd.ufdl' => ['ufd', 'ufdl'],
|
||||
'application/vnd.uiq.theme' => 'utz',
|
||||
'application/vnd.umajin' => 'umj',
|
||||
'application/vnd.unity' => 'unityweb',
|
||||
'application/vnd.uoml+xml' => 'uoml',
|
||||
'application/vnd.vcx' => 'vcx',
|
||||
'application/vnd.visio' => ['vsd', 'vst', 'vss', 'vsw'],
|
||||
'application/vnd.visionary' => 'vis',
|
||||
'application/vnd.vsf' => 'vsf',
|
||||
'application/vnd.wap.wbxml' => 'wbxml',
|
||||
'application/vnd.wap.wmlc' => 'wmlc',
|
||||
'application/vnd.wap.wmlscriptc' => 'wmlsc',
|
||||
'application/vnd.webturbo' => 'wtb',
|
||||
'application/vnd.wolfram.player' => 'nbp',
|
||||
'application/vnd.wordperfect' => 'wpd',
|
||||
'application/vnd.wqd' => 'wqd',
|
||||
'application/vnd.wt.stf' => 'stf',
|
||||
'application/vnd.xara' => 'xar',
|
||||
'application/vnd.xfdl' => 'xfdl',
|
||||
'application/voicexml+xml' => 'vxml',
|
||||
'application/widget' => 'wgt',
|
||||
'application/winhlp' => 'hlp',
|
||||
'application/wsdl+xml' => 'wsdl',
|
||||
'application/wspolicy+xml' => 'wspolicy',
|
||||
'application/x-7z-compressed' => '7z',
|
||||
'application/x-bittorrent' => 'torrent',
|
||||
'application/x-blorb' => ['blb', 'blorb'],
|
||||
'application/x-bzip' => 'bz',
|
||||
'application/x-cdlink' => 'vcd',
|
||||
'application/x-cfs-compressed' => 'cfs',
|
||||
'application/x-chat' => 'chat',
|
||||
'application/x-chess-pgn' => 'pgn',
|
||||
'application/x-conference' => 'nsc',
|
||||
'application/x-cpio' => 'cpio',
|
||||
'application/x-csh' => 'csh',
|
||||
'application/x-debian-package' => ['deb', 'udeb'],
|
||||
'application/x-dgc-compressed' => 'dgc',
|
||||
'application/x-director' => [
|
||||
'dir',
|
||||
'dcr',
|
||||
'dxr',
|
||||
'cst',
|
||||
'cct',
|
||||
'cxt',
|
||||
'w3d',
|
||||
'fgd',
|
||||
'swa',
|
||||
],
|
||||
'application/x-font-ttf' => ['ttf', 'ttc'],
|
||||
'application/x-font-type1' => ['pfa', 'pfb', 'pfm', 'afm'],
|
||||
'application/x-font-woff' => 'woff',
|
||||
'application/x-freearc' => 'arc',
|
||||
'application/x-futuresplash' => 'spl',
|
||||
'application/x-gca-compressed' => 'gca',
|
||||
'application/x-glulx' => 'ulx',
|
||||
'application/x-gnumeric' => 'gnumeric',
|
||||
'application/x-gramps-xml' => 'gramps',
|
||||
'application/x-gtar' => 'gtar',
|
||||
'application/x-hdf' => 'hdf',
|
||||
'application/x-install-instructions' => 'install',
|
||||
'application/x-iso9660-image' => 'iso',
|
||||
'application/x-java-jnlp-file' => 'jnlp',
|
||||
'application/x-latex' => 'latex',
|
||||
'application/x-lzh-compressed' => ['lzh', 'lha'],
|
||||
'application/x-mie' => 'mie',
|
||||
'application/x-mobipocket-ebook' => ['prc', 'mobi'],
|
||||
'application/x-ms-application' => 'application',
|
||||
'application/x-ms-shortcut' => 'lnk',
|
||||
'application/x-ms-wmd' => 'wmd',
|
||||
'application/x-ms-wmz' => 'wmz',
|
||||
'application/x-ms-xbap' => 'xbap',
|
||||
'application/x-msaccess' => 'mdb',
|
||||
'application/x-msbinder' => 'obd',
|
||||
'application/x-mscardfile' => 'crd',
|
||||
'application/x-msclip' => 'clp',
|
||||
'application/x-msdownload' => ['exe', 'dll', 'com', 'bat', 'msi'],
|
||||
'application/x-msmediaview' => [
|
||||
'mvb',
|
||||
'm13',
|
||||
'm14',
|
||||
],
|
||||
'application/x-msmetafile' => ['wmf', 'wmz', 'emf', 'emz'],
|
||||
'application/x-rar-compressed' => 'rar',
|
||||
'application/x-research-info-systems' => 'ris',
|
||||
'application/x-sh' => 'sh',
|
||||
'application/x-shar' => 'shar',
|
||||
'application/x-shockwave-flash' => 'swf',
|
||||
'application/x-silverlight-app' => 'xap',
|
||||
'application/x-sql' => 'sql',
|
||||
'application/x-stuffit' => 'sit',
|
||||
'application/x-stuffitx' => 'sitx',
|
||||
'application/x-subrip' => 'srt',
|
||||
'application/x-sv4cpio' => 'sv4cpio',
|
||||
'application/x-sv4crc' => 'sv4crc',
|
||||
'application/x-t3vm-image' => 't3',
|
||||
'application/x-tads' => 'gam',
|
||||
'application/x-tar' => 'tar',
|
||||
'application/x-tcl' => 'tcl',
|
||||
'application/x-tex' => 'tex',
|
||||
'application/x-tex-tfm' => 'tfm',
|
||||
'application/x-texinfo' => ['texinfo', 'texi'],
|
||||
'application/x-tgif' => 'obj',
|
||||
'application/x-ustar' => 'ustar',
|
||||
'application/x-wais-source' => 'src',
|
||||
'application/x-x509-ca-cert' => ['der', 'crt'],
|
||||
'application/x-xfig' => 'fig',
|
||||
'application/x-xliff+xml' => 'xlf',
|
||||
'application/x-xpinstall' => 'xpi',
|
||||
'application/x-xz' => 'xz',
|
||||
'application/x-zmachine' => 'z1',
|
||||
'application/xaml+xml' => 'xaml',
|
||||
'application/xcap-diff+xml' => 'xdf',
|
||||
'application/xenc+xml' => 'xenc',
|
||||
'application/xhtml+xml' => ['xhtml', 'xht'],
|
||||
'application/xml' => ['xml', 'xsl'],
|
||||
'application/xml-dtd' => 'dtd',
|
||||
'application/xop+xml' => 'xop',
|
||||
'application/xproc+xml' => 'xpl',
|
||||
'application/xslt+xml' => 'xslt',
|
||||
'application/xspf+xml' => 'xspf',
|
||||
'application/xv+xml' => ['mxml', 'xhvml', 'xvml', 'xvm'],
|
||||
'application/yang' => 'yang',
|
||||
'application/yin+xml' => 'yin',
|
||||
'application/zip' => 'zip',
|
||||
'audio/adpcm' => 'adp',
|
||||
'audio/basic' => ['au', 'snd'],
|
||||
'audio/midi' => ['mid', 'midi', 'kar', 'rmi'],
|
||||
'audio/mp4' => 'mp4a',
|
||||
'audio/mpeg' => [
|
||||
'mpga',
|
||||
'mp2',
|
||||
'mp2a',
|
||||
'mp3',
|
||||
'm2a',
|
||||
'm3a',
|
||||
],
|
||||
'audio/ogg' => ['oga', 'ogg', 'spx'],
|
||||
'audio/vnd.dece.audio' => ['uva', 'uvva'],
|
||||
'audio/vnd.rip' => 'rip',
|
||||
'audio/webm' => 'weba',
|
||||
'audio/x-aac' => 'aac',
|
||||
'audio/x-aiff' => ['aif', 'aiff', 'aifc'],
|
||||
'audio/x-caf' => 'caf',
|
||||
'audio/x-flac' => 'flac',
|
||||
'audio/x-matroska' => 'mka',
|
||||
'audio/x-mpegurl' => 'm3u',
|
||||
'audio/x-ms-wax' => 'wax',
|
||||
'audio/x-ms-wma' => 'wma',
|
||||
'audio/x-pn-realaudio' => ['ram', 'ra'],
|
||||
'audio/x-pn-realaudio-plugin' => 'rmp',
|
||||
'audio/x-wav' => 'wav',
|
||||
'audio/xm' => 'xm',
|
||||
'image/bmp' => 'bmp',
|
||||
'image/cgm' => 'cgm',
|
||||
'image/g3fax' => 'g3',
|
||||
'image/gif' => 'gif',
|
||||
'image/ief' => 'ief',
|
||||
'image/jpeg' => ['jpeg', 'jpg', 'jpe'],
|
||||
'image/ktx' => 'ktx',
|
||||
'image/png' => 'png',
|
||||
'image/prs.btif' => 'btif',
|
||||
'image/sgi' => 'sgi',
|
||||
'image/svg+xml' => ['svg', 'svgz'],
|
||||
'image/tiff' => ['tiff', 'tif'],
|
||||
'image/vnd.adobe.photoshop' => 'psd',
|
||||
'image/vnd.dece.graphic' => ['uvi', 'uvvi', 'uvg', 'uvvg'],
|
||||
'image/vnd.dvb.subtitle' => 'sub',
|
||||
'image/vnd.djvu' => ['djvu', 'djv'],
|
||||
'image/vnd.dwg' => 'dwg',
|
||||
'image/vnd.dxf' => 'dxf',
|
||||
'image/vnd.fastbidsheet' => 'fbs',
|
||||
'image/vnd.fpx' => 'fpx',
|
||||
'image/vnd.fst' => 'fst',
|
||||
'image/vnd.fujixerox.edmics-mmr' => 'mmr',
|
||||
'image/vnd.fujixerox.edmics-rlc' => 'rlc',
|
||||
'image/vnd.ms-modi' => 'mdi',
|
||||
'image/vnd.ms-photo' => 'wdp',
|
||||
'image/vnd.net-fpx' => 'npx',
|
||||
'image/vnd.wap.wbmp' => 'wbmp',
|
||||
'image/vnd.xiff' => 'xif',
|
||||
'image/webp' => 'webp',
|
||||
'image/x-3ds' => '3ds',
|
||||
'image/x-cmu-raster' => 'ras',
|
||||
'image/x-cmx' => 'cmx',
|
||||
'image/x-freehand' => ['fh', 'fhc', 'fh4', 'fh5', 'fh7'],
|
||||
'image/x-icon' => 'ico',
|
||||
'image/x-mrsid-image' => 'sid',
|
||||
'image/x-pcx' => 'pcx',
|
||||
'image/x-pict' => ['pic', 'pct'],
|
||||
'image/x-portable-anymap' => 'pnm',
|
||||
'image/x-portable-bitmap' => 'pbm',
|
||||
'image/x-portable-graymap' => 'pgm',
|
||||
'image/x-portable-pixmap' => 'ppm',
|
||||
'image/x-rgb' => 'rgb',
|
||||
'image/x-tga' => 'tga',
|
||||
'image/x-xbitmap' => 'xbm',
|
||||
'image/x-xpixmap' => 'xpm',
|
||||
'image/x-xwindowdump' => 'xwd',
|
||||
'message/rfc822' => ['eml', 'mime'],
|
||||
'model/iges' => ['igs', 'iges'],
|
||||
'model/mesh' => ['msh', 'mesh', 'silo'],
|
||||
'model/vnd.collada+xml' => 'dae',
|
||||
'model/vnd.dwf' => 'dwf',
|
||||
'model/vnd.gdl' => 'gdl',
|
||||
'model/vnd.gtw' => 'gtw',
|
||||
'model/vnd.mts' => 'mts',
|
||||
'model/vnd.vtu' => 'vtu',
|
||||
'model/vrml' => ['wrl', 'vrml'],
|
||||
'model/x3d+binary' => 'x3db',
|
||||
'model/x3d+vrml' => 'x3dv',
|
||||
'model/x3d+xml' => 'x3d',
|
||||
'text/cache-manifest' => 'appcache',
|
||||
'text/calendar' => ['ics', 'ifb'],
|
||||
'text/css' => 'css',
|
||||
'text/csv' => 'csv',
|
||||
'text/html' => ['html', 'htm'],
|
||||
'text/n3' => 'n3',
|
||||
'text/plain' => [
|
||||
'txt',
|
||||
'text',
|
||||
'conf',
|
||||
'def',
|
||||
'list',
|
||||
'log',
|
||||
'in',
|
||||
],
|
||||
'text/prs.lines.tag' => 'dsc',
|
||||
'text/richtext' => 'rtx',
|
||||
'text/sgml' => ['sgml', 'sgm'],
|
||||
'text/tab-separated-values' => 'tsv',
|
||||
'text/troff' => [
|
||||
't',
|
||||
'tr',
|
||||
'roff',
|
||||
'man',
|
||||
'me',
|
||||
'ms',
|
||||
],
|
||||
'text/turtle' => 'ttl',
|
||||
'text/uri-list' => ['uri', 'uris', 'urls'],
|
||||
'text/vcard' => 'vcard',
|
||||
'text/vnd.curl' => 'curl',
|
||||
'text/vnd.curl.dcurl' => 'dcurl',
|
||||
'text/vnd.curl.scurl' => 'scurl',
|
||||
'text/vnd.curl.mcurl' => 'mcurl',
|
||||
'text/vnd.dvb.subtitle' => 'sub',
|
||||
'text/vnd.fly' => 'fly',
|
||||
'text/vnd.fmi.flexstor' => 'flx',
|
||||
'text/vnd.graphviz' => 'gv',
|
||||
'text/vnd.in3d.3dml' => '3dml',
|
||||
'text/vnd.in3d.spot' => 'spot',
|
||||
'text/vnd.sun.j2me.app-descriptor' => 'jad',
|
||||
'text/vnd.wap.wml' => 'wml',
|
||||
'text/vnd.wap.wmlscript' => 'wmls',
|
||||
'text/x-asm' => ['s', 'asm'],
|
||||
'text/x-fortran' => ['f', 'for', 'f77', 'f90'],
|
||||
'text/x-java-source' => 'java',
|
||||
'text/x-opml' => 'opml',
|
||||
'text/x-pascal' => ['p', 'pas'],
|
||||
'text/x-nfo' => 'nfo',
|
||||
'text/x-setext' => 'etx',
|
||||
'text/x-sfv' => 'sfv',
|
||||
'text/x-uuencode' => 'uu',
|
||||
'text/x-vcalendar' => 'vcs',
|
||||
'text/x-vcard' => 'vcf',
|
||||
'video/3gpp' => '3gp',
|
||||
'video/3gpp2' => '3g2',
|
||||
'video/h261' => 'h261',
|
||||
'video/h263' => 'h263',
|
||||
'video/h264' => 'h264',
|
||||
'video/jpeg' => 'jpgv',
|
||||
'video/jpm' => ['jpm', 'jpgm'],
|
||||
'video/mj2' => 'mj2',
|
||||
'video/mp4' => 'mp4',
|
||||
'video/mpeg' => ['mpeg', 'mpg', 'mpe', 'm1v', 'm2v'],
|
||||
'video/ogg' => 'ogv',
|
||||
'video/quicktime' => ['qt', 'mov'],
|
||||
'video/vnd.dece.hd' => ['uvh', 'uvvh'],
|
||||
'video/vnd.dece.mobile' => ['uvm', 'uvvm'],
|
||||
'video/vnd.dece.pd' => ['uvp', 'uvvp'],
|
||||
'video/vnd.dece.sd' => ['uvs', 'uvvs'],
|
||||
'video/vnd.dece.video' => ['uvv', 'uvvv'],
|
||||
'video/vnd.dvb.file' => 'dvb',
|
||||
'video/vnd.fvt' => 'fvt',
|
||||
'video/vnd.mpegurl' => ['mxu', 'm4u'],
|
||||
'video/vnd.ms-playready.media.pyv' => 'pyv',
|
||||
'video/vnd.uvvu.mp4' => ['uvu', 'uvvu'],
|
||||
'video/vnd.vivo' => 'viv',
|
||||
'video/webm' => 'webm',
|
||||
'video/x-f4v' => 'f4v',
|
||||
'video/x-fli' => 'fli',
|
||||
'video/x-flv' => 'flv',
|
||||
'video/x-m4v' => 'm4v',
|
||||
'video/x-matroska' => ['mkv', 'mk3d', 'mks'],
|
||||
'video/x-mng' => 'mng',
|
||||
'video/x-ms-asf' => ['asf', 'asx'],
|
||||
'video/x-ms-vob' => 'vob',
|
||||
'video/x-ms-wm' => 'wm',
|
||||
'video/x-ms-wmv' => 'wmv',
|
||||
'video/x-ms-wmx' => 'wmx',
|
||||
'video/x-ms-wvx' => 'wvx',
|
||||
'video/x-msvideo' => 'avi',
|
||||
'video/x-sgi-movie' => 'movie',
|
||||
];
|
||||
|
||||
public function mimeType(): string
|
||||
{
|
||||
return array_rand($this->mimeTypes, 1);
|
||||
}
|
||||
|
||||
public function extension(): string
|
||||
{
|
||||
$extension = $this->mimeTypes[array_rand($this->mimeTypes, 1)];
|
||||
|
||||
return is_array($extension) ? $extension[array_rand($extension, 1)] : $extension;
|
||||
}
|
||||
|
||||
public function filePath(): string
|
||||
{
|
||||
return tempnam(sys_get_temp_dir(), 'faker');
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Core;
|
||||
|
||||
use Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class Number implements Extension\NumberExtension
|
||||
{
|
||||
public function numberBetween(int $min = 0, int $max = 2147483647): int
|
||||
{
|
||||
$int1 = min($min, $max);
|
||||
$int2 = max($min, $max);
|
||||
|
||||
return mt_rand($int1, $int2);
|
||||
}
|
||||
|
||||
public function randomDigit(): int
|
||||
{
|
||||
return $this->numberBetween(0, 9);
|
||||
}
|
||||
|
||||
public function randomDigitNot(int $except): int
|
||||
{
|
||||
$result = $this->numberBetween(0, 8);
|
||||
|
||||
if ($result >= $except) {
|
||||
++$result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function randomDigitNotZero(): int
|
||||
{
|
||||
return $this->numberBetween(1, 9);
|
||||
}
|
||||
|
||||
public function randomFloat(?int $nbMaxDecimals = null, float $min = 0, ?float $max = null): float
|
||||
{
|
||||
if (null === $nbMaxDecimals) {
|
||||
$nbMaxDecimals = $this->randomDigit();
|
||||
}
|
||||
|
||||
if (null === $max) {
|
||||
$max = $this->randomNumber();
|
||||
|
||||
if ($min > $max) {
|
||||
$max = $min;
|
||||
}
|
||||
}
|
||||
|
||||
if ($min > $max) {
|
||||
$tmp = $min;
|
||||
$min = $max;
|
||||
$max = $tmp;
|
||||
}
|
||||
|
||||
return round($min + $this->numberBetween() / mt_getrandmax() * ($max - $min), $nbMaxDecimals);
|
||||
}
|
||||
|
||||
public function randomNumber(?int $nbDigits = null, bool $strict = false): int
|
||||
{
|
||||
if (null === $nbDigits) {
|
||||
$nbDigits = $this->randomDigitNotZero();
|
||||
}
|
||||
$max = 10 ** $nbDigits - 1;
|
||||
|
||||
if ($max > mt_getrandmax()) {
|
||||
throw new \InvalidArgumentException('randomNumber() can only generate numbers up to mt_getrandmax()');
|
||||
}
|
||||
|
||||
if ($strict) {
|
||||
return $this->numberBetween(10 ** ($nbDigits - 1), $max);
|
||||
}
|
||||
|
||||
return $this->numberBetween(0, $max);
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Core;
|
||||
|
||||
use Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class Uuid implements Extension\UuidExtension
|
||||
{
|
||||
private Extension\NumberExtension $numberExtension;
|
||||
|
||||
public function __construct(?Extension\NumberExtension $numberExtension = null)
|
||||
{
|
||||
|
||||
$this->numberExtension = $numberExtension ?: new Number();
|
||||
}
|
||||
|
||||
public function uuid3(): string
|
||||
{
|
||||
// fix for compatibility with 32bit architecture; each mt_rand call is restricted to 32bit
|
||||
// two such calls will cause 64bits of randomness regardless of architecture
|
||||
$seed = $this->numberExtension->numberBetween(0, 2147483647) . '#' . $this->numberExtension->numberBetween(0, 2147483647);
|
||||
|
||||
// Hash the seed and convert to a byte array
|
||||
$val = md5($seed, true);
|
||||
$byte = array_values(unpack('C16', $val));
|
||||
|
||||
// extract fields from byte array
|
||||
$tLo = ($byte[0] << 24) | ($byte[1] << 16) | ($byte[2] << 8) | $byte[3];
|
||||
$tMi = ($byte[4] << 8) | $byte[5];
|
||||
$tHi = ($byte[6] << 8) | $byte[7];
|
||||
$csLo = $byte[9];
|
||||
$csHi = $byte[8] & 0x3f | (1 << 7);
|
||||
|
||||
// correct byte order for big edian architecture
|
||||
if (pack('L', 0x6162797A) == pack('N', 0x6162797A)) {
|
||||
$tLo = (($tLo & 0x000000ff) << 24) | (($tLo & 0x0000ff00) << 8)
|
||||
| (($tLo & 0x00ff0000) >> 8) | (($tLo & 0xff000000) >> 24);
|
||||
$tMi = (($tMi & 0x00ff) << 8) | (($tMi & 0xff00) >> 8);
|
||||
$tHi = (($tHi & 0x00ff) << 8) | (($tHi & 0xff00) >> 8);
|
||||
}
|
||||
|
||||
// apply version number
|
||||
$tHi &= 0x0fff;
|
||||
$tHi |= (3 << 12);
|
||||
|
||||
// cast to string
|
||||
return sprintf(
|
||||
'%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x',
|
||||
$tLo,
|
||||
$tMi,
|
||||
$tHi,
|
||||
$csHi,
|
||||
$csLo,
|
||||
$byte[10],
|
||||
$byte[11],
|
||||
$byte[12],
|
||||
$byte[13],
|
||||
$byte[14],
|
||||
$byte[15],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Core;
|
||||
|
||||
use Faker\Extension;
|
||||
use Faker\Provider\DateTime;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class Version implements Extension\VersionExtension
|
||||
{
|
||||
private Extension\NumberExtension $numberExtension;
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $semverCommonPreReleaseIdentifiers = ['alpha', 'beta', 'rc'];
|
||||
|
||||
public function __construct(?Extension\NumberExtension $numberExtension = null)
|
||||
{
|
||||
|
||||
$this->numberExtension = $numberExtension ?: new Number();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents v2.0.0 of the semantic versioning: https://semver.org/spec/v2.0.0.html
|
||||
*/
|
||||
public function semver(bool $preRelease = false, bool $build = false): string
|
||||
{
|
||||
return sprintf(
|
||||
'%d.%d.%d%s%s',
|
||||
$this->numberExtension->numberBetween(0, 9),
|
||||
$this->numberExtension->numberBetween(0, 99),
|
||||
$this->numberExtension->numberBetween(0, 99),
|
||||
$preRelease && $this->numberExtension->numberBetween(0, 1) === 1 ? '-' . $this->semverPreReleaseIdentifier() : '',
|
||||
$build && $this->numberExtension->numberBetween(0, 1) === 1 ? '+' . $this->semverBuildIdentifier() : '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common pre-release identifier
|
||||
*/
|
||||
private function semverPreReleaseIdentifier(): string
|
||||
{
|
||||
$ident = Extension\Helper::randomElement($this->semverCommonPreReleaseIdentifiers);
|
||||
|
||||
if ($this->numberExtension->numberBetween(0, 1) !== 1) {
|
||||
return $ident;
|
||||
}
|
||||
|
||||
return $ident . '.' . $this->numberExtension->numberBetween(1, 99);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common random build identifier
|
||||
*/
|
||||
private function semverBuildIdentifier(): string
|
||||
{
|
||||
if ($this->numberExtension->numberBetween(0, 1) === 1) {
|
||||
// short git revision syntax: https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection
|
||||
return substr(sha1(Extension\Helper::lexify('??????')), 0, 7);
|
||||
}
|
||||
|
||||
// date syntax
|
||||
return DateTime::date('YmdHis');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Faker;
|
||||
|
||||
/**
|
||||
* This generator returns a default value for all called properties
|
||||
* and methods.
|
||||
*
|
||||
* @mixin Generator
|
||||
*
|
||||
* @deprecated Use ChanceGenerator instead
|
||||
*/
|
||||
class DefaultGenerator
|
||||
{
|
||||
protected $default;
|
||||
|
||||
public function __construct($default = null)
|
||||
{
|
||||
trigger_deprecation('fakerphp/faker', '1.16', 'Class "%s" is deprecated, use "%s" instead.', __CLASS__, ChanceGenerator::class);
|
||||
|
||||
$this->default = $default;
|
||||
}
|
||||
|
||||
public function ext()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attribute
|
||||
*
|
||||
* @deprecated Use a method instead.
|
||||
*/
|
||||
public function __get($attribute)
|
||||
{
|
||||
trigger_deprecation('fakerphp/faker', '1.14', 'Accessing property "%s" is deprecated, use "%s()" instead.', $attribute, $attribute);
|
||||
|
||||
return $this->default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __call($method, $attributes)
|
||||
{
|
||||
return $this->default;
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Faker;
|
||||
|
||||
class Documentor
|
||||
{
|
||||
protected $generator;
|
||||
|
||||
public function __construct(Generator $generator)
|
||||
{
|
||||
$this->generator = $generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFormatters()
|
||||
{
|
||||
$formatters = [];
|
||||
$providers = array_reverse($this->generator->getProviders());
|
||||
$providers[] = new Provider\Base($this->generator);
|
||||
|
||||
foreach ($providers as $provider) {
|
||||
$providerClass = get_class($provider);
|
||||
$formatters[$providerClass] = [];
|
||||
$refl = new \ReflectionObject($provider);
|
||||
|
||||
foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflmethod) {
|
||||
if ($reflmethod->getDeclaringClass()->getName() == 'Faker\Provider\Base' && $providerClass != 'Faker\Provider\Base') {
|
||||
continue;
|
||||
}
|
||||
$methodName = $reflmethod->name;
|
||||
|
||||
if ($reflmethod->isConstructor()) {
|
||||
continue;
|
||||
}
|
||||
$parameters = [];
|
||||
|
||||
foreach ($reflmethod->getParameters() as $reflparameter) {
|
||||
$parameter = '$' . $reflparameter->getName();
|
||||
|
||||
if ($reflparameter->isDefaultValueAvailable()) {
|
||||
$parameter .= ' = ' . var_export($reflparameter->getDefaultValue(), true);
|
||||
}
|
||||
$parameters[] = $parameter;
|
||||
}
|
||||
$parameters = $parameters ? '(' . implode(', ', $parameters) . ')' : '';
|
||||
|
||||
try {
|
||||
$example = $this->generator->format($methodName);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$example = '';
|
||||
}
|
||||
|
||||
if (is_array($example)) {
|
||||
$example = "array('" . implode("', '", $example) . "')";
|
||||
} elseif ($example instanceof \DateTime) {
|
||||
$example = "DateTime('" . $example->format('Y-m-d H:i:s') . "')";
|
||||
} elseif ($example instanceof Generator || $example instanceof UniqueGenerator) { // modifier
|
||||
$example = '';
|
||||
} else {
|
||||
$example = var_export($example, true);
|
||||
}
|
||||
$formatters[$providerClass][$methodName . $parameters] = $example;
|
||||
}
|
||||
}
|
||||
|
||||
return $formatters;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This interface is experimental and does not fall under our BC promise
|
||||
*/
|
||||
interface AddressExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* @example '791 Crist Parks, Sashabury, IL 86039-9874'
|
||||
*/
|
||||
public function address(): string;
|
||||
|
||||
/**
|
||||
* Randomly return a real city name.
|
||||
*/
|
||||
public function city(): string;
|
||||
|
||||
/**
|
||||
* @example 86039-9874
|
||||
*/
|
||||
public function postcode(): string;
|
||||
|
||||
/**
|
||||
* @example 'Crist Parks'
|
||||
*/
|
||||
public function streetName(): string;
|
||||
|
||||
/**
|
||||
* @example '791 Crist Parks'
|
||||
*/
|
||||
public function streetAddress(): string;
|
||||
|
||||
/**
|
||||
* Randomly return a building number.
|
||||
*/
|
||||
public function buildingNumber(): string;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This interface is experimental and does not fall under our BC promise
|
||||
*/
|
||||
interface BarcodeExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* Get a random EAN13 barcode.
|
||||
*
|
||||
* @example '4006381333931'
|
||||
*/
|
||||
public function ean13(): string;
|
||||
|
||||
/**
|
||||
* Get a random EAN8 barcode.
|
||||
*
|
||||
* @example '73513537'
|
||||
*/
|
||||
public function ean8(): string;
|
||||
|
||||
/**
|
||||
* Get a random ISBN-10 code
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/International_Standard_Book_Number
|
||||
*
|
||||
* @example '4881416324'
|
||||
*/
|
||||
public function isbn10(): string;
|
||||
|
||||
/**
|
||||
* Get a random ISBN-13 code
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/International_Standard_Book_Number
|
||||
*
|
||||
* @example '9790404436093'
|
||||
*/
|
||||
public function isbn13(): string;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This interface is experimental and does not fall under our BC promise
|
||||
*/
|
||||
interface BloodExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* Get an actual blood type
|
||||
*
|
||||
* @example 'AB'
|
||||
*/
|
||||
public function bloodType(): string;
|
||||
|
||||
/**
|
||||
* Get a random resis value
|
||||
*
|
||||
* @example '+'
|
||||
*/
|
||||
public function bloodRh(): string;
|
||||
|
||||
/**
|
||||
* Get a full blood group
|
||||
*
|
||||
* @example 'AB+'
|
||||
*/
|
||||
public function bloodGroup(): string;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This interface is experimental and does not fall under our BC promise
|
||||
*/
|
||||
interface ColorExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* @example '#fa3cc2'
|
||||
*/
|
||||
public function hexColor(): string;
|
||||
|
||||
/**
|
||||
* @example '#ff0044'
|
||||
*/
|
||||
public function safeHexColor(): string;
|
||||
|
||||
/**
|
||||
* @example 'array(0,255,122)'
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function rgbColorAsArray(): array;
|
||||
|
||||
/**
|
||||
* @example '0,255,122'
|
||||
*/
|
||||
public function rgbColor(): string;
|
||||
|
||||
/**
|
||||
* @example 'rgb(0,255,122)'
|
||||
*/
|
||||
public function rgbCssColor(): string;
|
||||
|
||||
/**
|
||||
* @example 'rgba(0,255,122,0.8)'
|
||||
*/
|
||||
public function rgbaCssColor(): string;
|
||||
|
||||
/**
|
||||
* @example 'blue'
|
||||
*/
|
||||
public function safeColorName(): string;
|
||||
|
||||
/**
|
||||
* @example 'NavajoWhite'
|
||||
*/
|
||||
public function colorName(): string;
|
||||
|
||||
/**
|
||||
* @example '340,50,20'
|
||||
*/
|
||||
public function hslColor(): string;
|
||||
|
||||
/**
|
||||
* @example array(340, 50, 20)
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function hslColorAsArray(): array;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This interface is experimental and does not fall under our BC promise
|
||||
*/
|
||||
interface CompanyExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* @example 'Acme Ltd'
|
||||
*/
|
||||
public function company(): string;
|
||||
|
||||
/**
|
||||
* @example 'Ltd'
|
||||
*/
|
||||
public function companySuffix(): string;
|
||||
|
||||
public function jobTitle(): string;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This interface is experimental and does not fall under our BC promise
|
||||
*/
|
||||
interface CountryExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* @example 'Japan'
|
||||
*/
|
||||
public function country(): string;
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* FakerPHP extension for Date-related randomization.
|
||||
*
|
||||
* Functions accepting a date string use the `strtotime()` function internally.
|
||||
*
|
||||
* @experimental
|
||||
*
|
||||
* @since 1.20.0
|
||||
*/
|
||||
interface DateTimeExtension
|
||||
{
|
||||
/**
|
||||
* Get a DateTime object between January 1, 1970, and `$until` (defaults to "now").
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
* @param string|null $timezone zone timezone for generated date, fallback to `DateTime::$defaultTimezone` and `date_default_timezone_get()`.
|
||||
*
|
||||
* @see \DateTimeZone
|
||||
* @see http://php.net/manual/en/timezones.php
|
||||
* @see http://php.net/manual/en/function.date-default-timezone-get.php
|
||||
*
|
||||
* @example DateTime('2005-08-16 20:39:21')
|
||||
*/
|
||||
public function dateTime($until = 'now', ?string $timezone = null): \DateTime;
|
||||
|
||||
/**
|
||||
* Get a DateTime object for a date between January 1, 0001, and now.
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
* @param string|null $timezone zone timezone for generated date, fallback to `DateTime::$defaultTimezone` and `date_default_timezone_get()`.
|
||||
*
|
||||
* @example DateTime('1265-03-22 21:15:52')
|
||||
*
|
||||
* @see http://php.net/manual/en/timezones.php
|
||||
* @see http://php.net/manual/en/function.date-default-timezone-get.php
|
||||
*/
|
||||
public function dateTimeAD($until = 'now', ?string $timezone = null): \DateTime;
|
||||
|
||||
/**
|
||||
* Get a DateTime object a random date between `$from` and `$until`.
|
||||
* Accepts date strings that can be recognized by `strtotime()`.
|
||||
*
|
||||
* @param \DateTime|string $from defaults to 30 years ago
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
* @param string|null $timezone zone timezone for generated date, fallback to `DateTime::$defaultTimezone` and `date_default_timezone_get()`.
|
||||
*
|
||||
* @see \DateTimeZone
|
||||
* @see http://php.net/manual/en/timezones.php
|
||||
* @see http://php.net/manual/en/function.date-default-timezone-get.php
|
||||
*/
|
||||
public function dateTimeBetween($from = '-30 years', $until = 'now', ?string $timezone = null): \DateTime;
|
||||
|
||||
/**
|
||||
* Get a DateTime object based on a random date between `$from` and an interval.
|
||||
* Accepts date string that can be recognized by `strtotime()`.
|
||||
*
|
||||
* @param \DateTime|int|string $from defaults to 30 years ago
|
||||
* @param string $interval defaults to 5 days after
|
||||
* @param string|null $timezone zone timezone for generated date, fallback to `DateTime::$defaultTimezone` and `date_default_timezone_get()`.
|
||||
*
|
||||
* @see \DateTimeZone
|
||||
* @see http://php.net/manual/en/timezones.php
|
||||
* @see http://php.net/manual/en/function.date-default-timezone-get.php
|
||||
*/
|
||||
public function dateTimeInInterval($from = '-30 years', string $interval = '+5 days', ?string $timezone = null): \DateTime;
|
||||
|
||||
/**
|
||||
* Get a date time object somewhere inside the current week.
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
* @param string|null $timezone zone timezone for generated date, fallback to `DateTime::$defaultTimezone` and `date_default_timezone_get()`.
|
||||
*
|
||||
* @see \DateTimeZone
|
||||
* @see http://php.net/manual/en/timezones.php
|
||||
* @see http://php.net/manual/en/function.date-default-timezone-get.php
|
||||
*/
|
||||
public function dateTimeThisWeek($until = 'now', ?string $timezone = null): \DateTime;
|
||||
|
||||
/**
|
||||
* Get a date time object somewhere inside the current month.
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
* @param string|null $timezone timezone for generated date, fallback to `DateTime::$defaultTimezone` and `date_default_timezone_get()`.
|
||||
*
|
||||
* @see \DateTimeZone
|
||||
* @see http://php.net/manual/en/timezones.php
|
||||
* @see http://php.net/manual/en/function.date-default-timezone-get.php
|
||||
*/
|
||||
public function dateTimeThisMonth($until = 'now', ?string $timezone = null): \DateTime;
|
||||
|
||||
/**
|
||||
* Get a date time object somewhere inside the current year.
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
* @param string|null $timezone timezone for generated date, fallback to `DateTime::$defaultTimezone` and `date_default_timezone_get()`.
|
||||
*
|
||||
* @see \DateTimeZone
|
||||
* @see http://php.net/manual/en/timezones.php
|
||||
* @see http://php.net/manual/en/function.date-default-timezone-get.php
|
||||
*/
|
||||
public function dateTimeThisYear($until = 'now', ?string $timezone = null): \DateTime;
|
||||
|
||||
/**
|
||||
* Get a date time object somewhere inside the current decade.
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
* @param string|null $timezone timezone for generated date, fallback to `DateTime::$defaultTimezone` and `date_default_timezone_get()`.
|
||||
*
|
||||
* @see \DateTimeZone
|
||||
* @see http://php.net/manual/en/timezones.php
|
||||
* @see http://php.net/manual/en/function.date-default-timezone-get.php
|
||||
*/
|
||||
public function dateTimeThisDecade($until = 'now', ?string $timezone = null): \DateTime;
|
||||
|
||||
/**
|
||||
* Get a date time object somewhere inside the current century.
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
* @param string|null $timezone timezone for generated date, fallback to `DateTime::$defaultTimezone` and `date_default_timezone_get()`.
|
||||
*
|
||||
* @see \DateTimeZone
|
||||
* @see http://php.net/manual/en/timezones.php
|
||||
* @see http://php.net/manual/en/function.date-default-timezone-get.php
|
||||
*/
|
||||
public function dateTimeThisCentury($until = 'now', ?string $timezone = null): \DateTime;
|
||||
|
||||
/**
|
||||
* Get a date string between January 1, 1970, and `$until`.
|
||||
*
|
||||
* @param string $format DateTime format
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
*
|
||||
* @see https://www.php.net/manual/en/datetime.format.php
|
||||
*/
|
||||
public function date(string $format = 'Y-m-d', $until = 'now'): string;
|
||||
|
||||
/**
|
||||
* Get a time string (24h format by default).
|
||||
*
|
||||
* @param string $format DateTime format
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
*
|
||||
* @see https://www.php.net/manual/en/datetime.format.php
|
||||
*/
|
||||
public function time(string $format = 'H:i:s', $until = 'now'): string;
|
||||
|
||||
/**
|
||||
* Get a UNIX (POSIX-compatible) timestamp between January 1, 1970, and `$until`.
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
*/
|
||||
public function unixTime($until = 'now'): int;
|
||||
|
||||
/**
|
||||
* Get a date string according to the ISO-8601 standard.
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
*/
|
||||
public function iso8601($until = 'now'): string;
|
||||
|
||||
/**
|
||||
* Get a string containing either "am" or "pm".
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
*
|
||||
* @example 'am'
|
||||
*/
|
||||
public function amPm($until = 'now'): string;
|
||||
|
||||
/**
|
||||
* Get a localized random day of the month.
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
*
|
||||
* @example '16'
|
||||
*/
|
||||
public function dayOfMonth($until = 'now'): string;
|
||||
|
||||
/**
|
||||
* Get a localized random day of the week.
|
||||
*
|
||||
* Uses internal DateTime formatting, hence PHP's internal locale will be used (change using `setlocale()`).
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
*
|
||||
* @example 'Tuesday'
|
||||
*
|
||||
* @see setlocale
|
||||
* @see https://www.php.net/manual/en/function.setlocale.php Set a different output language
|
||||
*/
|
||||
public function dayOfWeek($until = 'now'): string;
|
||||
|
||||
/**
|
||||
* Get a random month (numbered).
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
*
|
||||
* @example '7'
|
||||
*/
|
||||
public function month($until = 'now'): string;
|
||||
|
||||
/**
|
||||
* Get a random month.
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
*
|
||||
* @see setlocale
|
||||
* @see https://www.php.net/manual/en/function.setlocale.php Set a different output language
|
||||
*
|
||||
* @example 'September'
|
||||
*/
|
||||
public function monthName($until = 'now'): string;
|
||||
|
||||
/**
|
||||
* Get a random year between 1970 and `$until`.
|
||||
*
|
||||
* @param \DateTime|int|string $until maximum timestamp, defaults to "now"
|
||||
*
|
||||
* @example '1987'
|
||||
*/
|
||||
public function year($until = 'now'): string;
|
||||
|
||||
/**
|
||||
* Get a random century, formatted as Roman numerals.
|
||||
*
|
||||
* @example 'XVII'
|
||||
*/
|
||||
public function century(): string;
|
||||
|
||||
/**
|
||||
* Get a random timezone, uses `\DateTimeZone::listIdentifiers()` internally.
|
||||
*
|
||||
* @param string|null $countryCode two-letter ISO 3166-1 compatible country code
|
||||
*
|
||||
* @example 'Europe/Rome'
|
||||
*/
|
||||
public function timezone(?string $countryCode = null): string;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* An extension is the only way to add new functionality to Faker.
|
||||
*
|
||||
* @experimental This interface is experimental and does not fall under our BC promise
|
||||
*/
|
||||
interface Extension
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class ExtensionNotFound extends \LogicException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This interface is experimental and does not fall under our BC promise
|
||||
*/
|
||||
interface FileExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* Get a random MIME type
|
||||
*
|
||||
* @example 'video/avi'
|
||||
*/
|
||||
public function mimeType(): string;
|
||||
|
||||
/**
|
||||
* Get a random file extension (without a dot)
|
||||
*
|
||||
* @example avi
|
||||
*/
|
||||
public function extension(): string;
|
||||
|
||||
/**
|
||||
* Get a full path to a new real file on the system.
|
||||
*/
|
||||
public function filePath(): string;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
use Faker\Generator;
|
||||
|
||||
/**
|
||||
* @experimental This interface is experimental and does not fall under our BC promise
|
||||
*/
|
||||
interface GeneratorAwareExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* This method MUST be implemented in such a way as to retain the
|
||||
* immutability of the extension, and MUST return an instance that has the
|
||||
* new Generator.
|
||||
*/
|
||||
public function withGenerator(Generator $generator): Extension;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
use Faker\Generator;
|
||||
|
||||
/**
|
||||
* A helper trait to be used with GeneratorAwareExtension.
|
||||
*/
|
||||
trait GeneratorAwareExtensionTrait
|
||||
{
|
||||
/**
|
||||
* @var Generator|null
|
||||
*/
|
||||
private $generator;
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function withGenerator(Generator $generator): Extension
|
||||
{
|
||||
$instance = clone $this;
|
||||
|
||||
$instance->generator = $generator;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* A class with some methods that may make building extensions easier.
|
||||
*
|
||||
* @experimental This class is experimental and does not fall under our BC promise
|
||||
*/
|
||||
final class Helper
|
||||
{
|
||||
/**
|
||||
* Returns a random element from a passed array.
|
||||
*/
|
||||
public static function randomElement(array $array)
|
||||
{
|
||||
if ($array === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $array[array_rand($array, 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all hash sign ('#') occurrences with a random number
|
||||
* Replaces all percentage sign ('%') occurrences with a non-zero number.
|
||||
*
|
||||
* @param string $string String that needs to bet parsed
|
||||
*/
|
||||
public static function numerify(string $string): string
|
||||
{
|
||||
// instead of using randomDigit() several times, which is slow,
|
||||
// count the number of hashes and generate once a large number
|
||||
$toReplace = [];
|
||||
|
||||
if (($pos = strpos($string, '#')) !== false) {
|
||||
for ($i = $pos, $last = strrpos($string, '#', $pos) + 1; $i < $last; ++$i) {
|
||||
if ($string[$i] === '#') {
|
||||
$toReplace[] = $i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($nbReplacements = count($toReplace)) {
|
||||
$maxAtOnce = strlen((string) mt_getrandmax()) - 1;
|
||||
$numbers = '';
|
||||
$i = 0;
|
||||
|
||||
while ($i < $nbReplacements) {
|
||||
$size = min($nbReplacements - $i, $maxAtOnce);
|
||||
$numbers .= str_pad((string) mt_rand(0, 10 ** $size - 1), $size, '0', STR_PAD_LEFT);
|
||||
$i += $size;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $nbReplacements; ++$i) {
|
||||
$string[$toReplace[$i]] = $numbers[$i];
|
||||
}
|
||||
}
|
||||
|
||||
return self::replaceWildcard($string, '%', static function () {
|
||||
return mt_rand(1, 9);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all question mark ('?') occurrences with a random letter.
|
||||
*
|
||||
* @param string $string String that needs to bet parsed
|
||||
*/
|
||||
public static function lexify(string $string): string
|
||||
{
|
||||
return self::replaceWildcard($string, '?', static function () {
|
||||
return chr(mt_rand(97, 122));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces hash signs ('#') and question marks ('?') with random numbers and letters
|
||||
* An asterisk ('*') is replaced with either a random number or a random letter.
|
||||
*
|
||||
* @param string $string String that needs to bet parsed
|
||||
*/
|
||||
public static function bothify(string $string): string
|
||||
{
|
||||
$string = self::replaceWildcard($string, '*', static function () {
|
||||
return mt_rand(0, 1) === 1 ? '#' : '?';
|
||||
});
|
||||
|
||||
return self::lexify(self::numerify($string));
|
||||
}
|
||||
|
||||
private static function replaceWildcard(string $string, string $wildcard, callable $callback): string
|
||||
{
|
||||
if (($pos = strpos($string, $wildcard)) === false) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
for ($i = $pos, $last = strrpos($string, $wildcard, $pos) + 1; $i < $last; ++$i) {
|
||||
if ($string[$i] === $wildcard) {
|
||||
$string[$i] = call_user_func($callback);
|
||||
}
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Faker\Extension;
|
||||
|
||||
/**
|
||||
* @experimental This interface is experimental and does not fall under our BC promise
|
||||
*/
|
||||
interface NumberExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* Returns a random number between $int1 and $int2 (any order)
|
||||
*
|
||||
* @param int $min default to 0
|
||||
* @param int $max defaults to 32 bit max integer, ie 2147483647
|
||||
*
|
||||
* @example 79907610
|
||||
*/
|
||||
public function numberBetween(int $min, int $max): int;
|
||||
|
||||
/**
|
||||
* Returns a random number between 0 and 9
|
||||
*/
|
||||
public function randomDigit(): int;
|
||||
|
||||
/**
|
||||
* Generates a random digit, which cannot be $except
|
||||
*/
|
||||
public function randomDigitNot(int $except): int;
|
||||
|
||||
/**
|
||||
* Returns a random number between 1 and 9
|
||||
*/
|
||||
public function randomDigitNotZero(): int;
|
||||
|
||||
/**
|
||||
* Return a random float number
|
||||
*
|
||||
* @example 48.8932
|
||||
*/
|
||||
public function randomFloat(?int $nbMaxDecimals, float $min, ?float $max): float;
|
||||
|
||||
/**
|
||||
* Returns a random integer with 0 to $nbDigits digits.
|
||||
*
|
||||
* The maximum value returned is mt_getrandmax()
|
||||
*
|
||||
* @param int|null $nbDigits Defaults to a random number between 1 and 9
|
||||
* @param bool $strict Whether the returned number should have exactly $nbDigits
|
||||
*
|
||||
* @example 79907610
|
||||
*/
|
||||
public function randomNumber(?int $nbDigits, bool $strict): int;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user