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

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

Введение

Аудитория

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

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

Мотивация

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

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

OAuth-прокси

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

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

Прокси-сервер OAuth основан на проекте с открытым исходным кодом под названием 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, соедините дополнительные 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>

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

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

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

Примечание . На момент написания этой статьи библиотека 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>

Вызов blogger.useOAuth('google') указывает библиотеке использовать прокси-сервер OAuth (вместо 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 делает аутентификацию гаджета. Сочетание этого мощного инструмента с клиентской библиотекой Google Data JavaScript упрощает создание интересных, интерактивных и сложных гаджетов.

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

Ресурсы