октябрь 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