Octubre de 2008
Introducción
Público
En este artículo, se explica cómo crear un gadget de Blogger. Se supone que conoces las APIs de datos de Google y la biblioteca cliente de JavaScript. También debes dominar JavaScript y tener experiencia en la implementación de un gadget de OpenSocial con gadgets.* API.
En este ejemplo, también se muestra cómo usar correctamente bibliotecas externas en tus gadgets. Usé jQuery (principalmente por sus efectos de IU) y TinyMCE, un excelente complemento de editor de texto enriquecido WYSIWYG.
Motivación
Se necesita muy poco código JavaScript para crear un gadget que use JSON con una de las APIs de datos de Google. El principal inconveniente de este tipo de gadget es que los datos son públicos y de solo lectura. Para crear gadgets más interesantes, necesitas acceder a los datos privados de un usuario (algo que requiere autenticación). Hasta ahora, no había una buena manera de aprovechar las APIs de la Cuenta de Google. AuthSub requiere redireccionamientos del navegador, y ClientLogin expone las credenciales de un usuario del lado del cliente. Incluso hackear un gadget type="url"
ha sido inconveniente.
Ingresa al proxy de OAuth.
Proxy de OAuth
Si no conoces OAuth, se trata de un estándar de autenticación que permite que un usuario comparta sus datos privados con otro sitio web o gadget. La especificación de OAuth requiere que todas las solicitudes de datos estén firmadas digitalmente. Esto es excelente para la seguridad, pero, en el caso de un gadget de JavaScript, administrar claves privadas y crear firmas digitales no es seguro. También se suma la complicación de los problemas multidominio.
Por suerte, estos problemas se resuelven aprovechando una función de la plataforma de gadgets llamada proxy de OAuth. El proxy de OAuth está diseñado para facilitar la vida de los desarrolladores de gadgets. Oculta gran parte de los detalles de autenticación de OAuth y hace el trabajo pesado por ti. El proxy firma las solicitudes de datos en nombre de tu gadget, por lo que no es necesario administrar claves privadas ni preocuparse por firmar solicitudes. ¡Simplemente funciona!
El proxy de OAuth se basa en un proyecto de código abierto llamado Shindig, que es una implementación de la especificación de gadgets.
Nota: El proxy de OAuth solo se admite para los gadgets que utilizan la API de gadgets.*
y se ejecutan en contenedores de OpenSocial.
No es compatible con la API de gadgets heredados.
Comenzar
El resto de este instructivo se centrará en la creación de un gadget para acceder a los datos de Blogger de un usuario. Veremos la autenticación (con el proxy de OAuth), el uso de la biblioteca cliente de JavaScript y, por último, la publicación de una entrada en Blogger.
Autenticación
Primero, debemos indicarle al gadget que use OAuth. Para ello, agrega el elemento <OAuth>
en la sección <ModulePrefs>
del gadget:
<ModulePrefs> ... <OAuth> <Service name="google"> <Access url="https://www.google.com/accounts/OAuthGetAccessToken" method="GET" /> <Request url="https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.blogger.com/feeds/" method="GET" /> <Authorization url="https://www.google.com/accounts/OAuthAuthorizeToken? oauth_callback=http://oauth.gmodules.com/gadgets/oauthcallback" /> </Service> </OAuth> ... </ModulePrefs>
Los tres extremos de URL del elemento <Service>
corresponden a los extremos de tokens de OAuth de Google. A continuación, se explica cada parámetro de la consulta:
scope
Este parámetro es obligatorio en la URL de la solicitud. El gadget solo podrá acceder a los datos de los
scope
que se usen en este parámetro. En este ejemplo, el gadget accederá a Blogger. Si tu gadget quería acceder a más de una API de datos de Google, concatena lasscope
adicionales con un%20
. Por ejemplo, si deseas acceder a Calendar y Blogger, establece el alcance enhttp://www.blogger.com/feeds/%20http://www.google.com/calendar/feeds/
.oauth_callback
Este parámetro es opcional en la URL de autorización. La página de aprobación de OAuth redireccionará a esta URL después de que el usuario apruebe el acceso a sus datos. Puedes optar por no incluir este parámetro, establecerlo en tu propia "página aprobada" o, preferentemente, usar
http://oauth.gmodules.com/gadgets/oauthcallback
. Esta última opción proporciona la mejor experiencia del usuario cuando los usuarios instalan tu gadget por primera vez. Esa página proporciona un fragmento de JavaScript que cierra automáticamente la ventana emergente.
Ahora que nuestro gadget usa OAuth, el usuario debe aprobar el acceso a sus datos. Este es el flujo de autenticación:
- El gadget se carga por primera vez y trata de acceder a los datos de Blogger del usuario.
- La solicitud falla porque el usuario no otorgó acceso al gadget. Afortunadamente, el objeto que se devuelve en la respuesta contiene una URL (
response.oauthApprovalUrl
) a la que enviaremos al usuario para que acceda. El gadget muestra el mensaje "Acceder a Blogger" y establece su href en el valor deoauthApprovalUrl
. - A continuación, el usuario hace clic en "Acceder a Blogger" y la página de aprobación de OAuth se abre en una ventana separada. El gadget espera a que el usuario termine el proceso de aprobación mostrando el vínculo "Aprobé el acceso".
- En la ventana emergente, el usuario elegirá si otorga o deniega el acceso a nuestro gadget. Una vez que haga clic en "Otorgar acceso", se lo dirigirá a
http://oauth.gmodules.com/gadgets/oauthcallback
y se cerrará la ventana. - El gadget reconoce que se cerró la ventana y vuelve a intentar acceder a Blogger solicitando los datos del usuario. Para detectar el cierre de la ventana, usé un controlador de ventanas emergentes. Si no usas ese código, el usuario puede hacer clic manualmente en "Aprobé el acceso".
- El gadget ahora muestra su IU normal. Esta vista persistirá, a menos que se revoque el token de autenticación en IssuedAuthSubTokens.
Por lo tanto, según los pasos anteriores, los gadgets tienen la noción de tres estados diferentes:
- Sin autenticar. El usuario debe iniciar el proceso de aprobación.
- Se espera a que el usuario apruebe el acceso a sus datos.
- Autenticado. El gadget muestra su estado funcional normal.
En mi gadget, usé contenedores <div>
para separar cada etapa:
<Content type="html"> <![CDATA[ <!-- Normal state of the gadget. The user is authenticated --> <div id="main" style="display:none"> <form id="postForm" name="postForm" onsubmit="savePost(this); return false;"> <div id="messages" style="display: none"></div> <div class="selectFeed">Publish to: <select id="postFeedUri" name="postFeedUri" disabled="disabled"><option>loading blog list...</option></select> </div> <h4 style="clear:both">Title</h4> <input type="text" id="title" name="title"/> <h4>Content</h4> <textarea id="content" name="content" style="width:100%;height:200px;"></textarea> <h4 style="float:left;">Labels (comma separated)</h4><img src="blogger.png" style="float:right"/> <input type="text" id="categories" name="categories"/> <p><input type="submit" id="submitButton" value="Save"/> <input type="checkbox" id="draft" name="draft" checked="checked"/> <label for="draft">Draft?</label></p> </form> </div> <div id="approval" style="display: none"> <a href="#" id="personalize">Sign in to Blogger</a> </div> <div id="waiting" style="display: none"> <a href="#" id="approvalLink">I've approved access</a> </di <!-- An errors section is not necessary but great to have --> <div id="errors" style="display: none"></div> <!-- Also not necessary, but great for informing users --> <div id="loading"> <h3>Loading...</h3> <p><img src="ajax-loader.gif"></p> </div> ]]> </Content>
Cada <div>
se muestra por sí solo con showOnly()
. Consulta el gadget de ejemplo completo para obtener detalles sobre esa función.
Usa la biblioteca cliente de JavaScript
Para recuperar contenido remoto en OpenSocial, debes llamar al método gadgets.io.makeRequest
con la API de gadgets.*
.
Sin embargo, como estamos compilando un gadget de Google Data, no es necesario que toquemos las APIs de gadgets.io.*
. En su lugar, aprovecha la biblioteca cliente de JavaScript, que tiene métodos especiales para realizar solicitudes a cada servicio de datos de Google.
Nota: En el momento de escribir este artículo, la biblioteca de JavaScript solo admite Blogger, Calendario, Contactos, Finanzas y Google Base. Para usar una de las otras APIs, usa gadgets.io.makeRequest
sin la biblioteca.
Cómo cargar la biblioteca
Para cargar la biblioteca de JavaScript, incluye el cargador común en la sección <Content>
y, luego, importa la biblioteca una vez que se haya inicializado el gadget. Proporcionar una devolución de llamada a gadgets.util.registerOnLoadHandler()
ayudará a determinar cuándo estará listo el gadget:
<Content type="html"> <![CDATA[ ... <script src="https://www.google.com/jsapi"></script> <script type="text/javascript"> var blogger = null; // make our service object global for later // Load the JS library and try to fetch data once it's ready function initGadget() { google.load('gdata', '1.x', {packages: ['blogger']}); // Save overhead, only load the Blogger service google.setOnLoadCallback(function () { blogger = new google.gdata.blogger.BloggerService('google-BloggerGadget-v1.0'); blogger.useOAuth('google'); fetchData(); }); } gadgets.util.registerOnLoadHandler(initGadget); </script> ... ]]> </Content>
La llamada a blogger.useOAuth('google')
le indica a la biblioteca que use el proxy de OAuth (en lugar de AuthSubJS, su método de autenticación normal).
Por último, el gadget intenta recuperar los datos de Blogger del usuario llamando a fetchData()
. Ese método se define a continuación.
Obteniendo datos
Ahora que todo está configurado, ¿cómo GET
o POST
datos a Blogger?
Un paradigma común en OpenSocial es definir una función llamada fetchData()
en tu gadget. Por lo general, este método controla las diferentes etapas de la autenticación y recupera datos con gadgets.io.makeRequest
. Como usamos la biblioteca cliente de JavaScript, gadgets.io.makeRequest
se reemplaza por una llamada a blogger.getBlogFeed()
:
function fetchData() { jQuery('#errors').hide(); var callback = function(response) { if (response.oauthApprovalUrl) { // You can set the sign in link directly: // jQuery('#personalize').get(0).href = response.oauthApprovalUrl // OR use the popup.js handler var popup = shindig.oauth.popup({ destination: response.oauthApprovalUrl, windowOptions: 'height=600,width=800', onOpen: function() { showOnly('waiting'); }, onClose: function() { showOnly('loading'); fetchData(); } }); jQuery('#personalize').get(0).onclick = popup.createOpenerOnClick(); jQuery('#approvalLink').get(0).onclick = popup.createApprovedOnClick(); showOnly('approval'); } else if (response.feed) { showResults(response); showOnly('main'); } else { jQuery('#errors').html('Something went wrong').fadeIn(); showOnly('errors'); } }; blogger.getBlogFeed('http://www.blogger.com/feeds/default/blogs', callback, callback); }
La segunda vez que se llama a esta función, response.feed
contiene datos.
Nota: getBlogFeed()
usa la misma función para su devolución de llamada y el controlador de errores.
Publica una entrada en Blogger
El último paso es publicar una entrada nueva en un blog. El siguiente código muestra lo que sucede cuando el usuario hace clic en el botón "Guardar".
function savePost(form) { jQuery('#messages').fadeOut(); jQuery('#submitButton').val('Publishing...').attr('disabled', 'disabled'); // trim whitespace from the input tags var input = form.categories.value; var categories = jQuery.trim(input) != '' ? input.split(',') : []; jQuery.each(categories, function(i, value) { var label = jQuery.trim(value); categories[i] = { scheme: 'http://www.blogger.com/atom/ns#', term: label }; }); // construct the blog post entry var newEntry = new google.gdata.blogger.BlogPostEntry({ title: { type: 'text', text: form.title.value }, content: { type: 'text', text: form.content.value }, categories: categories }); // publish as draft? var isDraft = form.draft.checked; if (isDraft) { newEntry.setControl({draft: {value: google.gdata.Draft.VALUE_YES}}); } // callback for insertEntry() var handleInsert = function(entryRoot) { var entry = entryRoot.entry; var str = isDraft ? '(as draft)' : '<a href="' + entry.getHtmlLink().getHref() + '" target="_blankt">View it</a>'; jQuery('#messages').html('Post published! ' + str).fadeIn(); jQuery('#submitButton').val('Save').removeAttr('disabled'); }; // error handler for insertEntry() var handleError = function(e) { var msg = e.cause ? e.cause.statusText + ': ' : ''; msg += e.message; alert('Error: ' + msg); }; blogger.insertEntry(form.postFeedUri.value, newEntry, handleInsert, handleError); }
Conclusión
Ahora tienes los componentes básicos para comenzar a codificar un gadget sobre las APIs de Google Data.
Esperamos que este artículo te haya ayudado a comprender lo simple que es la autenticación de gadgets con el proxy de OAuth. Combinar esta potente herramienta con la biblioteca cliente de JavaScript de Google Data facilita la creación de gadgets interesantes, interactivos y sofisticados.
Si tienes preguntas o comentarios sobre este artículo, visítanos en el foro de debate de las APIs de Cuentas de Google.
Recursos
- Cómo escribir gadgets de OAuth (documentación completa de los gadgets)
- Cómo usar OAuth con las APIs de Google Data (artículo sobre el uso de OAuth con las APIs de Google Data)
- Autenticación de OAuth para aplicaciones web (documentación completa de OAuth)
- Biblioteca cliente de JavaScript
- Foro de debate sobre las APIs de Cuentas de Google