gino Wiki

Ultimo aggiornamento: 2020-09-02

Indice

Caratteristiche principali

gino è un CMS scritto in PHP che offre tutti gli strumenti necessari per poter creare un sito web e gestire i contenuti al suo interno in maniera semplice ed efficace, svincolando l'amministratore da conoscenze tecniche di programmazione.

Le caratteristiche principali di gino sono:

top

Requisiti

gino è stato sviluppato su un ambiente LAMP e richiede un Server Web, un Database Server e il linguaggio di programmazione PHP.

PHP

gino richiede php>=7.0 con le estensioni:

A livello di configurazione occorre abilitare le seguenti funzioni.

Abilitare gli short open tag, a livello del file php.ini oppure all'interno della configurazione del virtual host:

php_admin_value short_open_tag "On"

Abilitare allow_url_fopen per poter utilizzare il servizio reCaptcha di Google, in quanto viene chiamato un indirizzo esterno (https://www.google.com/recaptcha/api/siteverify). Nel file php.ini:

allow_url_fopen = On

Sistemi operativi

gino può identificare automaticamente il sistema operativo sul quale è installato e impostare automaticamente i percorsi interni.
In particolare gino è stato testato in ambienti Windows, MacOSX e Linux.

Web Server

Il Web Server preferito di gino è Apache, tuttavia gino è stato testato anche su altri web server quali Internet Information Server (IIS) e Nginx.

Apache

In gino è presente un file htaccess il cui utilizzo è quello di convertire l'url richiesto e di cercare di ottimizzare le prestazioni dell'applicazione. Questo file richiama i seguenti moduli di Apache:

Per poter utilizzare il file htaccess Apache deve essere configurato per leggere i file htaccess, ovvero modificare la sezione Directory nel seguente modo:

AllowOverride AuthConfig

Per poter utilizzare tutti i moduli sopra indicati, modificare la sezione Directory come segue:

AllowOverride AuthConfig FileInfo Indexes

Per poter utilizzare il meccanismo della cache, nella sezione Directory:

Options FollowSymLinks

in caso contrario:

Options None

Internet Information Server (IIS)

Nel caso in cui si utlizzi il Web Server IIS il file preposto alla conversione dell'indirizzo richiesto è web.config.

Database Server

Attualmente in gino sono implementati i connettori per i seguenti database

Il database predefinito è MySQL nella versione mysql>=5.6.

top

Installazione

Per installare gino seguire la seguente procedura semplificata.

1. Download gino

Puoi scaricare gino da github utilizzando il bottone di download, oppure puoi clonare il progetto utilizzando il comando di git

git clone git://github.com/otto-torino/gino.git ./gino.git

2. Preparare la root directory

Il pacchetto deve essere scompattato in una directory servita dal server web.

3. Creare il database

Creare un database vuoto e importare il file dbgino.sql (MySQL).

Con MySQL per creare un database vuoto è possibile utilizzare un programma quale phpMyAdmin, MySQLWorkbench o similari; in alternativa utilizzare la shell:

# mysqladmin -h localhost -u root -p create dbname
# mysql -u root -p dbname < dbgino.sql

4. Configurare gino

Impostare i parametri base del sito nel file configuration.php

5. Primo accesso

Accedere a gino digitando sulla barra del browser l'indirizzo dell'applicazione, ad esempio http://localhost/gino.

Prendendo a riferimento l'esempio precedente, per accedere all'area amministrativa digitare: http://localhost/gino/admin.
In gino è presente un utente amministratore predefinito con i parametri

6. Impostazioni principali

Nella sezione Amministrazione -> Impostazioni modificare i parametri principali

Naturalmente tutte le opzioni presenti potranno essere modificate e inserite in seguito. Si consiglia tuttavia di impostare correttamente almeno i parametri indicati per evitare ad esempio di saltare delle traduzioni, di dover modificare in seguito le password degli utenti, o di confondere il sito sul quale si lavora.

Per la descrizione di tutte le impostazioni di sistema vedi la sezione specifica.

7. Problemi di permessi sulle directory

Si possono verificare dei malfunzionamenti inerenti la creazione automatica di directory, o semplicemente l'upload di file. In entrambi i casi il malfunzionamento potrebbe essere ricondotto alla non adeguata presenza di permessi sulle directory.

La tipologia di permessi da impostare varia in riferimento agli utenti e ai gruppi proprietari delle directory. In generale nel caso in cui l'utente e il gruppo siano gli stessi del server web (su debian www-data:www-data) è sufficiente impostare [755], mentre negli altri casi [777].
Le sotto-directory create dall'applicativo prenderanno direttamente i permessi corretti.

Nel caso del trasferimento di un sito occorre verificare i permessi delle sotto-directory di contents.

top

Composer

Composer è un Dependency Manager per PHP, ovvero è uno strumento per la gestione delle dipendenze in PHP; in pratica permette di dichiarare le librerie da cui dipende il progetto gestendole per noi (installazioni/aggiornamenti).
Il sito del progetto è https://getcomposer.org/, dove è possibile consultare la documentazione, anche per il primo utilizzo (Basic usage).

Per poter utilizzare Composer è necessario installarlo, e a questo proposito Composer offre un comodo programma di installazione che è possibile eseguire direttamente da riga di comando. Nella pagina Getting Started del progetto si trovano tutte le indicazioni sulla procedura di installazione.
Occorre inoltre tenere presente che conviene installare col Composer soltanto i pacchetti esterni necessari, in quanto lo spazio richiesto potrebbe essere importante.

Il repository dei pacchetti di Composer è Packagist, dove oltretutto si trovano anche informazioni su Composer.

Funzionamento

Per iniziare a utilizzare Composer nel progetto occorre definire un file composer.json. questo file descrive le dipendenze del progetto e potrebbe contenere anche altri metadati.

La prima (e spesso unica) cosa specificata in questo file è la chiave require. In questo modo si sta semplicemente dicendo a Composer da quali pacchetti dipende il progetto.

I pacchetti vengono installati nella directory vendor.

Comandi

Per installare le dipendenze definite per il progetto:

$ composer install

Per aggiornare le dipendenze alla loro utima versione:

$ composer update
$ composer update monolog/monolog [...]

Pacchetti in gino

I pacchetti che attualmente potrebbero venire installati e utilizzati in gino sono:

mpdf

mPDF is a PHP class to generate PDF files from HTML with Unicode/UTF-8 and CJK support.

$ composer require mpdf/mpdf

phpmailer

PHPMailer is a full-featured email creation and transfer class for PHP.

Aggiungere questa linea al file composer.json e lanciare il comando composer install

"phpmailer/phpmailer": "~6.0"

oppure eseguire

$ composer require phpmailer/phpmailer

phpunit

PHPUnit is the PHP Unit Testing framework.

$ composer require --dev phpunit/phpunit ^6.5
top

Architettura

L'architettura di gino a partire dalla versione 2.0.0 è stata profondamente rinnovata per gestire più linearmente il flusso che, ricevuta una richiesta, la elabora e fornisce una risposta. A questo proposito è stata creata tutta una nuova serie di classi che servono allo scopo. Segue uno schema del flusso di esecuzione di una richiesta.

Gestione della richiesta HTTP

Una richiesta HTTP viene veicolata al file index.php, il quale funge da entry point per qualunque request che non punti ad un file o directory esistente. Il file richiama Gino.Core::answer() che a sua volta istanzia Gino.Http.Request, per impostare tutte le informazioni importanti di una richiesta HTTP, e Gino.Router per gestire il routing della request HTTP.
La risposta viene poi confezionata da Gino.Router::route().

Gino utilizza un wrapper Gino.Http.Request che contiene le proprietà della richiesta HTTP.
La classe Gino.Router si occupa di effettuare il rewriting dell'url in modo da settare correttamente le proprietà della Gino.Http.Request anche in presenza di pretty urls. Quindi effettua il routing della richiesta, andando a chiamare il metodo della classe Gino.Controller indicato dall'url in questione.
Il metodo della classe Gino.Controller riceve come primo parametro l'oggetto Gino.Http.Request e deve restituire una istanza di Gino.Http.Response.
La classe Gino.Core riceve la risposta, controlla che sia del tipo Gino.Http.Response, ed in caso affermativo la invia, in caso negativo invia una Gino.Http.ResponseNotFound.

Rappresentazione del flusso e dei metodi interessati

	index.php
		|=> core->answer()
			|=> registry->request
			|=> registry->router
					|=> urlRewrite() => rewritePathInfo() => legge il file config/route.inc
					|=> request->updateUrl()
		|=> router->route() => setUrlParams()

Nel file config/route.inc è possibile definire gli alias agli indirizzi delle risorse.

Template/Viste

I metodi delle classi Gino.Controller possono interagire con le classi di tipo Gino.Model per riempire un context da dare in pasto ad una Gino.View, la quale renderizza un template e ritorna un html.

I template della vista non devono includere tutto il documento html utilizzando dei partials, ma possono utilizzare la classe wrapper Gino.Document che gestisce Skin (insieme di template e css associati a determinati url o classi di url) per inserire il proprio contenuto all'interno di un template globale (un documento html completo).

In particolare è Gino.Document::render() che crea il corpo della risposta HTTP andando a recuperare la Skin da utilizzare per generare il documento (Gino.Document::getSkin()) e impostando le variabili del Registro (Gino.Registry) utilizzate all'interno del template (Gino.Document::setHeadVariables()).

Skin base:

LabelRegoleSessioneRegexp / Url
Home Mobilesessione+regexpL_mobile=1#^index.php$#
Pagine Mobilesessione+regexpL_mobile=1#.*#
Pagina Autenticazioneurlindex.php?evt[auth.login]
_popupregexp#&_popup=1#
Pagine Amministrazioneregexp#evt\[\w+.((manage)|(wrapper))\w*\]#
Home Amministrazioneurlindex.php?evt[index.admin_page]
Pagine Pubblicheregexp#evt\[(?!index)#
Pagine Privateregexp#evt\[(?!index)#
Home Pubblicaregexp#(index.php(\?evt\[index.index_page\])?[^\[\]]*)?$#
Home Privataregexp#(index.php(\?evt\[index.index_page\])?[^\[\]]*)?$#
Defaultregexp#^.*$#

Eccezioni

Tutte le eccezioni vengono raccolte dalla classe Gino.Core, la stessa che utilizza Gino.Router per arrivare alla risposta HTTP, con il supporto della classe Gino.Logger.

Il comportamento a seguito del verificarsi di un'eccezione dipende dal valore della costante DEBUG, definita nel file configuration.php. Se il debug è attivo viene stampato a video uno stack trace dell'Exception più il valore di alcune variabili di sistema, altrimenti tutto ciò viene inviato via email agli amministratori dell'applicativo (costante ADMINS in configuration.php) mentre all'utente viene mostrata una risposta http definita dalla sottoclasse di Exception in questione, o di default una 500 Internal Server Error.

top

Namespace

gino integra i namespace, schematizzati nella tabella che segue.

NomeDirectoryDescrizione
Gino.AppappNamespace dei moduli applicativi. La classe controller e le classi modello della stessa applicazione avranno lo stesso namespace Gino.App[.NomeClasseController]
Gino.Pluginlib/pluginNamespace dei plugin
Gino.Httplib/classes/httpNamespace delle risposte HTTP
Gino.Exceptionlib/classes/exceptionNamespace delle eccezioni
Ginolib/classes
lib/classes/mvc
lib/classes/fields
Namespace base di gino
top

Registro

Il registro è un Gino.Singleton che può conservare dati accessibili e modificabili da tutte le classi di gino.

Caricamento Javascript

I file Js vengono trattati in diversi modi a seconda del valore della costante DEBUG e del tipo di caricamento nel registro, con Gino.Registry::addCoreJS(), Gino.Registry::addJS() e Gino.Registry::addCustomJS().

I file caricati con Gino.Registry::addCustomJS() vengono accodati a quelli caricati con Gino.Registry::addJS().

Registry::addCoreJS()

Con questo metodo vengono caricati i file javascript del core di gino. Vengono caricati prima degli altri file javascript.

Registry::addJS()

Con Debug True i file vengono caricati uno per uno nella versione originale, mentre con Debug False i file vengono minificati e compressi in unico file (Gino.Compressor::mergeJs()).

Il file che viene generato viene salvato nella directory cache/js, e viene soltanto recuperato se risulta già presente.

Registry::addCustomJS()

Con Debug True i file vengono caricati uno per uno nella versione originale, mentre con Debug False occorre verificare il valore dell'opzione handle. I file con l'opzione pari a False vengono caricati uno per uno nella versione originale, mentre per quelli con l'opzione pari a True occorre verificare ulteriormente i valori delle opzioni compress e minify.

Se il valore di compress è True il file viene inglobato nel file generale (Compressor::mergeJs()) che comprende i file caricati con Gino.Registry::addJS(). Naturalmente il file compresso viene anche minificato.

Se il valore di compress è False e il valore di minify è True il file viene minificato, altrimenti viene caricata la sua versione originale.

Registry::addRawJS()

Questo metodo viene usato per caricare i file javascript in stile html.

top

Indirizzi/Url

In gino, come in tutte le applicazioni web, spesso si utilizzano le querystring per ottenere i parametri direttamente dall'url della pagina. Per esempio se si vuole mostrare una specifica sezione, in modo dinamico, abbiamo una situazione simile alla seguente: http://otto.to.it.it/index.php?evt[news.view]&id=4.

Questo indirizzo non è molto leggibile e, soprattutto, potrebbe contenere molte più variabili. Per riscrivere questi url in un altro modo si ricorre al modulo mod_rewrite di Apache. Le ragioni sono essenzialmente tre:

  1. Leggibilità: aspetto fondamentale, in quanto l'utente si troverà più a suo agio e si potrà orientare meglio, nel vedere un url del tipo:
    http://otto.to.it.it/news/detail/31-03-2017-test/
    invece che
    http://otto.to.it.it/index.php?evt[news.view]&id=4
  2. Semplicità: per permettere all'utente di ricordarsi meglio la pagina
  3. Sicurezza: riscrivendo l'url, le querystring non saranno visibili direttamente a un eventuale malintenzionato, che vuole provare a iniettare qualche SQL Injection, ma deve ottenerle tramite una procedura più elaborata.

Come è descritto nella sezione Architettura, la classe Gino.Router si occupa di effettuare il rewriting dell'url in modo da impostare correttamente le proprietà della Gino.Http.Request anche in presenza di pretty urls. Quindi effettua il routing della richiesta, andando a chiamare il metodo della classe Gino.Controller indicato dall'url in questione.

Segue un elenco di alcuni indirizzi particolari

Descrizione Indirizzo Indirizzo breve Permesso
Home page amministrazione index/admin_page admin core.is_staff
Logout index.php?action=logout core.is_logged

Costruzione di un indirizzo

In gino un link per classi di tipo Gino.Controller può essere generato utilizzando il metodo Gino.Controller.link.

$link = $this->link('page', 'view', array('id' => $entry->slug)).'#comments');

Variazioni sul tema sono:

$request->root_absolute_url.$link;		// per mostrare a video l'url completo
return new \Gino\Http\Redirect($link);	// per effettuare un redirect

Definizione di alias route

Nel file config/route.inc è possibile definire degli alias agli indirizzi delle risorse. Le tipologie di alias sono tre:

