Создание гаджета данных Google

Эрик Бидельман, команда Google Data API
Октябрь 2008 г.

Введение

Аудитория

В этой статье мы покажем вам, как создать гаджет Blogger. Предполагается, что вы знакомы с API Google Data и клиентской библиотекой JavaScript . Вы также должны свободно владеть JavaScript и иметь опыт реализации гаджета OpenSocial с использованием API Gadgets.* .

Этот пример также демонстрирует, как успешно использовать внешние библиотеки в ваших гаджетах. Я использовал jQuery (в основном для UI-эффектов) и TinyMCE — отличный плагин для WYSIWYG-редактора форматированного текста.

Мотивация

Для создания гаджета, использующего JSON с одним из API Google Data, требуется совсем немного JavaScript-кода. Главный недостаток такого гаджета заключается в том, что данные общедоступны и доступны только для чтения. Для создания более интересных гаджетов необходим доступ к личным данным пользователя (что требует аутентификации). До сих пор не существовало эффективного способа использовать API Google Account . AuthSub требует перенаправления браузера, а ClientLogin предоставляет учётные данные пользователя на стороне клиента. Даже использование гаджета type="url" было неудобным.

Введите OAuth-прокси.

OAuth-прокси

Если вы не знакомы с OAuth, это стандарт аутентификации, позволяющий пользователю делиться своими личными данными с другим веб-сайтом или гаджетом. Спецификация OAuth требует, чтобы все запросы данных имели цифровую подпись. Это отлично с точки зрения безопасности, но в случае гаджета JavaScript управление закрытыми ключами и создание цифровых подписей небезопасно. Кроме того, возникают дополнительные сложности из-за проблем с междоменным доступом.

К счастью, эти проблемы решаются благодаря функции платформы гаджетов под названием OAuth Proxy. OAuth Proxy призван упростить жизнь разработчикам гаджетов. Он скрывает большую часть данных аутентификации OAuth и берёт на себя всю сложную работу. Proxy подписывает запросы данных от имени вашего гаджета, поэтому вам не нужно управлять закрытыми ключами или беспокоиться о подписи запросов. Он просто работает!

OAuth Proxy основан на проекте с открытым исходным кодом Shindig , который представляет собой реализацию спецификации гаджета.

Примечание: OAuth-прокси поддерживается только для гаджетов, использующих API gadgets.* и работающих в контейнерах OpenSocial. Он не поддерживается для устаревшего API гаджетов .

Начиная

Оставшаяся часть этого руководства посвящена созданию гаджета для доступа к данным пользователя Blogger. Мы пройдём аутентификацию (с помощью прокси-сервера OAuth), использование клиентской библиотеки JavaScript и, наконец, публикацию записи в Blogger.

Аутентификация

Для начала нам нужно настроить гаджет на использование OAuth. Для этого добавьте элемент <OAuth> в раздел <ModulePrefs> гаджета:

<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>

Три конечные точки URL в элементе <Service> соответствуют конечным точкам токена OAuth Google . Ниже приведено объяснение параметров запроса:

  • scope

    Этот параметр обязателен в URL-адресе запроса. Ваш гаджет сможет получить доступ только к данным из scope действия, указанных в этом параметре. В этом примере гаджет будет обращаться к Blogger. Если вашему гаджету требуется доступ к нескольким API Google Data, объедините дополнительные scope с помощью %20 . Например, если вам нужен доступ и к Календарю, и к Blogger, установите область действия http://www.blogger.com/feeds/%20http://www.google.com/calendar/feeds/ .

  • oauth_callback

    Этот параметр необязателен в URL-адресе авторизации. Страница подтверждения OAuth будет перенаправлена на этот URL-адрес после того, как пользователь подтвердит доступ к своим данным. Вы можете не указывать этот параметр, указать свою собственную «страницу одобрения» или, что предпочтительнее, использовать http://oauth.gmodules.com/gadgets/oauthcallback . Последний вариант обеспечивает наилучший пользовательский интерфейс при первой установке гаджета. На этой странице представлен фрагмент JavaScript-кода, который автоматически закрывает всплывающее окно.

