Il leggendario CGIDEV2

Transcript

Il leggendario CGIDEV2
Il leggendario CGIDEV2
Terzo articolo
di Giovanni Battista Perotti
Il sussidiario di CGIDEV2
Per fare in modo che i lettori possano più comodamente
riprodurre - ed eventualmente estendere – gli esempi riportati in
questi articoli, abbiamo creato un Sussidiario, composto dalla
libreria SUSSIDEV2 e dalla cartella IFS /sussidev2 contenenti il
materiale esemplificativo utilizzato in questi articoli.
Il Sussidiario si scarica dalla pagina
http://easy400.coraltree.co.uk/sussidev2-3.zip .
Un-zippando il file ricevuto si ottiene un file di salvataggio ed un
testo Readme che spiega come installare il tutto e farlo funzionare
su AS/400.
E’ richiesto almeno il rilascio V5R2.
Questo Sussidiario viene aggiornato ad ogni nuovo articolo.
Dopo i primi due articoli di questa serie ci aspettiamo che il lettore
sappia ormai che cosa è il protocollo CGI, in che cosa consista la nonpersistenza dei programmi CGI e come CGIDEV2 utilizzi l’HTML
esterno. Il lettore ormai dovrebbe avere qualche dimestichezza con
l’HTML ed il CSS, dovrebbe essere in grado di scrivere con CGIDEV2
un semplice programma RPG-ILE per la immissione di dati e dovrebbe
aver letto qualcosa sul linguaggio Javascript.
In questo terzo articolo intendiamo fare un piccolo esempio di
Javascript, illustrare un programma CGI che gestisce un “subfile” e
spiegare un metodo per emettere quelle finestrelle chiamate pop up.
Un esempio di Javascript
Javascript è un linguaggio fondato su oggetti, che consente di fornire agli
script HTML funzioni elaborative locali progettate dallo sviluppatore. Come
l’HTML, Javascript è un linguaggio interpretato (non compilato), supportato
da tutti i più importanti browser commerciali. Alcuni browser (per esempio
MS Internet Explorer) ne supportano particolari estensioni (dialetti) a loro
uso e consumo, ma non capite da altri browser. A parte ciò, la base
comune, capìta dai vari browser, è molto ampia e consente di sviluppare
con sicurezza funzioni gestibili su qualunque piattaforma client.
Come qualunque linguaggio di programmazione, l’uso di Javascript non si
limita a particolari funzioni. Nella mia personale esperienza, l’uso più
frequente consiste nel controllo (validation) del contenuto dei campi di input
e nell’apertura di finestre (pop-up) nella pagina WEB.
Quando tempo fa iniziai ad utilizzare Javascript, raccolsi alcuni appunti in
Figura 1
un manualetto che successivamente pubblicai, è tuttora disponibile alla
pagina http://www.easy400.net/js2/start ed è scaricabile. In Internet si possono reperire innumerevoli manuali di Javascript. Io consiglio quello di
W3Schools, http://www.w3schools.com/js/default.asp .
Nell’esempio pubblicato nel precedente numero (Immissione Nuovi Clienti), il programma CGI controllava i dati di input e, se li trovava errati o
mancanti, emetteva in risposta dei messaggi di errore. Il controllo dei dati di input, almeno per quanto riguarda lo loro completezza ed il loro
formato, risulta molto più efficace se effettuato direttamente sul client: si evita una transazione verso il server ed il tempo di risposta è immediato.
Alcune regole delle funzioni Javascript
Apriamo una parentesi per citare alcune regole del codice Javascript, indispensabili per capire il nostro esempio.
• Le funzioni Javascript devono essere racchiuse tra le tag <script language=”javascript”> e </script>. Queste tag possono trovarsi dovunque nell’HTML. Tra le due
tag si possono definire quante funzioni si desidera.
• Il nome di una funzione è libero, ma deve essere sempre seguito dalle parentesi tonde (). Queste parentesi possono racchiudere nomi di variabili che possono
essere passate alla funzione; in questo caso le devono essere separate da virgole. Esempio di funzione: puntoNave(longitudine,latitudine).
• In Javascript tutto è case sensitive, cioè sensibile al maiuscolo e al minuscolo.
• Le istruzioni della funzione devono essere racchiuse tra parentesi graf { }
• Ogni istruzione deve terminare con un ; o con una andata a capo.
• Una condizione deve essere racchiusa tra parentesi tonde ().Una sequenza di istruzioni condizionate devono essere racchiuse tra parentesi graf { }. Esempio di
condizione e di sequenza di istruzioni condizionate:
if (x<10) {alert(“x errato”); return}
• Il carattere = significa assegnazione di valore; == significa uguale; && significa AND; || significa OR; ! significa NOT.
• Per citare il valore di un campo di input di un form:
• Per citare la lunghezza di un campo di input di un form:
document.nome_form.nome_campo.value
document.nome_form.nome_campo.value.length
• Il risultato di un confronto può unicamente essere true (vero) o false (falso)
• Per sottomettere (inviare al server) un form si usa la funzione primitiva submit()
document.nome_form.submit()
Alcune considerazioni sul debbugging di Javascript
Commettere errori scrivendo funzioni di Javascript è un fatto normalissimo, il linguaggio è estremamente sensibile al minimo errore. Al tempo
stesso, non essendo compilato, non offre l’aiuto cui noi siamo abituati dai compilatori. Pertanto, correggere un errore di Javascript può sembrare
una missione impossibile. La funzione non funziona, non succede nulla, tu provi e riprovi, le ore passano ma non ne vieni a capo. Ecco qualche
consiglio:
1. Realizza in un piccolo HTML un campione della tua funzione Javascript. Ti basta creare sul PC in piccolo file .html, aggiungervi la funzione Javascript e
provarla. Le prove “in vitro” sono più semplici.
2. Usa il browser FireFox. Dopo aver eseguito la funzione, usa la tab Strumenti->Console degli errori per vedere le segnalazioni di errore. A questo punto sai
perché la funzione ha arrestato la sua esecuzione.
3. Per una ancor più semplice diagnostica, installa il componente aggiuntivo (gratuito) Firebug che ti consente di rilevare automaticamente gli errori.
4. Se hai necessità di sapere il valore assunto da una variabile, aggiungi nel punto opprtuno della funzione il comando
alert(nome_della_variabile)
Il Javascript utilizzato nell’esempio
Nell’esempio che proponiamo (Vedi Figura 1), si utilizza una
funzione Javascript per controllare la completezza dei valori di
input ed in particolare per controllare il formato di due di essi, il
CAP e l’indirizzo e-mail. Quando si chiede di inviare il form
premendo il bottone di invio, la funzione Javascript effettua i
controlli: se non vengono trovate eccezioni il form viene
inoltrato al server; in caso contrario, viene emesso un
messaggio di avviso (alert) ed il form non viene inviato.
Per effettuare questi controlli abbiamo creato un nuovo stream
file HTML /sussidev2/html/pgm1A.htm inizialmente identico a
quello pubblicato nell’articolo precedente, apportandovi poi due
modifiche:
1. Tra le tag <head> e </head> abbiamo inserito il Javascript
in Figura 2, il quale contiene una funzione denominata
“vai()”
2. Alla fine del form abbiamo sostituito l’input di tipo submit con
un input di tipo button, il quale – se premuto (onClick) –
richiama la funzione javascript “vai()” (vedi Figura 3). In
sostanza il form non viene più inviato quando si preme il
bottone Invio, in quanto esso non è più di tipo submit. La
sottomissione del form è ora delegata alla funzione “vai()”.
La funzione vai()
<script language="javascript">
function vai() {
reCAP=/\d{5}/
reEmail=/.+@.+\..+/
if (document.nuovocli.xragsoc.value.length<1)
{alert("Manca la Ragione sociale"); document.nuovocli.xragsoc.focus(); return;}
if (document.nuovocli.xindir.value.length<3)
{alert("Indirizzo non valido"); document.nuovocli.xindir.focus(); return;}
if (reCAP.test(document.nuovocli.xcap.value)==false)
{alert("CAP non valido"); document.nuovocli.xcap.focus(); return;}
if (document.nuovocli.xlocal.value.length<1)
{alert("Manca la Località"); document.nuovocli.xlocal.focus(); return;}
if (document.nuovocli.xprov.value.length<2)
{alert("Provincia non valida"); document.nuovocli.xprov.focus(); return;}
if (document.nuovocli.xcontat.value.length<1)
{alert("Manca il Nome per contatto"); document.nuovocli.xcontat.focus(); return;}
if (reEmail.test(document.nuovocli.xemail.value)==false)
{alert("E-mail non valido"); document.nuovocli.xemail.select(); return;}
document.nuovocli.submit();
}
</script>
Figura 2
<input type=button class="bott" value="invio" onClick=vai()>
Figura 3
Dopo aver definito due particolari costanti (reCAP e reMail), viene
controllata la lunghezza di ciascun campo di input. Se la lunghezza è inferiore ad un dato valore minimo, viene emesso un messaggio di errore (funzione primitiva
alert() ), il cursore viene spostato sul campo in causa (funzione primitiva focus() ) e viene effettuata l’uscita dalla funzione (return). Nel caso che l’errore riguardi
l’indirizzo e-mail, anziché focus() si è usata la funzione select() che, oltre a posizionare il cursore, scurisce lo sfondo del campo e lo prepara per la cancellazione.
Le due costanti definite all’inizio della funzione sono chiamate Regular Expression. Si tratta di maschere che definiscono il formato di una variabile. Per verificare se
una variabile è conforme al formato di una regular expression, si usa la funzione primitiva test() il cui risultato è true o false.
regular_expression.test(valore_della_variabile)
• Regular expression reCAP=/\d{5}/
ƒ
Il carattere / iniziale ed il carattere / finale delimitano la regular expression
ƒ
\d{5} indica che sono attesi cinque caratteri numerici
• Regular expression reEmail=/.+@.+\..+/
ƒ
ƒ
ƒ
ƒ
Il carattere / iniziale ed il carattere / finale delimitano la regular expression
.+ indica che sono attesi uno o più caratteri di qualunque tipo
@ indica che è atteso il carattere @
\. indica che è atteso il carattere punto decimale
Il programma RPG
Abbiamo pensato di riutilizzare il programma RPG-ILE sussidev2/pgm1 (membro PGM1 di SUSSIDEV2/QRPGLESRC) presentato nell’articolo
precedente per utilizzare il nuovo stream file HTML /sussidev2/html/pgm1A.htm contenente il Javascript sopra discusso.
Per poter fare in modo che questo programma possa utilizzare a richiesta o il precedente HTML /sussidev2/html/pgm1.htm o quello nuovo
/sussidev2/html/pgm1A.htm :
1. Nel form di /sussidev2/html/pgm1A.htm abbiamo aggiunto un campo di input nascosto, di nome “xhtml”
<form name="nuovocli" method="post" action="/sussidev2p/pgm1.pgm"> con l’intesa che, se il programma riceve “a” nella variabile di input , dovrà
caricare l’HTML esterno /sussidev2/html/pgm1A.htm anziché l’HTML esterno
<input type="hidden" name="xhtml" value="a">
/sussidev2/html/pgm1.htm . Viene da sé che in partenza, si dovrà utilizzare la
<input type="hidden" name="xrequest" value="immissione">
URL http://.../sussidev2p/pgm1.pgm per fargli caricare l’HTML esterno
<table>
/sussidev2/html/pgm1.htm , mentre si dovrà utilizzare la URL
… … …
http://.../sussidev2p/pgm1.pgm?xhtml=a per fargli caricare l’HTML esterno
</table>
/sussidev2/html/pgm1A.htm .
</form>
2.
Nel sorgente del programma RPG-ILE sussidev2/pgm1 (membro PGM1 di SUSSIDEV2/QRPGLESRC) abbiamo dovuto
a. Posporre il caricamento dell’HTML esterno
// Preliminari
(LoadHTML) alla ricezione delle variabili di
exsr RcvInput;
//ricevi le variabili di input
input (RcvInput)
exsr LoadHTML;
//carica l'HTML esterno
exsr OpnF;
//apri i file
b.
Ricevere la variabile di input xhtml e far
dipendere dal suo valore il caricamento
dell’HTML esterno:
D
xhtml
s
1
xhtml=zhbGetVarUpper('xhtml');
if xhtml<>'A';
extHTML='/sussidev2/html/pgm1.htm';
else;
extHTML='/sussidev2/html/pgm1A.htm';
endif;
IfsMultIndicators=getHtmlIfsMult(%trim(exthtml):'<as400>');
ESEMPIO CGI no. 2
Questo secondo esempio di programma CGI
illustra come presentare un “subfile”, in pratica
una tabella di dati derivanti dai record di un file,
nel nostro caso la Anagrafica Clienti già
utilizzata nell’esempio no.1.
SCHEMA BASE
Intendiamo partire con un programma base, al
quale aggiungeremo via via vari dispositivi. Il
programma base intende produrre una lista
brutale dei record del file, Il risultato voluto è in
Figura 4.
Figura 4
Si notino i radio-button alla sinistra di ogni riga. In questo caso i radio- button servono per
trasmettere al programma il codice cliente. Un altro dispositivo è l’associazione del nome di
contatto al suo indirizzo e-mail: vogliamo che, premendo sul nome, il mailer del nostro client
apra un nuovo messaggio indirizzato a quella persona.
Come al solito, la prima attività consiste nello sviluppare la presentazione, cioè
l’HTML esterno, che in questo caso è lo stream file /sussidev2/html/pgm2.htm,
illustrato nella figura 5.
La sezione “inizio“ non presenta novità, rispetto a quanto già fatto negli esempi precedenti.
Abbiamo semplicemente predisposto un’area <script language=”javascript”></script> che
per ora è vuota, la utilizzeremo successivamente.
La sezione “fine” di coda è ancor più ovvia.
L’interesse è tutto per le quattro sezioni con le quali viene gestita la tabella (la tabella è il
corrispondente HTML del “subfile” dei file display).
1. Esiste una sezione di inizio tabella, di nome ListStr, la quale
a.
Dichiara un form di nome clilist con un campo di input nascosto di
(hidden) di nome xrequest . Al momento il form non ci interessa, servirà
piu avanti, quando da questa pagina si potranno formulare richieste al
server.
b.
Da inizio ad una tabella tramite la tag <table>
2. La tecnica che si utilizza nel CGI per riempire la tabella è quella di emettere , in
questo caso, per ogni record di anagrafica letto, una riga di tabella
(<tr><td>…</td>…<td></td></tr>) tramite una sezione HTML che qui si chiama
Listrow. Chi ha vissuto l’epica del Sistema/36 si ricorderà che questa tecnica si
chiamava “riga variabile”. Tra le varie celle (<td>…</td>) segnaliamo:
a.
La prima cella, un radio-button al quale sarà assegnato, come valore da
trasmettere in input qualora fosse selezionato, il codice cliente
b.
L’ultima cella, in cui il nome della persona di contatto viene circondato
da un hyperlink che realizza appunto l’aggancio desiderato al mailer del
client per aprire una nuovo messaggio da inviare.
3. Dopo l’ultima riga di tabella, viene scritta la sezione ListButtons che contiene i bottoni
“Nuovo”, “Varia” e “Cancella”. Si noti che tutti questi bottoni gestiscono l’evento
onClick che serve per richiamare una funzione Javascript quando il bottone viene
premuto. In questo momento però le funzioni Javascript Nuovo(), Change() e
Delete() non esistono ancora, quindi i bottoni non sono ancora operativi.
4. Se invece la anagrafica clienti non contiene alcun record, il CGI non potrà scrivere
alcuna riga di tabella del tipo sopra esposto. Dovrà allora emettere una sola riga di
tabella che spieghi che il file è vuoto. Questo è appunto lo scopo della sezione HTML
di nome ListNone.
5. Una tabella HTML va sempre chiusa. A questo scopo è predisposta la sezione HTML
nome ListEnd, la quale ne approfitta per chiudere anche il form.
<as400>inizio
Content-type: text/html
*** SEZIONE INIZIO HTML
<html>
<head>
<title>Gestione clienti</title>
<link rel="SHORTCUT ICON" href="/sussidev2/graphics/favicon.ico">
<link rel="stylesheet" type="text/css" href="/sussidev2/css/css.css">
<script language="javascript">
</script>
</head>
<body>
<table>
<tr><td><img src="/sussidev2/graphics/redBaron.jpg"></td>
<td width="20">&nbsp;</td>
<td class="title">Gestione Clienti</td></tr>
</table>
<as400>ListStr
*** SEZIONE INIZIO TABELLA
<form name="clilist" method="post" action="/sussidev2p/pgm2.pgm">
<input type="hidden" name="xrequest">
<table>
<as400>ListRow
*** SEZIONE RIGA VARIABILE
<tr><td><input type="radio" name="xclicod"
value="/%clicod%/"></td>
<td>/%cliragsoc%/</td>
<td>/%cliindir%/</td>
<td>/%clicap%/</td>
<td>/%clilocal%/</td>
<td>/%cliprov%/</td>
<td><a href="mailto: /%cliemail%/">/%clicontat%/</a></td></tr>
<as400>ListButtons
*** SEZIONE BOTTONI DI FINE TABELLA
<tr><td align="center" colspan=”7”>
<table>
<tr><td><input type="button" class="bott" value="Nuovo"
onClick=Nuovo()></td>
<td><input type="button" class="bott" value="Varia"
onClick=Change("/%totrows%/")></td>
<td><input type="button" class="bott" value="Cancella"
onClick=Delete("/%totrows%/")></td>
</td></tr>
</table>
</td></tr>
<as400>ListNone
*** SEZIONE TABELLA VUOTA
<tr><td align=center class="errmsg">
Non ci sono clienti</td></tr>
<as400>ListEnd
*** SEZIONE FINE TABELLA
</table>
<input type="hidden" name="xposRagSoc">
</form>
<as400>fine
*** SEZIONE FINE HTML
</body>
</html>
Figura 5
Veniamo ora al programma CGI, che si chiama SUSSIDEV2/PGM2 e si invoca con la URL http://.../sussidev2p/pgm2.pgm . Il suo sorgente è il
membro PGM2 nel file SUSSIDEV2/QRPGLESRC ed è esposto in Figura 6.
Annotazioni sullo schema base del programma
Lo schema base del programma ha grosso modo la stessa struttura del programma PGM1 discusso nel secondo articolo di questa serie, quindi non spendiamo altro
tempo per commentarlo, se non per alcuni particolari.
• Dei due file specificati, al momento viene utilizzato solo il file ANACLI01, che è organizzato per “ragione sociale”. Questo file inoltre dichiara una Information Data
Structure INFDS01 la quale consente di sapere, tramite il sottocampo ONbrRecs, quanti record esistono nel file.
• Nella subroutine di ricezione delle variabili di input RcvInput viene ricevuta la variabile xrequest, la quale pero nel form HTML risulta non contenere nulla. Si ricorda
che con questa variabile di input noi intendiamo che venga trasmesso al CGI il compito fondamentale da espletare. In assenza di tale informazione, il CGI assume
che il compito sia quello di elencare (subroutine List) i record dell’anagrafica. In effetti nel minuscolo albero decisionale select/endsl avviene proprio questo.
• La subroutine List ha un particolare problema nell’editare in output HTML il campo clicod del record anagrafica. Il campo è numerico, ma il dato deve essere
trasmesso come carattere tramite la procedura updhtmlvar. Nella conversione da numerico a carattere tramite la BIF %char vanno persi gli zeri non significativi,
che noi vogliamo invece mantenere. Ecco il perché delle istruzioni marcate in color verde.
*=============================================================
* Gestione clienti
*
* CRTBNDRPG PGM(SUSSIDEV2/PGM2)
*
SRCFILE(SUSSIDEV2/QRPGLESRC) DFTACTGRP(*NO)
*
ACTGRP(PGM2) DBGVIEW(*SOURCE)
*
*=============================================================
/copy SUSSIDEV2/qrpglesrc,hspecs
/copy SUSSIDEV2/qrpglesrc,hspecsbnd
FANACLI
uf
e
k disk
usropn
FANACLI01 if
e
k disk
usropn
rename(clircd:clircd01)
F
infds(InfDS01)
/copy SUSSIDEV2/qrpglesrc,prototypeb
/copy SUSSIDEV2/qrpglesrc,usec
* Information Data Structure per file ANACLI01
D InfDS01
ds
D FileFbk
1
80
D OpenFbk
81
240
D
OFileName
83
92
D
OFileLib
93
102
D
OFileMbr
129
138
D
ONbrRecs
156
159b 0
D
OAccType
160
161
D
ODupKeyInd
162
162
D
OSrcFilInd
163
163
* Numero delle variabili di input
D nbrVars
s
10i 0
* Saved query string
Dsavedquerystring...
D
s
32767
varying
*
D extHtml
s
2000
* Indicatori per la procedura GetHtmlIfsMult
* (caricamento HTML esterno)
D IfsMultIndicators...
D
ds
D NoErrors
n
D NameTooLong
n
D NotAccessible
n
D NoFilesUsable
n
D DupSections
n
D FileIsEmpty
n
* Variabili di input
D xrequest
s
10
D xclicodC
s
9
D xposRagSoc
s
like(cliRagSoc)
D xpagesizeC
s
3
D xpagesize
s
3p 0
* Altre variabili
D rc
s
10i 0
D clicodC
s
9
D zero9
s
9
inz('000000000')
D i
s
10i 0
D l
s
10i 0
D totrows
s
10i 0
Figura 6A
*====================================================
* Logica principale
*====================================================
/free
// Preliminari
exsr RcvInput;
exsr LoadHTML;
exsr OpnF;
//ricevi le variabili di input
//carica l'HTML esterno
//apri i file
// Incomincia a scrivere l'inizio dell'HTML di risposta
wrtsection('inizio');
// Analizza il tipo di richiesta e prendi il percorso opportuno
select;
when xrequest='TOPPAGE';
exsr TopPage;
exsr List;
endsl;
// Scrivi la fine dell'HTML di risposta
wrtsection('fine');
// Lancia il buffer della risposta HTML e termina il programma
exsr Exit;
/end-free
*=================================================
* Ricevi le variabili di input
*=================================================
/free
Begsr RcvInput;
//acquisisci il buffer di input
nbrVars=zhbgetinput(savedquerystring:qusec);
//acquisisci le variabili di input
xrequest=zhbGetVarUpper('xrequest');
if xrequest=' ';
xrequest='TOPPAGE';
endif;
Endsr;
/end-free
*===============================================
* Carica l'HTML esterno
*===============================================
/free
Begsr LoadHTML;
extHTML='/sussidev2/html/pgm2.htm';
IfsMultIndicators=getHtmlIfsMult(%trim(exthtml):'<as400>');
//Se il caricamento dell'HTML esterno fallisce, ritorna
if NoErrors=*off;
return;
endif;
Endsr;
/end-free
*==============================================
* Inizia dalla prima pagina (TOPPAGE)
*==============================================
/free
Begsr TopPage;
// Posizionati sul primo record
xposRagSoc=*loval;
setll xposRagSoc clircd01;
Endsr;
/end-free
Figura 6B
*==============================================
* Fornisci la tabella clienti
*==============================================
/free
Begsr List;
// Se non ci sono clienti, emetti messaggio ed esci
if ONbrRecs=0;
wrtsection('ListStr ListNone ListEnd');
leavesr;
endif;
// Scrivi la sezione inizio tabella clienti
wrtsection('ListStr');
// Scrivi una riga variabile della tabella clienti
// per ogni record letto
read clircd01;
dow not %eof;
clicodC=%char(clicod);
l=%len(%trim(clicodC));
if l<%len(clicodC);
clicodC=%subst(zero9:1:%len(clicodC)-l) +
%trim(clicodC);
endif;
updhtmlvar('clicod' :clicodC );
updhtmlvar('cliragsoc':cliragsoc);
updhtmlvar('cliindir' :cliindir );
updhtmlvar('clicap' :clicap );
updhtmlvar('clilocal' :clilocal );
updhtmlvar('cliprov' :cliprov );
updhtmlvar('clicontat':clicontat);
updhtmlvar('cliemail' :cliemail );
wrtsection('ListRow');
totrows=totrows+1; //numero di righe scritte
read clircd01;
enddo;
// Scrivi la sezione Bottoni di fine tabella
updhtmlvar('totrows':%editc(totrows:'Z'));
wrtsection('ListButtons');
// Scrivi la sezione Fine tabella
wrtsection('ListEnd');
*=============================================
* Apertura file
*=============================================
/free
Begsr OpnF;
if not %open(ANACLI);
rc=docmd('ovrdbf ANACLI SUSSIDEV2/ANACLI secure(*yes)');
open ANACLI;
endif;
if not %open(ANACLI01);
rc=docmd('ovrdbf ANACLI01 SUSSIDEV2/ANACLI01
secure(*yes)');
open ANACLI01;
endif;
Endsr;
/end-free
*=============================================
* Chiusura file
*=============================================
/free
Begsr CloF;
if %open(ANACLI);
close ANACLI;
rc=docmd('dltovr ANACLI');
endif;
if %open(ANACLI01);
close ANACLI01;
rc=docmd('dltovr ANACLI01');
endif;
Endsr;
/end-free
*============================================
* Fine programma
*============================================
/free
Begsr Exit;
exsr CLOF;
//chiudi i file
wrtsection('*fini');
//spedisci il buffer di output
return;
//ritorna con *LR in *off
Endsr;
/end-free
Endsr;
/end-free
Figura 6C
Figura 6D
PRIMA VARIAZIONE – Il bottone ‘Cancella’
Come prima variazione allo schema base vogliamo attivare il bottone “Cancella”. Per farlo, dovremo dotare l’HTML della funzione Javascript
Delete() richiamata nell’onClick del bottone e fare in modo che il programma CGI PGM2 onori la relativa richiesta.
Funzioni Javascript
Si noti innanzi tutto (Vedi Figura 5) che il bottone Cancella richiama la funzione Delete() passandole un parametro /%totrows%
onClick=Delete("/%totrows%/")
Questo parametro, che viene riempito dal programma RPG PGM2, è il numero di righe nella tabella.
Le funzioni Javascript vengono posizionate tra le tag <script language=”javascript”> e </script> nella sezione di inizio dell’HTML (Vedi Figura 7).
Si noti innanzi tutto la presenza di una variabile globale di nome “xindex”. La variabili globali sono variabili accessibili a tutte le funzioni comprese tra le tag <script
language=”javascript”> e </script> .
Le nuove funzioni sono due:
<script language="javascript">
var xindex;
function anyradio(n) {
if (n>1) {
for (i=0; i<n; i++) {
if (document.clilist.xclicod[i].checked==true) {xindex=i; return true};
}
alert("devi scegliere una voce!"); return false;
}
else
document.clilist.xclicod.checked=true; return true;
}
Figura 7
function Delete(n) {
var ragsoc;
if (anyradio(n)==false) return;
if (n==1) {ragsoc=document.clilist.xragsoc.value};
if (n>1) {ragsoc=document.clilist.xragsoc[xindex].value};
if (confirm("Confermi la cancellazione del cliente " + ragsoc + " ?")==true) {
document.clilist.xrequest.value="delete"; document.clilist.submit();}
}
</script>
1. anyradio(n) – Questa funzione riceve come parametro (n) il numero di righe della tabella. Il suo scopo è quello di stabilire se sia stato selezionato un radio-
button. Quando più variabili di input hanno lo stesso nome (in questo caso i campi di input della riga variabile che hanno nome “xclicod”(i radio-button) e
“xragsoc” (campi hidden)), esse vengono a costituire una schiera, in cui il primo elemento ha indice 0, il secondo elemento ha indice 1 e così via. Se il numero
di righe n è superiore ad 1, la funzione esegue un loop (di tipo for) per trovare quale sia il radio-button selezionato: se lo trova, salva il suo indice nella
variabile globale “xindex” e restituisce true alla funzione chiamante; se non lo trova, emettere un alert e restituisce false alla funzione chiamante. Se il numero
di righe n è uguale ad 1, le variabili “xclicod” e “xragsoc” non sono schiere e questa funzione si limita a forzare come selezionato l’unico bottone ed a restituire
true alla funzione chiamante.
2. Delete(n) – Questa funzione riceve come parametro (n) il numero di righe della tabella. Dopo aver definito una variabile di nome “ragsoc”, questa funzione
invoca la funzione anyradio(n). Se da essa riceve false (ci sono più righe di tabella, ma nessun radio-button è stato scelto), termina senza fare nulla. Altrimenti,
avendo ricevuto true (ci sono più righe di tabella ed è stato scelto un bottone, oppure c’è una sola riga in tabella), carica nella variabile “ragsoc” il valore della
ragione sociale del cliente (prelevandolo dalla schiera della variabile “xragsoc” in caso di più righe, oppure dall’unica variabile “xragsoc” in caso di una sola
riga) e utilizza la variabile “ragsoc” per chiedere all’utente conferma della cancellazione. Se l’utente conferma, allora la funzione forza il valore “delete” nella
variabile di input “xrequest” e sottomette il form “clilist”.
Variazioni al programma PGM2
Il programma nella variabile di input “xrequest” troverà il valore “delete” ed eseguirà la routine appropriata per cancellare dal file di anagrafica ANACLI (organizzato
per codice cliente) il record con il codice cliente passato nella variabile di input “xclicod” (il radio-button selezionato dall’utente). Successivamente il programma
provvederà a fornire una nuova lista dei clienti. Ci limitiamo a mostrare in Figura 8 le modifiche apportate al programma.
* Variabili di input
D xrequest
s
D xclicodC
s
D xposRagSoc
s
10
9
like(cliRagSoc)
xclicodC =zhbGetVar('xclicod');
// Analizza il tipo di richiesta e prendi il percorso opportuno
select;
when xrequest='TOPPAGE';
exsr List;
when xrequest='DELETE';
exsr Delete;
exsr List;
endsl;
*========================================================
* Cancella il cliente di codice "xclicodC"
*========================================================
/free
Begsr Delete;
clicod=%dec(xclicodC:9:0); //convert to packed (9 0)
chain clicod clircd;
if %found;
delete clircd;
endif;
Endsr;
/end-free
Figura 8
SECONDA VARIAZIONE – Il
bottone ‘Nuovo’
Come seconda variazione allo schema base vogliamo attivare
il bottone “Nuovo” (Immissione nuovo cliente). Esistono vari
modi per fare ciò. Per esempio, assegnare un nuovo valore
alla variabile di input “xrequest” e spedire il form al programma
PGM2 perché emetta una pagina per la immissione di un
nuovo cliente. Ma noi abbiamo già questo programma, è il
PGM1. Potremmo quindi aggiungere qualcosa nell’ HTML
/sussidev2/html/pgm2.htm per richiamare il PGM1. Già, ma
quando avessimo il PGM1 (Nuovo Cliente) sullo schermo
(Figura 1), come faremmo per ritornare al PGM2 (Gestione
Figura 9
Clienti, Figura 4)? Tornare indietro nella storia delle pagine del
browser? Proprio no, troppo pericoloso, così si commettono errori.
La soluzione migliore è quella di chiamare il PGM1 aprendolo in una finestrella apposita, un cosiddetto “pop up” (Vedi Figura 9). Una volta ricevuta
la richiesta di immissione nuovo cliente, il PGM1 emette, nella pagina del pop up, un HTML che segnala alla schermata Gestione Clienti di rivisualizzare i dati e quindi ordina al pop up di chiudersi. Tutto questo con un poco di Javascript.
Javascript per aprire la pagina Nuovo Cliente in un pop up
Nella Figura 10 presentiamo il Javascript da inserire in /sussidev2/html/pgm2.htm per far sì
che ciò avvenga.
<script src="/sussidev2/js/openPopup.txt"></script>
<script language=”Javascript”>
function Nuovo() {
myurl="/sussidev2p/pgm1.pgm?xhtml=a&xclosepopup=yes"
openPop(myurl,"500")
}
</script>
• La funzione Nuovo() (richiamata dal bottone “Nuovo”), chiama la funzione openPop() passandole
due parametri: l’URL da richiamare e la dimensione orizzontale (500 pixel) della finestrella di pop up.
L’URL contiene il valore "/sussidev2p/pgm1.pgm?xhtml=a&xclosepopup=yes", dove
xhtml=a serve a PGM1 per caricare l’html esterno /sussidev2/html/pgm1a.htm
ƒ
xclosepopup=yes fa sapere a PGM1 che dopo aver registrato il cliente deve chiudere
ƒ
il pop up.
Figura 10
• La funzione openPop() è abbastanza complessa, fondata sulla funzione primitiva window.open().
Data la sua utilità generale, la abbiamo definita esternamente nello stream file
/sussidev2/js/openPopup.txt . Per includerla dinamicamente nell’HTML esterno di /sussidev2/html/pgm2.htm è sufficiente aggiungervi questa dichiarativa:
<script src="/sussidev2/js/openPopup.txt"></script>
Aggiunte all’HTML esterno del programma PGM1 (Nuovo Cliente)
All’HTML esterno del programma PGM1, /sussidev/html/pgm1A.htm, vanno apportate le modifiche seguenti:
a. Nel form “nuovocli” va aggiunto la variabile di input nascosta “xclosepopup” in modo che il programma PGM1, quando emette la schermata di immissione
nuovo cliente, si ritrovi in input lo stesso valore di “xclosepopup” che aveva ricevuto alla sua chiamata iniziale:
<form name="nuovocli" method="post"
action="/sussidev2p/pgm1.pgm">
<input type="hidden" name="xhtml" value="a">
<input type="hidden" name="xrequest" value="immissione">
<input type="hidden" name="xclosepopup" value="/%xclosepopup%/">
<table>
Figura 11
b.
Va aggiunta una sezione di HTML e Javascript che PGM1 deve far pervenire al browser successivamente alla immissione del nuovo cliente perché faccia
ri-visualizzare l’elenco clienti e chiuda il pop up:
<as400>popupClose
Content-type: text/html
*** SEZIONE CHIUSURA POP UP
<html><head><script language="javascript">
function epilog() {
window.opener.document.clilist.xrequest.value="";
window.opener.document.clilist.xposRagSoc="/%xposragsoc%/";
window.opener.document.clilist.submit();
window.close();}
</script><body onLoad=epilog()></body></html>
Figura 12
L’evento onLoad, che avviene non appena il browser ha ricevuto e interpretato lo script, fa partire la funzione epilog(). Questa imposta i campi di input
nascosti del form “clilist” dell’ HTML (Figura 5) che ha aperto (window.opener) il popup, quindi sottomette il form “clilist” in modo tale da far ri-visualizzare
l’elenco clienti. Infine esegue la funzione primitiva window.close(), che chiude il pop up.
Aggiunte alla logica del programma PGM1 (Nuovo Cliente)
Al programma PGM1 vanno apportate delle modifiche per regolare il suo comportamento quando lavora in pop up (xclosepopup=yes).
1. Va definita la variabile di programma in cui va ricevuta la variabile di input “xclosepopup”. Una volta ricevuta, questa variabile va replicata nell’html di output,
così da essere ricevuta nuovamente nella fase in cui avviene la registrazione del cliente (xrequest=’IMMISSIONE’)
2. Nella fase di registrazione del cliente (xrequest=’IMMISSIONE’), se la richiesta arriva dal pop up (xclosepopup=’YES’), va emessa una unica sezione HTML
dello stream file /sussidev/html/pgm1A.htm, quella denominata “popupClose” (vedi Figura 11).
TERZA VARIAZIONE – Il bottone ‘Varia’
Come terza variazione allo schema base vogliamo attivare il bottone “Varia” (Aggiornamento
cliente). La tecnica è la stessa utilizzata nella seconda variazione: aprire un pop up che richiami un
programma CGI per la variazione. Il programma CGI, una volta effettuata la variazione, farà rivisualizzare l’elenco clienti e chiuderà il pop up. In Figura 13 la funzione Javascript Change() che
sarà eseguita quando si preme il bottone “Varia”.
• La funzione Change() si comporta come la funzione Delete() in Figura 7. Nella variabile “zclicod” carica il
codice cliente selezionato, che poi utilizza come parametro nella URL.
• Si noti come nella URL venga invocato il programma PGM1B. Per il sorgente di questo programma e per il
suo HTML esterno rimandiamo al Sussidiario (libreria SUSSIDEV2), in quanto in questo caso le tecniche
utilizzate non costituiscono una novità.
QUARTA VARIAZIONE – Lo ‘scroll’ della tabella
function Change(n) {
var zclicod;
if (anyradio(n)==false) return;
if (n==1) {zclicod=document.clilist.xclicod.value};
if (n>1)
{zclicod=document.clilist.xclicod[xindex].value};
myurl="/sussidev2p/pgm1b.pgm?xclicod=" + zclicod;
openPop(myurl,"500");
}
Figura 13
Così come stanno le cose a questo punto, il PGM2 fornisce una tabella contenente tutti i nominativi clienti. A lungo andare, via via che il numero
dei clienti cresce, questo metodo non va bene, sia perché la lunghezza della pagina in arrivo comporta tempi di risposta rilevanti, sia perché
diventa problematico gestire un elenco di qualche centinaio di nominativi. Bisogna quindi fornire strumenti per
• avere pagine di dimensione contenuta
• poter avanzare o retrocedere con le pagine
• posizionarsi sulla pagina contenente un dato nominativo
Chi ha già affrontato la gestione a programma della paginazione di un subfile 5250, ha una idea di come il problema si risolva. Con L’HTML ed il
CGI la soluzione è abbastanza simile. Il risultato che vogliamo ottenere con il nostro programma CGI è illustrato in Figura 14.
• Per il posizionamento si utilizza questo campo di
immissione:
Una volta
immesso il testo occorre premere la freccetta celeste.
• Per muoversi tra le pagine, si utilizza questo dispositivo:
prima pagina
pagina successiva
ultima pagina
pagina precedente
home Sussidiario
Figura 14
• Questo
muovendo il
serve per variare il numero di righe di una pagina. Dopo aver immesso il numero, occorre uscire dal campo di immissione,
cursore con un mouse o con il tasto di Tab.
La realizzazione di questo supporto per la paginazione ha richiesto una aggiunta di Javascript allo stream file /sussidev2/html/pgm2.htm e la
aggiunta di qualche subroutine al programma CGI SUSSIDEV2/PGM2.
HTML e Javascript a supporto della paginazione
<script language=”Javascript”>
function goHome() {window.location="/sussidev2/html/indice.html"; return false;
}
function StartFrom() {
document.clilist.xposRagSoc.value=document.clilist.posTo.value.toUpperCase();
SamePage();}
function SamePage() {
document.clilist.xrequest.value="samepage";
document.clilist.submit();}
function TopPage() {
document.clilist.xrequest.value="toppage";
document.clilist.submit();}
function RollDown() {
document.clilist.xrequest.value="rolldown";
document.clilist.submit();}
function RollUp() {
document.clilist.xrequest.value="rollup";
document.clilist.submit();}
function LastPage() {
document.clilist.xrequest.value="lastpage";
document.clilist.submit();}
</script>
</head>
• Dimensione della pagina – Quando si esce dal campo di input che
specifica il numero di righe per pagina e si è variato tale numero,
viene eseguita la funzione SamePage().
ƒ
La funzione SamePage() viene utilizzata per chiedere
a PGM2 di emettere un elenco di una pagina di clienti
a partire dalla ragione sociale specificata nella
variabile di input nascosta xposRagSoc. Questa
variabile viene inizializzata da PGM2 quando scrive la
prima riga della pagina.
• Posizionamento – La freccetta celeste alla destra del campo di
input per il posizionamento punta alla funzione StartFrom(). Questa
funzione converte in maiuscolo quanto immesso nel campo di
posizionamento e assegna il valore risultante ad una variabile di input
nascosta, di nome xposRagSoc. Questa variabile viene utilizzata da
PGM2 per sapere quale era il record cliente con cui iniziava la pagina.
Subito dopo, la funzione StartFrom() invoca la funzione SamePage().
• Pagina avanti, pagina indietro, prima pagina, ultima pagina –
Le quattro freccette incaricate di ciò chiamano rispettivamente le
funzioni RollUp(), RollDown(), TopPage() e LastPage(). Queste
funzioni assegnano un valore appropriato alla variabile di input
nascosta xrequest e sottomettono il form clilist.
• Home page del Sussidiario – Quando si preme la casetta al
centro delle quattro freccette, viene eseguita la funzione goHome().
Figura 15- HTML e Javascript a supporto della paginazione
<body>
<form name="clilist" method="post" action="/sussidev2p/pgm2.pgm">
<table>
<tr><td><img src="/sussidev2/graphics/redBaron.jpg"></td>
<td width="20">&nbsp;</td>
<td><table>
<tr><td colspan=2 align="center" class="title">
Gestione Clienti</td></tr>
<tr><td><input type=text name="posTo" size="30" maxlength="50></td>
<td align=center style="cursor:hand">
<img src="/sussidev2/graphics/arrow_back.png"
alt="Parti da" title="Parti da"
onclick="return StartFrom()"></td></tr>
</table>
<td width="20">&nbsp;</td>
<td><FIELDSET>
<legend class=lgd1>Paginazione</legend>
<table cellspacing=0 cellpadding=0 border=0>
<tr><td><img src="/sussidev2/graphics/c.gif" width="22" height="22"></td>
<td align=center style="cursor:hand">
<img src="/sussidev2/graphics/arrow_top.png"
alt="Prima pagina" title="Prima pagina"
onclick="return TopPage()"></td>
<td></td>
<td rowspan=3>
<table>
<tr><td align=center>righe/pagina:<br>
<input type="text" name="xpagesize" size="3" maxlength="3"
value="/%pagesize%/"
onChange=SamePage()>
</td></tr>
</table>
</td></tr>
<tr><td align=center style="cursor:hand">
<img src="/sussidev2/graphics/arrow_back.png"
alt="Pagina precedente" title="Pagina precedente"
onclick="return RollDown()"></td>
<td align=center>
<input type=image src="/sussidev2/graphics/home.jpg"
alt="Home" title="Home"
onClick="return goHome()"></td>
<td align=center style="cursor:hand">
<img src="/sussidev2/graphics/arrow_next.png"
alt="Pagina successiva" title="Pagina successiva"
onclick="return RollUp()"></td></tr>
<tr><td><img src="/sussidev2/graphics/c.gif" width="22" height="22"></td>
<td align=center style="cursor:hand">
<img src="/sussidev2/graphics/arrow_down.png"
alt="Ultima pagina" title="Ultima pagina"
onclick="return LastPage()"></td></tr>
</table>
</FIELDSET>
</form>
</td>
</tr>
</table>
Subroutine di PGM2 a supporto della paginazione - Nella Figura 16 riportiamo unicamente le zone variate.
*=========================================
* Logica principale
*=========================================
/free
// Preliminari
exsr RcvInput;
//ricevi le variabili di input
exsr LoadHTML;
//carica l'HTML esterno
exsr OpnF;
//apri i file
// Incomincia a scrivere l'inizio dell'HTML di risposta
wrtsection('inizio');
// Analizza il tipo di richiesta e prendi il percorso
// opportuno
select;
when xrequest='TOPPAGE';
exsr TopPage;
exsr List;
when xrequest='SAMEPAGE';
exsr SamePage;
exsr List;
when xrequest='ROLLDOWN';
exsr RollDown;
exsr List;
when xrequest='ROLLUP';
exsr RollUp;
exsr List;
when xrequest='LASTPAGE';
exsr LastPage;
exsr List;
when xrequest='DELETE';
exsr Delete;
exsr List;
endsl;
// Scrivi la fine dell'HTML di risposta
wrtsection('fine');
// Lancia il buffer della risposta HTML e
// termina il programma
exsr Exit;
/end-free
*===========================================
* Ricevi le variabili di input
*===========================================
/free
Begsr RcvInput;
//acquisisci il buffer di input
nbrVars=zhbgetinput(savedquerystring:qusec);
xrequest =zhbGetVarUpper('xrequest');
if xrequest=' ';
xrequest='TOPPAGE';
endif;
xclicodC =zhbGetVar('xclicod');
xposRagSoc=zhbGetVar('xposRagSoc');
//ricevi e replica il numero righe per pagina
xpagesizeC=zhbGetVar('xpagesize');
if xpagesizeC=' ';
xpagesizeC='010';
endif;
l=%len(%trim(xpagesizeC));
if l<%size(xpagesizeC);
xpagesizeC=%subst(zero9:1:
%size(xpagesizeC)-l)+
%trim(xpagesizeC);
endif;
xpagesize=%dec(xpagesizeC:3:0); //cvt packed (3 0)
updhtmlvar('pagesize':%editc(xpagesize:'Z'));
Endsr;
/end-free
Figura 16
*==========================================
* Stessa pagina (SAMEPAGE)
*==========================================
/free
Begsr SamePage;
// Posizionati sul record della prima riga della pagina
setll xposRagSoc clircd01;
Endsr;
/end-free
*==========================================
* Pagina indietro (ROLLDOWN)
*==========================================
/free
Begsr RollDown;
// Posizionati sulla ragione sociale da cui partire
setll xposRagsoc clircd01;
// Arretra di una pagina
i=0;
dow i<=xpagesize;
readp clircd01;
if %eof;
cliragsoc=*loval;
setll cliragsoc clircd01;
leave;
endif;
i=i+1;
enddo;
Endsr;
/end-free
*==========================================
* Pagina avanti (ROLLUP)
*==========================================
/free
Begsr RollUp;
// Posizionati sulla ragione sociale da cui partire
setll xposRagsoc clircd01;
// Avanza di una pagina
i=0;
dow i<=xpagesize-1;
read clircd01;
if %eof;
leave;
endif;
i=i+1;
enddo;
//Leggi ancora un record per vedere se siamo a fine file
read clircd01;
if %eof;
exsr LastPage;
leavesr;
else;
readp clircd01;
endif;
Endsr;
/end-free
*===========================================
* Ultima pagina (LASTPAGE)
*===========================================
/free
Begsr LastPage;
// Posizionati sulla ragione sociale da cui partire
cliragsoc=*hival;
setgt cliragsoc clircd01;
i=0;
dow i<=xpagesize;
readp clircd01;
if %eof;
cliragsoc=*loval;
setll cliragsoc clircd01;
leave;
endif;
i=i+1;
enddo;
Endsr;
/end-free
Nel prossimo articolo porteremo un esempio di Immissione Ordini che illustrerà come ricevere molteplici variabili di input con
lo stesso nome. Inoltre spiegheremo due tecniche per limitare l’accesso alle sole persone autorizzate.