Nei nomi degli alias non è possibile utilizzare il carattere della costante URL_SEPARATOR.

$config_url_alias = [
	'page/view/slug-page' => 'alias-page'
];

$config_instances_alias = [
	'news/archive' => 'hotnews/view'
];

$config_url_change = [
	'maps/view/turismo-rifugi' => 'osm/view/rifugi'
];
top

Javascript

Tutti i file javascript di sistema si trovano nella directory lib/js/. In particolare gino utilizza jQuery.

I file del core di gino sono definiti nel file config/common.inc e vengono caricati attraverso il metodo Gino.Document::setHeadVariables():

SITE_JS."/modernizr.js"
SITE_JS."/gino-min.js"
// jquery
SITE_JS."/jquery/jquery-3.3.1.min.js",
SITE_JS."/jquery/jquery-ui-1.12.1.js",
SITE_JS."/jquery/core.js",
// A kickass library used to manage poppers in web applications
SITE_JS."/popper.min.js",
SITE_JS."/bootstrap-4.1.3/js/bootstrap.min.js",

jQuery

Per poter usare jQuery può essere necessario incapsulare lo script in una funzione; segue un esempio

<script>
(function($) {
	$('#collapseLink').click(function() {
		$('#collapseFormFilter').toggle();
	});
})(jQuery);
</script>

Bootstrap

Il file bootstrap.min.js viene generato all'indirizzo http://getbootstrap.com/customize/.

top

Modello

Definizione del modello

Ogni modello definisce i propri campi nel metodo statico columns(). In questo metodo vengono definiti tutti i campi del modello utilizzando le seguenti opzioni:

$columns[field_name] = new field_class(array(
	'name'			=> string field_name,
	'primary_key'		=> bool,
	'label'		=> string label,
	'unique_key'		=> bool,
	'auto_increment'	=> bool,
	'required'		=> bool,
	'widget'		=> string	// sovrascrive il default widget
	'default'		=> string|int,
	'max_lenght'		=> int,
	'int_digits'		=> int,
	'decimal_digits'	=> int
));

dove le chiavi sono quelle definite come opzioni del costruttore della classe Field.

Unitamente alle suddette opzioni comuni, ogni tipo di campo può definire delle opzioni specifiche, quali ad esempio

$columns[field_name] = new ForeignKeyField(array(
	[...]
	'foreign'=>'\Gino\App\Page\PageCategory',
	'foreign_order'=>'name ASC',
	'add_related' => true,
	'add_related_url' => $controller->linkAdmin(array(), "block=ctg&insert=1"),
));
$columns[field_name] = new ImageField(array(
	[...]
	'extensions'=>self::$_extension_img,
	'resize'=>false,
	'path'=>null,
	'add_path'=>null
));
$columns[field_name] = new SlugField(array(
	[...]
	'autofill'=>'title',
));

Importante: nelle definizioni dei campi non devono essere presenti riferimenti ai valori assunti dai modelli.

Inoltre ogni modello imposta nel metodo statico Gino.Model::properties($model, $controller) le eventuali proprietà specifiche di ogni campo, ad esempio

$property['image'] = array(
	'path' => $controller->getBasePath(),
	'add_path' => $controller->getAddPath($model->id)
);

Istanziamento del modello

Si istanzia il modello e si recuperano le colonne attraverso l'istruzione

{Model_Name}::$columns={Model_Name}::columns();

Il costruttore di Gino.Model recupera i valori del record

$this->fetchColumns($id);

e li carica in $this->_p

$this->_p[field_name] = value|object

$this->_p viene utilizzato per ottenere e impostare una proprietà (Gino.Model::__get(), Gino.Model::__set()).

Nota: il valore dei campi di tipo ManyToMany è un array che racchiude i valori id dei record della tabella di join associata al modello.

In gino sono presenti due tipologie di classi per gestire i campi del modello

La classe Build viene istanziata richiamando il metodo Gino.Model::build() che istanzia la classe Gino.{Type}Build passandole nel costruttore le proprietà del campo del modello (Gino.Model::getProperties()) e le proprietà del campo della tabella (Gino.{Type}Field::getProperties()).
Gino.Model::getProperties() aggiunge alle proprietà specifiche del campo del modello (definite in {Model}::properties()) le opzioni model, field_object, table e value.

Descrizione metodi

Di seguito vengono descritti i metodi che definiscono e gestiscono un modello.

Metodo Descrizione Struttura codice Note
Model::columns() Struttura dei campi di una tabella.
Definire il metodo nei modelli
Opzioni generali del campo
field_name = new \Gino\{Type}Field(array(
	'name' => string,
	'label' => string,
	'primary_key' => bool,
	'unique_key' => bool,
	'auto_increment' => bool,
	'default' => mixed,
	'max_lenght' => integer,
	'required' => boolean,
	'int_digits' => integer,
	'decimal_digits' => integer
));
Ogni tipo di campo può avere delle opzioni specifiche
Model::getProperties() Attraverso Model::properties() recupera le eventuali proprietà del campo di una tabella dipendenti dai valori del record, e definisce le opzioni model, field_object, table e value.
array(
	'model' => object,
	'field_object' => object,
	'value' => mixed,
	'table' => string
);
Model::properties($model, $controller) Recupera le eventuali proprietà del campo di una tabella dipendenti dai valori del record (ad esempio dal valore id).
Definire il metodo nei modelli
$property[field_name] = array(
	'path' => $controller->getBasePath(),
	'add_path' => $controller->getAddPath($model->id)
);
    	
Queste proprietà sovrascrivono quelli definite nella struttura dei campi
Model::build($field_obj) Metodo che istanzia la classe {Type}Build passandole tutte le proprietà. La classe {Type}Build gestisce
  • gli input form (formElement())
  • il trattamento dei dati recuperati dall'input per l'inserimento in database (clean())
  • la visualizzazione html del valore (Build::printValue())

Nella tabella che segue sono elencati i tipi di campo con le loro specifiche opzioni.

Tipo di campo Opzioni specifiche
BooleanField
  • choice (array): elenco degli elementi di scelta
CharField
  • trnsl (boolean): campo con traduzioni
DateField
DatetimeField
  • auto_now (boolean): imposta automaticamente il valore del campo al tempo/ora corrente ogni volta che l'oggetto viene salvato (default: true)
  • auto_now_add (boolean): imposta automaticamente il valore del campo al tempo/ora corrente la prima volta che l'oggetto viene creato (default: true)
  • view_input (boolean): per visualizzare l'input nel form (default false)
DirectoryField
  • path (string): percorso assoluto della directory superiore
  • prefix (string): prefisso da aggiungere al nome della directory
  • default_name (array): valori per il nome di default
    • field (string): nome dell'input dal quale ricavare il nome della directory (default id)
    • maxlentgh (integer): numero di caratteri da considerare nel nome dell'input (default 15)
    • value_type (string): tipo di valore (default string)
EmailField
  • trnsl (false)
EnumField
  • choice (array): elenco degli elementi di scelta
  • value_type (string): tipo di valore del campo, accetta i valori
    • int, campo numerico (default)
    • string, campo alfanumerico
FileField
  • extensions (array): estensioni lecite di file
  • path (mixed)
    • string, percorso assoluto fino a prima del valore del record ID
      $controller = new auth(); 'path' => $controller->getBasePath(),
    • array
      'path' => array('\Gino\App\Attachment\AttachmentItem', 'getPath'),
  • add_path (string): parte del percorso assoluto dal parametro @a path fino a prima del file
  • prefix (string)
  • check_type (boolean)
  • types_allowed (array)
  • max_file_size (integer)
FloatField
ForeignKeyField
  • foreign (string): nome della classe della chiave esterna
  • foreign_where (mixed): condizioni della query
    • string, es. "cond1='$cond1' AND cond2='$cond2'"
    • array, es. array("cond1='$cond1'", "cond2='$cond2'")
  • foreign_order (string): ordinamento dei valori (es. name ASC); default 'id'
  • foreign_controller (object): oggetto del controller della classe della chiave esterna
  • add_related (boolean)
  • add_related_url (string)
ImageField Opzioni di FileField (extensions, types_allowed) +
  • resize (boolean)
  • thumb (boolean)
  • prefix_file (string)
  • prefix_thumb (string)
  • width (integer)
  • height (integer)
  • thumb_width (integer)
  • thumb_height (integer)
Il thumbnail dell'immagine viene generato automaticamente se è abilitata l'opzione resize (default true), e se al contempo l'opzione thumb è pari a true (default) e l'opzione prefix_thumb non è nulla (default thumb_).
IntegerField
ManytoManyField
  • add_related (bool): includere o meno un bottone che permetta l'inserimento di nuovi modelli correlati nello stesso contesto
  • add_related_url (string): path alla vista per inserimento modello correlato
  • m2m (object): classe del modello correlato (nome completo di namespace)
  • m2m_where (mixed): condizioni della query per filtrare i possibili modelli da associare
    • string, es. "cond1='$cond1' AND cond2='$cond2'"
    • array, es. array("cond1='$cond1'", "cond2='$cond2'")
  • m2m_order (string): ordinamento dei valori (es. name ASC)
  • m2m_controller (\Gino\Controller): classe Controller del modello m2m, essenziale se il modello appartiene ad un modulo istanziabile
  • join_table (string): nome tabella di join
ManytoManyThroughField
  • controller (object): controller del modello cui appartiene il campo
  • m2m (string): classe attraverso la quale si esprime la relazione molti a molti (nome completo di namespace)
  • m2m_controller (object): oggetto controller da passare evenualmente al costruttore della classe m2m
  • remove_fields (array): elenco dei campi da non mostrare nella porzione di form gestita da ManyToManyThroughBuild con widget unit
MulticheckField
  • refmodel (object): classe del modello correlato (nome completo di namespace)
  • refmodel_where (mixed): condizioni della query per filtrare i possibili modelli da associare
    • string, es. "cond1='$cond1' AND cond2='$cond2'"
    • array, es. array("cond1='$cond1'", "cond2='$cond2'")
  • refmodel_order (string): ordinamento dei valori (es. name ASC)
  • refmodel_controller (\Gino\Controller): classe Controller del modello refmodel, essenziale se il modello appartiene ad un modulo istanziabile
SlugField
  • autofill (string|array): nome o array di nomi dei campi da utilizzare per calcolare lo slug. Se vengono dati più campi vengono concatenati con un dash '-'
  • trnsl (false)
TagField
  • model_controller_class: nome classe del controller
  • model_controller_instance: id istanza del controller
TextField
  • trnsl (boolean): campo con traduzioni
TimeField
YearField

Gestione degli output/valori

Il valore in output del modello per la visualizzazione html viene ricavato dal metodo Gino.Model::shows(), che istanzia la classe {Type}Build e recupera il valore attraverso il metodo Gino.Build::printValue() che viene eventualmente subclassato dalle classi che estendono Build (vedi BooleanBuild).

I valori puliti recuperati dai record della tabella oppure trattati per essere salvati nei campi della tabella vengono gestiti dalla classe Gino.Field e dalle classi che la subclassano.

Per quanto riguarda i campi di tipo ManyToManyThroughField il valore ritornato da Gino.ManyToManyThroughField::valueFromDb() è un array contenente i valori id dei record di associazione. Allo stesso modo, nel costruttore di Gino.ManyToManyThroughBuild::__construct() il valore viene definito come array dei valori id dei record associati.

top

Gestione dei form

Generazione del form (amministrazione Modello)

L'interfaccia di amministrazione di un Modello, con inserimento, modifica ed eliminazione, viene gestita dalla classe Gino.AdminTable.
In particolare il wrapper del backoffice completo del Modello è Gino.AdminTable::backOffice().

Il form viene poi costruito richiamando Gino.ModelForm::view(). Questo metodo non è altro che l'interfaccia per il metodo di renderizzazione del form (Gino.Form::render()), al quale vengono passati l'istanza di Gino.Model e gli eventuali campi da mostrare (proprietà Gino.ModelForm::$_fields).
L'elenco dei campi da mostrare come input viene passato come opzione (array) al costruttore di Gino.ModelForm (nel caso in cui non vengano indicati dei campi vengono presi tutti i campi del modello); a sua volta il costruttore costruisce la proprietà $_fields (Gino.ModelForm::get()) che contiene l'elenco dei campi da mostrare nel formato array(string field => object build).

Gino.Form::render() renderizza la vista del form, mentre la generazione degli input form viene passata a Gino.Form::makeInputForm().

Gino.Form::makeInputForm() cicla sui campi, e per ogni campo richiama il relativo input

$structure[$field] = $build->formElement(object Gino.ModelForm, array options_input);

Gino.Build::formElement() stampa un elemento del form relativo al valore della chiave widget.
Gino.Build::formElement istanzia la classe del widget del campo relativo (che estende Gino.Widget()), imposta il valore da mostrare nell'input passandolo a Gino.Build::inputValue(), e richiama Gino.Build::printInputForm() passandogli come parametro un array associativo di opzioni del campo/modello unitamente alle opzioni degli elementi del form.

Valore nell'input form

Il valore da utilizzare nel parametro value di ogni input viene preso da una delle proprietà $this->_value, $this->_value_retrieve, $this->_value_input.

In pratica se si recupera il valore dalla sessione del form (ovvero si richiama Gino.Form::retvar()) in Gino.Widget::printInputForm() si utilizza $this->_value_retrieve altrimenti si utilizza $this->_value_input.

Classe WidgetRetrieve valueValue
CheckboxWidget-$this->_value
ConstantWidget-$this->_value_input
DatetimeWidgetretvar$this->_value_retrieve
DateWidgetretvar$this->_value_retrieve
EditorWidgetretvar$this->_value_retrieve
EmailWidgetretvar$this->_value_retrieve
FileWidget-$this->_value_input
FloatWidgetretvar$this->_value_retrieve
HiddenWidget-$this->_value_input
ImageWidget-$this->_value_input
MulticheckWidget-$this->_value
PasswordWidgetretvar$this->_value_retrieve
RadioWidgetretvar$this->_value_retrieve
SelectWidget-$this->_value
TextareaWidgetretvar$this->_value_retrieve
TextWidgetretvar$this->_value_retrieve
TimeWidgetretvar$this->_value_retrieve
UnitWidget--

Gino.Widget::inputValue() di default gestisce il valore da mostrare nell'input form con la funzione Gino.htmlInput().
Alcuni widget gestiscono diversamente questo valore:

Ad ora l'unico valore gestito dall'opzione data_type è regexp, per i campi che devono contenere regular expression. Questa opzione può essere definita nelle opzioni dei campi presenti nella renderizzazione del form in Gino.ModelForm::view():

$modelform = \Gino\Loader::load('ModelForm', array($this, []));

$form = $modelform->view(
	array(	// options_form
		'session_value' => 'dataform',
		'show_save_and_continue' => false,
		...
	),
	array(	// options_field
		'rexp' => ['data_type' => 'regexp', 'trnsl' => false]
	)
);

Input form

In ogni classe che estende Gino.Widget il metodo printInputForm() richiama il proprio specifico input dalla classe Gino.Input.

Salvataggio dei dati del form

Il salvataggio dei dati a seguito del submit di un form di inserimento/modifica viene effettuato utilizzando ModelForm, in particolare Gino.ModelForm::save(), dove ad ogni campo del modello viene associato il valore dell'input recuperato da Gino.{Type}Field::retrieveValue() e ripulito da Gino.{Type}Build::clean().

