октябрь 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, пользователю необходимо подтвердить доступ к своим данным. Вот поток аутентификации:
- Гаджет загружается в первый раз и пытается получить доступ к данным пользователя Blogger.
- Запрос не выполняется, поскольку пользователь не предоставил доступ к гаджету. К счастью, объект, возвращенный в ответе, содержит URL-адрес (
response.oauthApprovalUrl
), куда мы отправим пользователя для входа. Гаджет отображает «Войти в Blogger» и задает для атрибута href значениеoauthApprovalUrl
. - Затем пользователь нажимает «Войти в Blogger», и в отдельном окне открывается страница утверждения OAuth. Гаджет ждет, пока пользователь завершит процесс утверждения, отображая ссылку: «Я одобрил доступ».
- Во всплывающем окне пользователь выберет разрешить/запретить доступ к нашему гаджету. Как только они нажимают «Предоставить доступ», они перенаправляются на
http://oauth.gmodules.com/gadgets/oauthcallback
, и окно закрывается. - Гаджет распознает, что окно закрыто, и пытается получить доступ к Blogger во второй раз, повторно запрашивая данные пользователя. Чтобы обнаружить закрытие окна, я использовал обработчик всплывающих окон . Если вы не используете такой код, пользователь может вручную нажать «Доступ разрешен».
- Гаджет теперь отображает свой обычный пользовательский интерфейс. Это представление будет сохраняться до тех пор, пока маркер проверки подлинности не будет отозван в IssuedAuthSubTokens .
Таким образом, из приведенных выше шагов гаджеты имеют понятие трех разных состояний:
- Неаутентифицированный. Пользователь должен начать процесс утверждения.
- Ожидание подтверждения пользователем доступа к своим данным.
- Аутентифицировано. Гаджеты отображают нормальное функциональное состояние.
В моем гаджете я использовал контейнеры <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 .
Ресурсы
- Написание гаджетов OAuth (полная документация по гаджетам)
- Использование OAuth с API данных Google (статья об использовании OAuth с API данных Google)
- Аутентификация OAuth для веб-приложений (полная документация по OAuth)
- клиентская библиотека JavaScript
- Форум для обсуждения API учетных записей Google