novembre 2007
Obiettivo
Il Web è ricco di community incentrate su aree geografiche e interessi: chi ama i musei, le cattedrali europee, i parchi statali e così via. Pertanto, c'è sempre bisogno di uno sviluppatore (come te) per creare un sistema in cui gli utenti possano aggiungere dati geografici ai luoghi di una mappa ed è esattamente quello che faremo noi. Alla fine di questo articolo, sarà disponibile un sistema in cui gli utenti possono registrarsi, accedere e aggiungere luoghi con tag geografici. Il sistema utilizzerà AJAX per il front-end, PHP per lo script lato server e Fogli di lavoro Google per l'archiviazione. Se hai l'abitudine di utilizzare database MySQL per l'archiviazione, puoi modificare facilmente il codice qui per utilizzare invece un backend del database MySQL.
Questo articolo è suddiviso nei seguenti passaggi:
- Configurare il foglio di lavoro
- Lavorare con il framework Zend Gdata
- Creazione di funzioni globali
- Registrazione di un nuovo utente
- Accedere come utente
- Consentire agli utenti di aggiungere luoghi sulla mappa
- Creare la mappa
- Conclusione
Configurare il foglio di lavoro
Utilizzeremo Google Fogli di lavoro per archiviare tutti i dati del sistema. Esistono due tipi di dati che dobbiamo archiviare: informazioni degli account utente e luoghi aggiunti dagli utenti, quindi creeremo un foglio di lavoro per ogni tipo di dati. interagiremo con i fogli di lavoro utilizzando il loro feed elenco, che si basa sulla prima riga di un foglio di lavoro contenente le etichette colonna e su ogni riga successiva contenente i dati.
Visita docs.google.com e crea un nuovo foglio di lavoro. Rinomina il foglio di lavoro predefinito in "Utenti" e crea colonne denominate "utente", "password" e "sessione". Aggiungi un altro foglio, rinominalo in "Locations" e crea le colonne "user", "status", "lat", "lng" e "date". In alternativa, se non ritieni che il lavoro manuale debba essere eseguito, scarica questo modello e importalo in un foglio di lavoro Google utilizzando il comando File -> Import (Importa).
I dati dell'account utente devono essere mantenuti privati (visibili solo al proprietario del foglio di lavoro), mentre i luoghi aggiunti dall'utente verranno visualizzati su una mappa visibile pubblicamente. Fortunatamente, i fogli di lavoro di Google consentono di decidere selettivamente quali fogli di lavoro di un foglio di lavoro possono essere pubblici e quali devono rimanere privati (impostazione predefinita). Per pubblicare il foglio di lavoro "Sedi", fai clic sulla scheda "Pubblica", fai clic su "Pubblica ora", seleziona la casella di controllo "Ripubblica automaticamente", quindi nel menu a discesa "Quali parti?" seleziona "Foglio solo "Località". Le opzioni corrette sono mostrate nello screenshot di seguito:

Utilizzo di Zend GData Framework
L'API Google Fogli di lavoro fornisce un'interfaccia HTTP per le operazioni CRUD, come il recupero e l'inserimento di righe, l'aggiornamento e l'eliminazione di righe. Il framework Zend fornisce un wrapper PHP sopra l'API (e le altre API GData) in modo che tu non debba preoccuparti di implementare le operazioni HTTP non elaborate. Il framework Zend richiede PHP 5.
Se non lo hai già fatto, scarica il framework Zend e caricalo sul tuo server. Il framework è disponibile qui: http://framework.zend.com/download/gdata.
Devi modificare il valore include_path PHP per includere la libreria Zend. Puoi farlo in diversi modi, a seconda del livello di diritti di amministratore sul tuo server. Un modo è aggiungere questa riga sopra le istruzioni richieste in qualsiasi file PHP che utilizza la libreria:
ini_set("include_path", ".:/usr/lib/php:/usr/local/lib/php:../../../library/");
Per testarla, esegui la demo Fogli di lavoro inserendola dalla riga di comando nella cartella demo/Zend/Gdata:
php Spreadsheet-ClientLogin.php --user=YourGMailUsername --pass=YourPassword
Se funziona, dovrebbe essere visualizzato un elenco dei tuoi fogli di lavoro. Se ricevi un messaggio di errore, verifica che il percorso di inclusione sia impostato correttamente e che sia installato PHP 5.
Creazione di funzioni globali
Tutti gli script PHP che scriveremo per la mappa della community utilizzeranno i comuni comandi, le variabili e le funzioni che verranno inseriti in un unico file.
All'inizio del file, avremo le istruzioni necessarie per includere e caricare la libreria Zend, recuperata dall'esempio Fogli-ClientLogin.php.
Quindi definiamo le costanti che verranno utilizzate in tutti i file: la chiave del foglio di lavoro e i due ID del foglio di lavoro. Per trovare le informazioni del foglio di lavoro, aprilo, fai clic sulla scheda "Pubblica", quindi su "Altre opzioni di pubblicazione". Seleziona "ATOM" dall'elenco a discesa Formato file e fai clic su "Genera URL". Verrà visualizzato un messaggio simile al seguente:
http://spreadsheets.google.com/feeds/list/o16162288751915453340.4016005092390554215/od6/public/basic
La chiave del foglio di lavoro è la lunga stringa alfanumerica dopo "/list/" e l'ID del foglio di lavoro è la stringa di 3 caratteri successiva. Per trovare l'ID altro foglio di lavoro, seleziona l'altro foglio dal menu a discesa "Quali fogli?".
Quindi creeremo 3 funzioni: setupClient, getWkshtListFeed e PrintFeed. In setupClient, imposteremo il nome utente e la password di Gmail, eseguiremo l'autenticazione con ClientLogin e restituiremo un oggetto Zend_Gdata_spreadsheets. In getWkshtListFeed, restituiamo un elenco di fogli di lavoro per la chiave e l'ID del foglio di lavoro specificati, con una query di fogli di lavoro facoltativa (link). La funzione PrintFeed è tratta dall'esempio Fogli-ClientLogin.php e potrebbe esserti utile per il debug. Verrà creato un oggetto del feed che verrà stampato sullo schermo.
Il PHP che lo mostra è illustrato di seguito (communitymap_globals.php):
<?php ini_set("include_path", ".:/usr/lib/php:/usr/local/lib/php:../../../library/"); require_once 'Zend/Loader.php'; Zend_Loader::loadClass('Zend_Gdata'); Zend_Loader::loadClass('Zend_Gdata_ClientLogin'); Zend_Loader::loadClass('Zend_Gdata_Spreadsheets'); Zend_Loader::loadClass('Zend_Http_Client'); define("SPREADSHEET_KEY", "o16162288751915453340.4016005092390554215"); define("USER_WORKSHEET_ID", "od6"); define("LOC_WORKSHEET_ID", "od7"); function setupClient() { $email = "your.name@gmail.com"; $password = "yourPassword"; $client = Zend_Gdata_ClientLogin::getHttpClient($email, $password, Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME); $gdClient = new Zend_Gdata_Spreadsheets($client); return $gdClient; } function getWkshtListFeed($gdClient, $ssKey, $wkshtId, $queryString=null) { $query = new Zend_Gdata_Spreadsheets_ListQuery(); $query->setSpreadsheetKey($ssKey); $query->setWorksheetId($wkshtId); if ($queryString !== null) { $query->setSpreadsheetQuery($queryString); } $listFeed = $gdClient->getListFeed($query); return $listFeed; } function printFeed($feed) { print "printing feed"; $i = 0; foreach($feed->entries as $entry) { if ($entry instanceof Zend_Gdata_Spreadsheets_CellEntry) { print $entry->title->text .' '. $entry->content->text . "\n"; } else if ($entry instanceof Zend_Gdata_Spreadsheets_ListEntry) { print $i .' '. $entry->title->text .' '. $entry->content->text . "\n"; } else { print $i .' '. $entry->title->text . "\n"; } $i++; } } ?>
Registrazione di un nuovo utente
Per registrare un nuovo utente, vogliamo una pagina HTML rivolta all'utente con campi di testo e un pulsante Invia e uno script di backend PHP per aggiungere l'utente al foglio di lavoro.
Nello script PHP, includiamo prima lo script globale, quindi riceviamo i valori di nome utente e password dalla variabile GET. Quindi, configuriamo un client Fogli di lavoro e richiediamo al feed dell'elenco del foglio di lavoro degli utenti una stringa di query di limitare i risultati solo alle righe in cui la colonna del nome utente corrisponde al nome utente trasmesso nello script. Se non riceviamo righe nel risultato del feed elenco, possiamo procedere con sicurezza sapendo che il nome utente trasmesso è univoco. Prima di inserire una riga nel feed elenco, creiamo un array associatovo dei valori della colonna: il nome utente, una crittografia della password che utilizza la funzione sha1 di PHP e un carattere di riempimento per la sessione. In seguito, chiamiamo insertRow sul client per i fogli di lavoro e inserisci l'array associatovo, la chiave dei fogli di lavoro e l'ID del foglio di lavoro. Se l'oggetto restituito è ListListEntry, mostriamo un messaggio Successo.
Il PHP che lo visualizza è riportato di seguito (communitymap_newuser.php):
<?php require_once 'communitymap_globals.php'; $username = $_GET['username']; $password = $_GET['password']; $gdClient = setupClient(); $listFeed = getWkshtListFeed($gdClient, SPREADSHEET_KEY, USER_WORKSHEET_ID, ('user='.$username)); $totalResults = $listFeed->totalResults; if ( $totalResults != "0") { // Username already exists exit; } $rowArray["user"] = $username; $rowArray["password"] = sha1($password); $rowArray["session"] = "a"; $entry = $gdClient->insertRow($rowArray, SPREADSHEET_KEY, USER_WORKSHEET_ID); if ($entry instanceof Zend_Gdata_Spreadsheets_ListEntry) { echo "Success!"; } ?>
Nella pagina di registrazione possiamo includere l'API di Google Maps in modo da utilizzare la sua funzione wrapper XMLHttpRequest chiamata GDownloadUrl. Quando l'utente fa clic sul pulsante di invio, riceviamo il nome utente e la password dai campi di testo, costruiamo una stringa di parametri dai loro valori e richiamano GDownloadUrl nell'URL e nei parametri dello script. Poiché stiamo inviando informazioni sensibili, utilizziamo la versione HTTP POST di GDownloadUrl (inviando i parametri come terzo argomento anziché aggiungendoli all'URL). Nella funzione di callback, verifichiamo che la risposta funzioni correttamente e visualizziamo un messaggio appropriato per l'utente.
Di seguito sono riportati uno screenshot e il codice per una pagina di registrazione di esempio (communitymap_register.htm):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title> Community Map - Register/Login </title> <script src="http://maps.google.com/maps?file=api&v=2&key=abcdef" type="text/javascript"></script> <script type="text/javascript"> function register() { var username = document.getElementById("username").value; var password = document.getElementById("password").value; var url = "communitymap_newuser.php?"; var params = "username=" + username + "&password=" + password; GDownloadUrl(url, function(data, responseCode) { if (data.length > 1) { document.getElementById("message").innerHTML = "Successfully registered." + "<a href='communitymap_login.htm'>Proceed to Login</a>."; } else { document.getElementById("message").innerHTML = "Username already exists. Try again."; } }, params); } </script> </head> <body> <h1>Register for Community Map</h1> <input type="text" id="username"> <input type="password" id="password"> <input type="button" onclick="register()" value="Register"> <div id="message"></div> </body> </html>
Accesso di un utente
Per consentire agli utenti di accedere al nostro sistema, vogliamo una pagina HTML rivolta all'utente, che chieda loro di inserire nome utente e password, e uno script PHP per verificare le informazioni di accesso, creare un ID di sessione e ritrasmetterlo alla pagina di accesso per impostare un cookie. L'utente rimarrà collegato tramite il cookie di sessione nelle pagine successive.
Nello script PHP, includiamo prima lo script globale, quindi riceviamo i valori di nome utente e password dalla variabile GET. Quindi, configuriamo un client Fogli di lavoro e richiediamo al feed dell'elenco del foglio di lavoro degli utenti una stringa di query di limitare i risultati solo alle righe in cui la colonna del nome utente corrisponde al nome utente trasmesso nello script.
Nella riga restituita, verificheremo che l'hash della password trasmesso corrisponda a quello memorizzato nel foglio di lavoro. In questo caso, creeremo un ID sessione utilizzando le funzioni md5, uniqid e rand. Aggiorneremo poi la riga del foglio di lavoro con la sessione e la mostreremo sullo schermo se l'aggiornamento della riga ha esito positivo.
Il codice PHP che compare è mostrato di seguito (communitymap_loginuser.php):
<?php require_once 'communitymap_globals.php'; $username = $_POST['username']; $password = $_POST['password']; $gdClient = setupClient(); $listFeed = getWkshtListFeed($gdClient, SPREADSHEET_KEY, USER_WORKSHEET_ID, ('user='.$username)); $password_hash = sha1($password); $row = $listFeed->entries[0]; $rowData = $row->getCustom(); foreach($rowData as $customEntry) { if ($customEntry->getColumnName()=="password" && $customEntry->getText()==$password_hash) { $updatedRowArray["user"] = $username; $updatedRowArray["password"] = sha1($password); $updatedRowArray["session"] = md5(uniqid(rand(), true)); $updatedRow = $gdClient->updateRow($row, $updatedRowArray); if ($updatedRow instanceof Zend_Gdata_Spreadsheets_ListEntry) { echo $updatedRowArray["session"]; } } } ?>
Nella pagina di accesso, possiamo includere di nuovo l'API di Google Maps in modo da poter utilizzare la funzione del wrapper XMLHttpRequest, denominata GDownloadUrl. Quando l'utente fa clic sul pulsante Invia, il nome utente e la password vengono recuperati dai campi di testo, viene creato l'URL dello script con i parametri di ricerca e viene chiamata GDownloadUrl nell'URL dello script. Nella funzione di callback, impostiamo un cookie con l'ID sessione restituito dallo script oppure, se non viene restituito, viene generato un messaggio di errore. La funzione setCookie deriva da un cookie.js basato sul tutorial JavaScript di w3c: http://www.w3schools.com/js/js_cookies.asp.
Di seguito sono riportati uno screenshot e il codice per una pagina di accesso di esempio (communitymap_login.htm):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Community Map - Login</title> <script src="http://maps.google.com/maps?file=api&v=2&key=abcdef" type="text/javascript"></script> <script src="cookies.js" type="text/javascript"></script> <script type="text/javascript"> function login() { var username = document.getElementById("username").value; var password = document.getElementById("password").value; var url = "communitymap_loginuser.php?username=" + username + "&password=" + password; GDownloadUrl(url, function(data, responseCode) { if (data.length > 1) { setCookie("session", data, 5); } else { document.getElementById("nessage").innerHTML = "Error. Try again."; } }); } </script> </head> <body> <h1>Login for Community Map</h1> <input type="text" id="username"> <input type="password" id="password"> <input type="button" onclick="login()" value="Login"> <div id="message"></div> </body> </html>
Consentire agli utenti di aggiungere luoghi sulla mappa
Per consentire a un utente di aggiungere luoghi alla nostra mappa, vogliamo che venga visualizzata una pagina HTML rivolta all'utente per indicare le informazioni sulla posizione dell'utente e due script PHP: uno per verificare che gli utenti abbiano effettuato l'accesso tramite il cookie impostato e l'altro per aggiungere la località al foglio di lavoro relativo alle sedi.
Nel primo script PHP che controlla se un utente ha eseguito l'accesso, includiamo prima lo script globale e quindi riceviamo il valore della sessione dalla variabile GET. A questo punto, configuriamo un client Fogli di lavoro e richiediamo al feed elenco del foglio di lavoro degli utenti una stringa di query di limitare i risultati solo a quelle righe in cui la colonna di sessione corrisponde al valore della sessione trasmesso nello script. Analizziamo poi le voci personalizzate di quel feed (quelle che corrispondono alle intestazioni di colonna) e stampiamo il nome utente corrispondente di quella sessione, se disponibile.
Il PHP che lo mostra è mostrato qui sotto (communitymap_checksession.php):
<?php require_once 'communitymap_globals.php'; $session = $_GET['session']; $gdClient = setupClient(); $listFeed = getWkshtListFeed($gdClient, SPREADSHEET_KEY, USER_WORKSHEET_ID, ('session='.$session)); if ( count($listFeed->entries) > 0) { $row = $listFeed->entries[0]; $rowData = $row->getCustom(); foreach($rowData as $customEntry) { if ($customEntry->getColumnName()=="user") { echo $customEntry->getText(); } } } ?>
Nel secondo script PHP che consente a un utente di aggiungere una località, prima replica il codice da communitymap_checksession.php, per assicurarci che l'utente esegua ancora l'accesso e sia valido. Quindi, una volta ottenuto un nome utente valido dal foglio degli utenti, otteniamo i valori di posizione, lat e lng dalla variabile GET. Inseriamo tutti quei valori in una matrice associativa e aggiungiamo anche un valore "date" utilizzando la funzione date() di PHP, in modo che sappiamo quando l'utente ha aggiunto il luogo. Passiamo l'array associatovo, la costante della chiave dei fogli di lavoro e l'ID del foglio di lavoro delle posizioni costante nella funzione insertRow. Se la riga di una nuova sede è stata aggiunta al foglio di lavoro, verrà visualizzato il messaggio "Operazione riuscita". Se visualizzi un errore in questo passaggio, è probabile che ci sia una mancata corrispondenza con i nomi delle intestazioni di colonna. Le chiavi nella matrice associativa devono corrispondere alle intestazioni di colonna nel foglio di lavoro specificato dalla chiave e dall'ID del foglio di lavoro.
Il PHP che lo mostra qui sotto (communitymap_addlocation.php):
<?php require_once 'communitymap_globals.php'; $session = $_GET['session']; $gdClient = setupClient(); $listFeed = getWkshtListFeed($gdClient, SPREADSHEET_KEY, USER_WORKSHEET_ID, ('session='.$session)); if ( count($listFeed->entries) > 0) { $row = $listFeed->entries[0]; $rowData = $row->getCustom(); foreach($rowData as $customEntry) { if ($customEntry->getColumnName()=="user") { $user = $customEntry->getText(); } } $place = $_GET['place']; $lat = $_GET['lat']; $lng = $_GET['lng']; $rowArray["user"] = $user; $rowArray["place"] = $place; $rowArray["lat"] = $lat; $rowArray["lng"] = $lng; $rowArray["date"] = date("F j, Y, g:i a"); $entry = $gdClient->insertRow($rowArray, SPREADSHEET_KEY, LOC_WORKSHEET_ID); if ($entry instanceof Zend_Gdata_Spreadsheets_ListEntry) { echo "Success!\n"; } } ?>
Nella pagina di aggiunta della località, possiamo includere di nuovo l'API di Google Maps in modo da poter utilizzare GDownloadUrl e creare una mappa. Dopo il caricamento della pagina, utilizziamo la funzione getCookie di cookie.js per recuperare il valore della sessione. Se la stringa di sessione è nulla o vuota, viene visualizzato un messaggio di errore. In caso contrario, chiamiamo GDownloadUrl su map.checksession.php, inviando la sessione. Se viene restituito un nome utente, mostriamo un messaggio di benvenuto all'utente, mostriamo il modulo di aggiunta di un luogo e carichiamo la mappa. È un campo di testo dell'indirizzo, della mappa e dei campi di testo per il toponimo, la latitudine e la longitudine. Se l'utente non conosce già la latitudine/longitudine del luogo, può geocodificarlo inserendo il suo indirizzo nel modulo e premendo "invia". Verrà inviata una chiamata al GClientGeocoder dell'API Map, che posizionerà un indicatore sulla mappa se trova l'indirizzo e completerà automaticamente i campi di testo lat/lng.
Quando l'utente è soddisfatto, può premere il pulsante "Aggiungi sede". Quindi, nel file JavaScript, otterremo i valori per user, place, lat e lng e li invieremo allo script communitymap_addlocation.php con GDownloadUrl
.
Se lo script restituisce un esito positivo, verrà visualizzato un messaggio che indica la riuscita dell'operazione.
Di seguito sono riportati uno screenshot e il codice per un esempio di pagina di aggiunta località (communitymap_addlocation.htm):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> <title>Community Map - Add a Place!</title> <script src="http://maps.google.com/maps?file=api&v=2.x&key=abcdef" type="text/javascript"></script> <script src="cookies.js" type="text/javascript"></script> <script type="text/javascript"> //<![CDATA[ var map = null; var geocoder = null; var session = null; function load() { session = getCookie('session'); if (session != null && session != "") { url = "communitymap_checksession.php?session=" + session; GDownloadUrl(url, function(data, responseCode) { if (data.length > 0) { document.getElementById("message").innerHTML = "Welcome " + data; document.getElementById("content").style.display = "block"; map = new GMap2(document.getElementById("map")); map.setCenter(new GLatLng(37.4419, -122.1419), 13); geocoder = new GClientGeocoder(); } }); } else { document.getElementById("message").innerHTML = "Error: Not logged in."; } } function addLocation() { var place = document.getElementById("place").value; var lat = document.getElementById("lat").value; var lng = document.getElementById("lng").value; var url = "communitymap_addlocation.php?session=" + session + "&place=" + place + "&lat=" + lat + "&lng=" + lng; GDownloadUrl(url, function(data, responseCode) { GLog.write(data); if (data.length > 0) { document.getElementById("message").innerHTML = "Location added."; } }); } function showAddress(address) { if (geocoder) { geocoder.getLatLng( address, function(point) { if (!point) { alert(address + " not found"); } else { map.setCenter(point, 13); var marker = new GMarker(point, {draggable:true}); document.getElementById("lat").value = marker.getPoint().lat().toFixed(6); document.getElementById("lng").value = marker.getPoint().lng().toFixed(6); map.addOverlay(marker); GEvent.addListener(marker, "dragend", function() { document.getElementById("lat").value = marker.getPoint().lat().toFixed(6); document.getElementById("lng").value = marker.getPoint().lng().toFixed(6); }); } } ); } } //]]> </script> </head> <body onload="load()" onunload="GUnload()"> <div id="message"></div> <div id="content" style="display:none"> <form action="#" onsubmit="showAddress(this.address.value); return false"> <p> <input type="text" size="60" name="address" value="1600 Amphitheatre Pky, Mountain View, CA" /> <input type="submit" value="Geocode!" /> </form> </p> <div id="map" style="width: 500px; height: 300px"></div> Place name: <input type="text" size="20" id="place" value="" /> <br/> Lat: <input type="text" size="20" id="lat" value="" /> <br/> Lng: <input type="text" size="20" id="lng" value="" /> <br/> <input type="button" onclick="addLocation()" value="Add a location" /> </form> </div> </body> </html>
Creazione della mappa
Poiché hai reso pubblico il foglio di lavoro delle località nel primo passaggio, non è necessaria alcuna programmazione lato server per creare una mappa. In effetti, non è richiesta alcuna programmazione. Puoi utilizzare questa procedura guidata Fogli di lavoro -> Mappa per generare tutto il codice necessario per la mappa. La procedura guidata scarica le voci del foglio di lavoro nella pagina aggiungendo un tag script che punta all'output JSON del feed e specifica una funzione di callback che viene chiamata dopo il download di JSON. Ulteriori informazioni sono disponibili qui.
Esempio di codice HTML per eseguire questa operazione, disponibile qui: mainmap.htm. Di seguito è riportato uno screenshot:

Conclusione
Speriamo che sul tuo server sia in esecuzione il tuo sistema di mappe fornito dagli utenti. Questo articolo fornisce il codice di base necessario per gli aspetti essenziali di questo sistema, ma ora che hai acquisito familiarità con la libreria dei fogli di lavoro Zend, dovresti essere in grado di estendere il sistema per soddisfare le tue esigenze specifiche. Se hai riscontrato errori durante il processo, ricorda che puoi utilizzare il comando echo
in PHP o il codice GLog.write()
dell'API Map in JavaScript per il debug e puoi sempre pubblicare post nei forum per sviluppatori dell'API di Google Maps o dell'API Fogli di lavoro per ricevere ulteriore assistenza.