Il valore di una variabile in una richiesta HTTP viene recuperato dai singoli campi (Gino.{Type}Field::retrieveValue()) e successivamente viene ripulito attraverso i metodi Gino.{Type}Build::clean(), dove ogni tipologia di campo (eccetto i campi ManyToManyThroughField) applica delle funzioni specifiche di clean (lib/func.var.php)

$retrieve_value = field_object->retrieveValue(field_name);
build_object = model_object->build(field_object);
$value = build_object->clean($retrieve_value, opt_element);

Per ripulire una variabile in una richiesta HTTP senza passare da ModelForm occorre utilizzare semplicemente le funzioni di clean come nell'esempio seguente:

$value = clean_int($this->_request->GET[field_name]);

Il salvataggio effettivo dei dati del modello nel record del database viene effettuato dal metodo Gino.Model::save().
In questo metodo è poi Gino.Field::valueToDb() che imposta il tipo del valore recuperato dal form e ripulito dal clean per essere poi utilizzato per la definizione della query e la gestione dei ManyToMany. Gino.Field::valueToDb() viene richiamato attraverso Gino.Model::__set().

Gino.Build::clean() ripulisce un input per l'inserimento del valore in database.

FIELD CLASSESWIDGETBUILD CLASSESBUILD OPTIONSCLEAN FUNCTION (nel file func.var.php)CLEAN OPTIONS
BooleanFieldtextBooleanBuild-clean_bool
CharFieldtextCharBuildtypeoftext (text|html)clean_text (default) | clean_htmlclean_text:
escape (default true)
clean_html:
escape (default true)
strip_tags (default null)
strip_embedded (default false)
allowable_tags (default null)
DateFielddateDateBuild-clean_date (-> clean_text)typeofdate (date|datetime, default 'date')
separator (default '/')
DatetimeFielddatetime, nullDatetimeBuild-clean_date (-> clean_text)
DirectoryFieldtextDirectoryBuild-clean_text
EmailFieldemailEmailBuild-clean_email (-> clean_text)
EnumFieldradioEnumBuild-clean_int (default) | clean_text
FileFieldfileFileBuild--- personalizzato
FloatFieldfloatFloatBuild-clean_float
ForeignKeyFieldselectForeignKeyBuild-clean_int
ImageFieldimageImageBuild--- extend FileBuild
IntegerFieldtextIntegerBuild-clean_int
ManyToManyFieldmulticheckManyToManyBuild-clean_arraydatatype (int|string|float|bool, default 'int')
asforminput (default true)
ManyToManyThroughFieldunitManyToManyThroughBuild-- passa attraverso Gino.ModelForm::m2mThroughAction()
MulticheckFieldmulticheckMulticheckBuild-clean_arrayasforminput false
SlugFieldtextSlugBuild-clean_text
TagField-TagBuild-clean_text
TextFieldtextarea (default), editorTextBuildwidget (textarea|editor), typeoftext (text|html)clean_text (default) | clean_html (con widget 'editor' o typeoftext 'html')
TimeFieldtimeTimeBuild-clean_time
YearFieldtextYearBuild-clean_int

Segue un esempio di impostazione di widget diversi dal default e di opzioni di Gino.Build::clean() e delle funzioni di clean. I campi dell'esempio sono di tipo Gino.TextField

$admin_table = new \Gino\AdminTable($this, array());

$backend = $admin_table->backOffice([Model_Name], array(...), array(...), array(
	[...]
	name_text_field1 => array(
		[...]
		'widget'=>'editor',
	),
	name_text_field2 => array(
		[...]
		//'typeoftext' => 'text'	// default
	),
	name_text_field3 => array(
		[...]
		'typeoftext' => 'html'
	)
));
return $backend;

Generazione form non amministrativo

Si consideri ad esempio il form per effettuare il login, che mostra soltanto i campi username e userpwd del modello Gino.App.Auth.User

$mform = \Gino\Loader::load('ModelForm', array(new User(null), array('fields'=>array('username', 'userpwd'))));

$form = $mform->view(
	array(
		'form_id' => 'login',
		'show_save_and_continue' => false,
		'view_title' => false,
		'addCell' => array('username'=>array('name'=>'action', 'field'=>\Gino\Input::hidden('action', 'auth'))),
		's_name' => 'submit_login',
		's_value' => _("login")
	),
	array(
		'username' => array('trnsl'=>false),
		'userpwd' => array('trnsl'=>false)
	)
);

Un altro esempio si può visualizzare nella classe Gino.Css, dove il form viene costruito nel seguente modo

$mform = \Gino\Loader::load('ModelForm', array(new Css('layout', array('id' => $this->id)), array(
	'form_id' => 'gform',
	 //'fields' => array()
)));

$form = $mform->view(
	array(
		'session_value' => 'dataform',
		'show_save_and_continue' => false,
		'view_title' => true,
		'form_title' => $this->id ? sprintf(_('Modifica "%s"'), htmlChars($this->label)) : _("Nuovo foglio di stile"),
		'f_action' => $this->_registry->router->link('layout', 'actionCss'),
		's_value' => $this->id ? _("modifica") : _("inserisci"),
	),
	array(
	  'label'=>array("size"=>40, "maxlength"=>200, "trnsl"=>true),
	  'description' => array("cols"=>45, "rows"=>4, "trnsl"=>true),
	  'filename' => array('extensions' => array("css"))
	)
);

mentre l'action è la seguente

$mform = \Gino\Loader::load('ModelForm', array(new Css('layout', array('id' => $this->id)), array(
	'form_id' => 'gform',
)));
$mform->saveSession('dataform');
$result = $mform->save();
return new Redirect($this->_registry->router->link($this->_interface, 'manageLayout', array(), array('block' => 'css')));

Form non automatizzato

Un esempio di form non automatizzato

$gform = Loader::load('Form', array());
$gform->load('dataform');

$buffer = $gform->open($form_action, '', 'label', array('form_id'=>'gform'));
$buffer .= \Gino\Input::hidden('id', $this->id);
$buffer .= \Gino\Input::input_label('label', 'text', $gform->retvar('label', \Gino\htmlInput($this->label)), _("Etichetta"), array("required"=>true, "size"=>40, "maxlength"=>200, "trnsl"=>true, "trnsl_table"=>$this->_tbl_data, "trnsl_id"=>$this->id));
$buffer .= \Gino\Input::textarea_label('description', $gform->retvar('description', \Gino\htmlInput($this->description)), _("Descrizione"), array("required"=>true, "cols"=>45, "rows"=>4, "trnsl"=>true, "trnsl_table"=>$this->_tbl_data, "trnsl_id"=>$this->id));

$buffer .= \Gino\Input::placeholderRow(null, \Gino\Input::submit('submit_action', _("salva")).\Gino\Input::submit('savecontinue_action', _("salva e continua la modifica")));
$buffer .= $gform->close();

Hidden Input

I campi nascosti si possono impostare utilizzando il metodo Gino.Form::setHidden(), come ad esempio

$form->setHidden(
	['activity' => ['value' => $activity->id, 'options' => ['id' => $activity->id]],
	'instance' => $this->_instance
]);

Campi automaticamente nascosti o rimossi

Alcuni campi impostati nel Modello diventano automaticamente "nascosti" o semplicemente vengono rimossi.

I campi IntegerField con la proprietà auto_increment pari a true prendono hidden come valore della proprietà widget.
I campi DatetimeField con la proprietà auto_now pari a true o auto_now_add true prendono null come valore della proprietà widget, per cui vengono rimossi.

In Gino.ModelForm::get(), che recupera la struttura dei campi, se il nome del campo è instance, la sua proprietà widget prende il valore null.

top

Controller

Gino.Controller è una classe astratta primitiva di tipo Controller (MVC), dalla quale discendono tutti i controller delle singole app.

Le singole applicazioni sono costituite da una o più classi ed eventualmente da altri file/directory, tutti raggruppati all'interno della cartella app/nomeclasse.
La classe principale è la classe controller che estende la classe Gino.Controller e gestisce le classi modello. Il nome del file della classe controller è nel formato class_[nomeclasse].php, mentre quello delle classi modello è nel formato class.[Modello].php.

Metodi per l'amministrazione

Per convenzione il metodo preposto a gestire la parte amministrativa si chiama manageDoc o manage[Nomeclasse]. Il nome è obbligatorio se si vuole che il modulo compaia automaticamente nell'Amministrazione, altrimenti si può utilizzare qualsiasi altro nome.
Nelle classi dei moduli istanziabili il nome del metodo è manageDoc, mentre in quelle dei moduli non istanziabili manage[Nomeclasse].

Metodi strutturali

deleteInstance()

Descrive le procedure da attivare al momento dell'eliminazione di una istanza. Questo metodo viene richiamato dal metodo Gino.App.Module.module::actionRemoveModule().

In generale con l'istanza vengono eliminati

getClassElements()

struttura degli elementi:

return array(
	"tables" => array([string nome_tabella[, string nome_tabella]]),
	"css" => array([string file_css]),
	"views" => array([string nome_file] => [string label_file]),
	"folderStructure" => array(string content_dir=>null[|array(string dir=>null[|[array(...)])])
);

outputFunctions()

Il metodo outputFunctions() della classe di tipo Gino.Controller contiene i metodi pubblici che possono essere richiamati dal layout (ovvero quelli quindi inseriti nel template) e quelli che possono essere richiamati direttamente nell'indirizzo (URL).
outputFunctions() viene infatti letto dal motore di generazione dei layout (e in questo caso vengono considerati soltanto i metodi presenti in outputFunctions() e contestualmente non presenti nel file INI) e dal motore di generazione di voci di menu (vengono presi i metodi presenti nel file INI e contestualmente in outputFunctions()) per presentare una lista di output associati all'istanza di classe.

Mentre nel file INI devono essere esplicitati tutti i metodi che vengono chiamati via URL, non tutti questi metodi devono essere inseriti in outputFunctions(), ma soltanto quelli che possono essere di un qualche interesse per l'utilizzatore. A meno di particolari eccezioni, relativamente ai metodi presenti nel file INI in outputFunctions() non vengono quindi esplicitati i metodi che fanno riferimento all'area amministrativa o che sono richiamati via ajax.

In pratica vengono inseriti i nomi dei metodi richiamabili direttamente nell'URL e che possono essere inseriti come voce di menu (ad esempio ci saranno i metodi archive, feedRSS, ma non detail, che richiede come parametro anche il valore id della risorsa), e i nomi dei metodi che possono essere richiamati all'interno di un template (ad esempio evidence, last, showcase).

Nella generazione dei template l'elenco dei moduli selezionabili viene generato da Gino.App.Layout.layout::moduleList() per la gestione di template di tipo a blocchi, consentendo l'inserimento dell'output all'interno della struttura con un click, e da Gino.App.Layout.layout::modulesCodeList() per la gestione di template di tipo free.

return array(
	method_name => array(
		'label' => (string) description,
		'permissions' => (array) [code_perm]
	)
);

Esempio:

return array(
	"view" => array("label"=>_("..."), "permissions" => []),
	"last" => array("label"=>_("..."), "permissions" => ['can_admin', 'can_write'],
	"showcase" => array("label"=>_("..."), "permissions" => ['can_admin', 'can_view']
);

Opzioni di classe

Di seguito i passaggi per gestire le opzioni di classe.

Nel costruttore, se previsti, indicare i valori di default

$this->_optionsValue = array(
	'last_news_number' => 3,
	'items_for_page' => 5,
);

inizializzare i valori delle opzioni attraverso il metodo Gino.Controller::setOption()

$this->_last_news_number = $this->setOption('last_news_number', array('value'=>$this->_optionsValue['last_news_number']));
$this->_items_for_page = $this->setOption('items_for_page', array('value'=>$this->_optionsValue['items_for_page']));

passare alla classe Gino.Option i riferimenti per costruire il form di gestione delle opzioni di classe

$this->_options = new \Gino\Options($this);
$this->_optionsLabels = array(
	"last_news_number" => array(
		'label'=>_("Numero ultime news"),
		'value'=>$this->_optionsValue['last_news_number'],
		'section'=>true,
		'section_title'=>_('Ultime news')
	),
	"items_for_page" => array(
		'label'=>_("Numero news per pagina"),
		'value'=>$this->_optionsValue['items_for_page'],
		'section'=>true,
		'section_title'=>_('Archivio news')
	),
);

Opzioni dei campi

Le opzioni che possono essere associate a ciascun campo sono:

Informazioni sui campi

Le informazioni sui campi della tabella vengono recuperate dal metodo Gino.Db::fieldInformations(), e vengono caricate nei parametri name (string), type (string), length (integer).

I tipi di campo gestiti sono:

SEO

Nei metodi pubblici delle classi Gino.Controller che forniscono un output per il front-end si possono personalizzare i META tag generali del sito ed impostare quelli per il SEO.

META tag

Il tag TITLE e i META description e keywords vengono caricati automaticamente nella pagina anche se non vengono definiti: in questo caso gino utilizza i valori salvati nell'applicazione Impostazioni, sezione Meta.

A livello di codice è Gino.Document::setHeadVariables() che carica nel Registro le variabili dei tag META della pagina (title, description, keywords, favicon).

Social META tag

Nei metodi pubblici delle classi Gino.Controller è quindi possibile personalizzare i META tag generali ed impostare dei META aggiuntivi caricandoli direttamente nel Registro utilizzando la sintassi:

Gino.Registry->addMeta(['name' => string, 'property' => string, 'content' => string]);

oppure utilizzando il metodo Gino.Controller::setSEOSettings() che carica le impostazioni dei tag meta per SEO nel Registro.

Seguono degli esempi di personalizzazione:

$this->setSEOSettings([
	'title' => $item->ml('title'),
	'description' => $item->ml('text'),
	'keywords' => $item->tags,
	'url' => $this->link($this->_instance_name, 'detail', array('id' => $item->slug), '', array('abs'=>true)),
	'type' => 'article',
	'image' => $item->getImgPath()
]);

Visualizzazione dei contenuti

In generale i metodi preposti a visualizzare i contenuti vengono costruiti in modo tale da concedere l'accesso soltanto agli utenti in possesso dei necessari permessi (vedi Gino.Access:requirePerm()).
Le seguenti stringhe rappresentano un esempio di utilizzo:

$this->requirePerm('can_admin');
$this->requirePerm(['can_admin', 'can_write', 'can_view']);

Metodi pubblici che forniscono un output per il front-end

Come è stato descritto poco sopra nei Metodi strutturali del Controller, le classi controller implementano il metodo outputFunctions() attraverso il quale definiscono i metodi pubblici che forniscono un output per il front-end.

I metodi che è possibile gestire come blocchi all'interno del template sono quelli definiti nel metodo outputFunctions() delle classi Controller e che non sono richiamabili direttamente da URL, ovvero non sono definiti nel file INI. La logica è definita nel metodo Gino.App.Layout.layout::getMethodData().

Il codice di sostituzione da inserire nei file di template per visualizzare le viste (che non compaiono nel file INI) è il seguente, dove method_name deve essere presente in outputFunctions():

{module [sysclassid|classid]=[valore id di \Gino\App\Module\ModuleInstance o \Gino\App\SysClass\ModuleApp] func=method_name}

Caricare file CSS/JavaScript

Per caricare un file css o javascript specifici della classe o istanza utilizzare il registro.
Esempio:

$registry = Registry::instance();
$registry->addJs($this->_class_www."/blog.js");
$registry->addCustomJs($this->_class_www."/blog.js", array('compress'=>false, 'minify'=>false));
$registry->addRawJs("<script src=\"https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.js\"></script>");
$registry->addCss($this->_class_www."/blog_".$this->_instance_name.".css");

Strumenti aggiuntivi

Questi strumenti sono di complemento e di aiuto alla costruzione di una pagina.

Potete trovare altri strumenti di complemento nei plugin.

top

Gestione dei segnali

La classe EventDispatcher implementa il pattern Mediator per gestire segnali tra classi. EventDispatcher permette di registrare delle callback da eseguire quando si verifica un evento scatenato da qualunque altra classe, e viene utilizzato ad esempio per effettuare il salvataggio di tag solamente se il salvataggio del record principale è andato a buon fine.

Registrazione callback:

// registra una callback (in questo esempio $this->save()) quando il modello [$this->_model] emette un segnale post_save
$event_dispatcher = EventDispatcher::instance();
$event_dispatcher->listenEmitter($this->_model, 'post_save', array($this, 'save'));

Emissione del segnale (in Gino.Model::save()):

// il modello $this emette il segnale post_save passando il parametro array('model' => $this) alle callback in ascolto
$event_dispatcher->emit($this, 'post_save', array('model' => $this));
top

Gestione degli errori

Gli errori vengono gestiti in due modi:

Reindirizzamento a un indirizzo indicato

L'errore viene salvato in sessione, avviene quindi il reindirizzamento. L'errore viene mostrato al nuovo url e poi tolto dalla sessione per evitare ripetizioni.

Con i form il metodo Gino.Form::load() recupera i valori inseriti negli input e salvati nella sessione del form per ripolare gli input quando si verifica un errore.
Questo meccanismo funziona quando l'errore viene gestito attraverso Gino.Error::errorMessage() in quanto è questo il metodo che crea la variabile di sessione GINOERRORMSG.

return \Gino\Error::errorMessage(array('error' => error_string_or_code), $controller->link(instance, method, array[]));

Il testo dell'errore può essere personalizzato (string) oppure può corrispondere a un codice (integer) presente nel metodo Gino.Error::codeMessages().

Eccezione

throw new \Exception(error_string);

throw new \Gino\Exception\Exception404();
top

Tag ed argomenti correlati

gino incorpora una gestione centralizzata dei tag. Ciascun modello può definire un campo di tipo tag, da trattare con la classe Gino.TagField. Questo permette di automatizzare la gestione (salvataggio, suggerimenti) che viene implementata nella classe Gino.GTag.
In questo modo tutti i tag di tutti i contenuti (moduli istanziabili e non) sono gestiti in due tabelle, una per i tag, l'altra per le associazioni. È stato quindi possibile implementare una funzionalità "argomenti correlati", che permette di pescare tutti i contenuti correlati (stessi tag) a quello visualizzato.

Definizione campo tag:

// all'interno del metodo statico columns() di un modello
$columns['tags'] = new \Gino\TagField(array(
	'name' => 'tags',
	'label' => array(_('Tag'), _("elenco separato da virgola")),
	'required' => false,
	'max_lenght' => 255,
	'model_controller_class' => 'news',
	'model_controller_instance' => null,	// 0 if not istantiable (@see page)
));

// and in the properties() method
$property['tags'] = array(
	'model_controller_instance' => $controller->getInstance(),
);

Contenuti correlati:

// ricava i contenuti correlati alla pagina con id $page_entry->id
$related_contents = \Gino\GTag::getRelatedContents($this->getClassName(), 'PageEntry', $page_entry->id);
top

Creazione thumbnail

La nuova classe Gino.GImage consente di creare thumbnail di dimensioni custom al volo, e massimizzando l'entropia del risultato finale. Questo è molto comodo perché invece di generare automaticamente all'upload dell'immagine, una thumb di dimensioni predefinite, possiamo creare tutte le thumb che vogliamo a partire da una stessa immagine e di qulunque dimensione, andando anche a modificare il form factor con la "quasi" certezza di ottenere nel risultato la parte più rappresentativa dell'immagine. Naturalmente le thumb create sono tenute in cache per non generare più volte una stessa immagine.

All'interno di una view:

    <?php
    // genera una thumb dell'immagine con path $n->getImgPath() di dimensioni 100x100
    $image = new \Gino\GImage(\Gino\absolutePath($n->getImgPath())); $thumb = $image->thumb(100, 100);
    ?>
    <img class="left" style="margin: 0 10px 10px 0" src="<?= $thumb->getPath() ?>" />
top

Compressione js e css

La classe Gino.Compressor si occupa di gestire la minificazione ed il merge dei file css e js. È una buona pratica quella di unire tutte le risorse e minificarle per velocizzare l'esperienza utente. Questo perché unendo i file limitiamo il numero di richieste http eseguite al caricamento della pagina e minificando limitiamo le dimensioni del file.
gino è in grado di fare tutto ciò automaticamente e tenere in cache i file generati finché la loro data di ultima modifica è succesiva a tutte quelle dei file che lo compongono. La compressione avviene comunque solamente in produzione (DEBUG=FALSE), mentre in fase di debug i file vengono inseriti normalmente all'interno del documento.

top

Gestione moduli

I moduli sono le applicazioni che dotano gino di tutte le sue funzionalità.
Alcuni moduli sono presenti di default in quanto sono parte integrante del core di gino, mentre altri moduli possono essere installati successivamente.

Una caratteristica fondamentale dei moduli è di poter essere instanziabili (campo instantiable), ovvero di poter utilizzare diverse istanze di uno specifico modulo di sistema.

I Moduli di sistema gestiscono le classi fondamentali del sistema e le applicazioni aggiuntive. In questa sezione è possibile installare nuove classi, rimuovere quelle presenti (campo removable) ed effettuare gli aggiornamenti. Ciascuna modifica deve essere fatta con criterio, sapendo che è possibile compromettere la stabilità del sistema in caso di operazioni errate.

I Moduli istanza non sono altro che istanze dei Moduli di sistema. Nella sezione Moduli è possibile creare nuovi moduli come istanze di classi o funzioni presenti nel sistema.
La procedura di installazione di un nuovo modulo prevede quindi che si installi prima la classe e che successivamente si creino una o più istanze di quella classe.

Installazione di un modulo di sistema

L'installazione di un modulo può essere effettuata in modo facilitato attraverso la selezione di un pacchetto di installazione o in modo manuale.

Installazione pacchetto

L'installazione di un modulo attraverso la selezione di un apposito pacchetto zip richiede che questo file sia preparato nel modo corretto.
In particolare il nome del file deve essere strutturato nel seguente modo:

[nome_classe]_[qualcosa].zip		// ad es. news_pkg.zip

inoltre deve essere creato scegliendo direttamente tutti i file/directory che lo compongono.
Importante: non creare il file zip della directory che contiene i file ma selezionando i file che vi sono contenuti.

Per un elenco delle libreria disponibili vedere l'area github della Otto.

Quando viene installato un nuovo modulo, il file del pacchetto viene scompattato in una cartella all'interno della cartella app. Il nome della cartella è quello indicato nell'opzione name del file config.txt.

Nel processo di installazione del pacchetto vengono create, se indicato nel metodo getClassElements() della classe controller dell'applicazione, anche le cartelle presenti nell'opzione folders. In generale è quasi sempre indicata la cartella dei contenuti all'interno della cartella contents.

Caratteristiche del pacchetto di installazione

Per costruire un pacchetto di installazione sono necessari:

Nel file config.txt il parametro name deve corrispondere al nome della classe.

Altri file di supporto:

Opzioni del file config.txt

Le opzioni devono essere scritte una di seguito all'altra separate da virgola.

File di configurazione di esempio:

name:"news",version:"2.1.0",tbl_name:"news",instantiable:"1",description:"Gestore di news categorizzate con gruppi di lavoro",removable:"1",folders:"contents/news"

Installazione manuale

Per eseguire l'installazione manuale effettuare il submit del form prendendo come riferimento il file config.txt.
In seguito effettuare la procedura indicata:

Installazione di una istanza

La creazione di una istanza avviene nella sezione Moduli. L'istanza viene creata a partire da un modulo di sistema, andando a creare un record nella tabella sys_module.

Nel processo di installazione dell'istanza viene creata, se indicato nel metodo getClassElements() della classe controller dell'applicazione, una cartella dei contenuti all'interno della cartella dei contenuti della classe; inoltre vengono creati i file css e i file delle viste specifici dell'istanza.
Il nome che identifica la cartella dell'istanza e i file è il nome indicato nel campo Nome del form di "Nuova istanza".

Installazione modulo/istanza - Esempio

Prendiamo ad esempio l'applicazione news (news_pkg.zip).

L'installazione del modulo crea le directory:

Infatti nel file config.txt troviamo l'opzione

folders:"contents/news"

Quando installiamo una istanza del modulo (denominandola news) il processo di installazione recupera il metodo getClassElements()

array(
	"tables"=>array(
		'news_article',
		...
	),
	"css"=>array(
		'news.css'
	),
	"views" => array(
		'archive.php' => _('Archivio news'),
		...
	),
	"folderStructure"=>array (
		CONTENT_DIR.OS.'news'=> array(
			'img' => null,
			'attachment' => null
		)
	)
)
e in questo caso crea le directory

e crea i file

Tabelle dei moduli

I valori dei moduli di sistema vengono salvati nella tabella sys_module_app, mentre i valori dei moduli non di sistema (istanze) vengono salvati nella tabella sys_module.

Campi dei moduli di sistema

Campi significativi dei moduli-istanza

top

Moduli installati

I Moduli di sistema installati di default sono

ImpostazionisysconfImpostazioni di sistema
LinguelanguageGestione delle lingue
Moduli di sistemasysClassGestione delle classi fondamentali del sistema
ModulimoduleCreazione di nuovi moduli come istanze di classi o funzioni presenti nel sistema
Creazione appbuildappGenera applicazioni (Moduli) pronte per essere personalizzate e installate in gino
UtentiuserGestione utenti
StatistichestatisticsResoconto degli accessi al sistema da parte degli utenti registrati
LayoutlayoutGestione del layout (css, template, skin)
Header e FootergraphicsGestione header e footer
AllegatiattachmentGestione allegati, ovvero di file che possono essere utilizzate come normali risorse all'interno del sito
MenumenuGestione menu
PaginepageGestione pagine
Indexindex
Ricerca nel sitosearchSiteModulo di ricerca nel sito
phpModuleViewphpModuleViewModulo attraverso il quale è possibile istanziare un nuovo modulo contenente codice PHP
StrumentiinstrumentsAlcuni strumenti, quali l'elenco delle risorse disponibili (con i relativi link) e dei mime type
AutenticazioneauthModulo utenti, gruppi e permessi
Funzioni di sistemasysfuncFunzioni di sistema
PostpostGestione articoli, post
CalendarcalendarCalendario appuntamenti

I Moduli predefiniti creati come istanze di classi sono

Menu amministrazione
Menu principale
Top Bar
Top Bar Admin
Articoli
Calendario
top

Layout

In questa sezione è possibile gestire il layout del sito. Ad ogni request viene associata una skin, la quale caricherà il template associato ed eventualmente un foglio di stile. I passi da seguire per personalizzare il layout di una pagina o sezione del sito sono i seguenti:

  1. Creare ed uploadare un foglio di stile se necessario.
  2. Creare un template a blocchi utilizzando il motore di gino (file .tpl) oppure un template libero (file .php).
  3. Creare una skin alla quale associare il template ed eventualmente il foglio di stile. La skin viene poi associata alla pagina o alla sezione desiderata definendo url, espressioni regolari di url oppure variabili di sessione.
  4. Impostare la priorità della skin spostandola in alto o in basso.

CSS

Upload di fogli di stile da associare eventualmente ad una skin. Il css viene accodato ai file di default di gino, pertanto è possibile definire nuovi stili o sovrascrivere quelli già presenti.

Template

I template sono file PHP che vengono associati a delle skin per essere renderizzati quando vengono chiamate queste ultime.

Ogni template è associato a un file il cui nome può essere liberamente definito nella fase di creazione ma non modificato in seguito. Il file viene salvato nella directory templates ed ha come estensione php.

Nella maschera di modifica e inserimento è presente il campo 'css' nel quale si può specificare un foglio di stile che viene caricato nella maschera di creazione del template. Selezionando un file css, il foglio di stile non viene automaticamente associato al template, cosa che deve essere fatta al momento di creazione della skin, ma viene utilizzato per creare un template adatto se si ha in previsione di utilizzarlo all'interno di una skin con un css che modifichi le dimensioni degli elementi strutturali.

Funzionamento

Nel template è possibile controllare finemente ogni aspetto del layout finale della pagina. Il template comprende l'intero documento, dalla definizione del DOCTYPE alla chiusura del tag html. È possibile utilizzare codice php e si hanno a disposizione tutte le librerie di gino. In questo caso non è necessario associare fogli di stile caricati a proposito, in quanto si possono direttamente controllare le chiamate a css, javascript etc... modificando l'intestazione del documento.

Oltre al Menu, in gino sono già presenti alcuni moduli pronti per essere utilizzati nella barra di navigazione:

<!-- Menu -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1" style="overflow: auto;">
	{module classid=4 func=render}
</div><!-- /.navbar-collapse -->

<!-- Language -->
<div class="navbar-language">
	{module sysclassid=2 func=choiceLanguage}
</div>

<!-- Full Search -->
<div class="navbar-search">
	{module sysclassid=13 func=form}
</div>

<!-- Login -->
<?php if(!$registry->session->user_id): ?>
<div class="navbar-login">
	<a href="auth/login">Accedi</a>
</div>
<?php endif; ?>

Gestione dei contenuti

La classe Gino.Document crea il documento html da inviare come corpo della risposta HTTP. Il corpo della risposta viene creato da Gino.Document::render() che:

Il template viene parserizzato due volte.
La prima volta vengono eseguiti i metodi definiti nei tag {% block instance %} (oppure {module [...]} per compatibilità con le versioni precedenti) e vengono letti i file definiti attraverso i tag {% block 'template' %}. In questo modo vengono salvate eventuali modifiche al registry che viene utilizzato per includere javascript, css e meta nel tag HEAD del documento. L'output viene quindi tenuto in memoria, mentre il template non viene toccato.

La seconda volta viene parserizzato per sostituire effettivamente i segnaposto dei moduli con l'output precedentemente salvato nella prima parserizzazione. Non si possono sostituire gli output già alla prima parserizzazione, e poi fare un eval del template perché altrimenti eventuali contenuti degli output potrebbero causare errori di interpretazione dell'eval: è sufficiente una stringa '

I placeholder del template vengono parserizzati dal metodo parseTpl che ritorna le corrispondenti porzioni di template oppure l'output di pagine o applicazioni.

Workflow

Il metodo parseTpl elabora il placeholder per identificare il tipo di elemento in modo da poterne recuperare il rispettivo contenuto.

#1 Placeholder di tipo template

Regular expression: preg_match("#{% block '(.*?)' %}#", $regex_marker)

Verifica se il placeholder corrisponde a un file PHP di template, ritornando in questo caso un blocco HTML.

Formati validi

{% block 'filename.php' %}
{% block 'appname/filename.php' %}

Alcuni esempi di placeholder

{% block 'head.php' %}
{% block 'core/debug_alert.php' %}
{% block 'news/head.php' %}

Come prima cosa verifica se nella stringa compresa tra gli apici esiste il carattere /.
In caso positivo verifica prima se esiste il file: templates/[contenuto-degli-apici]

Considerando gli esempi sopra riportati questo corrisponde a

templates/core/debug_alert.php

se questo file non esiste verifica se esiste il file: app/[appname]/templates/[filename]

Considerando gli esempi sopra riportati questo corrisponde a

app/news/templates/head.php

In caso negativo, ovvero il carattere / non è presente, verrà preso il file templates/[contenuto-degli-apici]

Considerando gli esempi sopra riportati questo corrisponde a

templates/head.php

#2 Placeholder di tipo istanza

Regular expression: preg_match("#{% block (.*?) %}#", $regex_marker)

Questo tipo di placeholder ritorna codice HTML ottenuto richiamando metodi pubblici di una pagina oppure di una applicazione.

I valori dell'istanza, del metodo e del parametro param vengono recuperati con una ulteriore regexp.

Se l'istanza è una pagina (page) viene richiamato il metodo modPage, se invece esistono istanza e metodo viene richiamato il metodo modClass; se sono entrambi nulli ({% block url %}) il contenuto è direttamente $this->_url_content.

Formati validi

{% block article.last param=(INT) %}

dove article è il nome dell'istanza e last il nome del metodo richiamato.

Si cerca prima nella tabella sys_module_app se il nome dell'istanza ha una corrispondenza con una applicazione non istanziabile (active='1' AND instantiable='0').
Nel caso di esito negativo si ricerca nella tabella sys_module una corrispondenza con una applicazione-istanza (name='article' AND active='1').

Alcuni esempi di placeholder

{% block article.last %}
{% block page.SLUG %}
{% block page.ID %}
{% block url %}

url permette di mostrare il contenuto dell'applicazione o della pagina richiamata da url (proprietà $_url_content).

page richiama una pagina, a condizione che sia stata pubblicata (published='1'); in questo caso viene chiamato page->box(ID).

#3 Placeholder di tipo istanza - DEPRECATO

Regular expression: preg_match("#{module(.*?)}#", $regex_marker))

Condizione deprecata mantenuta soltanto per compatibilità con le vecchie versioni.

Alcuni esempi di placeholder

{module classid=4 func=render}
{module sysclassid=2 func=choiceLanguage}
{module pageid=1}
{module id=0}

In questo caso il placeholder ha la seguente forma

{module classid=10 func=showcase param=(INT)}

dove al posto di classid posso avere pageid, classid, sysclassid. Alcuni esempi:

{module classid=4 func=render} metodo render dell'applicazione istanziabile corrispondente all'istanza con valore id 4 della tabella sys_module
{module sysclassid=2 func=choiceLanguage} metodo choiceLanguage dell'applicazione non istanziabile corrispondente al valore id 2 della tabella sys_module_app
{module pageid=1 func=full} metodo box della classe Gino.App.Page::page corrispondente al record con valore id 1 della tabella page_entry
{module id=0} contenuto del modulo o della pagina richiamato da url (proprietà $_url_content)

Esempio di utilizzo del Registro

I file javascript o css si possono caricare direttamente nel template e nelle viste:

$registry = \Gino\Registry::instance();
$registry->addRawJs("<script src=\"https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.js\"></script>");

$registry->addRawCss("<link rel=\"stylesheet\" href=\"https://style.example.com/leaflet.css\" integrity=\"dfbyoiht54ykdj\" crossorigin=\"\" />");

Skin

In questa sezione si definiscono le skin che richiamano uno specifico template e che opzionalmente possono richiamare un file css.
Ogni skin viene associata agli indirizzi specificati nei campi Espressione regolare e Indirizzi. Inoltre la skin può essere abbinata o meno ad una variabile di sessione e può essere impostata con Priorità massima

Funzionamento

La ricerca di una corrispondenza tra la pagina richiesta e la skin avviene in base a dei principi di priorità in base ai quali vengono controllati prima gli indirizzi appartenenti a skin con priorità massima e successivamente quelli appartenenti a skin con una variabile di sessione.
A seguire vengono controllati gli indirizzi delle skin senza priorità massima e senza variabile di sessione definiti nel campo urls e successivamente le classi di indirizzi definite nel campo rexp.

Gli indirizzi definiti nel campo urls hanno sempre la precedenza sulle classi di indirizzi definite nel campo rexp, anche nel caso di skin con priorità massima e di skin con variabile di sessione. Ad ogni condizione di confronto (priorità massima, variabile di sessione, indirizzi, classi di indirizzi) l'ultimo fattore che dirime la priorità di precedenza di una skin è infine dato dal campo priority, identificato dall'ordine nel quale le skin compaiono nell'elenco (in alto ci sono le skin con priorità maggiore).

Dettagli campi del form

Nel campo Variabile di sessione si può inserire il valore di una variabile di sessione nel formato sessionname=sessionvalue. Nelle skin con variabile di sessione deve essere impostato anche il campo Indirizzi (urls) o il campo Espressione regolare (rexp), ricordando che il primo predomina sul secondo.
Nel caso in cui non si debba abbinare alla variabile di sessione un particolare indirizzo o espressione regolare, dandole quindi una priorità su tutti gli indirizzi, è necessario impostare il campo rexp col valore #.*#.

Nel campo Indirizzi si può inserire un indirizzo o più indirizzi separati da virgola ai quali associare la skin. Tali indirizzi hanno la priorità rispetto alle classi di indirizzi nel momento in cui viene cercata la skin da associare al documento richiesto.
Le classi di indirizzi, definite mediante il campo Espressione regolare nel formato PCRE, permettono di fare il matching con tutti gli indirizzi che soddisfano l'espressione regolare inserita.

Nei campi Espressione regolare e Indirizzi possono essere inseriti valori nel formato permalink o in quello nativo di gino.

Sintesi regole di matching indirizzi/classi

Quando viene richiesta una pagina (url) il sistema inizia a controllare il matching tra la pagina richiesta e gli indirizzi associati alle skin a partire dalla skin col valore del campo priority più basso (priorità maggiore) continuando a salire.
Se la corrispondenza non viene trovata, la ricerca continua utilizzando le espressioni regolari associate alle skin, sempre a partire dalla skin col valore del campo priority più basso.

L'ordine di priorità delle skin può essere aggirato utilizzando il campo Priorità massima e il campo Variabile di sessione. Le skin con queste impostazioni vengono infatti controllate prima delle altre.

Viste

In questa sezione si possono modificare le viste di sistema di gino. Sono viste generali utilizzate da buona parte dei moduli e dalla stessa area amministrativa.

Per visualizzare un esempio di come si possono caricare i file javascript e css direttamente nelle viste vai al template.

Procedura semplificata dell'associazione di un template a una pagina

top

Contenuti per il layout

I contenuti che vanno a comporre il layout possono essere

Tra i moduli di sistema che concorrono a comporre un layout di pagina, i più utilizzati sono Menu, Header e Footer e phpModuleView.

Menu

La classe menu mette a disposizione i moduli Menu principale e Menu amministrazione. È possibile selezionarli a partire da "Selezione modulo" -> "Moduli".

Per visualizzare nell'area amministrativa il collegamento al Menu amministrazione occorre impostare nel file configuration.php la costante VIEW_HIDDEN_APPS a true.

Header e Footer

Per una gestione personalizzata di header e footer attraverso la classe Gino.App.Graphics.graphics. Sono disponibili un header e un footer personalizzabili ed utilizzabili nella composizione del layout. Ciascuno di essi può essere di tipo grafico, cioè un'immagine (jpg, png o gif) oppure generato attraverso del codice html.

Per visualizzare nell'area amministrativa il collegamento a Header e Footer occorre impostare nel file configuration.php la costante VIEW_HIDDEN_APPS a true.

phpModuleView

Il modulo permette di eseguire codice php completamente personalizzabile, e di visualizzare l'output prodotto. Per precauzione tutte le funzioni di php che permettono di eseguire programmi direttamente sulla macchina sono vietate. Nel caso in cui venisse rilevata la presenza di una di queste funzioni il codice non verrebbe eseguito e l'output risultante sarebbe nullo.

Per una corretta integrazione dell'output prodotto all'interno del layout del sito, si consiglia di non utilizzare le funzioni per la stampa diretta echo e print, ma di immagazzinare tutto l'output all'interno della variabile $buffer, che verrà stampata all'interno del layout.

Si consiglia di fare molta attenzione perché nonostante l'accesso alle funzionalità più pericolose del php sia proibito, si ha un controllo completo sulle variabili, ed in caso di cattvo uso del modulo si potrebbe seriamente compromettere la visualizzazione del modulo o dell'intero sito.

Per creare un modulo contenente codice php è necessario creare un modulo di classe selezionando come classe phpModuleView. I moduli Top Bar e Top Bar Admin sono stati creati a partire da questo modulo.

Per visualizzare nell'area amministrativa il collegamento a Top Bar e Top Bar Admin occorre impostare nel file configuration.php la costante VIEW_HIDDEN_APPS a true.

top

Autenticazione

In gino è possibile gestire gli utenti attraverso l'applicazione Autenticazione (namespace Gino.App.Auth). Questa applicazione si preoccupa inoltre di gestire i processi inerenti l'autenticazione, mentre è la classe Gino.Access che fornisce i metodi che vengono utilizzati per verificare l'accesso a gino e alle sue funzionalità.

Permessi

In gino le singole applicazioni possono gestire l'accesso a specifiche funzionalità/pagine attraverso dei permessi appositamente definiti nel controller e caricati nella tabella auth_permission. In questa tabella il campo admin specifica se il permesso necessita dell'accesso all'area amministrativa.

Ogni utente può essere associato a uno di questi permessi, e tale associazione viene registrata nella tabella auth_user_perm. Inoltre ogni utente può essere associato a uno o più gruppi (tabella auth_group). I gruppi sono utili per definire un insieme di permessi che devono essere comuni per una data tipologia di utenti; i gruppi possono infatti essere associati ai permessi e alle istanze.

In aggiunta ai permessi specifici delle applicazioni, in gino sono presenti due permessi di sistema che sono stati collegati alla classe Gino.Core:

Importante: per permettere a un utente l'accesso a funzionalità amministrative di qualsiasi applicazione non è sufficiente assegnargli i permessi relativi, ma occorre anche assegnargli il permesso di accedere all'area amministrativa (Modulo: core).

Utenti

Gli utenti di gino sono utenti creati all'interno del CMS e vengono salvati nella tabella auth_user. In gino è stata implementata anche l'autenticazione Ldap che può essere attivata nelle Opzioni dell'applicazione Autenticazione e per quale si rimanda alla sezione apposita.

Come è stato evidenziato nel paragrafo precedente, l'utente può essere associato a dei permessi ma può essere anche salvato con la qualifica di Super-amministratore. In questo caso l'utente potrà accedere a tutte le funzionalità indipendentemente dai permessi associati.

L'utente abilitato alla gestione degli utenti ha il potere di inserire, modificare ed eliminare un utente, e di assegnargli il ruolo di Super-amministratore.

Reindirizzamento alla home amministrativa

Quando si chiama l'indirizzo /admin/ è la classe Gino.Router che comunica a gino di andare alla home di amministrazione (Gino.Router::rewritePathInfo()), ovvero di richiamare l'indirizzo ?evt[index.admin_page] (Gino.App.Index.index::admin_page()).
È infatti la classe Gino.Router che gestisce il routing di una request HTTP, chiamando la classe e il metodo che devono fornire risposta (vedi la sezione architettura).

Nel caso in cui l'utente non possieda i permessi per accedere alla home ammnistrativa, il metodo admin_page() imposta in $request->session->auth_redirect l'indirizzo al quale reindirizzare l'utente ad avvenuta autenticazione (ovverlo l'indirizzo della home amministrativa, ?evt[index.admin_page]) ed effettua il reindirizzamento alla pagina di autenticazione:

return new \Gino\Http\Redirect($this->link('auth', 'login'));

Gino.App.Auth.auth::login() verifica se è stato impostato un indirizzo al quale reindirizzare l'utente ad avvenuta autenticazione, altrimenti verifica se esiste $_SERVER['HTTP_REFERER'] (Gino.Http.Request::__construct()).
Nel caso in cui l'utente abbia effettuato l'autenticazione e debba essere reindirizzato alla home amministrativa ma non possieda i permessi per accedervi, l'indirizzo della home amministrativa viene ridefinito nella home del sito (HOME_FILE).

Registrazione automatica

L'applicazione Gino.App.Auth implementa la funzionalità di registrazione automatica al sito. Introduce il concetto di profilo di registrazione (Gino.App.Auth.RegistrationProfile). Un profilo porta con se alcune informazioni: titolo e testo della pagina di registrazione, termini e condizioni del servizio, abilitazione automatica degli utenti registrati (in caso contrario dovranno essere attivati da un amministratore dell'applicazione autenticazione), eventuale altro modulo che gestisce informazioni in input richieste all'utente che si registra, gruppi (Gino.App.Auth.Group) associati all'utente registrato.

