T ToutadminDocumentazione

Installare l'edizione PHP

L'edizione PHP si deposita così com'è su un hosting condiviso: niente Composer, niente gestore di pacchetti, nessun servizio da tenere in funzione. Una cartella, un file di configurazione, un'attività pianificata. Questa guida va dalla cartella vuota all'istanza in produzione, compreso ciò che la maggior parte delle guide tralascia: i permessi, il TLS, l'attività pianificata, i backup e l'aggiornamento.

Che cosa serve#

ElementoVersionePerché
PHP8.1 o più recenteTipi unione, enum, readonly, never: il codice li usa.
pdo_sqliteL'intera banca dati. Senza, non gira nulla.
mbstringTrattamento corretto delle stringhe in sedici lingue.
opensslconsigliataCifratura dei segreti nella banca dati (password SMTP, token di esternalizzazione).
curlconsigliataWebhook in uscita e invio dei backup a terzi.
intlconsigliataDate scritte come nell'edizione Node. Senza, i nomi dei mesi vengono dai dizionari.

La procedura guidata d'installazione verifica tutto questo e lo mostra a schermo prima di lasciarvi proseguire: ciò che è bloccante ferma l'installazione, ciò che è facoltativo viene segnalato senza bloccare.

Nessuna dipendenza

Non c'è nulla da installare con composer: il lettore IMAP, l'archivio tar.gz, il PDF, il CSV, il codice QR e il client HTTP sono scritti dentro il prodotto. È ciò che permette di depositarlo su un hosting dove non avete una riga di comando.

1. Caricare i file#

Recuperate l'archivio dell'edizione PHP, poi caricatelo. La cartella è fatta così:

toutadmin/
├── app/            il codice (mai servito dal server web)
│   ├── Core/       nucleo: instradamento, banca dati, sessioni, sicurezza
│   ├── Modules/    dominio: risorse umane, gestione, progetti, qualità…
│   ├── Controllers/
│   ├── views/      modelli
│   ├── locales/    i sedici dizionari
│   └── schema.sql  lo schema, identico a quello dell'edizione Node
├── public/         ← la radice web punta qui, e da nessun'altra parte
│   ├── index.php   il controllore frontale
│   ├── css/ js/
├── data/           banca dati, cassaforte, libro firme, documenti ricevuti, backup
├── tools/          cron.php, check-keys.php
├── tests/
├── config.sample.php
└── VERSION
La radice web punta su public/, non sulla cartella

È il punto più importante di questa pagina. Se il server serve l'intera cartella, data/app.sqlite diventa scaricabile da chiunque: tutta la banca dati, comprese le impronte delle password e la cassaforte. La cartella data/ sta sopra public/ apposta.

Su un hosting condiviso#

Si presentano due casi. Se il vostro fornitore permette di scegliere la radice di un dominio (cPanel, Plesk, e la maggior parte degli hosting in modalità «cartella radice»), depositate la cartella fuori da www/ e fate puntare il dominio a toutadmin/public.

Se la radice è imposta — spesso www/ o public_html/ — depositatevi il contenuto di public/ e il resto al di sopra, poi correggete il percorso in cima a public/index.php:

www/                  ← radice imposta
├── index.php
├── css/  js/
toutadmin/            ← il resto, fuori dalla portata del web
├── app/
├── data/
└── config.php
// www/index.php — la riga da adattare
require_once __DIR__ . '/../toutadmin/app/bootstrap.php';

Come ultima risorsa, se non potete mettere nulla al di sopra della radice, il file .htaccess consegnato dentro data/ rifiuta già l'accesso. È una cintura, non una soluzione: preferite sempre la cartella fuori dalla radice.

2. Scrivere la configurazione#

cp config.sample.php config.php

Poi aprite config.php:

<?php
return [
    // Banca dati SQLite e cartella dati: fuori dalla radice web.
    'db_path'  => __DIR__ . '/data/app.sqlite',
    'data_dir' => __DIR__ . '/data',

    // Chiave propria dell'istanza. Da generare una volta:
    //   php -r "echo bin2hex(random_bytes(32));"
    'session_secret' => '…',

    // Token d'installazione: vedi il passo successivo.
    'install_token' => '',

    // Indirizzo pubblico, per i collegamenti inviati per email.
    'base_url' => 'https://intranet.esempio.it',

    // Da attivare solo dietro un proxy fidato.
    'trust_proxy' => false,

    // Tetti: tentativi d'accesso per quarto d'ora e per indirizzo,
    // richieste al minuto e per indirizzo.
    'login_rate_limit'  => 10,
    'global_rate_limit' => 300,

    // Scadenza: inattività, poi durata assoluta che nessuna attività prolunga.
    'session_idle_minutes' => 60,
    'session_max_hours'    => 12,
];
Il segreto dell'istanza

Sigilla le firme del libro firme e cifra i segreti nella banca dati. Perderlo invalida delle firme. Se lasciate il valore d'esempio, il prodotto ne genera uno da sé in data/.instance-key (permessi 0600) — ricordatevi allora di salvare quel file insieme al resto.

Potete anche tenere la configurazione altrove: la variabile d'ambiente TOUTADMIN_CONFIG indica un altro file.

3. Porre un token d'installazione#

Fra il caricamento dei file e il vostro passaggio nella procedura guidata, l'istanza è di chi la trova: il primo arrivato crea l'account di amministrazione. La finestra è breve, ma basta. Ponete dunque un valore a caso prima della messa in linea:

php -r "echo bin2hex(random_bytes(16));"
# poi in config.php:
'install_token' => 'c3f1…',

La procedura guidata lo chiede, lo confronta a tempo costante — un confronto ordinario lascerebbe indovinare il token carattere per carattere — e registra ogni rifiuto. Senza il token non viene creato nulla. Una volta installata l'istanza, la procedura si chiude da sé: il token può restare, non serve più.

4. Permessi#

PercorsoDirittiChi scrive
data/0770Il server web, e lui soltanto
data/app.sqlite0660Creato dall'installazione
config.php0640Nessuno — sola lettura per il server
app/, public/0755Nessuno — sola lettura
# esempio su un server dove PHP gira come www-data
sudo chown -R voi:www-data /var/www/toutadmin
sudo find /var/www/toutadmin -type d -exec chmod 750 {} \;
sudo find /var/www/toutadmin -type f -exec chmod 640 {} \;
sudo chmod 770 /var/www/toutadmin/data
sudo chmod 750 /var/www/toutadmin/public

SQLite scrive anche file vicini (-wal, -shm): è la cartella a dover essere scrivibile, non solo la banca dati.

5. Il server web#

Nginx#

server {
    listen 443 ssl http2;
    server_name intranet.esempio.it;

    root /var/www/toutadmin/public;   # e nient'altro
    index index.php;

    ssl_certificate     /etc/letsencrypt/live/intranet.esempio.it/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/intranet.esempio.it/privkey.pem;

    client_max_body_size 20M;          # depositi in cassaforte, documenti ricevuti

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    # I file depositati non sono mai serviti direttamente: passano
    # per una rotta che verifica la sessione e l'impronta.
    location ~ ^/(data|app|tools|tests)/ { deny all; }
}

server {
    listen 80;
    server_name intranet.esempio.it;
    return 301 https://$host$request_uri;
}

Apache#

<VirtualHost *:443>
    ServerName intranet.esempio.it
    DocumentRoot /var/www/toutadmin/public

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/intranet.esempio.it/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/intranet.esempio.it/privkey.pem

    <Directory /var/www/toutadmin/public>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Il file public/.htaccess consegnato rimanda già tutti gli indirizzi a index.php. Su un hosting condiviso Apache, è tutto ciò che serve.

TLS#

Il prodotto pone l'intestazione Strict-Transport-Security non appena la connessione è cifrata, e mai in chiaro: annunciata da una pagina non cifrata non verrebbe letta, e rinchiuderebbe una prova locale in https per sei mesi. Su un server che amministrate voi:

sudo certbot --nginx -d intranet.esempio.it

Dietro un proxy o un bilanciatore che termina il TLS, mettete 'trust_proxy' => true — e soltanto lì: altrimenti chiunque potrebbe annunciare l'indirizzo IP e il protocollo che vuole in un'intestazione.

6. L'attività pianificata#

Un sito PHP gira solo al momento di una richiesta. Ciò che l'edizione Node fa nella sua scansione oraria — otto operazioni — qui si fa dal cron dell'hosting:

* * * * * /usr/bin/php /var/www/toutadmin/tools/cron.php >> /var/log/toutadmin-cron.log 2>&1
Che cosa fa l'attivitàCon quale ritmo
Chiude gli account il cui contratto è scadutoa ogni passaggio
Emette le fatture degli abbonamenti in scadenzaa ogni passaggio, senza mai fatturare due volte
Trasforma le scadenze in notifichea ogni passaggio, senza doppioni
Ripulisce le notifiche lette e il registro di auditsecondo la conservazione scelta
Preleva la casella contabile (IMAP)se il prelievo è configurato
Svuota la coda dei webhook, con i suoi ritentativia ogni passaggio
Crea il backup automatico e lo porta fuoriquando l'intervallo è trascorso

Chiamare lo script più spesso dell'intervallo configurato non fa backup più spesso: nulla si avvia prima della scadenza. Su un hosting che limita il cron a un passaggio ogni quindici minuti, sostituite * * * * * con */15 * * * * — i webhook partiranno semplicemente con quel ritardo.

Su un hosting senza cron

Certi hosting d'ingresso non ne offrono. Una chiamata HTTP da un servizio terzo (un sorvegliante di disponibilità e simili) non basta: cron.php rifiuta di essere eseguito altrove che in riga di comando, proprio perché un indirizzo pubblico non possa scatenare un backup. In quel caso, il backup si lancia a mano dalla schermata Backup.

7. La procedura guidata#

Aprite il vostro dominio. Finché non esiste alcun account, ogni indirizzo rimanda a /installation. La procedura sta in una pagina:

  1. I prerequisiti, verificati e mostrati — ciò che blocca è segnato come tale.
  2. Il token d'installazione, se è configurato.
  3. L'azienda: il nome mostrato dappertutto, e il numero di giorni di ferie annuali attribuito a ogni nuovo dipendente non freelance.
  4. La lingua predefinita dell'istanza, fra le 16.
  5. L'account di amministrazione: indirizzo e password di almeno dodici caratteri.

Tutto è scritto in una sola transazione. Non appena esiste un account, /installation rimanda alla pagina d'accesso: la procedura si è chiusa da sé, non c'è alcun file da cancellare a mano.

8. Verificare che tutto funzioni#

# i test, sulla macchina (non scrivono nella vostra banca dati)
php tests/run.php

# le traduzioni: nessuna schermata impiega una chiave assente dai dizionari
php tools/check-keys.php

Poi, nell'interfaccia: create un membro, entrate con lui, depositate un allegato, lanciate un backup manuale e verificatelo dalla schermata Backup. Questi quattro gesti toccano la banca dati, i file, i diritti e l'archivio — vale a dire tutto ciò che l'hosting deve saper fare.

9. Aggiornare#

# 1. un backup prima, sempre
php tools/cron.php                     # o la schermata Backup

# 2. sostituire il codice, non i dati
#    app/ public/ tools/ tests/ VERSION  ← sostituiti
#    data/ config.php                    ← conservati tali e quali

# 3. la banca dati si aggiorna da sé alla prima richiesta

Lo schema evolve per migrazioni idempotenti: rigiocare l'aggiornamento non rompe nulla. Un'istanza dell'edizione Node e una dell'edizione PHP condividono lo stesso schema — 142 tabelle, 1311 colonne — e le stesse password: una banca dati passa dall'una all'altra senza conversione.

10. Fare backup sul serio#

L'archivio tar.gz porta la banca dati (copiata con VACUUM INTO, dunque coerente anche durante una scrittura) e le cinque cartelle dei file: foto di profilo, curriculum ricevuti, cassaforte, libro firme, documenti ricevuti. Ogni file vi porta la sua impronta SHA-256, verificata al ripristino.

Un archivio rimasto sul server che protegge non protegge nulla: configurate una destinazione remota (FTPS o Google Drive) dalla schermata Backup. Il dettaglio è nella pagina Backup e ripristino.

Provare prima in locale#

cp config.sample.php config.php
php -S localhost:8000 -t public public/index.php

Il server integrato di PHP basta per scoprire il prodotto. Non va bene per la produzione: un solo processo, niente TLS, nessun limite di carico.

Documentazione di Toutadmin — costruita il 2026-09-13. Sito autonomo, indipendente dal software.