Теперь, когда наш гаджет использует OAuth, пользователю необходимо подтвердить доступ к своим данным. Вот схема аутентификации:

  1. Гаджет загружается в первый раз и пытается получить доступ к данным Blogger пользователя.
  2. Запрос не выполняется, поскольку пользователь не предоставил доступ к гаджету. К счастью, объект, возвращаемый в ответе, содержит URL ( response.oauthApprovalUrl ), на который мы перенаправим пользователя для входа. Гаджет отображает ссылку «Войти в Blogger» и устанавливает для своего атрибута href значение oauthApprovalUrl .
  3. Затем пользователь нажимает кнопку «Войти в Blogger», и в отдельном окне открывается страница подтверждения OAuth. Гаджет ожидает завершения процесса подтверждения, отображая ссылку «Я разрешил доступ».
  4. Во всплывающем окне пользователь может выбрать, предоставить или запретить доступ к нашему гаджету. После нажатия кнопки «Предоставить доступ» он перейдет на http://oauth.gmodules.com/gadgets/oauthcallback , и окно закроется.
  5. Гаджет распознаёт закрытие окна и пытается повторно обратиться к Blogger, повторно запрашивая данные пользователя. Для обнаружения закрытия окна я использовал обработчик всплывающих окон . Если вы не используете такой код, пользователь может вручную нажать кнопку «Я разрешаю доступ».
  6. Гаджет теперь отображает свой обычный пользовательский интерфейс. Этот вид сохранится, пока токен аутентификации не будет отозван в IssuedAuthSubTokens .

Итак, исходя из вышеизложенных шагов, гаджеты имеют понятие трех различных состояний:

  1. Не аутентифицирован. Пользователю необходимо начать процесс подтверждения.
  2. Ожидание одобрения пользователем доступа к своим данным.
  3. Аутентификация пройдена. Гаджет отображает нормальное функциональное состояние.

В моем гаджете я использовал контейнеры <div> для разделения каждого этапа:

<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&gt

Каждый <div> отображается отдельно с помощью функции showOnly() . Подробности об этой функции см. в полном примере гаджета .

Использование клиентской библиотеки JavaScript

Чтобы получить удалённый контент в OpenSocial, вы вызываете метод gadgets.io.makeRequest , используя API gadgets.* . Однако, поскольку мы создаём гаджет Google Data, нет необходимости обращаться к API gadgets.io.* . Вместо этого используйте клиентскую библиотеку JavaScript, которая содержит специальные методы для отправки запросов к каждой службе Google Data.

Примечание : На момент написания этой статьи библиотека JavaScript поддерживает только Blogger , Calendar , Contacts , Finance и Google Base . Чтобы использовать другие API, используйте gadgets.io.makeRequest без библиотеки.

Загрузка библиотеки

Чтобы загрузить библиотеку JavaScript, включите общий загрузчик в раздел <Content> и импортируйте библиотеку после инициализации гаджета. Передача обратного вызова gadgets.util.registerOnLoadHandler() поможет определить готовность гаджета:

<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&gt

Вызов blogger.useOAuth('google') указывает библиотеке использовать OAuth Proxy (вместо AuthSubJS — её стандартного метода аутентификации). Наконец, гаджет пытается получить данные пользователя из Blogger, вызывая fetchData() . Этот метод описан ниже.

Извлечение данных

Теперь, когда все настроено, как нам GET или POST данные в Blogger?

Распространённая парадигма в OpenSocial — определение функции fetchData() в вашем гаджете. Этот метод обычно обрабатывает различные этапы аутентификации и извлекает данные с помощью gadgets.io.makeRequest . Поскольку мы используем клиентскую библиотеку JavaScript, gadgets.io.makeRequest заменяется вызовом 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);
}

При втором вызове этой функции response.feed содержит данные.

Примечание : getBlogFeed() использует одну и ту же функцию для обратного вызова и обработчика ошибок.

Опубликовать запись в Blogger

Последний шаг — публикация новой записи в блоге. Код ниже демонстрирует, что происходит, когда пользователь нажимает кнопку «Сохранить».

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);
}

Заключение

Теперь у вас есть все необходимое для начала разработки гаджета на основе API данных Google.

Надеюсь, эта статья помогла вам оценить, насколько просто использовать OAuth Proxy для аутентификации гаджетов. Сочетание этого мощного инструмента с клиентской библиотекой JavaScript Google Data позволяет легко создавать интересные, интерактивные и сложные гаджеты.

Если у вас есть вопросы или комментарии по этой статье, посетите наш форум по обсуждению API аккаунтов Google .

Ресурсы