Friday, November 30, 2012

Google App Engine Facebook Application Example Using Python

This is a detailed example built on Facebook Python SDK example. In this example I'll take it step by step from the very beginning to login, get user data, get friends list, invite friends, and post to wall. So let's start. (also check the Ruby on Rails with Koala example)

- Download "facebook.py" from this link on GitHub.

- Add the downloaded file to your Google App Engine project.

- You can now make you own Facebook application code or re-use the current example (which I choose to do).

- Take these global variables and imports into your application (your GAE python file)
FACEBOOK_APP_ID = "123456789" #your own FB app id here
FACEBOOK_APP_SECRET = "756483568435475" #your own FB app secret here
INVITATION_TEXT = "I invite you to try my app. It is amazing!"

import facebook
import os.path
import wsgiref.handlers
import logging
import urllib2
import hashlib

from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext.webapp import template
from google.appengine.api.urlfetch import fetch

import webapp2

- Add this Model and Base Class that will be used to determine the user ID from cookies
class User(db.Model):
    id = db.StringProperty(required=True)
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)
    name = db.StringProperty(required=True)
    profile_url = db.StringProperty(required=True)
    access_token = db.StringProperty(required=True)

class BaseHandler(webapp.RequestHandler):
    """Provides access to the active Facebook user in self.current_user

    The property is lazy-loaded on first access, using the cookie saved
    by the Facebook JavaScript SDK to determine the user ID of the active
    user. See http://developers.facebook.com/docs/authentication/ for
    more information.
    """
    @property
    def current_user(self):
        if not hasattr(self, "_current_user"):
            self._current_user = None
            cookie = facebook.get_user_from_cookie(
                self.request.cookies, FACEBOOK_APP_ID, FACEBOOK_APP_SECRET)
            if cookie:
                # Store a local instance of the user data so we don't need
                # a round-trip to Facebook on every request
                user = User.get_by_key_name(cookie["uid"])
                if not user:
                    graph = facebook.GraphAPI(cookie["access_token"])
                    profile = graph.get_object("me")
                    user = User(key_name=str(profile["id"]),
                                id=str(profile["id"]),
                                name=profile["name"],
                                profile_url=profile["link"],
                                access_token=cookie["access_token"])
                    user.put()
                elif user.access_token != cookie["access_token"]:
                    user.access_token = cookie["access_token"]
                    user.put()
                self._current_user = user
        return self._current_user

- Now let's create our handler. Note that in case you wan to use your application inside Facebook frame, you will have to handle POST request too.

- Logging In and Inviting Friends

class HomeHandler(BaseHandler):
    def get(self):
        self.show_main()

    def post(self):
        self.show_main()
        
    def show_main(self):
        path = os.path.join(os.path.dirname(__file__), "templates/main.html")
        args = dict(current_user=self.current_user,
                    facebook_app_id=FACEBOOK_APP_ID,
                    invitation_text=INVITATION_TEXT)
        self.response.out.write(template.render(path, args))

- Now let's create the template for the HTML template. Here is how it goes: If the passed "current_user" parameter is valid, it will show the content of your app and a link to a JQuery function to invite friends using Facebook Javascript SDK initialized at the bottom. Otherwise, it will show a Facebook login button.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>Application Title</title>
    
    <script type="text/javascript" src="/js/jquery-1.8.2.min.js"></script>
    <script type="text/javascript">
        $('#sendRequest').click(function() {
          FB.ui(
            {
              method  : 'apprequests',
              message : $(this).attr('data-message')
            },
            function (response) {
              // If response is null the user canceled the dialog
              if (response != null) {
                //logResponse(response);
              }
            }
          );
        });
    });
    </script>
  </head>
  <body>
  
    {% if not current_user %}
    <div style="margin:0 auto; text-align:center;">
        <fb:login-button autologoutlink="false" scope="publish_stream"></fb:login-button>
    </div> 
    
    {% else %}
    
    <br>
    Welcome! Here is my application.
    <br>
    <a href="#" id="sendRequest" data-message="{{invitation_text}}">
      Send Requests
    </a>
    
    {% endif %}

    <div id="fb-root"></div>
    <script>
      window.fbAsyncInit = function() {
        FB.init({appId: '{{ facebook_app_id }}', status: true, cookie: true,
                 xfbml: true});
        FB.Event.subscribe('{% if current_user %}auth.logout{% else %}auth.login{% endif %}', function(response) {
          window.location.reload();
        });
      };
      (function() {
        var e = document.createElement('script');
        e.type = 'text/javascript';
        e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
        e.async = true;
        document.getElementById('fb-root').appendChild(e);
      }());
    </script>
  </body>
</html>

- At this point, you should be having a working Python Google App Engine Facebook application. Next I'll go though some basic information that you may need to implement in your service. You may prefer XML, JSON, or a custom format. Anyways, here is the basic example that you may customize as you want and add more handling for special cases.

