Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

XCalendar: un calendario in Ajax e PHP

Tutorial passo per passo per creare un semplice ma potente calendario per la gestione degli eventi personali
Tutorial passo per passo per creare un semplice ma potente calendario per la gestione degli eventi personali
Link copiato negli appunti

Introduzione

Caratteristiche base

L'applicazione che andremo a descrivere è un semplice calendario dove ciascun utente (identificato tramite login) potrà inserire i propri eventi e visualizzare gli eventi inseriti precedentemente. Ciascun evento oltre a un titolo, ad una breve descrizione, ad una data di decorrenza e una durata, è identificato da un colore che specifica la tipologia di evento (lavoro, famiglia, amici, altro). Come per la precedente applicazione, l'XFile, anche l'XCalendar presenta una struttura composta da pià componenti, ognuno specifico per un determinato compito, caso d'uso.

Una demo dell'applicazione è disponibile su questa pagina del mio sito. Dalla pagina di presentazione di Xfile, invece, è possibile scaricare il pacchetto zip che contiene tutti i file necessari all'implementazione e il cui codice andremo ad analizzare nel corso del tutorial.

L'accesso all'applicazione

L'accesso all'applicazione non è stato realizzato seguendo il paradigma AJAX ma secondo una tecnica tradizionale, soprattutto per motivi di sicurezza. L'unico aspetto interessante è la cosiddetta registrazione "live". Successivamente tratteremo nello specifico questo componente.

I casi d’uso dell’applicazione

L’insieme dei casi d’uso può essere riassunto in questo piccolo schema:

1. getAllEvents (permette di ottenere tutti gli eventi di una determinata settimana – può essere paragonato ad un refresh della pagina)

2. addNewEvent (permette di aggiungere un nuovo evento)

3. updateEvent (permette di modificare un evento già esistente)

4. removeEvent (permette di rimuovere un evento già esistente)

Come per i precedenti progetti anche questo è stato sviluppato tenendo conto di possibili futuri ampliamenti delle potenzialità del prodotto.

Le librerie utilizzate

Questa applicazione non presenta nessuna libreria "nuova" rispetto ai precedenti tutorial. Le citerò solamente, in quanto il lettore interessato al loro utilizzo può facilemente reperire delle spiegazioni nel precedenti tutorial (Xchat, Xmap, XFile):

- JSON (libreria server-side per l’encoding di variabili semplici e complesse in entità JSON facilmente integrabili nel motore JavaScript)

- Database.class (banale libreria per eseguire in maniera astratta query su database MySql)

- net.ContentLoader (libreria client-side che facilita notevolmente la gestione della connessione con il server tramite l’oggetto XmlHttpRequest).

La divisione dei compiti

Una caratteristica dell'applicazione è la forte dipendenza dell'aspetto client da quello server. Le principali operazioni vengono infatti svolte dai componenti server soprattutto riguardo la gestione delle date in quanto il linguaggio client-side JavaScript non fornisce strumenti secondo me adeguati.

La struttura dei dati

La struttura necessita di un database di supporto per salvare gli utenti registrati e gli eventi inseriti. L'architettura è molto semplice ed è formata da due tabelle. La tabella events presenta id, id dell'utente, titolo, testo, data di inizio, durata e tipologia dell'evento:

CREATE TABLE `events` (
`id` int(11) NOT NULL auto_increment,
`user` int(11) NOT NULL default '0',
`title` varchar(255) NOT NULL default '',
`text` tinytext NOT NULL,
`t` timestamp NULL default NULL,
`length` char(2) NOT NULL default '',
`type` int(1) NOT NULL default '0',
PRIMARY KEY (`id`)
)

La tabella user presenta id, username e password (criptata) dell'utente:

CREATE TABLE `users` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(255) NOT NULL default '',
`password` varchar(255) NOT NULL default '',
PRIMARY KEY (`id`)
)

L'autenticazione