A ciascun profilo è associato un url con il form di registrazione, generato con la seguente regola: auth/registration/[ID_PROFILO]

Quando un utente inizia la procedura di registrazione e completa il format iniziale, viene creato un record del modello Gino.App.Auth.RegistrationRequest, contenente i dati inseriti dall'utente più la data ed un collegamento al profilo di registrazione. Viene inoltre inviata una mail all'utente il quale dovrà confermare l'indirizzo email cliccando sul link ricevuto.

A questo punto se è attiva l'abilitazione automatica per il profilo in questione l'utente sarà immediatamente attivo dopo aver confermato l'indirizzo email. Questo significa che viene creato un record del modello Gino.App.Auth.User, al quale vengono associati i gruppi di utenza definiti nel profilo.

Invece se l'abilitazione automatica è disabilitata l'utente dovrà attendere l'attivazione da parte di un amministratore del modulo autenticazione, il quale riceverà una email di avvertimento.

Una volta attivo l'utente può loggarsi ed effettuare le operazioni consentite ai gruppi ai quali è stato associato. Inoltre dispone di una pagina di profilo (auth/profile/) dove poter visionare e modificare alcune delle informazioni inserite, modificare la password, eliminare l'account e iscriversi ad altri profili di registrazione attivi.

In quest'ultimo caso l'utente dovrà inserire solamente ulteriori informazioni richieste da moduli associati con i nuovi profili, e verrà automaticamente abilitato.

Tutti i template delle viste e mail coinvolti nella procedura di registrazione sono editabili dalla tab 'frontend' in area amministrativa.

Moduli informazioni aggiuntive legati ad un profilo

L'eventuale modulo da associare al profilo serve per gestire informazioni addizionali. Infatti il modulo Gino.App.Auth si occupa di gestire i campi nome, cognome, username, password ed email. Se si vogliono richiedere ulteriori dati all'utente, il modulo associato dovrà implementare i metodi necessari per la generazione degli input per l'inserimento dei campi aggiuntivi, la loro validazione ed eliminazione.

