יצירת גאדג'ט נתונים של Google

אריק בידלמן, צוות Google Data APIs
אוקטובר 2008

מבוא

Audience

במאמר הזה נסביר איך יוצרים גאדג'ט של Blogger. אנחנו מניחים שאתם מכירים את Google Data APIs ואת ספריית הלקוחות של JavaScript. צריך גם להתמצא ב-JavaScript ולתרגל ניסיון בעבודה עם גאדג'ט של OpenSocial באמצעות הגאדג'טים.* API.

דוגמה זו גם מדגימה כיצד להשתמש בהצלחה בספריות חיצוניות בגאדג'טים שלך. השתמשתי ב-jQuery (בעיקר באפקטים של ממשק המשתמש) וב-TinyMCE, פלאגין עשיר של WYSIWYG לעריכת טקסט.

למה בחרנו לעשות זאת?

צריך מעט מאוד JavaScript כדי ליצור גאדג'ט שמשתמש ב-JSON עם אחד מממשקי ה-API של הנתונים של Google. אחד המעצבנים ביותר של גאדג'ט כזה הוא שהנתונים הם ציבוריים וקריאה בלבד. כדי לבנות גאדג'טים מעניינים יותר, נדרשת גישה לנתונים הפרטיים של המשתמש (משהו הדורש אימות). עד עכשיו, לא הייתה דרך מצוינת לנצל את ממשקי ה-API של חשבון Google. AuthSub מחייב הפניות מחדש של הדפדפן ו-ClientLogin חושף את פרטי הכניסה של המשתמש, בצד הלקוח. אפילו הפריצה לגאדג'ט של type="url" לא הייתה נוחה.

יש להזין את שרת ה-proxy של OAuth.

Proxy ל-OAuth

אם אינכם מכירים את OAuth, זהו תקן אימות שמאפשר למשתמש לשתף את המידע הפרטי שלו עם אתר או גאדג'ט אחר. מפרט ה-OAuth דורש שכל הבקשות לנתונים יהיו חתומות באופן דיגיטלי. זו שיטה מצוינת לאבטחה, אבל במקרה של גאדג'ט JavaScript, ניהול מפתחות פרטיים ויצירת חתימות דיגיטליות לא מאובטח. קיים גם סיבוך נוסף של בעיות בין דומיינים.

למרבה המזל, בעיות אלה נפתרות על ידי ניצול תכונה מפלטפורמת הגאדג'טים שנקראת שרת proxy ל-OAuth. שרת ה-proxy של OAuth נועד להקל על מפתחי גאדג'טים. היא מסתירה חלק גדול מפרטי האימות של OAuth ומבצעת את העבודה הקשה. שרת ה-proxy חותם על בקשות נתונים בשם הגאדג'ט שלך, כך שאין צורך לנהל מפתחות פרטיים או לדאוג לבקשות חתימה. זה פשוט עובד!

שרת ה-proxy של OAuth מבוסס על פרויקט קוד פתוח בשם Shindig, שהוא יישום של מפרט הגאדג'ט.

הערה: שרת ה-proxy של OAuth נתמך רק עבור גאדג'טים המשתמשים ב-API של gadgets.* ופועלים בקונטיינרים של OpenSocial. הוא לא נתמך ב-API לגאדג'טים מדור קודם.

תחילת העבודה

המשך המדריך הזה יתמקד ביצירת גאדג'ט כדי לקבל גישה לנתוני Blogger של המשתמש. אנחנו נעבור את תהליך האימות (באמצעות שרת proxy ל-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. אם הגאדג'ט שלך רוצה לגשת ליותר מ-Google Data API אחד, צריך לשרשר את scope הנוספים עם %20. לדוגמה, אם רוצים לגשת גם ליומן Google וגם ל-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" והוא מגדיר את הערך שלו ל-oauthApprovalUrl.
  3. אחר כך המשתמש לוחץ על 'כניסה ל-Blogger' ודף אישור ה-OAuth נפתח בחלון נפרד. הגאדג'ט ממתין למשתמש שיסיים את תהליך האישור על ידי הצגת קישור: "אישרתי את הגישה".
  4. בחלון הקופץ, המשתמש יבחר להעניק/לדחות גישה לגאדג'ט שלנו. לאחר הלחיצה על 'הענקת גישה', הם יועברו אל http://oauth.gmodules.com/gadgets/oauthcallback והחלון ייסגר.
  5. הגאדג'ט מזהה את החלון שנסגר ומנסה לגשת ל-Blogger פעם נוספת על ידי בקשה חוזרת של נתוני המשתמש. כדי לסגור את החלון, השתמשתי במטפל בחלון קופץ. אם לא תשתמש בקוד כזה, המשתמש יוכל ללחוץ ידנית על 'אישור הגישה'.
  6. לגאדג'ט מוצג כעת ממשק המשתמש הרגיל שלו. התצוגה הזו תישמר, אלא אם אסימון האימות יבוטל בקטע IssueAuth.

אז מהצעדים המתוארים למעלה, יש לגאדג'טים תצוגה של שלושה מצבים שונים:

  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 , ביומן, באנשי קשר, בפיננסים וב-Google Base. כדי להשתמש באחד מממשקי ה-API האחרים, צריך להשתמש ב-gadgets.io.makeRequest ללא הספרייה.

הספרייה בטעינה

כדי לטעון את ספריית JavaScript, עליכם לכלול את רכיב העומס הנפוץ בקטע <Content> ולייבא את הספרייה אחרי שהגאדג'ט הופעל. הזנת קריאה חוזרת (callback) אל 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') מורה לספרייה להשתמש בשרת ה-proxy של 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() משתמשת באותה פונקציה ל-callback ולטיפול בשגיאות.

פרסום רשומה ב-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);
}

סיכום

עכשיו יש לכם את אבני הבניין כדי להתחיל לקודד גאדג'ט בנוסף ל-Google Data APIs.

אנחנו מקווים שמאמר זה העניק לך הערכה עד כמה פשוט שרת ה-proxy של OAuth מאפשר אימות גאדג'טים. השילוב של כלי הכוח הזה עם ספריית הלקוח של JavaScript ב-Google Data מאפשר לבנות בקלות גאדג'טים מעניינים, אינטראקטיביים ומתוחכמים.

אם יש לך שאלות או תגובות בנוגע למאמר, ניתן להיכנס אלינו בפורום הדיון של ממשקי ה-API בחשבונות Google.

משאבים