Essendo questa componente non strattamente correlata con l'attività principale dell'applicazione (quella di gestire un'agenda) preferisco analizzarla in modo isolato. L'autenticazione è composta principalmente da due file:

- login.php

- logout.php

Come detto in precedenza, questo particolare processo non è stato realizzato in AJAX e quindi non è questa la principale sede dove discuterne. Per completezza mi limiterò a pubblicare il codice delle pagine e a darne un breve commento.

Login

<?php
if(!empty($_POST) &&
strlen(trim($_POST['username']))!=0 &&
strlen(trim($_POST['password']))!=0) {
session_start();
include("php/dbconf.php");
$conn = mysql_connect(DBHOST, DBUSER, DBPASS);
mysql_select_db(DBNAME, $conn);
$query = "SELECT * FROM users
WHERE username = '".$_POST['username']."'
AND password=MD5('".$_POST['password']."')";
$result = mysql_query($query, $conn);
if(mysql_num_rows($result) != 1) {
//l'utente è nuovo
$query = "INSERT INTO users(username,password)
VALUES('".$_POST['username']."',
MD5('".$_POST['password']."'))";
mysql_query($query, $conn);
$_SESSION['xcalendar_user'] =
mysql_insert_id($conn);
} else {
//l'utente esiste già
$user = mysql_fetch_assoc($result);
$_SESSION['xcalendar_user'] = $user['id'];
}
header("Location:index.php");
exit();
}
?>

Da come si può facilemente intuire leggendo il codice, l'accesso all'agenda non può essere negato a nessuno in quanto se l'utente non è regolarmente registrato, verrà aggiunto automaticamente prima di accedere all'applicazione. Ovviamente questa registrazione "live" non è la soluzione migliore in applicazioni più complesse.

In entrambi casi quindi viene valorizzata la variabile di sessione xcalendar_user con l'id dell'utente corrente e poi viene effettuato un redirect verso index.php.

Logout

<?php
session_start();
session_destroy();
header("Location:login.php");
?>

Credo che non siano necessarie ulteriori spiegazioni a questo codice banale.

Le componenti server-side

Caso d’uso 1: getAllEvents

Il primo caso d'uso che analizzeremo è relativo all'ottenimento di tutti gli eventi relativi ad una determinata settimana. Questo è sicuramente il caso d'uso più complesso ed è quello che viene invocato più frequentemente:

1.appena si accede all'applicazione

2.nel momento in cui si scorre tra le settimane

3.subito dopo ciascun'altro caso d'uso (quindi dopo un aggiunta, una modifica o una rimozione) come refresh della pagina.

Analizziamo il codice dividendolo in più parti:

session_start();
if(!isset($_SESSION['xcalendar_user'])) exit();
include("dbconf.php");
include("database.class.php");
include("json.php");
$userId = $_SESSION['xcalendar_user'];

Innanzitutto, trattandosi di un'applicazione condivisa tra più utenti, controlliamo che esista un utente loggato e in caso contrario interrompiamo l'esecuzione della pagina. Includiamo poi le librerie necessarie (dbconf.php contiene i parametri di accesso al db) e salviamo dentro $userId l'id dell'utente corrente.

$diffDays = isset($_POST['currentWeek']) ?
$_POST['currentWeek']*7 : 0;
$currentDate = mktime (0,0,0,
date("m"), date("d")+$diffDays, date("y"));
$weekDay = date("w", $currentDate);
if($weekDay == 0) $weekDay = 6;
else $weekDay--;
$start = date("d", $currentDate)-$weekDay;
$startDay = mktime (0,0,0,
date("m", $currentDate),$start ,
date("y", $currentDate));
$endDay = mktime (0,0,0,
date("m", $currentDate),$start+7 ,
date("y", $currentDate));;
$days = array();
for($i=$start; $i<=$start+6; $i++)
$days[] = date("d/m/y",
mktime (0,0,0, date("m", $currentDate),
$i , date("y", $currentDate)));

Una volta verificato che l'utente ha tutti i diritti di accesso alla pagina, controlliamo l'esistenza di un parametro currentWeek. In caso positivo $diffDays viene valorizzata a 0 altrimenti al valore del parametro moltiplicato per 7: questo per identificare i giorni di differenza di giorni tra la settimana corrente e quella richiesta.

Grazie a mktime() otteniamo la data corrente e grazie a date() il giorno della settimana associato a questa data. Questa operazione è necessaria per identificare all'interno di $start la data del lunedi associato al numero della settimana richiesta. Successivamente sempre grazie a mktime() valorizziamo $startDay con la data di partenza della settimana e $endDay con la data finale della settimana (tramite un semplice +7). In queste operazioni non dobbiamo preoccuparci di un eventuale passaggio di mese (29 aprile + 7 giorni non da come risultato 36 aprile...) in quanto la funzione mktime() gestisce in maniera perfetta queste operazioni. Infine riempiamo l'array $days con le 6 date nel formato d/m/y.

$db = new database();
$query = "SELECT *, UNIX_TIMESTAMP(t) as tt
FROM events
WHERE t>= FROM_UNIXTIME({$startDay})
AND t <= FROM_UNIXTIME({$endDay})
AND user = $userId";
$events = $db->doQuery($query);
$db->close();

A questo punto possiamo eseguire un'interrogazione su db per ottenere tutti gli eventi associati al utente corrente per la settimana richiesta.

for($i=0; $i<count($events); $i++) {
$start = $events[$i]['tt'];
$events[$i]['weekDay'] =
date("w", $start)==0 ? 7: date("w", $start);
$events[$i]['start'] = date("H", $start);
$events[$i]['controlDate'] =
date("d/m/y H:i", $start);
}
$result = array("days" => $days, "events" => $events);
$json = new json();
echo $json->encode($result);

Ora per ciascun evento inseriamo il giorno della settimana (indice weekday), l'ora di partenza (indice start) e una data formattata come d/m/y H:i con lo scopo di eseguire dei controlli di coerenza client-side (indice controlDate).

Una volta valorizzato completamente l'array $events, creiamo un nuovo array associativo $result composto dall'elenco dei giorni e dall'elenco degli eventi e lo encodiamo in JSON per poter essere "compreso" dal motore JavaScript.

Caso d'uso 2: addNewEvent

Questo caso d'uso semplicemente si occupa , una volta effettuati i controlli sulla sessione e le inclusioni delle librerie esterne, di recuperare i dati dalla request, formattarli e eseguire una query di inserimento.

$userId = $_SESSION['xcalendar_user'];
$db = new database();
$title = mysql_real_escape_string($_POST['title']);
$text = isset($_POST['text'])
? mysql_real_escape_string($_POST['text']) : "";
$length =
isset($_POST['length'])&&strlen($_POST['length'])>0
? $_POST['length'] : 1;
$date = $_POST['date'];
$type = $_POST['type'];
$day = substr($date,0,2);
$month = substr($date,3,5);
$year = substr($date,6,8);
$hour = substr($date,9,11);
$date = mktime($hour, 0, 0, $month, $day, $year);
$query = "
INSERT INTO events(user,title,text,t,length,type)
VALUES($userId,'$title','$text',
FROM_UNIXTIME($date),$length,$type)";
$db->doQuery($query);
$db->close();

Caso d’uso 3: updateEvent

Questo terzo caso è molto simile al precedente in quanto l'unica cosa che cambia è la struttura della query che invece di essere di inserimento è di modifica. Prima di modificare un evento ovviamente è necessario effettuare un controllo di legittimità sull'evento che l'utente cerca di modificare.

$userId = $_SESSION['xcalendar_user'];
$eventId = $_POST['id'];
$db = new database();

$query = "SELECT * FROM events WHERE id = $eventId";
$result = $db->doQuery($query);
if(count($result) == 0)
die("Evento non esistente");
$event = $result[0];
if($event['user'] != $userId)
die("Non sei il proprietario dell'evento");

$title = mysql_real_escape_string($_POST['title']);
$text = isset($_POST['text'])
? mysql_real_escape_string($_POST['text']) : "";
$type = $_POST['type'];

$query = "UPDATE events
SET title = '$title', text='$text',
type = $type WHERE id = $eventId";
$db->doQuery($query);

Caso d’uso 4: removeEvent

L'ultimo caso d'uso è molto semplice: dopo aver effettuato gli stessi controlli di legittimità del caso d'uso precedente, esegue una query di eliminazione sull'evento.

$userId = $_SESSION['xcalendar_user'];
$eventId = $_POST['eventId'];
$db = new database();

$query = "SELECT * FROM events WHERE id = $eventId";
$result = $db->doQuery($query);
if(count($result) == 0) die("Evento non esistente");
$event = $result[0];
if($event['user'] != $userId)
die("Non sei il proprietario dell'evento");

$query = "DELETE FROM events WHERE id = $eventId";
$db->doQuery($query);

Tabella riassuntiva dei casi d’uso

Figura 1 - Tabella dei casi d'uso
Tabella

Le componenti client-side

L'interfaccia grafica

L'applicazione è composta principalmente da un piccolo menu che permettere di scorrere tra le settimane, da una grande tabella che rappresenta i 7 giorni della settimana e un form che viene mostrato solo al momento necessario.

<h1>XCalendar</h1>
<a href="logout.php">Logout</a><br/>
<a href="#" onclick="getPrevWeekEvents(); return false"><< Prev</a>
<a href="#" onclick="getNextWeekEvents(); return false">Next >></a>

Il piccolo menu non fa altro che invocare le funzioni JavaScript getPrevWeekEvents e getNextWeekEvents.

<th>Lunedi<br/><span id="day1"></span></th>
<th>Martedi<br/><span id="day2"></span></th>
<th>Mercoledi<br/><span id="day3"></span></th>
<th>Giovedi<br/><span id="day4"></span></th>
<th>Venerdi<br/><span id="day5"></span></th>
<th>Sabato<br/><span id="day6"></span></th>
<th>Domenica<br/><span id="day7"></span></th>

Le celle d'intestazione della tabella presentano degli span vuoti che verranno valorizzati nel momento del refresh della pagina con i numeri dei giorni corrispondenti alla settimana che l'utente sta visualizzando.

<tr>
<td class="time">08:00</td>
<td class="class1"><a href="#" onclick="openWindow(8,1,addEvent)">Add event</a></td>
<td class="class2"><a href="#" onclick="openWindow(8,2,addEvent)">Add event</a></td>
<td class="class1"><a href="#" onclick="openWindow(8,3,addEvent)">Add event</a></td>
<td class="class2"><a href="#" onclick="openWindow(8,4,addEvent)">Add event</a></td>
<td class="class1"><a href="#" onclick="openWindow(8,5,addEvent)">Add event</a></td>
<td class="class2"><a href="#" onclick="openWindow(8,6,addEvent)">Add event</a></td>
<td class="class1"><a href="#" onclick="openWindow(8,7,addEvent)">Add event</a></td>
</tr>

Ciascuna cella della tabella presenta una funzione callback per l'evento onclick. La funzione accetta come parametri l'ora di partenza, il giorno e la funzione che deve essere associata al submit del form (ho riportato solamente una riga della tabella).

<div id="window">
<div class="title">Dettagli evento</div>
<div class="container">
Data:<br/>
<b id="date"></b><br/><br/>
Titolo evento:<br/>
<input type="text" id="title"/><br/><br/>
Descrizione:<br/>
<textarea id="text"></textarea><br/><br/>
Tipologia:<br/>
<select id="type">
<option value="1">Working</option>
<option value="2">Family</option>
<option value="3">Friends</option>
<option value="4">Other</option>
</select>
<br/><br/>
Durata: <i>(in ore)</i><br/>
<input type="text" id="length" value="1"/><br/><br/>
<input type="hidden" id="id"/>
<input type="submit" id="formSubmit" class="button"/>
<br/><br/>
<input type="submit" class="button" value="Chiudi" onclick="closeWindow()"/>
</div>
</div>

Il form è contenuto in un div (window) che viene reso visibile solo in seguito di determinate azioni effettuate dall'utente. Gli elementi fondamentali del form, oltre ai campi di input (title, text, type e length) sono l'elemento <b> con id=date e il campo hidden che conterrà l'id dell'evento da modificare. Oltre a questi sono presenti due button il primo per inviare il form e il secondo per chiudere la finestra.

Le variabili globali

Come tutte le applicazioni, anche l'XCalendar ha bisogno di variabili globali accessibili da qualsiasi funzione del programma. Nel dettaglio queste variabili sono:

- currentWeek (contiene l'indice della settimana che viene visualizzata – 0 indica la settimana attuale)

- currentEventsElements (contiene l'array degli elementi <div> evento)

- currentEventsObject (contiene l'array degli oggetti evento).

Caso d'uso 1: il refresh della pagina e la navigazione tra le settimane

Un refresh della pagina può verificarsi secondo queste tre funzioni:

function getThisWeekEvents() {
getEventByWeek();
}

function getNextWeekEvents() {
currentWeek++;
getEventByWeek();
}

function getPrevWeekEvents() {
currentWeek--;
getEventByWeek();
}
window.onload = function() {
getThisWeekEvents();
}

Entrambe invocano getEventByWeek eventualmente incrementando o decrementando la variabile globale currentWeek. Inoltre all'onload della pagina viene chiamata la funzione getThisWeekEvents() che si occupa di recuperare gli eventi della settimana corrente.

function getEventByWeek() {
param="currentWeek="+currentWeek;
new net.ContentLoader("php/getAllEvents.php", callback, param);
}

La funzione getEventByWeek (invocata dalle tre precedenti funzioni) si occupa solamente di aprire una connessione verso il file getAllEvents.php passandogli come parametro appunto l'indice della settimana. Come callback viene passata appunto una funzione di nome callback. Analizziamo questa funzione:

function callback(txt) {
removeEvents();
var result = eval('(' + txt + ')');
var days = result.days;
for(i=0; i < days.length; i++) document.getElementById("day"+(i+1))
.innerHTML = days[i];
var events = result.events;
for(e in events) {
//div dell'evento
var div = document.createElement("DIV");
div.className = "event";
div.id = events[e].id;
if(events[e].type == 1)
div.className+= " working";
else if(events[e].type == 2)
div.className+= " family";
else if(events[e].type == 3)
div.className+= " friends";
else if(events[e].type == 4)
div.className+= " other";
div.style.height =
(events[e].length * 50)+"px";
div.style.top =
(54+ 50 * (events[e].start-8))+"px";
div.style.left =
(events[e].weekDay * 102)+"px";
var title = document.createElement("DIV");
title.className = "title";
title.innerHTML = events[e].title;
div.appendChild(title);
var text = document.createElement("DIV");
text.className = "text";
text.innerHTML = events[e].text
div.appendChild(text);
var buttons = document.createElement("DIV");
buttons.className = "buttons";
var updImage = document.createElement("IMG");
updImage.src="img/upd.png";
updImage.onclick = getEventDetails;
updImage.alt = "updateEvent";
buttons.appendChild(updImage);
var remImage = document.createElement("IMG");
remImage.src="img/rem.png";
remImage.onclick = removeEvent;
remImage.alr = "removeEvent";
buttons.appendChild(remImage);
div.appendChild(buttons);
document.body.appendChild(div);
currentEventsElements.push(div);
currentEventsObjects.push(events[e]);
}
}

La funzione per prima cosa invoca removeEvent() (lo vedremo successivamente anche se il suo scopo dovrebbe essere chiaro) e costruisce l'oggetto response tramite la funziozione eval().

Una volta ottenuto l'oggetto response, riempiamo l'intestazione della tabella con le date contenute dentro l'array days e successivamente cicliamo l'array events. Per ogni evento costruiamo un elemento <div>, gli associamo come id l'id dell'evento, gli modifichiamo lo style in base alla tipologia di evento e gli impostiamo le dimensioni e il posizionamento in base all'ora di inizio, al giorno della settimana e alla durata. Inoltre aggiungiamo due immagini (upd.png e rem.png) che sono associate alle funzioni getEventDetails e removeEvent (le vedremo nei relativi casi d'uso).

Inoltre l'elemento div e l'oggetto event vengono accodati rispettivamente negli array globali currentEventsElements e currentEventsObjects per avere tutto a disposizione in momenti successivi.

function removeEvents() {
while(currentEventsElements.length>0)
document.body.removeChild(
currentEventsElements.pop())
currentEventsObjects = new Array();
}

La funzione removeEvents si occupa di eliminare tutti gli elementi presenti dentro currentEventsElements in modo da "azzerare" la pagina.

Caso d'uso 2: la creazione di un nuovo evento

Questo caso d'uso viene attivato al verificarsi dell'evento onclick su una cella vuota della tabella rappresentante l'intera settimana. Come è possibile notare nelle pagine precedenti, un click su una cella vuota permette di invocare la funzione openWindow passandogli come parametri l'orario di partenza, il giorno della settimana (da 1 a 7 / da lunedi a venerdi) e la funzione che deve essere chiamata all'onsubmit del form che viene successivamente mostrato.

Per esempio la cella:

<td class="class1"><a href="#" onclick="openWindow(10,3,addEvent)">Add event</a></td>

permette di aprire una nuova finestra e quindi un nuovo form per creare un evento datato mercoledi alle ore 10 (per individiare la settimana corrente si utilizzerà la variabile globale salvata in precedenza).

function openWindow(hour, weekDay, callback) {
if(hour<10) hour = "0"+hour;
document.getElementById("date").innerHTML =
document.getElementById("day"+weekDay)
.innerHTML+" "+hour+":00";
document.getElementById("window").style.display=
"block";
document.getElementById("formSubmit").onclick =
function() {
validateForm(callback);
}
if(callback == updateEvent) document.getElementById("length").readOnly = true;
else
document.getElementById("length").readOnly = false;
}

La funzione oltre a modificare alcuni aspetti grafici della finestra, salva nell'elemento con id="date" la data di inizio dell'evento, imposta come callback del submit la funzione validateForm girandogli il callback ricevuto (nel caso di nuovo evento la funzione addEvent) e rende scrivibile o meno il campo length (modificabile sono in aggiunta di un nuovo evento e non in modifica).

function validateForm(callback) {
var title = document.getElementById("title").value;
var length =
document.getElementById("length").value;
var date =
document.getElementById("date").innerHTML;
var hour = date.substring(
date.indexOf(" ")+1,date.indexOf(" ")+3);
if(hour.substring(0,1)=="0")
hour = hour.substring(1,2);
if(title.length==0) {
alert("Compila il campo titolo!");
return;
}
if(isNaN(length)
|| parseInt(hour)+parseInt(length) > 21) {
alert("Campo durata errato!");
return;
}

if(!validateDate(date, length)) {
alert("Non puoi sovrapporre eventi!");
return;
}

callback.call();
}

La funzione semplicemente recupera i dati dal form e esegue alcuni controlli di validità dell'evento. Se tutti i controlli sono positivi chiamerà appunto la funzione ricevuta come parametro che si occuperà di effettuare una nuova richiesta al server indicandogli i dati del nuovo evento. Prima di analizzare questa funzione (addEvent) è necessario soffermarsi sulla funzione validateDate e sul controllo clientSide per evitare sovrapposizione di eventi.

function validateDate(date, length) {
date = parseDate(date);
var sameDayEvents = getSameDayEvents(date);
if(sameDayEvents.length == 0) return true;
var currentStart =
parseInt(date['time'].substring(0,
date['time'].indexOf(":")),10);
var currentEnd =
parseInt(currentStart, 10) + parseInt(length, 10);
error = false;
for(e in sameDayEvents) {
var eventDate =
parseDate(currentEventsObjects[e].controlDate);
var startEvent =
parseInt(eventDate['time'].substring(0, eventDate['time'].indexOf(":")),10);
var endEvent = parseInt(startEvent, 10) +
parseInt(currentEventsObjects[e].length, 10);
if(currentStart < startEvent
&& currentEnd > startEvent) {
error=true;
break;
}
}
return !error;
}

function getSameDayEvents(date) {
var sameDayEvents = new Array();
for(e in currentEventsObjects) {
var eventDate =
parseDate(currentEventsObjects[e].controlDate);
if(eventDate['date'] == date['date'])
sameDayEvents.push(
currentEventsObjects[e].controlDate);
}
return sameDayEvents;
}

function parseDate(date) {
var parse = new Array();
parse['time'] = date.substring(date.indexOf(" ")+1);
parse['date'] = date.substring(0, date.indexOf(" "));
return parse;
}

La funzione validateData si occupa innanzitutto, grazie alla funzione getSameDayEvents di ottenere un array contenente gli eventi esistenti per la stessa giornata nella quale si sta tentando di creare un nuovo evento. Poi per ciascuno di essi controlla che non ci siano sovrapposizioni di orario (se esiste un evento di un ora che parte alle 11, non sarà possibile crearne uno di 3 ore che partirà alle 10). A supporto di queste funzioni, ne esiste una terza – parseDate– che semplicemente formatta le date in un array dividendo la componente "data" dalla parte "tempo". Su queste funzioni non mi soffermo più di tanto in quanto la gestione delle date in JavaScript non è materiale di questo tutorial.

Una volta che tutti i controlli sono stati effettuati (questi controlli come si può vedere dal codice, verranno invocati anche in caso di modifica di un evento già esistente) viene invocata la funzione passata come parametro all'inizio di questi step procedurali, cioè addEvent.

function addEvent(title, description, time) {
document.getElementById("length").readOnly = false;
var title = document.getElementById("title").value;
var text = document.getElementById("text").value;
var length =
document.getElementById("length").value;
var date =
document.getElementById("date").innerHTML;
var type = document.getElementById("type").value;
var param = "title="+title;
param+="&text="+text;
param+="&date="+date;
param+="&length="+length;
param+="&type="+type;
new net.ContentLoader(
"php/addNewEvent.php", getEventByWeek, param);
closeWindow();
}

La funzione è quasi banale: vengono recuperati i dati dal form, viene costruita la query string e viene creata una richiesta verso il file addnewEvent.php. Una volta ricevuta la conferma di creazione, viene rinfrescata la pagina tramite getEventByWeek e viene chiusa la finestra.

function closeWindow() { document.getElementById('window').style.display=
'none'
document.getElementById("title").value = "";
document.getElementById("text").value ="";
document.getElementById("length").value = 1;
document.getElementById("type").value = 1;
document.getElementById("id").value ="";
}

CloseWindow non si occupa solamente di nascondere la finestra contenente ma anche di azzerare i vari campi del form.

Caso d'uso 3: la modifica dei dati di un evento già esistente

La creazione di un caso d'uso simile al precedente è facilitata dal fatto che la maggior parte delle funzioni sono già esistenti (per esempio l'apertura della finestra o la validazione del form). L'avvio del caso d'uso avviene successivamente al click dell'utente sull'iconcina presente nel <div> dell'evento (funzione getEventDetails). Questo farà appunto aprire una finestra contenente i dati dell'evento permettendo la loro modifica e successivo submit del form.

function getEventDetails() {
var eventId = getEventIdByImage(this);
for(e in currentEventsObjects) {
if(currentEventsObjects[e].id == eventId) {
var result = currentEventsObjects[e];
break;
}
}
if(result) {
document.getElementById("title").value =
result.title;
document.getElementById("text").value =
result.text;
document.getElementById("length").value =
result.length;
document.getElementById("type").value =
result.type;
document.getElementById("id").value = result.id;
var hour = result.hour;
var week = result.weekDay;
openWindow(hour, week, updateEvent);
}
}
function getEventIdByImage(image) {
return image.parentNode.parentNode.id;
}

La funzione getEventDetails si occupa di recuperare l'id dell'elemento <div> contenente l'evento (che ricordo è anche l'id del record salvato su db). Questo avviene grazie alla navigazione DOM e alla funzione getEventIdByImage partendo dall'immagine target dell'evento onclick. Una volta ottenuto l'id è possibile recuperare l'oggetto event dall'array globale currentEventObjects ciclando tra tutti gli oggetti inseriti precedentemente (ricordo dalla funzione callback del refresh della pagina). Una volta ottenuto l'evento riempiamo il form con i dati correnti e invochiamo nuovamente (perchè era la stessa funzione nel caso di creazione di eventi) openWindow passandogli stavolta come callback la funzione updateEvent (e non più addEvent!).

Da questo momento il motore JavaScritp si troverà a invocare le funzioni di validazione viste nel caso d'uso precedente. Se i dati saranno tutti corretti la riga callback.call non invocherà piu addEvent, ma come detto precedentemente updateEvent. Tramite questa tecnica di passare come parametro una funzione, siamo in grado di "riciclare" le funzioni di validazione indipendentemente dal caso d'uso corrente.

function updateEvent() {
var title = document.getElementById("title").value;
var text = document.getElementById("text").value;
var type = document.getElementById("type").value;
var id = document.getElementById("id").value;
var param = "title="+title;
param+="&text="+text;
param+="&type="+type;
param+="&id="+id;
new net.ContentLoader(
"php/updateEvent.php",getThisWeekEvents,
param);
closeWindow();
}

UpdateEventè molto simile ad AddEvent. Oltre alle informazioni sull'evento è necessario inviare al server (updateEvent.php) anche l'id dell'evento corrente per garantire un update corretto. Una volta ricevuta la risposta dal server è necessario un rinfresco della pagina.

Caso d'uso 4: l'eliminazione di un evento

Come succede spesso nelle applicazioni, la rimozione di un contenuto è la parte più veloce da realizzare perchè è quella che necesita di una singola informazione: l'id dell'elemento. Questo id è già a disposizione del client in quanto, come ripetuto in precedenza, ciascun <div> rappresentante un evento ha come id lo stesso id del record salvato su db (ma guarda caso...). L'onclick sull'icona di eliminazione non fa altro che invocare la funzione removeEvent.

function removeEvent() {
var eventId = getEventIdByImage(this);
param = "eventId="+eventId;
new net.ContentLoader(
"php/removeEvent.php",getThisWeekEvents,
param);
}

Grazie alla funzione getEventIdByImage recuperiamo l'id dell'evento da eliminare e lo inviamo come parametro al file removeEvent.php. Successivamente rinfreschiamo la pagina.

Conclusioni

Anche questa applicazione è stata, spero, analizzata nello specifico. Sicuramente con queste poche righe di codice non è possibile realizzare applicazioni che possono competere con i maggiori calendar presenti sul web (Google Calendar in primis) ma sicuramente permettono di entrare nella logica di COME poter realizzare applicazioni complesse, distribuite e usabili.

Ti consigliamo anche