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:
gino è stato sviluppato su un ambiente LAMP e richiede un Server Web, un Database Server e il linguaggio di programmazione 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
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.
Il Web Server preferito di gino è Apache, tuttavia gino è stato testato anche su altri web server quali Internet Information Server (IIS) e Nginx.
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
Nel caso in cui si utlizzi il Web Server IIS il file preposto alla conversione dell'indirizzo richiesto è web.config.
Attualmente in gino sono implementati i connettori per i seguenti database
Il database predefinito è MySQL nella versione mysql>=5.6.
Per installare gino seguire la seguente procedura semplificata.
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
Il pacchetto deve essere scompattato in una directory servita dal server web.
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
Impostare i parametri base del sito nel file configuration.php
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
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.
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.
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.
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.
Per installare le dipendenze definite per il progetto:
$ composer install
Per aggiornare le dipendenze alla loro utima versione:
$ composer update $ composer update monolog/monolog [...]
I pacchetti che attualmente potrebbero venire installati e utilizzati in gino sono:
mPDF is a PHP class to generate PDF files from HTML with Unicode/UTF-8 and CJK support.
$ composer require mpdf/mpdf
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 is the PHP Unit Testing framework.
$ composer require --dev phpunit/phpunit ^6.5
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.
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.
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:
Label | Regole | Sessione | Regexp / Url |
---|---|---|---|
Home Mobile | sessione+regexp | L_mobile=1 | #^index.php$# |
Pagine Mobile | sessione+regexp | L_mobile=1 | #.*# |
Pagina Autenticazione | url | index.php?evt[auth.login] | |
_popup | regexp | #&_popup=1# | |
Pagine Amministrazione | regexp | #evt\[\w+.((manage)|(wrapper))\w*\]# | |
Home Amministrazione | url | index.php?evt[index.admin_page] | |
Pagine Pubbliche | regexp | #evt\[(?!index)# | |
Pagine Private | regexp | #evt\[(?!index)# | |
Home Pubblica | regexp | #(index.php(\?evt\[index.index_page\])?[^\[\]]*)?$# | |
Home Privata | regexp | #(index.php(\?evt\[index.index_page\])?[^\[\]]*)?$# | |
Default | regexp | #^.*$# |
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.
gino integra i namespace, schematizzati nella tabella che segue.
Nome | Directory | Descrizione |
---|---|---|
Gino.App | app | Namespace dei moduli applicativi. La classe controller e le classi modello della stessa applicazione avranno lo stesso namespace Gino.App[.NomeClasseController] |
Gino.Plugin | lib/plugin | Namespace dei plugin |
Gino.Http | lib/classes/http | Namespace delle risposte HTTP |
Gino.Exception | lib/classes/exception | Namespace delle eccezioni |
Gino | lib/classes lib/classes/mvc lib/classes/fields | Namespace base di gino |
Il registro è un Gino.Singleton che può conservare dati accessibili e modificabili da tutte le classi di gino.
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().
Con questo metodo vengono caricati i file javascript del core di gino. Vengono caricati prima degli altri file javascript.
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.
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.
Questo metodo viene usato per caricare i file javascript in stile html.
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:
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
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 |
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
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' ];
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",
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>
Il file bootstrap.min.js viene generato all'indirizzo http://getbootstrap.com/customize/.
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) );
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.
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
|
Nella tabella che segue sono elencati i tipi di campo con le loro specifiche opzioni.
Tipo di campo | Opzioni specifiche |
---|---|
BooleanField |
|
CharField |
|
DateField | |
DatetimeField |
|
DirectoryField |
|
EmailField |
|
EnumField |
|
FileField |
|
FloatField | |
ForeignKeyField |
|
ImageField |
Opzioni di FileField (extensions, types_allowed) +
|
IntegerField | |
ManytoManyField |
|
ManytoManyThroughField |
|
MulticheckField |
|
SlugField |
|
TagField |
|
TextField |
|
TimeField | |
YearField |
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.
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.
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 Widget | Retrieve value | Value |
---|---|---|
CheckboxWidget | - | $this->_value |
ConstantWidget | - | $this->_value_input |
DatetimeWidget | retvar | $this->_value_retrieve |
DateWidget | retvar | $this->_value_retrieve |
EditorWidget | retvar | $this->_value_retrieve |
EmailWidget | retvar | $this->_value_retrieve |
FileWidget | - | $this->_value_input |
FloatWidget | retvar | $this->_value_retrieve |
HiddenWidget | - | $this->_value_input |
ImageWidget | - | $this->_value_input |
MulticheckWidget | - | $this->_value |
PasswordWidget | retvar | $this->_value_retrieve |
RadioWidget | retvar | $this->_value_retrieve |
SelectWidget | - | $this->_value |
TextareaWidget | retvar | $this->_value_retrieve |
TextWidget | retvar | $this->_value_retrieve |
TimeWidget | retvar | $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] ) );
In ogni classe che estende Gino.Widget il metodo printInputForm() richiama il proprio specifico input dalla classe Gino.Input.
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 CLASSES | WIDGET | BUILD CLASSES | BUILD OPTIONS | CLEAN FUNCTION (nel file func.var.php) | CLEAN OPTIONS |
---|---|---|---|---|---|
BooleanField | text | BooleanBuild | - | clean_bool | |
CharField | text | CharBuild | typeoftext (text|html) | clean_text (default) | clean_html | clean_text: escape (default true) clean_html: escape (default true) strip_tags (default null) strip_embedded (default false) allowable_tags (default null) |
DateField | date | DateBuild | - | clean_date (-> clean_text) | typeofdate (date|datetime, default 'date') separator (default '/') |
DatetimeField | datetime, null | DatetimeBuild | - | clean_date (-> clean_text) | |
DirectoryField | text | DirectoryBuild | - | clean_text | |
EmailField | EmailBuild | - | clean_email (-> clean_text) | ||
EnumField | radio | EnumBuild | - | clean_int (default) | clean_text | |
FileField | file | FileBuild | - | -- personalizzato | |
FloatField | float | FloatBuild | - | clean_float | |
ForeignKeyField | select | ForeignKeyBuild | - | clean_int | |
ImageField | image | ImageBuild | - | -- extend FileBuild | |
IntegerField | text | IntegerBuild | - | clean_int | |
ManyToManyField | multicheck | ManyToManyBuild | - | clean_array | datatype (int|string|float|bool, default 'int') asforminput (default true) |
ManyToManyThroughField | unit | ManyToManyThroughBuild | -- passa attraverso Gino.ModelForm::m2mThroughAction() | ||
MulticheckField | multicheck | MulticheckBuild | - | clean_array | asforminput false |
SlugField | text | SlugBuild | - | clean_text | |
TagField | - | TagBuild | - | clean_text | |
TextField | textarea (default), editor | TextBuild | widget (textarea|editor), typeoftext (text|html) | clean_text (default) | clean_html (con widget 'editor' o typeoftext 'html') | |
TimeField | time | TimeBuild | - | clean_time | |
YearField | text | YearBuild | - | 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;
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')));
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();
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 ]);
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.
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.
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].
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
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(...)])]) );
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'] );
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') ), );
Le opzioni che possono essere associate a ciascun campo sono:
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:
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.
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).
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() ]);
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']);
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}
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");
Questi strumenti sono di complemento e di aiuto alla costruzione di una pagina.
Potete trovare altri strumenti di complemento nei plugin.
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));
Gli errori vengono gestiti in due modi:
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().
throw new \Exception(error_string); throw new \Gino\Exception\Exception404();
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);
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() ?>" />
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.
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.
L'installazione di un modulo può essere effettuata in modo facilitato attraverso la selezione di un pacchetto di installazione o in modo manuale.
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.
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:
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"
Per eseguire l'installazione manuale effettuare il submit del form prendendo come riferimento il file config.txt.
In seguito effettuare la procedura indicata:
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".
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
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.
I Moduli di sistema installati di default sono
Impostazioni | sysconf | Impostazioni di sistema |
Lingue | language | Gestione delle lingue |
Moduli di sistema | sysClass | Gestione delle classi fondamentali del sistema |
Moduli | module | Creazione di nuovi moduli come istanze di classi o funzioni presenti nel sistema |
Creazione app | buildapp | Genera applicazioni (Moduli) pronte per essere personalizzate e installate in gino |
Utenti | user | Gestione utenti |
Statistiche | statistics | Resoconto degli accessi al sistema da parte degli utenti registrati |
Layout | layout | Gestione del layout (css, template, skin) |
Header e Footer | graphics | Gestione header e footer |
Allegati | attachment | Gestione allegati, ovvero di file che possono essere utilizzate come normali risorse all'interno del sito |
Menu | menu | Gestione menu |
Pagine | page | Gestione pagine |
Index | index | |
Ricerca nel sito | searchSite | Modulo di ricerca nel sito |
phpModuleView | phpModuleView | Modulo attraverso il quale è possibile istanziare un nuovo modulo contenente codice PHP |
Strumenti | instruments | Alcuni strumenti, quali l'elenco delle risorse disponibili (con i relativi link) e dei mime type |
Autenticazione | auth | Modulo utenti, gruppi e permessi |
Funzioni di sistema | sysfunc | Funzioni di sistema |
Post | post | Gestione articoli, post |
Calendar | calendar | Calendario appuntamenti |
I Moduli predefiniti creati come istanze di classi sono
Menu amministrazione |
Menu principale |
Top Bar |
Top Bar Admin |
Articoli |
Calendario |
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:
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.
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.
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; ?>
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 '' a far fallire l'eval.
I placeholder del template vengono parserizzati dal metodo parseTpl che ritorna le corrispondenti porzioni di template oppure l'output di pagine o applicazioni.
Il metodo parseTpl elabora il placeholder per identificare il tipo di elemento in modo da poterne recuperare il rispettivo contenuto.
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
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).
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) |
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=\"\" />");
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
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).
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.
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.
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.
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.
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.
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.
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.
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à.
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).
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.
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).
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.
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:
public function formAuthRegistration(\Gino\Form $formobj)
public function actionAuthRegistration(\Gino\Http\Request $request, \Gino\Form $formobj)
TRUE
in caso di successo oppure un array con codice di errore, es. array('error' => 'il dato richiesto non è corretto')
Inoltre il modulo può (non obbligatoriamente) implementare dei metodi che permettono di visualizzare e modificare tali informazioni aggiuntive all'interno della pagina del profilo:
public function authProfile()
public function updateAuthProfile()
Inoltre il modulo può implementare un ulteriore metodo chiamato quando un utente vuole rimuovere il proprio account:
public function deleteAuthAccount()
$request->user
)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.
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:
Un utente verificato da un server ldap deve essere verificato successivamente anche da gino; si possono verificare due possibilità:
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.
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.
Le variabili di sessione che vengono impostate all'avvio sono:
Le variabili di sessione che vengono impostate all'autenticazione sono:
Le librerie esterne sono:
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 è 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).
In gino sono previsti due meccanismi di controllo 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:
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; }
$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();
$response_captcha = $gform->checkCaptcha($request); if(is_string($response_captcha) or (is_bool($response_captcha) and !$response_captcha)) { $msg = $response_captcha; }
Lightbox for Bootstrap by @ashleydw (https://github.com/ashleydw/lightbox).
Fullcalendar 4.4.0 (http://fullcalendar.io/).
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.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.
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:
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 è 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 è 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.
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.
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/.
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.
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.
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.
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.
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.
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": "" } }
In gino sono implementati diversi tipi di cache:
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.
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); }
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.
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');
È 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;
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; } }
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.
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);
Per implementare in gino un nuovo database occorre:
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.
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:
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_article | tabella del modello Gino.App.News.Article |
news_category | tabella del modello Gino.App.News.Category |
news_article_category | tabella che registra le associazioni tra le news e le categorie |
news_opt | tabella delle opzioni dell'applicazione |
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.
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")
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', )
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);
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 }}
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:
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.
Alcune classi di bootstrap:
Elemento | Tag | Classe | Descrizione |
---|---|---|---|
Immagine | IMG | img-responsive | Rende l'immagine ridimensionabile |
Codice | PRE | prettyprint | Visualizzazione del codice |
Video | DIV | video-wrapper | Visualizzazione responsive di un video (iframe) |
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>
Le tabelle responsive possono essere costruite utilizzando Bootstrap oppure gli stili del file css css/responsive-table.css.
Gli esempi e la documentazione la potete trovare sul sito di bootstrap cercando table.
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");
All'interno delle viste non bisogna usare la variabile $buffer.
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>
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>
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 ?>
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\" />");
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
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.
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
L'installazione con MacOSX richiede che venga installato anche nodejs
$ which npm /usr/local/bin/npm
$ sudo npm install less -g
$ which lessc /usr/local/bin/lessc
$ lessc example.less > example.css
In gino sono presenti due tipologie di briciole di pane, quelle collegate al menu e quelle generiche (Gino.BreadCrumbs).
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:
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>
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.
In gino è possibile utilizzare tre tipi di ricerca
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 ;
La ricerca nelle interfacce utente può essere gestita sfruttando la classe Gino.SearchInterface.
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); }
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, );
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 ));
$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 ));
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'));
$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="= $feed_url ?>"></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>
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.
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.
Impostazioni | Principali impostazioni di sistema |
Lingue | Gestione delle lingue disponibili per le traduzioni |
Moduli di sistema | Modifica, installazione e rimozione dei moduli di sistema |
Moduli | Modifica, installazione e rimozione dei moduli di classi istanziate e moduli funzione |
Creazione app | Genera applicazioni (Moduli) pronte per essere personalizzate e installate in gino |
Statistiche | Statistiche degli accessi all'area privata |
Layout | Gestione di css, template, skin ed assegnazione a indirizzi o classi di indirizzi |
Header e Footer | Gestione personalizzata degli header e footer del sistema |
Allegati | Archivi di file con struttura ad albero |
Pagine | Pagine html con struttura ad albero |
Ricerca nel sito | Form di ricerca nel sito |
Strumenti | Alcuni strumenti, quali l'elenco delle risorse disponibili (con i relativi link) e dei mime type |
Autenticazione | Modulo 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 principale | Menu principale |
Menu amministrazione | Menu area amministrativa |
Top Bar | Barra superiore con scelta lingua ed autenticazione |
Top Bar Admin | Barra superiore con link diretto aall'amministrazione dei singoli moduli |
Nella sezione Impostazioni dell'area amministrativa si possono impostare i seguenti parametri
gestione multilingua | gestione multilingua del sito (default 'no') |
lingua di default | |
log accessi | |
metodo criptazione password | algoritmo 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 title | titolo del sito che comparirà nel tag title |
contenuto meta tag description | |
contenuto meta tag keywords | |
contenuto file robots | |
codice google analytics | |
site key reCAPTCHA | chiave 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 reCAPTCHA | chiave privata per l'utilizzo delle librerie reCAPTCHA di google |
chiave pubblica ShareThis | se 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 |
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:
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; }