การสร้างแกดเจ็ตข้อมูลของ Google

Eric Bidelman ทีม Google Data API
ตุลาคม 2008

บทนำ

ผู้ชม

บทความนี้จะแนะนำวิธีสร้างแกดเจ็ต Blogger โดยจะถือว่าคุณคุ้นเคยกับ Google Data API และไลบรารีของไคลเอ็นต์ JavaScript นอกจากนี้ คุณควรมีความเชี่ยวชาญใน JavaScript และมีประสบการณ์ในการติดตั้งใช้งานแกดเจ็ต OpenSocial โดยใช้ gadgets.* API

ตัวอย่างนี้ยังแสดงวิธีใช้ไลบรารีภายนอกในแกดเจ็ตให้สำเร็จอีกด้วย ฉันใช้ jQuery (ส่วนใหญ่ใช้สำหรับเอฟเฟกต์ UI) และ TinyMCE ซึ่งเป็นปลั๊กอินเครื่องมือแก้ไขข้อความ Rich Text แบบ WYSIWYG ที่ยอดเยี่ยม

แรงจูงใจ

คุณใช้ JavaScript เพียงเล็กน้อยก็สร้างแกดเจ็ตที่ใช้ JSON กับ Google Data API รายการใดรายการหนึ่งได้ ข้อเสียที่สำคัญของอุปกรณ์ดังกล่าวคือข้อมูล เป็นแบบสาธารณะและอ่านอย่างเดียว หากต้องการสร้างวิดเจ็ตที่น่าสนใจยิ่งขึ้น คุณต้องมีสิทธิ์เข้าถึงข้อมูลส่วนตัวของผู้ใช้ (ซึ่งต้องมีการตรวจสอบสิทธิ์) ที่ผ่านมายังไม่มีวิธีที่ยอดเยี่ยมในการใช้ประโยชน์จาก Google Account APIs AuthSub ต้องมีการเปลี่ยนเส้นทางของเบราว์เซอร์ และ ClientLogin จะแสดงข้อมูลเข้าสู่ระบบของผู้ใช้ที่ฝั่งไคลเอ็นต์ แม้แต่การแฮ็กอุปกรณ์ type="url" ก็ยังไม่สะดวก

ป้อนพร็อกซี OAuth

พร็อกซี OAuth

หากไม่คุ้นเคยกับ OAuth ก็เป็นมาตรฐานการตรวจสอบสิทธิ์ที่อนุญาตให้ผู้ใช้แชร์ข้อมูลส่วนตัวกับเว็บไซต์หรือแกดเจ็ตอื่น ข้อกำหนด OAuth กำหนดให้คำขอข้อมูลทั้งหมดต้องมีการลงนามแบบดิจิทัล ซึ่งเป็นเรื่องดีสำหรับความปลอดภัย แต่ในกรณีของแกดเจ็ต JavaScript การจัดการคีย์ส่วนตัวและการสร้างลายเซ็นดิจิทัลนั้นไม่ปลอดภัย นอกจากนี้ยังมีปัญหาข้ามโดเมนที่ซับซ้อนขึ้นด้วย

โชคดีที่ปัญหาเหล่านี้ได้รับการแก้ไขโดยการใช้ประโยชน์จากฟีเจอร์จากแพลตฟอร์มแกดเจ็ตที่เรียกว่า OAuth Proxy พร็อกซี OAuth ออกแบบมาเพื่อช่วยให้ชีวิตของนักพัฒนาแกดเจ็ตง่ายขึ้น โดยจะซ่อนรายละเอียดการตรวจสอบสิทธิ์ของ OAuth ส่วนใหญ่และจัดการงานที่ซับซ้อนให้คุณ พร็อกซีจะลงนามในคำขอข้อมูลในนามของแกดเจ็ตของคุณ จึงไม่จำเป็นต้องจัดการคีย์ส่วนตัวหรือกังวลเกี่ยวกับการลงนามในคำขอ ใช้งานได้ทันที

