outubro de 2008
Introdução
Público-alvo
Este artigo o ajudará a criar um gadget do Blogger. Você precisa conhecer as APIs de dados do Google e a biblioteca de cliente JavaScript. Você também precisa ser fluente em JavaScript e ter alguma experiência com a implementação de um gadget do GitHub usando o formato gadgets.* API.
Este exemplo também demonstra como usar bibliotecas externas com sucesso em seus gadgets. Usei o jQuery (principalmente para efeitos de IU) e o TinyMCE, um ótimo plug-in de editor de texto avançado do WYSIWYG.
Motivação
É necessário muito pouco JavaScript para criar um gadget que utilize JSON com uma das APIs de dados do Google. O maior problema de um gadget é o fato de os dados
serem públicos e somente leitura. Para criar mais gadgets interessantes, você precisa acessar os dados privados de um usuário (algo que requer autenticação). Até agora, não existia uma
ótima maneira de aproveitar as APIs da Conta do Google. O XPN requer redirecionamentos do navegador, e o ClientLogin expõe as credenciais de um usuário, do lado do cliente. Até mesmo invadir um gadget do type="url"
foi inconveniente.
Digite o proxy OAuth.
Proxy OAuth
Se você não conhece o OAuth, ele é um padrão de autenticação que permite ao usuário compartilhar dados privados com outro site ou gadget. A especificação do OAuth exige que todas as solicitações de dados sejam assinadas digitalmente. Isso é ótimo para a segurança, mas no caso de um gadget JavaScript, não é seguro gerenciar chaves privadas e criar assinaturas digitais. Também há a complicação adicional de problemas entre domínios.
Felizmente, esses problemas são resolvidos com o uso de um recurso da plataforma de gadgets chamado de proxy OAuth. O proxy OAuth foi projetado para facilitar a vida dos desenvolvedores de gadgets. Ele oculta grande parte dos detalhes de autenticação do OAuth e faz o trabalho pesado para você. O proxy assina solicitações de dados em nome do seu gadget, portanto, não é necessário gerenciar chaves privadas ou se preocupar com solicitações de assinatura. Simplesmente funciona!
O proxy OAuth é baseado em um projeto de código aberto chamado Shindig, que é uma implementação da especificação do gadget.
Observação: o proxy OAuth só é compatível com gadgets que utilizam a API gadgets.*
e são executados em contêineres do WebGL.
Ela não é compatível com a API de gadgets legados.
Como começar
No restante deste tutorial, o foco será a criação de um gadget para acessar os dados do Blogger de um usuário. Vamos passar pela autenticação (usando o proxy OAuth), pela biblioteca de cliente JavaScript e, por fim, por uma entrada no Blogger.
Autenticação
Antes de mais nada, precisamos dizer para o gadget usar o OAuth. Para fazer isso, adicione o elemento <OAuth>
na seção <ModulePrefs>
do 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>
Os três endpoints de URL no elemento <Service>
correspondem aos endpoints do token OAuth do Google. Veja a seguir uma explicação dos parâmetros de consulta:
scope
Este parâmetro é obrigatório no URL da solicitação. Seu gadget só poderá acessar os dados do(s)
scope
(s) usado(s) neste parâmetro. Neste exemplo, o gadget acessará o Blogger. Se o gadget quiser acessar mais de uma API de dados do Google, concatene asscope
adicionais com um%20
. Por exemplo, se você quiser acessar o Agenda e o Blogger, defina o escopo comohttp://www.blogger.com/feeds/%20http://www.google.com/calendar/feeds/
.oauth_callback
Esse parâmetro é opcional no URL de autorização. A página de aprovação do OAuth vai redirecionar você para esse URL depois que o usuário aprovar o acesso aos dados. É possível deixar esse parâmetro fora de você, defini-lo como sua própria "página aprovada" ou usar
http://oauth.gmodules.com/gadgets/oauthcallback
. A segunda oferece a melhor experiência do usuário quando ele instala o gadget pela primeira vez. Essa página fornece um snippet de JavaScript que fecha automaticamente a janela pop-up.
Agora que temos nosso gadget usando o OAuth, o usuário precisa aprovar o acesso aos seus dados. Este é o fluxo de autenticação:
- O gadget é carregado pela primeira vez e tenta acessar os dados do Blogger do usuário.
- A solicitação falha porque o usuário não concedeu acesso ao gadget. Felizmente, o objeto retornado na resposta
contém um URL (
response.oauthApprovalUrl
), em que vamos enviar o usuário para login. O gadget exibe a mensagem "Fazer login no Blogger" e define o valor deoauthApprovalUrl
. - Em seguida, o usuário clica em "Fazer login no Blogger", e a página de aprovação do OAuth é aberta em uma janela separada. O gadget aguarda a conclusão do processo de aprovação pelo usuário mostrando um link: "Eu aprovei o acesso".
- No pop-up, o usuário escolhe conceder/negar acesso ao nosso gadget. Depois de clicar em "Permitir acesso", o usuário será direcionado para
http://oauth.gmodules.com/gadgets/oauthcallback
, e a janela será fechada. - O gadget reconhece a janela fechada e tenta acessar o Blogger novamente, solicitando novamente os dados do usuário. Para detectar o fechamento da janela, usei um gerenciador de pop-ups. Se você não usar esse código, o usuário poderá clicar manualmente em "Aprovei o acesso".
- O gadget agora exibe a sua IU normal. Essa visualização persistirá, a menos que o token de autenticação seja revogado em IssuedtmpTokens.
Dos passos acima, os gadgets têm a noção de três estados diferentes:
- Não autenticado. O usuário precisa iniciar o processo de aprovação.
- Aguardando o usuário aprovar o acesso aos dados.
- Autenticado. Os gadgets mostram seu estado funcional normal.
No meu gadget, usei contêineres <div>
para separar cada estágio:
<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>
é exibido sozinho usando showOnly()
. Consulte o exemplo de gadget completo para ver detalhes sobre essa função.
Como usar a biblioteca de cliente JavaScript
Para buscar conteúdo remoto no GitHub, faça uma chamada para o método gadgets.io.makeRequest
usando a API gadgets.*
.
No entanto, como estamos criando um gadget de dados do Google, não é necessário tocar nas APIs gadgets.io.*
. Em vez disso, aproveite a biblioteca de cliente JavaScript, que tem métodos especiais para fazer solicitações para cada serviço de dados do Google.
Observação: no momento da publicação deste artigo, a biblioteca JavaScript era compatível apenas com Blogger, Agenda,
Contatos,
Finanças e Google Base. Para usar uma das outras
APIs, use gadgets.io.makeRequest
sem a biblioteca.
Carregar a biblioteca
Para carregar a biblioteca JavaScript, inclua o carregador comum na seção <Content>
e importe a biblioteca depois que o gadget for inicializado. Inserir um callback para gadgets.util.registerOnLoadHandler()
ajuda a determinar quando o gadget está pronto:
<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>
A chamada para blogger.useOAuth('google')
diz à biblioteca para usar o proxy OAuth em vez de CmdJS, seu método normal de autenticação.
Por fim, o gadget tenta recuperar os dados do Blogger do usuário chamando fetchData()
. Esse método é definido abaixo.
Buscando dados
Agora que tudo está configurado, como podemos GET
ou POST
os dados para o Blogger?
Um paradigma comum no WebGL é definir uma função chamada fetchData()
em seu gadget. Esse método normalmente processa os diferentes estágios
da autenticação e busca dados usando gadgets.io.makeRequest
. Como estamos usando a biblioteca de cliente JavaScript, gadgets.io.makeRequest
é substituído por uma chamada para 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); }
Na segunda vez que essa função é chamada, response.feed
contém dados.
Observação: a getBlogFeed()
usa a mesma função para o gerenciador de callback e erro.
Postar uma entrada no Blogger
A última etapa é postar uma nova entrada em um blog. O código abaixo demonstra o que acontece quando o usuário clica no botão "Salvar".
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); }
Conclusão
Agora você tem os elementos básicos para começar a codificar um gadget, além das APIs de dados do Google.
Esperamos que, neste artigo, você tenha apreciado a facilidade com que o proxy OAuth facilita a autenticação do gadget. Combinar essa ferramenta com a biblioteca de cliente JavaScript de dados do Google facilita a criação de gadgets interessantes, interativos e sofisticados.
Se você tiver alguma dúvida ou comentário sobre este artigo, acesse o fórum de discussão das APIs das Contas do Google.
Recursos
- Como escrever gadgets OAuth (documentação completa dos gadgets)
- Como usar o OAuth com as APIs de dados do Google (artigo sobre o uso do OAuth com as APIs de dados do Google)
- Autenticação do OAuth para aplicativos da Web (documentação completa do OAuth)
- Biblioteca de cliente JavaScript
- Fórum de discussão das APIs das Contas do Google