In particolare, se il modulo prevede l'inserimento di dati aggiuntivi gestiti nelle proprie tabelle devono essere definiti i seguenti metodi:

Inoltre il modulo può (non obbligatoriamente) implementare dei metodi che permettono di visualizzare e modificare tali informazioni aggiuntive all'interno della pagina del profilo:

Inoltre il modulo può implementare un ulteriore metodo chiamato quando un utente vuole rimuovere il proprio account:

Procedura di recupero delle credenziali di accesso

All'url auth/dataRecovery/ è presente la procedura per il recupero delle credenziali di accesso.

L'utente deve inserire la sua email collegata all'account. A questo punto riceverà un'email con un url da seguire (valido per il mese corrente) per effettuare il recupero della password. Una volta visitato questo url verrà generata una nuova password che verrà spedita insieme allo username originale all'indirizzo email inserito precedentemente.

Ldap

L'attivazione di Ldap richiede che si impostino correttamente i parametri di connessione al Server Ldap nel file app/auth/config.ldap.php e che si crei un utente da associare a quello Ldap, che può essere:

  1. l'utente con lo username indicato nelle opzioni Ldap (nel caso dell'utente unico)
  2. un utente di gino con lo username uguale a quello dell'autenticazione Ldap (nel caso di utente non unico)

