การสร้าง Google Data Gadget

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

บทนำ

ผู้ชม

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

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

แรงจูงใจ

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

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

พร็อกซี OAuth

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

แต่โชคดีที่ปัญหาเหล่านี้แก้ได้ด้วยการใช้ประโยชน์จากฟีเจอร์จากแพลตฟอร์มแกดเจ็ตที่เรียกว่าพร็อกซี OAuth พร็อกซี 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 เพิ่มเติมด้วย %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) ที่เราจะส่งผู้ใช้ไปเข้าสู่ระบบ แกดเจ็ตจะแสดง "ลงชื่อเข้าใช้บล็อกเกอร์" และตั้งค่า เป็นค่าของ 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>

<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>

การเรียก 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

ทรัพยากร