พร็อกซี OAuth สร้างขึ้นจากโปรเจ็กต์โอเพนซอร์สที่ชื่อ Shindig ซึ่งเป็นการติดตั้งใช้งานข้อกำหนดของแกดเจ็ต

หมายเหตุ: ระบบรองรับพร็อกซี OAuth เฉพาะสำหรับแกดเจ็ตที่ใช้ gadgets.* API และทำงานในคอนเทนเนอร์ 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 ทั้ง 3 รายการในองค์ประกอบ <Service> จะสอดคล้องกับปลายทางโทเค็น OAuth ของ Google คำอธิบายพารามิเตอร์การค้นหามีดังนี้

  • scope

    ต้องระบุพารามิเตอร์นี้ใน URL ของคำขอ แกดเจ็ตจะเข้าถึงได้เฉพาะข้อมูลจาก scope ที่ใช้ในพารามิเตอร์นี้ ในตัวอย่างนี้ แกดเจ็ตจะเข้าถึง Blogger หากแกดเจ็ตต้องการเข้าถึง Google Data API มากกว่า 1 รายการ ให้ต่อท้าย scope(s) เพิ่มเติมด้วย a %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 เป็นครั้งที่ 2 โดยการขอข้อมูลของผู้ใช้อีกครั้ง ฉันใช้ ตัวแฮนเดิลป๊อปอัปเพื่อตรวจหาการปิดหน้าต่าง หากคุณไม่ใช้รหัสดังกล่าว ผู้ใช้จะคลิก "ฉันอนุมัติการเข้าถึงแล้ว" ได้ด้วยตนเอง
  6. ตอนนี้แกดเจ็ตจะแสดง UI ปกติแล้ว มุมมองนี้จะยังคงอยู่จนกว่าจะเพิกถอนโทเค็นการตรวจสอบสิทธิ์ ในส่วน IssuedAuthSubTokens

ดังนั้นจากขั้นตอนข้างต้น แกดเจ็ตจึงมีแนวคิดเกี่ยวกับสถานะที่แตกต่างกัน 3 สถานะ ดังนี้

  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 โดยใช้ gadgets.* API แต่เนื่องจากเรากำลังสร้างแกดเจ็ตข้อมูล Google จึงไม่จำเป็นต้องใช้ gadgets.io.* API แต่ให้ใช้ประโยชน์จากไลบรารีของไคลเอ็นต์ JavaScript ซึ่งมีเมธอดพิเศษสำหรับการส่งคำขอ ไปยังบริการข้อมูลของ Google แต่ละรายการ

หมายเหตุ: ในขณะที่เขียนบทความนี้ ไลบรารี JavaScript รองรับเฉพาะ Blogger, ปฏิทิน, รายชื่อติดต่อ, การเงิน และ 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 (แทน 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);
}

เมื่อเรียกใช้ฟังก์ชันนี้เป็นครั้งที่ 2 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);
}

บทสรุป

ตอนนี้คุณมีองค์ประกอบพื้นฐานที่จะเริ่มเขียนโค้ดแกดเจ็ตบน Google Data API แล้ว

หวังว่าบทความนี้จะช่วยให้คุณเห็นว่าพร็อกซี OAuth ทำให้การตรวจสอบสิทธิ์แกดเจ็ตเป็นเรื่องง่ายเพียงใด การรวมเครื่องมืออันทรงพลังนี้เข้ากับไลบรารีของไคลเอ็นต์ JavaScript ของ Google Data จะช่วยให้สร้างแกดเจ็ตที่น่าสนใจ อินเทอร์แอกทีฟ และซับซ้อนได้ง่ายขึ้น

หากมีคำถามหรือความคิดเห็นเกี่ยวกับบทความนี้ โปรดไปที่ฟอรัมสนทนาเกี่ยวกับ Google Accounts API

แหล่งข้อมูล