Un utente verificato da un server ldap deve essere verificato successivamente anche da gino; si possono verificare due possibilità:

  1. esiste un unico utente di gino per tutti gli utenti ldap
    • in questo caso gli utenti ldap non avranno una utenza specifica su gino ma dopo l'autenticazione ldap accederanno a gino attraverso l'utente gino ldap con username uguale a quello definito nella costante LDAP_SINGLE_USER
    • i permessi di accesso alle funzionalità saranno uguali per tutti gli utenti ldap (come definiti per l'utente con username LDAP_SINGLE_USER)
  2. esistono tanti utenti di gino quanti sono gli utenti ldap
    • la costante LDAP_SINGLE_USER deve avere valore nullo
    • creare per ogni utente ldap un utente gino ldap con lo username uguale a quello ldap

In tutti e due i casi per gli "utenti gino ldap" non è necessario impostare una password in quanto questa viene gestita dal valore della costante LDAP_AUTH_PASSWORD.

JWT

Nella classe Gino.App.Auth.auth() i seguenti metodi implementano JWT e possono essere utilizzati per l'autenticazione di applicazioni javascript.

Per maggiori informazioni su JWT vai nell'apposita sezione.

top

Sessioni

Le variabili di sessione che vengono impostate all'avvio sono:

Le variabili di sessione che vengono impostate all'autenticazione sono:

top

Librerie esterne

Le librerie esterne sono:

CKEditor (versione 4.11.2)

Editor WYSIWYG testuale e HTML per il Web. La libreria si trova nella cartella ckeditor, mentre le impostazioni personalizzate per gino si trovano nella cartella config/custom_ckeditor.

CKEditor viene gestito nel metodo Gino.CKEditor::replace() che richiama i file della libreria e quelli di personalizzazione:

L'indirizzo del progetto è https://ckeditor.com/ckeditor-4/.
La documentazione è disponibile all'indirizzo https://ckeditor.com/docs/ckeditor4/latest/index.html, mentre l'indirizzo del builder dove si può personalizzare e scaricare l'editor è https://ckeditor.com/cke4/builder.

Mobile Detect

Mobile_Detect è una classe PHP per la rilevazione delle piattaforme più popolari dei dispositivi mobili: Android, Blackberry, Opera Mini, Palm, Windows Mobile, così come quelli generici.

L'indirizzo ufficiale del progetto è http://mobiledetect.net/, quello del repository https://github.com/serbanghita/Mobile-Detect.

La classe è stata riformattata e integrata in gino come Gino.MobileDetect (class.MobileDetect.php).

reCAPTCHA

In gino sono previsti due meccanismi di controllo captcha

  1. con le librerie reCAPTCHA di google
  2. con la classe Gino.Captcha

Le librerie reCAPTCHA vengono attivate automaticamente se sono state inserite la Site Key e la Secret Key reCaptcha nelle 'Impostazioni di sistema' dell'area amministrativa.
La documentazione sulle librerie reCAPTCHA si può trovare all'indirizzo https://developers.google.com/recaptcha/.

La procedura per ottenere le chiavi è la seguente:

  1. registrarsi al servizio (https://www.google.com/recaptcha/admin)
  2. aggiungere il dominio (Add New Site) e creare le chiavi (Create Key)

A livello di visualizzazione, se c'è la necessità di ridurre il box del robot è possibile utilizzare il seguente stile:

.g-recaptcha {
	transform: scale(0.77);
	transform-origin: 0 0 0;
}

Esempio di form

$form = $gform->open('', true, $required);
...
$form .= \Gino\Input::input_label('email', 'email', $gform->retvar('email', ''), _('email'), array("required" => true));
$form .= $gform->captcha(array('form_row' => true));
$form .= \Gino\Input::input_label('submit_action', 'submit', _("iscriviti"), '', array("classField"=>"submit"));
$form .= $gform->close();

Esempio di verifica

$response_captcha = $gform->checkCaptcha($request);
if(is_string($response_captcha) or (is_bool($response_captcha) and !$response_captcha)) {
	$msg = $response_captcha;
}

Lightbox for Bootstrap

Lightbox for Bootstrap by @ashleydw (https://github.com/ashleydw/lightbox).

Fullcalendar

Fullcalendar 4.4.0 (http://fullcalendar.io/).

OpenStreetMapDrawer

It uses the leaflet (https://leafletjs.com/) library with the mapbox (https://www.mapbox.com/) tiles.

Il progetto è all'indirizzo https://github.com/otto-torino/osmdrawer.git.

leaflet / mapbox

Leaflet.Elevation (http://mrmufflon.github.io/Leaflet.Elevation/)

A Leaflet (http://leafletjs.com) plugin to view an interactive height profile of polylines lines using d3 (http://d3js.org).

leaflet-elevation.js (https://raruto.github.io/leaflet-elevation/)

A Leaflet plugin that allows to add elevation profiles using d3js.

Leaflet.fullscreen (https://github.com/Leaflet/Leaflet.fullscreen)

A HTML5 fullscreen plugin for Leaflet.

Leaflet Control Search< (https://github.com/stefanocudini/leaflet-search)

A Leaflet control that search markers/features location by custom property.
Support ajax/jsonp autocompletion and JSON data filter/remapping.

top

Plugin

In gino è presente un sistema di gestione di plugin, ovvero di interfacce a librerie che estendono le funzionalità a gino. I plugin non sono subito disponibili a tutto il sistema ma devono essere espressamente richiamati al bisogno, ad esempio

require_once PLUGIN_DIR.OS.'plugin.mpdf.php';

I plugin devono rispettare le seguenti regole:

Sono disponibili i plugin per le seguenti librerie:

Interfacce ai database

In gino la connessione e le specificità dei database sono gestite come plugin. Le librerie devono implementare l'interfaccia Gino.DbManager e vengono richiamate dalla classe Gino.Db.

phpFastCache

phpFastCache è un sistema di caching di oggetti distribuiti ad alte prestazioni e di natura generica, ma destinato ad essere utilizzato per velocizzare le applicazioni web dinamiche alleviando il carico del database.

In gino questa libreria viene utilizzata essenzialmente per gestire la cache delle query. Il file del plugin è plugin.phpfastcache.php, la classe Gino.Plugin.plugin_phpfastcache.

Il sito del progetto è http://www.phpfastcache.com/.

mPDF

mPDF è una classe PHP che permette di generare file PDF da codice HTML con Unicode/UTF-8 e supporto CJK. Il file del plugin è plugin.mpdf.php, la classe Gino.Plugin.plugin_mpdf.

La libreria viene installata con Composer: vedi https://packagist.org/packages/mpdf/mpdf.
Progetto: vedi https://github.com/mpdf/mpdf.github.io.
Documentazione: vedi https://mpdf.github.io.

Per poter utilizzare mPDF è richiesta una versione di PHP >= 5.6.

PHPMailer

Libreria che permette di inviare comodamente messaggi di posta elettronica sia come semplice testo che in formato HTML, senza dimenticare la possibilità di inviare file in allegato alle email. Il file del plugin è plugin.phpmailer.php, la classe Gino.Plugin.plugin_phpmailer.

La libreria viene installata con Composer: vedi https://packagist.org/packages/phpmailer/phpmailer.
Progetto: vedi http://phpmailer.worxware.com/.

Nella directory lib/plugin è disponibile anche il file template-html-email.php che fornisce un template di esempio per la costruzione di una email html.

Google Chart Tools

Interfaccia alla libreria Google Chart Tools che permette di generare grafici interattivi per browser e device mobili.
Il file del plugin è plugin.charts.php, la classe Gino.Plugin.plugin_charts.

L'indirizzo dove è possibile reperire le informazioni sul funzionamento della libreria è il seguente https://developers.google.com/chart/.

JWT

Json Web Token (JWT) è uno standard, abbastanza recente, uscito all’inizio del 2015, che consente al server di scrivere in un messaggio (payload) chi è l’utente loggato (e altre info di contorno), incapsulare questo messaggio all’interno di un token e darlo al client che lo utilizzerà da ora in poi per le successive chiamate al server, informandolo così di chi sta effettuando la chiamata.
Questo consentirà al server di avere già le informazioni di autenticazione direttamente nel token stesso, evitando così dover passare dal database o di usare le sessioni per memorizzare le informazioni sull’autenticazione.

L’idea che c’è alla base del Json Web Token è che dopo l’autenticazione, il server prepara un token all’interno del quale racchiude un payload in cui viene dichiarato in maniera esplicita chi è l’utente loggato. Dentro il token, oltre il payload viene inserita la firma dal server (costituto dal payload stesso criptato con la sua chiave segreta in codifica hash 256).
Il client riceve il token e se vuole sarà libero di leggere il payload contenuto ma non potrà modificarlo poiché se lo facesse il token sarà invalidato dal server. Il client dovrà comunicare al server il token ricevuto per tutte le successive chiamate in cui è richiesta l’autenticazione.
Il server riceverà il token ed estrapolerà il payload ma prima si assicurerà che il token sia stato firmato e autentificato con la sua chiave privata.
Poiché il token contiene il payload con tutte le informazioni necessarie all’autenticazione (es. iduser), il server potrà evitare di passare ogni volta dal database per verificare a quale utente corrisponde quel token (ottimo per la scalabilità).

Il file del plugin è plugin.jwt.php, la classe Gino.Plugin.plugin_jwt.

top

Debug

In gino è disponibile la modalità debug ed è possibile visualizzare le statistiche sulle query.
Per attivare queste caratteristiche occorre impostare a TRUE i valori delle costanti DEBUG e SHOW_STATS nel file configuration.php.

In particolare in modalità debug i file statici (css e javascript) non vengono uniti e minificati e non vengono inviate email all'amministratore in caso di errore di sistema; operazioni che vengono effettuate in un ambiente in produzione, ovvero col valore FALSE di DEBUG.

Errori e warning

La classe Gino.Logger gestisce il logger per errori e warning. Se la costante DEBUG definita nel file configuration.php è settata a TRUE gli errori e i warning vengono stampati a video. In caso contrario (FALSE) viene inviata una mail agli indirizzi impostati nella costante ADMIN definita sempre nel file configuration.php. L' email non viene inviata soltanto nel caso in cui l'errore sia di tipo 404.

Unione e minificazione di file

In modalità non debug i file css e javascript vengono uniti e minificati. In particolare nella directory cache/css viene creato un unico file css che raggruppa tutti i file inseriti col metodo Gino.Registry::addCss().
Per quanto riguarda i file javascript, nella directory cache/js viene creato un unico file javascript che raggruppa tutti i file caricati col metodo Gino.Registry::addJs(). I file javascript inseriti col metodo Gino.Registry::addCustomJs() vengono caricati a seguire e vengono eventualmente compressi/minificati a seconda delle opzioni che sono state loro assegnate.

Statistiche su performance e query

Le statistiche sulle performance vengono mostrate in una barra a scomparsa soltanto se è stata attivata anche la modalità debug. In particolare si tratta di statistiche di esecuzione dello script e delle query.

top

Testing

PHPUnit

Il testing viene eseguito utilizzando PHPUnit (PHP Unit Testing framework) che viene installato con Composer.
Siti interessanti sono

Per effettuare i test su tutti i file presenti nella directory tests, dalla root del sito eseguire il comando

# vendor/phpunit/phpunit/phpunit --bootstrap vendor/autoload.php tests

Per eliminare l'opzione "--bootstrap vendor/autoload.php" dal comando, nel file composer.json aggiungere:

	"autoload": {
		"psr-0": {
			"tests": ""
		}
	}
top

Cache

In gino sono implementati diversi tipi di cache:

Impostazioni

L'abilitazione di queste tipologie di cache avviene nella sezione Amministrazione -> Impostazioni di sistema; in particolare si prendono in considerazione i valori dei campi enable_cache (abilitazione cache) e query_cache (abilitazione cache delle query) della tabella sys_conf.

Nelle Impostazioni di sistema è inoltre possibile eliminare la cache; in questo caso vengono eliminati ricorsivamente i file presenti nella directory impostata come CACHE_DIR.

Cache dei contenuti

La cache dei contenuti viene gestita dalle classi Gino.Cache, Gino.OutputCache e Gino.DataCache (presenti nel file class.Cache.php), e funziona memorizzando gli output su file.

La cache può venire generata per i contenuti relativi a skin e menu.
Per le skin e il menu è infatti possibile impostare un tempo di caching dei contenuti in secondi (campo cache di sys_layout_skin e sys_menu_voices).

Per quanto riguarda le skin la cache viene generata dal metodo che crea il corpo della risposta HTTP: Gino.Document::render()

$cache = new OutputCache($buffer, $obj_skin->cache ? true : false);
if($cache->start('skin', $this->_request->path.$this->_request->session->lng.$skin->id, $skin->cache)) {
	// if($tpl->free) ...
	$cache->stop(preg_replace_callback($regexp, array($this, 'parseModules'), $tpl_content));
	// else ...
	$cache->stop($headline.$content.$footline);
}

Per quanto riguarda il menu la cache viene generata dai metodi che ne gestiscono la visualizzazione e le briciole di pane: Gino.App.Menu.menu::render(), Gino.App.Menu.menu::breadCrumbs()

$cache = new \Gino\OutputCache($buffer, $this->_cache ? true : false);
if($cache->start($this->_instance_name, "view".$sel_voice.$session->lng, $this->_cache)) {
	// ...
	$GINO = $view->render($dict);
	$cache->stop($GINO);
}

if($cache->start($this->_instance_name, "breadcrumbs".$sel_voice.$this->_registry->request->session->lng, $this->_cache)) {
	// ...
	$cache->stop($buffer);
}

Cache delle query

La cache delle query viene gestita attraverso il metodo Gino.DbManager::select() e Gino.DbManager::execCustomQuery().
La singola query può non essere salvata nella cache impostando l'opzione cache=false nei summenzionati metodi.

Ogni volta che viene richiamato un metodo con istruzioni di tipo action (INSERT, UPDATE, DELETE) tutta la cache delle query viene ripulita.

Gino.DbManager è soltanto l'interfaccia per le librerie di connessione al database; il codice è sviluppato nelle librerie che implementato l'interfaccia, ovvero ad esempio Gino.Plugin.mysql.

Parametri di configurazione

Nel file configuration.php si definiscono le impostazioni generali per la cache delle query. Queste impostazioni vengono inizializzate nella classe plugin_phpfastcache (plugin.phpfastcache.php) per poter essere utilizzate dalla libreria phpFastCache.

I parametri possono essere passati anche come opzioni direttamente al costruttore della classe plugin_phpfastcache in modo da sovrascrivere quelli generali e poter cambiare metodo di caching.

Un esempio di configurazione

\phpFastCache::setup('storage', 'memcached');
\phpFastCache::setup('server', array(array('127.0.0.1', 11211, 1)));
\phpFastCache::setup('fallback', 'files');
\phpFastCache::setup("path", '/tmp');

Caching di una pagina

È possibile utilizzare phpFastCache anche per mettere in cache una pagina, ad esempio

$cache = new \Gino\Plugin\plugin_phpfastcache(array('cache_type'=>'files', 'cache_time'=>60*60));
// keyword = Webpage_URL
$keyword_webpage = md5($_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].$_SERVER['QUERY_STRING']);
$html = $cache->get($keyword_webpage);

if($html == null) {
    ob_start();
    /*
    ALL OF YOUR CODE GO HERE
    RENDER YOUR PAGE, DB QUERY, WHATEVER
    */

    // GET HTML WEBPAGE
    $html = ob_get_contents();
    // Save to Cache 30 minutes
    $cache->set($keyword_webpage,$html, 1800);
}
echo $html;

Cache in memoria

Per utilizzare un sistema di caching di dati in memoria caricare ad esempio i risultati delle query in un array in modo da evitare l'esecuzione multipla della stessa query:

if($enable_cache and $cache_single_query and isset($container_cache[$query])) {
	return $container_cache[$query];
}
else {
	// exec query and get the results in $dbresults
	if($enable_cache and $cache_single_query) {
		$container_cache[$query] = $dbresults;
	}
}
top

Database

I driver di connessione al database disponibili in gino sono

Il database che si intende utilizzare deve essere indicato nel file configuration.php; di default è MySQL, e se si vuole utilizzare la libreria PDO:

define("DBMS", 'mysql');
define("USE_PDO", false);

Le specificità di ogni database vengono gestite in appositi file di plugin che risiedono nella directory lib/plugin (per MySQL il file è plugin.mysql.php). Questi file implementano l'interfaccia Gino.DbManager che definisce i metodi che le librerie di connessione al database dovranno implementare.

L'interfaccia Gino.DbManager e l'inclusione dei file di connessione al database si trovano nel file class.Db.php.

Query e codice SQL personalizzato

Le query e il codice SQL specifico possono venire raggruppati nel file lib/sqlcode.php. In particolare ogni query o porzione di codice SQL deve essere inserito in un metodo il cui nome, per praticità, riporta le sue coordinate ed è così composto:

[nome_classe] + _ + [nome_metodo]

Per richiamare la query o il codice occorre includere il file, istanziare la classe e richiamare il metodo passandogli i parametri necessari

include_once(LIB_DIR.OS."sqlcode.php");
$obj = new sqlcode();
$call = get_class().'_methodName';
$query = $obj->$call($param1, $param2, $param3);

Implementare le librerie di un nuovo database

Per implementare in gino un nuovo database occorre:

top

Database - Tabelle

Ogni modulo di sistema è collegato ad una o più classi, e i dati vengono salvati in specifiche tabelle del database.
Il nome di una tabella è lasciato alla fantasia del programmatore, anche se si consiglia di mantenere un nome che rievochi quello della classe.

Criteri di costruzione di una tabella

Le tabelle relative ai modelli delle applicazioni possono essere gestite in modo automatico attraverso la classe Gino.AdminTable.
I modelli delle tabelle estendono la classe Gino.Model e in particolare il modello definisce i propri campi nel metodo statico Gino.Model::columns(). Le tabelle devono essere coerenti con la definizione del modello per cui devono essere costruite seguendo specifici criteri, ad esempio:

Tabelle per accedere alle funzioni standard di gino

Per quello che riguarda le tabelle di una applicazione, il loro nome dovrà rispecchiare il valore indicato nel campo tbl_name della tabella sys_module_app.

Prendendo come esempio l'applicazione news avremo le seguenti tabelle:

news_articletabella del modello Gino.App.News.Article
news_categorytabella del modello Gino.App.News.Category
news_article_categorytabella che registra le associazioni tra le news e le categorie
news_opttabella delle opzioni dell'applicazione
top

Traduzioni

In gino la gestione delle traduzioni avviene in tre modalità, ovvero attraverso le librerie gettext e la classe Gino.Locale per quanto riguarda la traduzione delle stringhe presenti all'interno del codice, mentre le traduzioni delle stringhe salvate nei record delle tabelle vengono gestite all'interno dello stesso form.

Stringhe del codice

gettext

Le stringhe presenti nel codice e che vengono mostrate a video in generale sono trattate per essere gestite con le librerie gettext.

Utilizzando queste librerie è possibile salvare tutte le stringhe da tradurre presenti nei file di gino in un unico file. Il file generato (languages/it_IT/LC_MESSAGES/messages.po) dovrà essere duplicato e le stringhe presenti al suo interno tradotte in un'altra lingua, ad esempio l'inglese; infine questo nuovo file dovrà essere salvato in una nuova directory specifica della lingua, ovvero per l'inglese languages/en_US/LC_MESSAGES/messages.po.

I file messages.po devono essere opportunamente compilati in modo da creare dei file messages.mo.

Per creare un corretto file messages.po la stringa presente nel codice deve essere scritta nel seguente modo:

_("Stringa da tradurre")

Gino.Locale

La classe Gino.Locale dovrebbe essere utilizzata per gestire la traduzione delle stringhe presenti all'interno del codice delle applicazioni (directory app).

Utilizzando questo meccanismo all'interno dei file non ci saranno più le stringhe da tradurre ma soltanto delle chiavi identificative delle stringhe.

La corrispondenza chiave=>stringa risiede in appositi file nella struttura di directory app/[nome_app]/language/it_IT/[nome_app]_lang.php.

Nei file del controller e dei modelli le stringhe vengono richiamate nel seguente modo

$locale = Locale::instance_to_class('nome_classe_controller');
$locale->get('string_1');

Le stringhe vengono scritte nei file delle traduzioni (uno per lingua) nel seguente modo:

return array(
	'stringa_1' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
	'stringa_2' => 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
)

Stringhe dei contenuti salvati nel database

Le stringhe salvate nei record delle tabelle vengono gestite all'interno del form di inserimento/modifica del record.

Per recuperare il valore della traduzione di un campo (ad esempio del campo chiamato name) esistono due modalità, sempre collegate al Modello.
Utilizzare il metodo Gino.Model::ml()

$obj = new ModelClassName($id);
$value = $obj->ml('name');

Utilizzare direttamente l'oggetto Gino.Translation che viene già istanziato in Gino.Model nella proprietà $this->_trd

$value = $this->_trd->selectTXT(self::$table, 'name', $id);
top

Pagine

Le pagine vengono gestite nell'applicazione di sistema Pagine.

Per ogni pagina è possibile definire un template personalizzato per la visualizzazione come pagina intera (richiamato da url) e come contenitore richiamato nel template del layout.
Le proprietà della pagina devono essere inserite all'interno di doppie parentesi graffe {{ proprietà }}. Segue un esempio:

<h1>{{ title }}</h1>
{{ map|gid:2 }}
{{ text }}

Politiche di visualizzazione

Le pagine sono generalmente accessibili a tutti gli utenti a un indirizzo del tipo page/view/[slug].

Nell'interfaccia di inserimento/modifica della pagina è prevista inoltre la possibilità di limitarne la visualizzazione a specifici utenti, e in particolare:

top

Definizione dei contenuti

La quasi totalità dei contenuti viene gestita attraverso le interfacce messe a disposizione dalle applicazioni presenti nell'area amministrativa. I dati inseriti negli appositi campi vengono poi presentati all'utente nel modo che è stato definito nelle varie viste.

All'interno di questa "struttura" i campi di tipo editor permettono di personalizzare ulteriormente i contenuti utilizzando le classi CSS di Bootstrap e altre classi definite in gino.

Classi CSS da utilizzare nell'editor

Alcune classi di bootstrap:

ElementoTagClasseDescrizione
ImmagineIMGimg-responsiveRende l'immagine ridimensionabile
CodicePREprettyprintVisualizzazione del codice
VideoDIVvideo-wrapperVisualizzazione responsive di un video (iframe)

Utilizzo della classe css video-wrapper

L'esempio che segue mostra come inserire un video youtube in una pagina

<div class="video-wrapper"><iframe frameborder="0" height="400" src="https://www.youtube.com/embed/CODE" width="730"></iframe></div>

Tabelle responsive

Le tabelle responsive possono essere costruite utilizzando Bootstrap oppure gli stili del file css css/responsive-table.css.

Bootstrap

Gli esempi e la documentazione la potete trovare sul sito di bootstrap cercando table.

CSS personalizzato

Come con Bootstrap l'HTML deve essere inserito in un input form di tipo editor in modalità Source; segue un esempio:

<div class="table-responsive-vertical">
<table class="table table-striped table-hover">
	<thead>
		<tr>
			<th>Tipo</th>
			<th>Data</th>
			<th>Meta</th>
			<th>Tempo</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td data-title="Tipo">Camminata</td>
			<td data-title="Data">21/12/2016</td>
			<td data-title="Meta">Pinerolo</td>
			<td data-title="Tempo">4h</td>
		</tr>
		<tr>
			<td data-title="Tipo">Riunione</td>
			<td data-title="Data">18/08/2016</td>
			<td data-title="Meta">Torino</td>
			<td data-title="Tempo">2h</td>
		</tr>
	</tbody>
</table>
</div>

Per poter funzionare correttamente nella pagina deve essere caricato il file css css/responsive-table.css.
In gino, di default questo file viene caricato soltanto nelle pagine page/view/[slug].

$this->_registry->addCss(CSS_WWW."/responsive-table.css");

Viste

All'interno delle viste non bisogna usare la variabile $buffer.

top

Lightbox

La libreria che viene utilizzata in gino è Lightbox for Bootstrap by @ashleydw (https://github.com/ashleydw/lightbox).

Condizione necessaria per l'utilizzo di questa libreria è che vengano caricati i seguenti file, inserendoli ad esempio nel metodo che richiama la vista:

$this->_registry->addJs(SITE_JS."/lightbox/dist/ekko-lightbox.js");
$this->_registry->addCss(CSS_WWW."/lightbox/dist/ekko-lightbox.css");

Then simply add data-toggle to your anchor tags.

<a href="image-path" data-toggle="lightbox" data-gallery="gallery">
	<img src="image-path" class="img-fluid rounded" />
</a>

Place this near on your page, probably near the end of the body section:

<script>
$(document).on("click", '[data-toggle="lightbox"]', function(event) {
	event.preventDefault();
	$(this).ekkoLightbox();
});
</script>
top

Modali

Le modali vengono costruite utilizzando il Modal Component di Bootstrap (http://getbootstrap.com/customize/).

La Modale può essere costruita in modo da richiamare sul momento il contenuto con una chiamata ajax oppure in modo da rendere visibile il contenuto che è già stato parserizzato nell'html ma che non è visibile.

Esempio di collegamento a una Modale con chiamata ajax

$url = $this->_registry->router->link($this->_controller->getInstanceName(), 'point', ['id' => 'slug-page', 'ajax' => 1]);
$modal_link = "<a href=\"".$url."\" class=\"modal-overlay\" data-type=\"ajax\" data-esc-close=\"true\" data-overlay=\"false\" data-title=\""._("Title Modal Page")."\">";

Esempio di collegamento a una Modale che è già stata costruita in modo nascosto nell'html

$modal = new \Gino\Modal([
	'modal_id' => 'ModalCenter'.$this->id,
	'modal_title_id' => 'ModalCenterTitle'.$this->id,
]);
$link = $modal->trigger('github.jpg', []);
// or
// $link = $modal->trigger(\Gino\icon('export'), ['tagname' => 'span']);

$link .= $modal->render("Media", $modal_body);

Il link generato avrà questa forma

<button type="button" class="btn btn-secondary btn-sm" data-toggle="modal" data-target="#fileModalCenter">github.jpg</button>
// case 2
<span class="" data-toggle="modal" data-target="#ModalCenter5">
	<span class="icon fa fa-save icon-tooltip" title=""></span>
</span>

Modalità di utilizzo in gino

Case 1: il contenuto viene caricato dinamicamente recuperando i dati da una sorgente esterna

Nel codice definire le variabili necessarie per la Modale

$modal = new Modal(['modal_id' => "myModal", 'modal_title_id' => 'myModalTitle']);
$modal->setModalTrigger('newClassName'); // if you want overwrite the default name
$this->_view->assign('trigger_modal', $modal->getModalTrigger());
$this->_view->assign('script_modal', $modal->loadDinamycData($link_modal));
$this->_view->assign('render_modal', $modal->render('Modal Title', null));

Nella vista impostare il link per l'apertura della modale, inserire la renderizzazione della modale e lo script per caricare dinamicamente i contenuti

<span class="icon fa fa-download fa-2x <?= $trigger_modal ?> link"></span> 

<?= $render_modal ?>
<?= $script_modal ?>

Case 2: la modale è già definita nella pagina (trigger link e modale)

Esempio tipico di preview di una immagine, tipo Lightbox (see Gino.Input::input_file()))

$modal = new \Gino\Modal([
	'modal_id' => $name.'ModalCenter',
	'modal_title_id' => $name.'ModalCenterTitle',
]);
$value_link = $modal->trigger($value, ['class' => 'btn btn-secondary btn-sm']);
$value_link .= $modal->render(_("Media"), "<img src=\"path_to_image\" />");
top

CSS

I file CSS del core di gino sono definiti nel file config/common.inc e vengono caricati attraverso il metodo Gino.Document::setHeadVariables():

SITE_JS."/bootstrap-4.1.3/css/bootstrap.min.css",
CSS_WWW."/styles.css",
CSS_WWW."/jquery-ui.min-1.12.1.css",
CSS_WWW."/jquery-ui.min-1.12.1-update.css",

In particolare il file styles.css viene generato dalla compilazione di file less.

Alcune regole per gestire gli stili

CSS delle Viste

Per personalizzare il css della vista di una applicazione conviene modificare i file css specifici dell'applicazione, quale ad ad esempio app/news/news.css.

Stili generali

Per la definizione degli stili generali del sito conviene invece lavorare sulle classi di bootstrap presenti nella directory css/less.

In questo caso occorre tenere presente le seguenti considerazioni

Il file che raggruppa tutti gli stili e le definizioni presenti nella directory less è styles.css. Questo file viene generato utilizzando un compilatore less che deve richiamare il file principale less/styles.less.
Una volta generato, il file styles.css deve essere copiato nella directory css.

$ lessc styles.less > style.css

Installazione compilatore less

L'installazione con MacOSX richiede che venga installato anche nodejs

top

Briciole di pane

In gino sono presenti due tipologie di briciole di pane, quelle collegate al menu e quelle generiche (Gino.BreadCrumbs).

Breadcrumbs generiche

All'interno di ogni vista deve essere definita la sequenza degli elementi come array di array; segue un esempio:

array(
	array('label' => _("Gallerie"), 'link' => $this->link('gallery', 'index')),
	array('label' => $category->ml('name'), 'current' => true)
)

Per ogni elemento possono essere definite le seguenti chiavi:

Esempio di utilizzo

1. Ricavare le briciole di pane per passarle come variabile alla vista

$obj = new \Gino\BreadCrumbs($this->_class_name);
$obj->setItems(array(
	array('label' => _("Gallerie"), 'link' => $this->link('gallery', 'index')),
	array('label' => $category->ml('name'), 'current' => true)
));
$breadcrumbs = $obj->render();

2. nel file della vista

<? if($breadcrumbs): ?>
	<?= $breadcrumbs ?>
<? endif ?>
<section id="gallery-category">
	...
</section>
top

Web Service Restful

Con gino è possibile realizzare un Web Service con l'approccio REST (REpresentational State Transfer).
Utilizzando il metodo Gino.Controller::REST() è possibile accedere ad una rappresentazione della risorsa identificata dall'URI (eseguendo implicitamente il metodo GET); non è possibile mappare le azioni 'CRUD' sui metodi HTTP.

top

Ricerca

In gino è possibile utilizzare tre tipi di ricerca

Ricerca fulltext pesata sulle tabelle - Modulo di ricerca nel sito

Il modulo mette a disposizione una interfaccia di ricerca nel sito (Gino.App.SearchSite::form). Per poter attivare la ricerca occorre eseguire le seguenti operazioni:

Funzione replace_ci:

DELIMITER $$

DROP FUNCTION IF EXISTS `replace_ci`$$
CREATE FUNCTION `replace_ci` ( str TEXT,needle CHAR(255),str_rep CHAR(255))
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE return_str TEXT;
SELECT replace(lower(str),lower(needle),str_rep) INTO return_str;
RETURN return_str;
END$$

DELIMITER ;

Ricerca nelle interfacce utente

La ricerca nelle interfacce utente può essere gestita sfruttando la classe Gino.SearchInterface.

Creare nel modello due metodi statici per definire l'insieme dei campi dei form di ricerca e le condizioni di ricerca dei record

L'elenco dei campi di ricerca da passare al costruttore è un array di elementi, nel quale le chiavi sono i nomi dei parametri di ricerca input form e i valori sono degli array che comprendono le opzioni necessarie per costruire gli input form. Il nome dell'input form viene creato unendo il valore dell'opzione before_input del costruttore e il nome del parametro.

Le opzioni valide per ogni tipo di campo di ricerca sono:

Seguono due esempi dei metodi.

public static function setSearchFields($controller, $fields) {

	$search_fields = array(
		'category' => array(
			'label' => _('Categoria'),
			'input' => 'select',
			'data' => Category::getForSelect($controller),
			'type' => 'int',
			'options' => null
		),
		'name' => array(
			'label' => _('Nome'),
			'input' => 'text',
			'type' => 'string',
			'options' => null
		),
		'code' => array(
			'label' => _('Codice'),
			'input' => 'text',
			'type' => 'string',
			'options' => array('size' => 8)
		),
		'date_from' => array(
			'label' => _('Da'),
			'input' => 'date',
			'type' => 'string',
			'options' => null
		),
		'date_to' => array(
			'label' => _('A'),
			'input' => 'date',
			'type' => 'string',
			'options' => null
		)
	);

	$array = array();
	if(count($fields)) {
		foreach($fields AS $field)
		{
			if(array_key_exists($field, $search_fields)) {
				$array[$field] = $search_fields[$field];
			}
		}
	}
	return $array;
}
public static function setConditionWhere($controller, $options = null) {

	$category = \Gino\gOpt('category', $options, null);
	$name = \Gino\gOpt('name', $options, null);
	$code = \Gino\gOpt('code', $options, null);
	$date_from = \Gino\gOpt('date_from', $options, null);
	$date_to = \Gino\gOpt('date_to', $options, null);

	$where = array("instance='".$controller->getInstance()."'");

	if($category) {
		$where[] = "category='$category'";
	}
	if($name) {
		$where[] = "name LIKE '%".$name."%'";
	}
	if($code) {
		$where[] = "code LIKE '%".$code."%'";
	}
	if($date_start) {
		$where[] = "insertion_date >= '".$date_start."'";
	}
	if($date_from) {
		$where[] = "insertion_date >= '".$date_from."'";
	}
	if($date_to) {
		$where[] = "insertion_date <= '".$date_to."'";
	}

	return implode(' AND ', $where);
}

2. Impostare i valori degli eventuali parametri passati attraverso un url

Il formato degli elementi è [field_name]=>[field_value], dove field_name deve corrispondere a una chiave dell'elenco dei campi di ricerca (proprietà $_fields).

$param_values = array(
	'category' => $ctg_id,
	'date_from' => $date_from ? \Gino\dbDateToDate($date_from) : null,
);

3. Istanziare la classe

Prima di istanziare la classe impostare i campi da mostrare nel form di ricerca:

$search_fields = ModelItem::setSearchFields($this, array('category', 'name', 'code', 'date_from', 'date_to'));
Loader::import('class', array('\Gino\SearchInterface'));
$obj_search = new \Gino\SearchInterface($search_fields, array(
	'identifier' => 'appSearch'.$this->_instance,
	'param_values' => $param_values
));

4. Impostare le chiavi di ricerca in sessione

$obj_search->sessionSearch();

Le fasi 3 e 4 possono venire riunite in un unico metodo nel controllore:

private function getObjectSearch($fields, $options=array()) {

	$search_fields = ModelItem::setSearchFields($this, $fields);
	Loader::import('class', array('\Gino\SearchInterface'));

	$obj_search = new \Gino\SearchInterface($search_fields, $options);
	$obj_search->sessionSearch();
	return $obj_search;
}

In questo caso per ottenere l'oggetto Gino.SearchInterface basterà richiamare il metodo:

$obj_search = $this->getObjectSearch(array('category', 'name', 'code', 'date_from', 'date_to'), array(
	'identifier' => 'appSearch'.$this->_instance,
	'param_values' => $param_values
));

5. Ottenere il risultato di una ricerca.

Per recuperare il risultato di una ricerca occorre prima recuperarne i valori, considerando che i valori provenienti da url sovrascrivono quelli salvati in sessione:

$search_values = $obj_search->getValues();

I valori vengono poi utilizzati nella definizione delle condizioni della query:

$conditions = array(
	'category' => array_key_exists('category', $search_values) ? $search_values['category'] : null,
	'name' => $search_values['name'],
	'code' => $search_values['code'],
	'date_from' => \Gino\dateToDbDate($search_values['date_from'], '/'),
	'date_to' => \Gino\dateToDbDate($search_values['date_to'], '/'),
);

Segue un esempio classico in gino:

$items_number = ModelItem::getCount($this, $conditions);
$paginator = Loader::load('Paginator', array($items_number, $this->_ifp));
$limit = $paginator->limitQuery();

$where = ModelItem::setConditionWhere($this, $conditions);
$items = ModelItem::objects($this, array('where' => $where, 'limit' => $limit, 'order' => 'insertion_date DESC'));

6. Nel dizionario della vista impostare il form e l'apertura del form

$dict = array(
	...
	'search_form' => $obj_search->formSearch($this->link($this->_instance_name, 'archive'), 'form_search_app'),
	'open_form' => $obj_search->getOpenform(),
);

Nella vista

// 1
<h1>
	<?= _('Items') ?>
	<a style="margin-left: 20px" class="fa fa-rss" href=""></a>
	<span class="fa fa-search link" onclick="if($('app_form_search').style.display=='block') $('app_form_search').style.display='none'; else $('app_form_search').style.display='block';"></span>
</h1>
<div id="app_form_search" style="display: <?= $open_form ? 'block' : 'none'; ?>;">
	<?= $search_form ?>
</div>
// 2
<h1>
	<?= _('Items') ?>
	<a style="margin-left: 20px" class="fa fa-rss" href="<?= $feed_url ?>"></a>
	<span class="fa fa-search link" onclick="$('app_form_search').toggleClass('hidden')"></span>
</h1>
<div id="app_form_search" class="<?= $open_form ? '' : 'hidden' ?>">
	<?= $search_form ?>
</div>

Con due form nella stessa pagina:

<h1><?= _('Items') ?>
<span class="fa fa-search link" onclick="$('app_form_search').toggleClass('hidden');$('app_form_search2').addClass('hidden')"></span>
<span class="icon fa fa-file-pdf-o icon-tooltip link black transition" onclick="$('app_form_search2').toggleClass('hidden');$('app_form_search').addClass('hidden');"></span>
</h1>
<div id="app_form_search2" class="<?= $open_form2 ? '' : 'hidden' ?>">
	<?= $search_form2 ?>
</div>
<div id="app_form_search" class="<?= $open_form ? '' : 'hidden' ?>">
	<?= $search_form ?>
</div>

Ricerca nelle interfacce utente con input select collegati tra loro con eventi onChange

La ricerca nelle interfacce utente che comprendono input select collegati tra loro con eventi onChange può essere gestita con la classe Gino.SearchEventInterface.
La modalità di utilizzo di questa classe in una interfaccia è descritta nel file lib/classes/class.SearchEventInterface.php.

top

Amministrazione

Nella parte amministrativa sono presenti due sezioni: Amministrazione sistema e Ammnistrazione moduli.

Nella sezione Amministrazione sistema risiedono le funzionalità proprie di gino, ovvero quei moduli che sono parte integrante del CMS e che non possono essere disinstallati.

ImpostazioniPrincipali impostazioni di sistema
LingueGestione delle lingue disponibili per le traduzioni
Moduli di sistemaModifica, installazione e rimozione dei moduli di sistema
ModuliModifica, installazione e rimozione dei moduli di classi istanziate e moduli funzione
Creazione appGenera applicazioni (Moduli) pronte per essere personalizzate e installate in gino
StatisticheStatistiche degli accessi all'area privata
LayoutGestione di css, template, skin ed assegnazione a indirizzi o classi di indirizzi
Header e FooterGestione personalizzata degli header e footer del sistema
AllegatiArchivi di file con struttura ad albero
PaginePagine html con struttura ad albero
Ricerca nel sitoForm di ricerca nel sito
StrumentiAlcuni strumenti, quali l'elenco delle risorse disponibili (con i relativi link) e dei mime type
AutenticazioneModulo utenti, gruppi e permessi

Nella sezione Amministrazione moduli risiedono le applicazioni aggiuntive e alcuni moduli che gino mette subito a disposizione. A questo proposito vedi la pagina sui moduli per il layout.

Menu principaleMenu principale
Menu amministrazioneMenu area amministrativa
Top BarBarra superiore con scelta lingua ed autenticazione
Top Bar AdminBarra superiore con link diretto aall'amministrazione dei singoli moduli
top

Impostazioni di sistema

Nella sezione Impostazioni dell'area amministrativa si possono impostare i seguenti parametri

gestione multilinguagestione multilingua del sito (default 'no')
lingua di default
log accessi
metodo criptazione passwordalgoritmo crittografico di hashing utilizzato per le password degli utenti (default 'md5')
email amministratore sito
email invio automatico comunicazioni
gestione mobile
abilitazione cache
contenuto meta tag titletitolo del sito che comparirà nel tag title
contenuto meta tag description
contenuto meta tag keywords
contenuto file robots
codice google analytics
site key reCAPTCHAchiave pubblica per l'utilizzo delle librerie reCAPTCHA di google); se le chiavi non vengono inserite il sistema utilizzerà una classe captcha interna. Si consiglia di attivare il servizio (vedi reCAPTCHA)
secret key reCAPTCHAchiave privata per l'utilizzo delle librerie reCAPTCHA di google
chiave pubblica ShareThisse viene inserito il codice ShareThis, le condivisioni social vengono gestite da quest'ultimo (https://platform.sharethis.com/) attraverso la funzione Gino.shareAll (lib/func.php); in caso contrario le condivisioni si appoggiano ad AddToAny (https://www.addtoany.com/)
Disqus shortname
top

Appendici

Warning "Page Has Expired"

Per maggiori informazioni http://shiflett.org/articles/how-to-avoid-page-has-expired-warnings.

Il messaggio di avviso dichiara che una pagina è scaduta. Non solo ci sono due cause differenti per questo avviso, ma il messaggio di avvertimento varia anche da browser a browser, anche nelle stesse circostanze.

L'avvertimento è il risultato di utenti che utilizzano il meccanismo di cronologia del browser per accedere a una pagina già vista, di solito facendo clic sul pulsante back o effettuando un refresh. Una causa del warning è che la pagina è scaduto dalla cache del browser, e in questo modo vuole informare l'utente che deve richiedere nuovamente la pagina, nel caso in cui questo non sia indesiderato.

Ma c'è ancora un altro scenario che può causare un warning page has expired. Quando una pagina nella history (compresa la pagina corrente) è richiesta con il metodo POST, il browser avvisa l'utente prima di richiedere nuovamente la pagina, perché una richiesta POST potrebbe eseguire una certa azione (resubmitting data).

Per evitare il messaggio si possono adottare un paio di soluzioni:

  1. Utilizzare un form GET. Dipende da quello che si sta facendo, ma questa non è sempre una buona soluzione in quanto ci sono ancora restrizioni di dimensione su una richiesta GET. Inoltre le informazioni vengono trasmesse nella querystring, che non è la più sicura delle opzioni.
  2. Eseguire un redirect lato server a una pagina differente dopo il form POST.

Esempio di soluzione in gino

In gino il warning si può incontrare quando si effettua una ricerca con un form POST. Il risultato della ricerca generalmente produce una pagina con un elenco di elementi. Passare da questa a una pagina di dettaglio e successivamente tornare indietro col back produce la pagina col warning.

Una soluzione consiste nel salvare le chiavi di ricerca in sessione per poterle poi recuperare.
Il form di ricerca non avrà come action il metodo che stampa i risultati (ex. view()) ma il metodo che salva le chiavi di ricerca (ex. search()), il quale a sua volta dovrà reindirizzare al metodo che stampa i risultati.

L'indirizzo della action del form di ricerca

$this->link($this->_instance_name, 'search')

Il metodo che salva le chiavi della ricerca e reindirizza alla pagina finale

public function search(\Gino\Http\Request $request) {

	if($request->POST)
	{
		$submit = \Gino\cleanVar($request->POST, 'search_submit', 'string');

		if($submit)	// Salva le variabili in sessione
		{
			// old version
			$this->setSessionSearch(array(
				'input_search1',
				'input_search2',
			));

			// VERIFICARE CON LA NUOVA CLASSE
			Loader::import('class', array('\Gino\SearchInterface'));
			$obj_search = new \Gino\SearchInterface($search_fields, array('identifier' => 'pressReviewSearch'.$this->_instance));
			$obj_search->sessionSearch();
		}
	}
	$url = $this->link($this->_instance_name, 'view');
	$response = new \Gino\Http\Redirect($url);
	return $response;
}
top