- User Profile

class ProfileHandler(BaseHandler):
    def get(self):
        self.show_profile()

    def post(self):
        self.show_profile()
        
    def show_profile(self):
        profile = ''
        current_user = self.current_user
        if current_user:
            graph = facebook.GraphAPI(current_user.access_token)
            profile = profile = graph.get_object("me")
        path = os.path.join(os.path.dirname(__file__), "templates/profile.html")
        args = dict(current_user=current_user,
                    facebook_app_id=FACEBOOK_APP_ID,
                    profile=profile)
        self.response.out.write(template.render(path, args))
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title></title>
  </head>
  <body>
    
    {% if current_user %}
        {% if profile %}
            {{profile}}
        {% endif %}
    {% endif %}

  </body>
</html>

- Friends List

class FriendsHandler(BaseHandler):
    def get(self):
        self.show_friends()

    def post(self):
        self.show_friends()
        
    def show_friends(self):
        friends_list = []
        current_user = self.current_user
        if current_user:
            graph = facebook.GraphAPI(current_user.access_token)
            friends_list = graph.get_connections("me", "friends")['data']
        path = os.path.join(os.path.dirname(__file__), "templates/friends.html")
        args = dict(current_user=current_user,
                    facebook_app_id=FACEBOOK_APP_ID,
                    friends_list=friends_list)
        self.response.out.write(template.render(path, args))
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title></title>
  </head>
  <body>
    
    {% if current_user %}
        {% if friends_list %}
            {{friends_list}}
        {% else %}
        {% endif %}
    {% else %}
    {% endif %}

  </body>
</html>

- Post Text to Wall (Arabic-friendly)

class PostTextHandler(BaseHandler):
    def post(self):
        text = self.request.get('text')
        if text:
            text = text.encode('utf-8') # for non-english text  
            current_user = self.current_user
            if current_user:
                graph = facebook.GraphAPI(current_user.access_token)
                graph.put_object("me", "feed", message=text)
                self.response.out.write('yes') # just a feedback reponse
            else:
                self.response.out.write('no') # just a feedback reponse
        else:
            self.response.out.write('no') # just a feedback reponse

- And off course do not forget to add these handlers to the suitable URLs for your service. (side note: the /?$ sign is useful to allow URL with/out the slash)
app = webapp2.WSGIApplication([
                            ('/', HomeHandler),
                            ('/profile/?$', ProfileHandler),
                            ('/friends/?$', FriendsHandler),
                            ('/posttext/?$', PostTextHandler)])

Thursday, November 15, 2012

A Complete Guide to Install Ruby and Rails on Windows

As usual, whenever I find a problem with something and then another problem appears with no guide except Google and my experience (if any), I make my own guide. This time it is a guide to installing Ruby then Rails on Windows.

Step 1 - Install Ruby (Ruby 1.9.3-p327 while writing this post) through RubyInstaller:


It is as easy as any other Windows Installer. Just go to RubyInstaller download page and download it. One thing to notice while installing is to check the option to add Ruby executables to your PATH, and maybe file association if you wish. Note: Make sure the installation path does not have spaces (like: Program Files) to avoid any terminal errors later.



Step 2 - Install Development Kit (DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe  while writing this post):


As their page says: "The RubyInstaller Development Kit (DevKit) is a MSYS/MinGW based toolkit than enables you to build many of the native C/C++ extensions available for Ruby." It will be needed to install some 'gems' for Rails later. The executable is no more than a 7zip archive that can be extracted to a folder of choice.

After extracting the folder,
- Open the command line (cmd) as administrator (Shift+Enter or right click and 'run as administrator') and browse to the devkit  directory.
- If we step to the normal step mentioned in the RubyInstaller Development Kit Wiki we will get an error like this: " registry.rb:172:in `find': unknown > > encoding name > > - CP720 (ArgumentError) ". So first, let's write this in the command line:
chcp 1256
- Next, let's initialize the devkit:
ruby dk.rb init
-  Make sure the generated "config.yml" file includes the right Ruby version.
- Next install devkit:
ruby dk.rb install
- You can follow the RubyInstaller Development Kit Wiki instructions to make sure it is installed properly.




Step 3 - Install Some Gems for Rails:


When installing Rails later, it will require some gems to install. So let's install them now.
- From command line, write:
gem install json -v  '1.7.5'
gem install coffee-rails -v '3.2.2'
- Wait for the download and installation to end.




Step 4 - Install Rails


- From command line, write:
gem install rails
- Wait for the download and installation to end.

Step 5 - First Rails Project


- From command line, cd to the folder of your choice or write the full path in the following line:
rails new ./projects/test_proj
- Wait for file creation to end, then cd to the project file.
- Write:
rails s
and your first Ruby on Rails service will be online on "localhost:3000"

Now you have your environment ready for Ruby on Rails development.