text: # -*- coding: utf-8 -*-
  '''
  
  Open Facebook allows you to use Facebook's open graph API with simple python code
  
  **Features**
  
      * Supported and maintained
      * Tested so people can contribute
      * Facebook exceptions are mapped
      * Logging
  
  
  **Basic examples**::
  
      facebook = OpenFacebook(access_token)
  
      # Getting info about me
      facebook.get('me')
  
      # Learning some more about fashiolista
      facebook.get('fashiolista')
  
      # Writing your first comment
      facebook.set('fashiolista/comments', message='I love Fashiolista!')
  
  
      # Posting to a users wall
      facebook.set('me/feed', message='check out fashiolista',
                   url='http://www.fashiolista.com')
  
      # Liking a page
      facebook.set('fashiolista/likes')
  
      # Getting who likes cocacola
      facebook.set('cocacola/likes')
  
      # Use fql to retrieve your name
      facebook.fql('SELECT name FROM user WHERE uid = me()')
  
      # Executing fql in batch
      facebook.batch_fql([
          'SELECT uid, name, pic_square FROM user WHERE uid = me()',
          'SELECT uid, rsvp_status FROM event_member WHERE eid=12345678',
      ])
  
      # Uploading pictures
      photo_urls = [
          'http://e.fashiocdn.com/images/entities/0/7/B/I/9/0.365x365.jpg',
          'http://e.fashiocdn.com/images/entities/0/5/e/e/r/0.365x365.jpg',
      ]
      for photo in photo_urls:
          print facebook.set('me/feed', message='Check out Fashiolista',
                             picture=photo, url='http://www.fashiolista.com')
  
  
  **Getting an access token**
  
  Once you get your access token, Open Facebook gives you access to the Facebook API
  There are 3 ways of getting a facebook access_token and these are currently
  implemented by Django Facebook.
  
  1. code is passed as request parameter and traded for an
      access_token using the api
  
  2. code is passed through a signed cookie and traded for an access_token
  
  3. access_token is passed directly (retrieved through javascript, which
      would be bad security, or through one of the mobile flows.)
  
  If you are looking to develop your own flow for a different framework have a look at
  Facebook's documentation:
  http://developers.facebook.com/docs/authentication/
  
  Also have a look at the :class:`.FacebookRequired` decorator and :func:`get_persistent_graph` function to
  understand the required functionality
  
  
  **Api docs**:
  
  
  '''
  
  from django.http import QueryDict
  import six
  from django.utils.http import urlencode
  from django_facebook import settings as facebook_settings
  from open_facebook import exceptions as facebook_exceptions
  from open_facebook.utils import json, encode_params, send_warning, memoized, \
      stop_statsd, start_statsd
  import logging
  
  from django_facebook.utils import to_int
  import ssl
  import re
  
  try:
      # python 2 imports
      from urlparse import urlparse
      from urllib2 import build_opener, HTTPError, URLError
  except ImportError:
      # python 3 imports
      from urllib.error import HTTPError, URLError
      from urllib.parse import urlparse
      from urllib.request import build_opener
  
  logger = logging.getLogger(__name__)
  
  
  # base timeout, actual timeout will increase when requests fail
  REQUEST_TIMEOUT = 10
  # two retries was too little, sometimes facebook is a bit flaky
  REQUEST_ATTEMPTS = 3
  
  
  class FacebookConnection(object):
  
      '''
      Shared utility class implementing the parsing
      of Facebook API responses
      '''
      api_url = 'https://graph.facebook.com/'
      # this older url is still used for fql requests
      old_api_url = 'https://api.facebook.com/method/'
  
      @classmethod
      def request(cls, path='', post_data=None, old_api=False, **params):
          '''
          Main function for sending the request to facebook
  
          **Example**::
              FacebookConnection.request('me')
  
          :param path:
              The path to request, examples: /me/friends/, /me/likes/
  
          :param post_data:
              A dictionary of data to post
  
          :param parms:
              The get params to include
          '''
          api_base_url = cls.old_api_url if old_api else cls.api_url
          if getattr(cls, 'access_token', None):
              params['access_token'] = cls.access_token
          url = '%s%s?%s' % (api_base_url, path, urlencode(params))
          response = cls._request(url, post_data)
          return response
  
      @classmethod
      def _request(cls, url, post_data=None, timeout=REQUEST_TIMEOUT,
                   attempts=REQUEST_ATTEMPTS):
          # change fb__explicitly_shared to fb:explicitly_shared
          if post_data:
              post_data = dict(
                  (k.replace('__', ':'), v) for k, v in post_data.items())
  
          logger.info('requesting url %s with post data %s', url, post_data)
          post_request = (post_data is not None or 'method=post' in url)
  
          if post_request and facebook_settings.FACEBOOK_READ_ONLY:
              logger.info('running in readonly mode')
              response = dict(id=123456789, setting_read_only=True)
              return response
  
          # nicely identify ourselves before sending the request
          opener = build_opener()
          opener.addheaders = [('User-agent', 'Open Facebook Python')]
  
          # get the statsd path to track response times with
          path = urlparse(url).path
          statsd_path = path.replace('.', '_')
  
          # give it a few shots, connection is buggy at times
          timeout_mp = 0
          while attempts:
              # gradually increase the timeout upon failure
              timeout_mp += 1
              extended_timeout = timeout * timeout_mp
              response_file = None
              encoded_params = encode_params(post_data) if post_data else None
              post_string = (urlencode(encoded_params)
                             if post_data else None)
              try:
                  start_statsd('facebook.%s' % statsd_path)
  
                  try:
                      response_file = opener.open(
                          url, post_string, timeout=extended_timeout)
                      response = response_file.read().decode('utf8')
                  except (HTTPError,) as e:
                      response_file = e
                      response = response_file.read().decode('utf8')
                      # Facebook sents error codes for many of their flows
                      # we still want the json to allow for proper handling
                      msg_format = 'FB request, error type %s, code %s'
                      logger.warn(msg_format, type(e), getattr(e, 'code', None))
                      # detect if its a server or application error
                      server_error = cls.is_server_error(e, response)
                      if server_error:
                          # trigger a retry
                          raise URLError(
                              'Facebook is down %s' % response)
                  break
              except (HTTPError, URLError, ssl.SSLError) as e:
                  # These are often temporary errors, so we will retry before
                  # failing
                  error_format = 'Facebook encountered a timeout (%ss) or error %s'
                  logger.warn(error_format, extended_timeout, str(e))
                  attempts -= 1
                  if not attempts:
                      # if we have no more attempts actually raise the error
                      error_instance = facebook_exceptions.convert_unreachable_exception(
                          e)
                      error_msg = 'Facebook request failed after several retries, raising error %s'
                      logger.warn(error_msg, error_instance)
                      raise error_instance
              finally:
                  if response_file:
                      response_file.close()
                  stop_statsd('facebook.%s' % statsd_path)
  
          # Faceboook response is either
          # Valid json
          # A string which is a querydict (a=b&c=d...etc)
          # A html page stating FB is having trouble (but that shouldnt reach
          # this part of the code)
          try:
              parsed_response = json.loads(response)
              logger.info('facebook send response %s' % parsed_response)
          except Exception as e:
              # using exception because we need to support multiple json libs :S
              parsed_response = QueryDict(response, True)
              logger.info('facebook send response %s' % parsed_response)
  
          if parsed_response and isinstance(parsed_response, dict):
              # of course we have two different syntaxes
              if parsed_response.get('error'):
                  cls.raise_error(parsed_response['error']['type'],
                                  parsed_response['error']['message'],
                                  parsed_response['error'].get('code'))
              elif parsed_response.get('error_code'):
                  cls.raise_error(parsed_response['error_code'],
                                  parsed_response['error_msg'])
  
          return parsed_response
  
      @classmethod
      def is_server_error(cls, e, response):
          '''
          Checks an HTTPError to see if Facebook is down or we are using the
          API in the wrong way
          Facebook doesn't clearly distinquish between the two, so this is a bit
          of a hack
          '''
          from open_facebook.utils import is_json
          server_error = False
          if hasattr(e, 'code') and e.code == 500:
              server_error = True
  
          # Facebook status codes are used for application logic
          # http://fbdevwiki.com/wiki/Error_codes#User_Permission_Errors
          # The only way I know to detect an actual server error is to check if
          # it looks like their error page
          # TODO: think of a better solution....
          error_matchers = [
              '<title>Facebook | Error</title>',
              'Sorry, something went wrong.'
          ]
          is_error_page = all(
              [matcher in response for matcher in error_matchers])
          if is_error_page:
              server_error = True
  
          # if it looks like json, facebook is probably not down
          if is_json(response):
              server_error = False
  
          return server_error
  
      @classmethod
      def raise_error(cls, error_type, message, error_code=None):
          '''
          Lookup the best error class for the error and raise it
  
          **Example**::
  
              FacebookConnection.raise_error(10, 'OAuthException')
  
          :param error_type:
              the error type from the facebook api call
  
          :param message:
              the error message from the facebook api call
  
          :param error_code:
              optionally the error code which facebook send
          '''
          default_error_class = facebook_exceptions.OpenFacebookException
  
          # get the error code
          error_code = error_code or cls.get_code_from_message(message)
          # also see http://fbdevwiki.com/wiki/Error_codes#User_Permission_Errors
          logger.info('Trying to match error code %s to error class', error_code)
  
          # lookup by error code takes precedence
          error_class = cls.match_error_code(error_code)
  
          # try to get error class by direct lookup
          if not error_class:
              if not isinstance(error_type, int):
                  error_class = getattr(facebook_exceptions, error_type, None)
              if error_class and not issubclass(error_class, default_error_class):
                  error_class = None
  
          # hack for missing parameters
          if 'Missing' in message and 'parameter' in message:
              error_class = facebook_exceptions.MissingParameter
  
          # hack for Unsupported delete request
          if 'Unsupported delete request' in message:
              error_class = facebook_exceptions.UnsupportedDeleteRequest
  
          # fallback to the default
          if not error_class:
              error_class = default_error_class
  
          logger.info('Matched error to class %s', error_class)
          error_message = message
          if error_code:
              # this is handy when adding new exceptions for facebook errors
              error_message = u'%s (error code %s)' % (message, error_code)
  
          raise error_class(error_message)
  
      @classmethod
      def get_code_from_message(cls, message):
          # map error classes to facebook error codes
          # find the error code
          error_code = None
          error_code_re = re.compile('\(#(\d+)\)')
          matches = error_code_re.match(message)
          matching_groups = matches.groups() if matches else None
          if matching_groups:
              error_code = to_int(matching_groups[0]) or None
  
          return error_code
  
      @classmethod
      def get_sorted_exceptions(cls):
          from open_facebook.exceptions import get_exception_classes
          exception_classes = get_exception_classes()
          exception_classes.sort(key=lambda e: e.range())
          return exception_classes
  
      @classmethod
      def match_error_code(cls, error_code):
          '''
          Return the right exception class for the error code
          '''
          exception_classes = cls.get_sorted_exceptions()
          error_class = None
          for class_ in exception_classes:
              codes_list = class_.codes_list()
              # match the error class
              matching_error_class = None
              for code in codes_list:
                  if isinstance(code, tuple):
                      start, stop = code
                      if error_code and start <= error_code <= stop:
                          matching_error_class = class_
                          logger.info('Matched error on code %s', code)
                  elif isinstance(code, (int, six.integer_types)):
                      if int(code) == error_code:
                          matching_error_class = class_
                          logger.info('Matched error on code %s', code)
                  else:
                      raise(
                          ValueError, 'Dont know how to handle %s of '
                          'type %s' % (code, type(code)))
              # tell about the happy news if we found something
              if matching_error_class:
                  error_class = matching_error_class
                  break
          return error_class
  
  
  class FacebookAuthorization(FacebookConnection):
  
      '''
      Methods for getting us an access token
  
      There are several flows we must support
      * js authentication flow (signed cookie)
      * facebook app authentication flow (signed cookie)
      * facebook oauth redirect (code param in url)
      These 3 options need to be converted to an access token
  
      Also handles several testing scenarios
      * get app access token
      * create test user
      * get_or_create_test_user
      '''
      @classmethod
      def convert_code(cls, code,
                       redirect_uri='http://local.mellowmorning.com:8000/facebook/connect/'):
          '''
          Turns a code into an access token
  
          **Example**::
  
              FacebookAuthorization.convert_code(code)
  
          :param code:
              The code to convert
  
          :param redirect_uri:
              The redirect uri with which the code was requested
  
          :returns: dict
  
          '''
          kwargs = cls._client_info()
          kwargs['code'] = code
          kwargs['redirect_uri'] = redirect_uri
          response = cls.request('oauth/access_token', **kwargs)
          return response
  
      @classmethod
      def extend_access_token(cls, access_token):
          '''
          https://developers.facebook.com/roadmap/offline-access-removal/
          We can extend the token only once per day
          Normal short lived tokens last 1-2 hours
          Long lived tokens (given by extending) last 60 days
  
          **Example**::
  
              FacebookAuthorization.extend_access_token(access_token)
  
          :param access_token:
              The access_token to extend
  
          :returns: dict
          '''
          kwargs = cls._client_info()
          kwargs['grant_type'] = 'fb_exchange_token'
          kwargs['fb_exchange_token'] = access_token
          response = cls.request('oauth/access_token', **kwargs)
          return response
  
      @classmethod
      def _client_info(cls):
          kwargs = dict(client_id=facebook_settings.FACEBOOK_APP_ID)
          kwargs['client_secret'] = facebook_settings.FACEBOOK_APP_SECRET
          return kwargs
  
      @classmethod
      def parse_signed_data(cls, signed_request,
                            secret=facebook_settings.FACEBOOK_APP_SECRET):
          '''
          Thanks to
          http://stackoverflow.com/questions/3302946/how-to-base64-url-decode-in-python
          and
          http://sunilarora.org/parsing-signedrequest-parameter-in-python-bas
          '''
          from open_facebook.utils import base64_url_decode_php_style, smart_str
          l = signed_request.split('.', 2)
          encoded_sig = l[0]
          payload = l[1]
          from open_facebook.utils import json
          sig = base64_url_decode_php_style(encoded_sig)
          import hmac
          import hashlib
          data = json.loads(base64_url_decode_php_style(payload).decode('utf-8'))
  
          algo = data.get('algorithm').upper()
          if algo != 'HMAC-SHA256':
              error_format = 'Unknown algorithm we only support HMAC-SHA256 user asked for %s'
              error_message = error_format % algo
              send_warning(error_message)
              logger.error('Unknown algorithm')
              return None
          else:
              expected_sig = hmac.new(smart_str(secret), msg=smart_str(payload),
                                      digestmod=hashlib.sha256).digest()
  
          if not sig == expected_sig:
              error_format = 'Signature %s didnt match the expected signature %s'
              error_message = error_format % (sig, expected_sig)
              send_warning(error_message)
              return None
          else:
              logger.debug('valid signed request received..')
              return data
  
      @classmethod
      def get_app_access_token(cls):
          '''
          Get the access_token for the app that can be used for
          insights and creating test users
          application_id = retrieved from the developer page
          application_secret = retrieved from the developer page
          returns the application access_token
          '''
          kwargs = {
              'grant_type': 'client_credentials',
              'client_id': facebook_settings.FACEBOOK_APP_ID,
              'client_secret': facebook_settings.FACEBOOK_APP_SECRET,
          }
          response = cls.request('oauth/access_token', **kwargs)
          return response['access_token']
  
      @memoized
      @classmethod
      def get_cached_app_access_token(cls):
          '''
          Caches the access token in memory, good for speeding up testing
          '''
          app_access_token = cls.get_app_access_token()
          return app_access_token
  
      @classmethod
      def create_test_user(cls, app_access_token, permissions=None, name=None):
          '''
          Creates a test user with the given permissions and name
  
          :param app_access_token:
              The application's access token
  
          :param permissions:
              The list of permissions to request for the test user
  
          :param name:
              Optionally specify the name
          '''
          if not permissions:
              permissions = ['read_stream', 'publish_stream',
                             'user_photos,offline_access']
          if isinstance(permissions, list):
              permissions = ','.join(permissions)
  
          default_name = 'Permissions %s' % permissions.replace(
              ',', ' ').replace('_', '')
          name = name or default_name
  
          kwargs = {
              'access_token': app_access_token,
              'installed': True,
              'name': name,
              'method': 'post',
              'permissions': permissions,
          }
          path = '%s/accounts/test-users' % facebook_settings.FACEBOOK_APP_ID
          # add the test user data to the test user data class
          test_user_data = cls.request(path, **kwargs)
          test_user_data['name'] = name
          test_user = TestUser(test_user_data)
  
          return test_user
  
      @classmethod
      def get_or_create_test_user(cls, app_access_token, name=None, permissions=None, force_create=False):
          '''
          There is no supported way of get or creating a test user
          However
          - creating a test user takes around 5s
          - you an only create 500 test users
          So this slows your testing flow quite a bit.
  
          This method checks your test users
          Queries their names (stores the permissions in the name)
  
          '''
          if not permissions:
              permissions = ['read_stream', 'publish_stream', 'publish_actions',
                             'user_photos,offline_access']
          if isinstance(permissions, list):
              permissions = ','.join(permissions)
  
          # hacking the permissions into the name of the test user
          default_name = 'Permissions %s' % permissions.replace(
              ',', ' ').replace('_', '')
          name = name or default_name
  
          # retrieve all test users
          test_users = cls.get_test_users(app_access_token)
          user_id_dict = dict([(int(u['id']), u) for u in test_users])
          user_ids = map(str, user_id_dict.keys())
  
          # use fql to figure out their names
          facebook = OpenFacebook(app_access_token)
          users = facebook.fql('SELECT uid, name FROM user WHERE uid in (%s)' %
                               ','.join(user_ids))
          users_dict = dict([(u['name'], u['uid']) for u in users])
          user_id = users_dict.get(name)
  
          if force_create and user_id:
              # we need the users access_token, the app access token doesn't
              # always work, seems to be a bug in the Facebook api
              test_user_data = user_id_dict[user_id]
              cls.delete_test_user(test_user_data['access_token'], user_id)
              user_id = None
  
          if user_id:
              # we found our user, extend the data a bit
              test_user_data = user_id_dict[user_id]
              test_user_data['name'] = name
              test_user = TestUser(test_user_data)
          else:
              # create the user
              test_user = cls.create_test_user(
                  app_access_token, permissions, name)
  
          return test_user
  
      @classmethod
      def get_test_users(cls, app_access_token):
          kwargs = dict(access_token=app_access_token)
          path = '%s/accounts/test-users' % facebook_settings.FACEBOOK_APP_ID
          # retrieve all test users
          response = cls.request(path, **kwargs)
          test_users = response['data']
          return test_users
  
      @classmethod
      def delete_test_user(cls, app_access_token, test_user_id):
          kwargs = dict(access_token=app_access_token, method='delete')
          path = '%s/' % test_user_id
  
          # retrieve all test users
          response = cls.request(path, **kwargs)
          return response
  
      @classmethod
      def delete_test_users(cls, app_access_token):
          # retrieve all test users
          test_users = cls.get_test_users(app_access_token)
          test_user_ids = [u['id'] for u in test_users]
          for test_user_id in test_user_ids:
              cls.delete_test_user(app_access_token, test_user_id)
  
  
  class OpenFacebook(FacebookConnection):
  
      '''
      The main api class, initialize using
  
      **Example**::
  
          graph = OpenFacebook(access_token)
          print(graph.get('me'))
  
      '''
  
      def __init__(self, access_token=None, prefetched_data=None,
                   expires=None, current_user_id=None, version=None):
          '''
              :param access_token:
                  The facebook Access token
          '''
          self.access_token = access_token
          # extra data coming from signed cookies
          self.prefetched_data = prefetched_data
  
          # store to enable detection for offline usage
          self.expires = expires
  
          # hook to store the current user id if representing the
          # facebook connection to a logged in user :)
          self.current_user_id = current_user_id
  
          if version is None:
              version = 'v1.0'
          self.version = version
  
      def __getstate__(self):
          '''
          Turns the object into something easy to serialize
          '''
          state = dict(
              access_token=self.access_token,
              prefetched_data=self.prefetched_data,
              expires=self.expires,
          )
          return state
  
      def __setstate__(self, state):
          '''
          Restores the object from the state dict
          '''
          self.access_token = state['access_token']
          self.prefetched_data = state['prefetched_data']
          self.expires = state['expires']
  
      def is_authenticated(self):
          '''
          Ask facebook if we have access to the users data
  
          :returns:  bool
          '''
          try:
              me = self.me()
          except facebook_exceptions.OpenFacebookException as e:
              if isinstance(e, facebook_exceptions.OAuthException):
                  raise
              me = None
          authenticated = bool(me)
          return authenticated
  
      def get(self, path, version=None, **kwargs):
          '''
          Make a Facebook API call
  
          **Example**::
  
              open_facebook.get('me')
              open_facebook.get('me', fields='id,name')
  
          :param path:
              The path to use for making the API call
  
          :returns:  dict
          '''
          version = version or self.version
          kwargs['version'] = version
          response = self.request(path, **kwargs)
          return response
  
      def get_many(self, *ids, **kwargs):
          '''
          Make a batched Facebook API call
          For multiple ids
  
          **Example**::
  
              open_facebook.get_many('me', 'starbucks')
              open_facebook.get_many('me', 'starbucks', fields='id,name')
  
          :param path:
              The path to use for making the API call
  
          :returns:  dict
          '''
          kwargs['ids'] = ','.join(ids)
          return self.request(**kwargs)
  
      def set(self, path, params=None, version=None, **post_data):
          '''
          Write data to facebook
  
          **Example**::
  
              open_facebook.set('me/feed', message='testing open facebook')
  
          :param path:
              The path to use for making the API call
  
          :param params:
              A dictionary of get params
  
          :param post_data:
              The kwargs for posting to facebook
  
          :returns:  dict
          '''
          version = version or self.version
          assert self.access_token, 'Write operations require an access token'
          if not params:
              params = {}
          params['method'] = 'post'
  
          params['version'] = version
          response = self.request(path, post_data=post_data, **params)
          return response
  
      def delete(self, path, *args, **kwargs):
          '''
          Delete the given bit of data
  
          **Example**::
  
              graph.delete(12345)
  
          :param path:
              the id of the element to remove
  
          '''
  
          kwargs['method'] = 'delete'
          self.request(path, *args, **kwargs)
  
      def fql(self, query, **kwargs):
          '''
          Runs the specified query against the Facebook FQL API.
  
          **Example**::
  
              open_facebook.fql('SELECT name FROM user WHERE uid = me()')
  
          :param query:
              The query to execute
  
          :param kwargs:
              Extra options to send to facebook
  
          :returns:  dict
          '''
          kwargs['q'] = query
          path = 'fql'
  
          response = self.request(path, **kwargs)
  
          # return only the data for backward compatability
          return response['data']
  
      def batch_fql(self, queries_dict):
          '''
          queries_dict a dict with the required queries
          returns the query results in:
  
          **Example**::
  
              response = facebook.batch_fql({
                  name: 'SELECT uid, name, pic_square FROM user WHERE uid = me()',
                  rsvp: 'SELECT uid, rsvp_status FROM event_member WHERE eid=12345678',
              })
  
              # accessing the results
              response['fql_results']['name']
              response['fql_results']['rsvp']
  
          :param queries_dict:
              A dictiontary of queries to execute
  
          :returns: dict
          '''
          query = json.dumps(queries_dict)
          query_results = self.fql(query)
          named_results = dict(
              [(r['name'], r['fql_result_set']) for r in query_results])
  
          return named_results
  
      def me(self):
          '''
          Cached method of requesting information about me
          '''
          me = getattr(self, '_me', None)
          if me is None:
              # self._me = me = self.get('me')
              self._me = me = self.get('me', fields="id,name,email,verified")
  
          return me
  
      def permissions(self):
          '''
          Shortcut for self.get('me/permissions') with some extra parsing
          to turn it into a dictionary of booleans
  
          :returns: dict
          '''
          permissions_dict = {}
          try:
              permissions = {}
              permissions_response = self.get('me/permissions')
  
              # determine whether we're dealing with 1.0 or 2.0+
              for permission in permissions_response.get('data', []):
                  # graph api 2.0+, returns multiple dicts with keys 'status' and
                  # 'permission'
                  if any(value in ['granted', 'declined'] for value in permission.values()):
                      for perm in permissions_response['data']:
                          grant = perm.get('status') == 'granted'
                          name = perm.get('permission')
                          # just in case something goes sideways
                          if grant and name:
                              permissions_dict[name] = grant
                  # graph api 1.0, returns single dict as {permission: intval}
                  elif any(value in [0, 1, '0', '1'] for value in permission.values()):
                      permissions = permissions_response['data'][0]
                      permissions_dict = dict([(k, bool(int(v)))
                                               for k, v in permissions.items()
                                               if v == '1' or v == 1])
                  break
          except facebook_exceptions.OAuthException:
              pass
  
          return permissions_dict
  
      def has_permissions(self, required_permissions):
          '''
          Validate if all the required_permissions are currently given
          by the user
  
          **Example**::
  
              open_facebook.has_permissions(['publish_actions','read_stream'])
  
          :param required_permissions:
              A list of required permissions
  
          :returns: bool
          '''
          permissions_dict = self.permissions()
          # see if we have all permissions
          has_permissions = True
          for permission in required_permissions:
              if permission not in permissions_dict:
                  has_permissions = False
          return has_permissions
  
      def my_image_url(self, size='large'):
          '''
          Returns the image url from your profile
          Shortcut for me/picture
  
          :param size:
              the type of the image to request, see facebook for available formats
  
          :returns: string
          '''
          query_dict = QueryDict('', True)
          query_dict['type'] = size
          query_dict['access_token'] = self.access_token
  
          url = '%sme/picture?%s' % (self.api_url, query_dict.urlencode())
          return url
  
      def request(self, path='', post_data=None, old_api=False, version=None, **params):
          url = self.get_request_url(path=path, old_api=old_api, version=version,
                                     **params)
          logger.info('requesting url %s', url)
          response = self._request(url, post_data)
          return response
  
      def get_request_url(self, path='', old_api=False, version=None, **params):
          '''
          Gets the url for the request.
          '''
          api_base_url = self.old_api_url if old_api else self.api_url
          version = version or self.version
  
          if getattr(self, 'access_token', None):
              params['access_token'] = self.access_token
  
          if api_base_url.endswith('/'):
              api_base_url = api_base_url[:-1]
  
          if path and path.startswith('/'):
              path = path[1:]
              
          if path == 'me':
              params['fields'] = 'email,first_name,last_name,name,cover,picture'
  
          url = '/'.join([api_base_url, version, path])
          return '%s?%s' % (url, urlencode(params))
  
  
  class TestUser(object):
  
      '''
      Simple wrapper around test users
      '''
  
      def __init__(self, data):
          self.name = data['name']
          self.id = data['id']
          self.access_token = data['access_token']
          self.data = data
  
      def graph(self):
          graph = OpenFacebook(self.access_token)
          return graph
  
      def __repr__(self):
          return 'Test user %s' % self.name
  