text: from django.contrib import auth
  from django.contrib.auth import authenticate, login
  from django.core.files.temp import NamedTemporaryFile
  from django.core.files.uploadedfile import InMemoryUploadedFile
  from django.db import transaction
  from django.db.utils import IntegrityError
  import json
  from django_facebook import exceptions as facebook_exceptions, \
      settings as facebook_settings, signals
  from django_facebook.api import get_facebook_graph
  from django_facebook.utils import get_registration_backend, get_form_class, \
      get_profile_model, to_bool, get_user_model, get_instance_for,\
      get_user_attribute, try_get_profile, get_model_for_attribute,\
      get_instance_for_attribute, update_user_attributes
  from random import randint
  import logging
  import sys
  import urllib
  try:
      import urllib2
  except ImportError:
      import urllib.error as urllib2
  
  
  logger = logging.getLogger(__name__)
  
  
  class CONNECT_ACTIONS:
  
      class LOGIN:
          pass
  
      class CONNECT(LOGIN):
          pass
  
      class REGISTER:
          pass
  
  
  def connect_user(request, access_token=None, facebook_graph=None, connect_facebook=False):
      '''
      Given a request either
  
      - (if authenticated) connect the user
      - login
      - register
      '''
      user = None
      graph = facebook_graph or get_facebook_graph(request, access_token)
  
      converter = get_instance_for('user_conversion', graph)
  
      assert converter.is_authenticated()
      facebook_data = converter.facebook_profile_data()
      force_registration = request.POST.get('force_registration') or \
          request.GET.get('force_registration') or \
          request.POST.get('force_registration_hard') or \
          request.GET.get('force_registration_hard')
  
      logger.debug('force registration is set to %s', force_registration)
      if connect_facebook and request.user.is_authenticated() and not force_registration:
          # we should only allow connect if users indicate they really want to connect
          # only when the request.CONNECT_FACEBOOK = 1
          # if this isn't present we just do a login
          action = CONNECT_ACTIONS.CONNECT
          # default behaviour is not to overwrite old data
          user = _connect_user(request, converter, overwrite=True)
      else:
          email = facebook_data.get('email', False)
          email_verified = facebook_data.get('verified', False)
          kwargs = {}
          if email and email_verified:
              kwargs = {'facebook_email': email}
          auth_user = authenticate(facebook_id=facebook_data['id'], **kwargs)
          if auth_user and not force_registration:
              action = CONNECT_ACTIONS.LOGIN
  
              # Has the user registered without Facebook, using the verified FB
              # email address?
              # It is after all quite common to use email addresses for usernames
              update = getattr(auth_user, 'fb_update_required', False)
              profile = try_get_profile(auth_user)
              current_facebook_id = get_user_attribute(
                  auth_user, profile, 'facebook_id')
              if not current_facebook_id:
                  update = True
              # login the user
              user = _login_user(request, converter, auth_user, update=update)
          else:
              action = CONNECT_ACTIONS.REGISTER
              # when force registration is active we should remove the old
              # profile
              try:
                  user = _register_user(request, converter,
                                        remove_old_connections=force_registration)
              except facebook_exceptions.AlreadyRegistered as e:
                  # in Multithreaded environments it's possible someone beats us to
                  # the punch, in that case just login
                  logger.info(
                      'parallel register encountered, slower thread is doing a login')
                  auth_user = authenticate(
                      facebook_id=facebook_data['id'], **kwargs)
                  if not auth_user:
                      # We don't have a valid user so raise
                      raise e
                  action = CONNECT_ACTIONS.LOGIN
                  user = _login_user(request, converter, auth_user, update=False)
  
      _update_likes_and_friends(request, user, converter)
  
      _update_access_token(user, graph)
  
      logger.info('connect finished with action %s', action)
  
      return action, user
  
  
  def _login_user(request, facebook, authenticated_user, update=False):
      login(request, authenticated_user)
  
      if update:
          _connect_user(request, facebook)
  
      return authenticated_user
  
  
  def _connect_user(request, facebook, overwrite=True):
      '''
      Update the fields on the user model and connects it to the facebook account
  
      '''
      if not request.user.is_authenticated():
          raise ValueError(
              'Connect user can only be used on authenticated users')
      if not facebook.is_authenticated():
          raise ValueError(
              'Facebook needs to be authenticated for connect flows')
  
      data = facebook.facebook_profile_data()
      facebook_id = data['id']
  
      # see if we already have profiles connected to this Facebook account
      old_connections = _get_old_connections(facebook_id, request.user.id)[:20]
      if old_connections and not (request.POST.get('confirm_connect') or
                                  request.GET.get('confirm_connect')):
          raise facebook_exceptions.AlreadyConnectedError(list(old_connections))
      user = _update_user(request.user, facebook, overwrite=overwrite)
  
      return user
  
  
  def _update_likes_and_friends(request, user, facebook):
      # store likes and friends if configured
      sid = transaction.savepoint()
      try:
          if facebook_settings.FACEBOOK_STORE_LIKES:
              facebook.get_and_store_likes(user)
          if facebook_settings.FACEBOOK_STORE_FRIENDS:
              facebook.get_and_store_friends(user)
          transaction.savepoint_commit(sid)
      except IntegrityError as e:
          logger.warn(u'Integrity error encountered during registration, '
                      'probably a double submission %s' % e,
                      exc_info=sys.exc_info(), extra={
                          'request': request,
                          'data': {
                              'body': unicode(e),
                          }
                      })
          transaction.savepoint_rollback(sid)
  
  
  def _update_access_token(user, graph):
      '''
      Conditionally updates the access token in the database
      '''
      profile = try_get_profile(user)
      model_or_profile = get_instance_for_attribute(
          user, profile, 'access_token')
      # store the access token for later usage if the profile model supports it
      if model_or_profile:
          # update if not equal to the current token
          new_token = graph.access_token != model_or_profile.access_token
          token_message = 'a new' if new_token else 'the same'
          logger.info(
              'found %s token %s', token_message, graph.access_token[:10])
          if new_token:
              logger.info('access token changed, updating now')
              model_or_profile.update_access_token(graph.access_token)
              model_or_profile.save()
              # see if we can extend the access token
              # this runs in a task, after extending the token we fire an event
              model_or_profile.extend_access_token()
  
  
  def _register_user(request, facebook, profile_callback=None,
                     remove_old_connections=False):
      '''
      Creates a new user and authenticates
      The registration form handles the registration and validation
      Other data on the user profile is updates afterwards
  
      if remove_old_connections = True we will disconnect old
      profiles from their facebook flow
      '''
      if not facebook.is_authenticated():
          raise ValueError(
              'Facebook needs to be authenticated for connect flows')
  
      # get the backend on new registration systems, or none
      # if we are on an older version
      backend = get_registration_backend()
      logger.info('running backend %s for registration', backend)
  
      # gets the form class specified in FACEBOOK_REGISTRATION_FORM
      form_class = get_form_class(backend, request)
  
      facebook_data = facebook.facebook_registration_data()
  
      data = request.POST.copy()
      for k, v in facebook_data.items():
          if not data.get(k):
              data[k] = v
      if remove_old_connections:
          _remove_old_connections(facebook_data['facebook_id'])
  
      if request.POST.get('force_registration_hard') or \
              request.GET.get('force_registration_hard'):
          data['email'] = data['email'].replace(
              '@', '+test%s@' % randint(0, 1000000000))
          
      if not data.get('email'):
          data['email'] = '%s@facebook.com' % data['username'] 
  
      form = form_class(data=data, files=request.FILES,
                        initial={'ip': request.META['REMOTE_ADDR']})
  
      if not form.is_valid():
          # show errors in sentry
          form_errors = form.errors
          error = facebook_exceptions.IncompleteProfileError(
              'Facebook signup incomplete')
          error.form = form
          raise error
  
      try:
          # for new registration systems use the backends methods of saving
          new_user = None
          if backend:
              new_user = backend.register(request,
                                          form=form, **form.cleaned_data)
          # fall back to the form approach
          if new_user is None:
              raise ValueError(
                  'new_user is None, note that backward compatability for the older versions of django registration has been dropped.')
      except IntegrityError as e:
          # this happens when users click multiple times, the first request registers
          # the second one raises an error
          raise facebook_exceptions.AlreadyRegistered(e)
  
      # update some extra data not yet done by the form
      new_user = _update_user(new_user, facebook)
  
      signals.facebook_user_registered.send(sender=get_user_model(),
                                            user=new_user, facebook_data=facebook_data, request=request, converter=facebook)
      # IS this the correct way for django 1.3? seems to require the backend
      # attribute for some reason
      new_user.backend = 'django_facebook.auth_backends.FacebookBackend'
      auth.login(request, new_user)
  
      return new_user
  
  
  def _get_old_connections(facebook_id, current_user_id=None):
      '''
      Gets other accounts connected to this facebook id, which are not
      attached to the current user
      '''
      user_or_profile_model = get_model_for_attribute('facebook_id')
      other_facebook_accounts = user_or_profile_model.objects.filter(
          facebook_id=facebook_id)
      kwargs = {}
  
      if current_user_id:
          # if statement since we need to support both
          user_model = get_user_model()
          if user_or_profile_model == user_model:
              kwargs['id'] = current_user_id
          else:
              kwargs['user'] = current_user_id
          other_facebook_accounts = other_facebook_accounts.exclude(**kwargs)
      return other_facebook_accounts
  
  
  def _remove_old_connections(facebook_id, current_user_id=None):
      '''
      Removes the facebook id for profiles with the specified facebook id
      which arent the current user id
      '''
      other_facebook_accounts = _get_old_connections(
          facebook_id, current_user_id)
      other_facebook_accounts.update(facebook_id=None)
  
  
  def _update_user(user, facebook, overwrite=True):
      '''
      Updates the user and his/her profile with the data from facebook
      '''
      # if you want to add fields to ur user model instead of the
      # profile thats fine
      # partial support (everything except raw_data and facebook_id is included)
      facebook_data = facebook.facebook_registration_data(username=False)
      facebook_fields = ['facebook_name', 'facebook_profile_url', 'gender',
                         'date_of_birth', 'about_me', 'website_url', 'first_name', 'last_name']
  
      # prepare first_name and last_name
      name = facebook_data.get('name', ' ').split()
      if len(name):
          facebook_data['first_name'] = name[0]
      if len(name) > 1:
          facebook_data['last_name'] = ' '.join(name[1:])
  
      facebook_data['facebook_profile_url'] = facebook_data.get('image')
  
      profile = try_get_profile(user)
      # which attributes to update
      attributes_dict = {}
  
      # send the signal that we're updating
      signals.facebook_pre_update.send(sender=get_user_model(), user=user,
                                       profile=profile, facebook_data=facebook_data)
  
      # set the facebook id and make sure we are the only user with this id
      current_facebook_id = get_user_attribute(user, profile, 'facebook_id')
      facebook_id_changed = facebook_data['facebook_id'] != current_facebook_id
      overwrite_allowed = overwrite or not current_facebook_id
  
      # update the facebook id and access token
      facebook_id_overwritten = False
      if facebook_id_changed and overwrite_allowed:
          # when not overwriting we only update if there is no
          # profile.facebook_id
          logger.info('profile facebook id changed from %s to %s',
                      repr(facebook_data['facebook_id']),
                      repr(current_facebook_id))
          attributes_dict['facebook_id'] = facebook_data['facebook_id']
          facebook_id_overwritten = True
  
      if facebook_id_overwritten:
          _remove_old_connections(facebook_data['facebook_id'], user.id)
  
      # update all fields on both user and profile
      for f in facebook_fields:
          facebook_value = facebook_data.get(f, False)
          current_value = get_user_attribute(user, profile, f, None)
          if facebook_value and not current_value:
              attributes_dict[f] = facebook_value
  
      # write the raw data in case we missed something
      serialized_fb_data = json.dumps(facebook.facebook_profile_data())
      current_raw_data = get_user_attribute(user, profile, 'raw_data')
      if current_raw_data != serialized_fb_data:
          attributes_dict['raw_data'] = serialized_fb_data
  
      image_url = facebook_data['image']
      # update the image if we are allowed and have to
      if facebook_settings.FACEBOOK_STORE_LOCAL_IMAGE:
          image_field = get_user_attribute(user, profile, 'image', True)
          if not image_field:
              image_name, image_file = _update_image(
                  facebook_data['facebook_id'], image_url)
              image_field.save(image_name, image_file)
  
      # save both models if they changed
      update_user_attributes(user, profile, attributes_dict)
      if getattr(user, '_fb_is_dirty', False):
          user.save()
      if getattr(profile, '_fb_is_dirty', False):
          profile.save()
  
      signals.facebook_post_update.send(sender=get_user_model(),
                                        user=user, profile=profile, facebook_data=facebook_data)
  
      return user
  
  
  def _update_image(facebook_id, image_url):
      '''
      Updates the user profile's image to the given image url
      Unfortunately this is quite a pain to get right with Django
      Suggestions to improve this are welcome
      '''
      image_name = 'fb_image_%s.jpg' % facebook_id
      image_temp = NamedTemporaryFile()
      try:
          image_response = urllib2.urlopen(image_url)
      except AttributeError:
          image_response = urllib.request.urlopen(image_url)
      image_content = image_response.read()
      image_temp.write(image_content)
      http_message = image_response.info()
      image_size = len(image_content)
      try:
          content_type = http_message.type
      except AttributeError:
          content_type = http_message.get_content_type()
      image_file = InMemoryUploadedFile(
          file=image_temp, name=image_name, field_name='image',
          content_type=content_type, size=image_size, charset=None
      )
      image_file.seek(0)
      image_temp.flush()
      return image_name, image_file
  
  
  def update_connection(request, graph):
      '''
      A special purpose view for updating the connection with an existing user
      - updates the access token (already done in get_graph)
      - sets the facebook_id if nothing is specified
      - stores friends and likes if possible
      '''
      converter = get_instance_for('user_conversion', graph)
      user = _connect_user(request, converter, overwrite=False)
      _update_likes_and_friends(request, user, converter)
      _update_access_token(user, graph)
      return user
  