Exemplo n.º 1
0
    def send(self, graph=None):
        result = None
        #update the last attempt
        self.last_attempt = datetime.datetime.now()
        self.save()

        #see if the graph is enabled
        profile = get_fb_profile(self.user)
        graph = graph or profile.get_offline_graph()
        user_enabled = profile.facebook_open_graph and self.facebook_user_id

        #start sharing
        if graph and user_enabled:
            graph_location = '%s/%s' % (
                self.facebook_user_id, self.action_domain)
            share_dict = self.get_share_dict()
            from open_facebook.exceptions import OpenFacebookException
            try:
                result = graph.set(graph_location, **share_dict)
                share_id = result.get('id')
                if not share_id:
                    error_message = 'No id in Facebook response, found %s for url %s with data %s' % (result, graph_location, share_dict)
                    logger.error(error_message)
                    raise OpenFacebookException(error_message)
                self.share_id = share_id
                self.error_message = None
                self.completed_at = datetime.datetime.now()
                self.save()
            except OpenFacebookException, e:
                logger.warn(
                    'Open graph share failed, writing message %s' % e.message)
                self.error_message = unicode(e)
                self.save()
Exemplo n.º 2
0
def retry_facebook_invite(modeladmin, request, queryset):
    '''
    Retries sending the invite to the users wall
    '''
    invites = list(queryset)
    user_invites = defaultdict(list)
    for invite in invites:
        user_invites[invite.user].append(invite)

    for user, invites in user_invites.items():
        profile = get_fb_profile(self.user)
        graph = profile.get_offline_graph()
        if not graph:
            error_message = 'couldnt connect to the graph, user access token is %s' % profile.access_token
            messages.error(request, error_message)
            continue
        logger.info('got graph %s for user %s, retrying %s invites',
                    graph, user, len(invites))
        for invite in invites:
            invite_result = invite.resend(graph)
            message = 'User %s sent attempt to sent with id %s s6 is %s' % (
                user, invite_result.wallpost_id, not invite_result.error)
            if invite_result.error:
                message += ' got error %s' % invite_result.error_message
            messages.info(request, message)

        profile.update_invite_denormalizations()
        profile.save()
Exemplo n.º 3
0
    def authenticate(self, facebook_id=None, facebook_email=None):
        '''
        Authenticate the facebook user by id OR facebook_email
        We filter using an OR to allow existing members to connect with
        their facebook ID using email.
        '''
        if facebook_id or facebook_email:
            profile_class = get_profile_class()
            profile_query = profile_class.objects.all().order_by('user')
            profile_query = profile_query.select_related('user')
            profile = None

            #filter on email or facebook id, two queries for better
            #queryplan with large data sets
            if facebook_id:
                profiles = profile_query.filter(facebook_id=facebook_id)[:1]
                profile = profiles[0] if profiles else None
            if profile is None and facebook_email:
                try:
                    user = models.User.objects.get(email=facebook_email)
                except models.User.DoesNotExist:
                    user = None
                profile = get_fb_profile(user) if user else None

            if profile:
                # populate the profile cache while we're getting it anyway
                user = profile.user
                user._profile = profile
                if facebook_settings.FACEBOOK_FORCE_PROFILE_UPDATE_ON_LOGIN:
                    user.fb_update_required = True
                return user
Exemplo n.º 4
0
def connect_user(request, access_token=None, facebook_graph=None):
    '''
    Given a request either

    - (if authenticated) connect the user
    - login
    - register
    '''
    user = None
    graph = facebook_graph or get_facebook_graph(request, access_token)
    facebook = FacebookUserConverter(graph)

    assert facebook.is_authenticated()
    facebook_data = facebook.facebook_profile_data()
    force_registration = request.REQUEST.get('force_registration') or\
        request.REQUEST.get('force_registration_hard')

    connect_facebook = to_bool(request.REQUEST.get('connect_facebook'))

    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
        user = _connect_user(request, facebook)
    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)
            if not get_fb_profile(auth_user).facebook_id:
                update = True
            #login the user
            user = _login_user(request, facebook, 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, facebook,
                                      remove_old_connections=force_registration)
            except facebook_exceptions.AlreadyRegistered, 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)
                action = CONNECT_ACTIONS.LOGIN
                user = _login_user(request, facebook, auth_user, update=False)
Exemplo n.º 5
0
 def resend(self, graph=None):
     from django_facebook.invite import post_on_profile
     if not graph:
         graph = get_fb_profile(self.user).get_offline_graph()
         if not graph:
             return
     facebook_id = self.user_invited
     invite_result = post_on_profile(
         self.user, graph, facebook_id, self.message, force_send=True)
     return invite_result
Exemplo n.º 6
0
def facebook_profile(open_graph_share):
    '''
    Nicely displayed version of the facebook user
    with user id and image and link to facebook :)
    '''
    user = open_graph_share.user
    profile = get_fb_profile(user)
    facebook_id = profile.facebook_id
    facebook_url = 'http://www.facebook.com/%s/' % facebook_id
    link = '<p><a href="%s"><img src="http://graph.facebook.com/%s/picture/?type=large" width="100px" style="float:left"/>%s</a><br/></p>' % (facebook_url, facebook_id, facebook_id)
    return link
Exemplo n.º 7
0
def _add_current_user_id(graph, user):
    '''
    set the current user id, convenient if you want to make sure you
    fb session and user belong together
    '''
    if graph:
        graph.current_user_id = None

    if user.is_authenticated() and graph:
        profile = get_fb_profile(user)
        facebook_id = getattr(profile, 'facebook_id', None)
        if facebook_id:
            graph.current_user_id = facebook_id
Exemplo n.º 8
0
def disconnect(request):
    '''
    Removes Facebook from the users profile
    And redirects to the specified next page
    '''
    if request.method == 'POST':
        messages.info(
            request, _("You have disconnected your Facebook profile."))
        profile = get_fb_profile(request.user)
        profile.disconnect_facebook()
        profile.save()
    response = next_redirect(request)
    return response
Exemplo n.º 9
0
def _update_access_token(user, graph):
    '''
    Conditionally updates the access token in the database
    '''
    profile = get_fb_profile(user)
    #store the access token for later usage if the profile model supports it
    if hasattr(profile, 'access_token'):
        # update if not equal to the current token
        new_token = graph.access_token != profile.access_token
        token_message = 'a new' if new_token else 'the same'
        logger.info('found %s token', token_message)
        if new_token:
            logger.info('access token changed, updating now')
            profile.access_token = graph.access_token
            profile.save()
            #see if we can extend the access token
            #this runs in a task, after extending the token we fire an event
            profile.extend_access_token()
Exemplo n.º 10
0
 def save(self, *args, **kwargs):
     if self.user and not self.facebook_user_id:
         self.facebook_user_id = get_fb_profile(self.user).facebook_id
     return models.Model.save(self, *args, **kwargs)
Exemplo n.º 11
0
def get_facebook_graph(request=None, access_token=None, redirect_uri=None, raise_=False):
    '''
    given a request from one of these
    - js authentication flow (signed cookie)
    - facebook app authentication flow (signed cookie)
    - facebook oauth redirect (code param in url)
    - mobile authentication flow (direct access_token)
    - offline access token stored in user profile

    returns a graph object

    redirect path is the path from which you requested the token
    for some reason facebook needs exactly this uri when converting the code
    to a token
    falls back to the current page without code in the request params
    specify redirect_uri if you are not posting and recieving the code
    on the same page
    '''
    #this is not a production flow, but very handy for testing
    if not access_token and request.REQUEST.get('access_token'):
        access_token = request.REQUEST['access_token']
    # should drop query params be included in the open facebook api,
    # maybe, weird this...
    from open_facebook import OpenFacebook, FacebookAuthorization
    from django.core.cache import cache
    parsed_data = None
    expires = None
    if hasattr(request, 'facebook'):
        graph = request.facebook
        _add_current_user_id(graph, request.user)
        return graph

    if not access_token:
        #easy case, code is in the get
        code = request.REQUEST.get('code')
        if code:
            logger.info('Got code from the request data')
        if not code:
            #signed request or cookie leading, base 64 decoding needed
            signed_data = request.REQUEST.get('signed_request')
            cookie_name = 'fbsr_%s' % facebook_settings.FACEBOOK_APP_ID
            cookie_data = request.COOKIES.get(cookie_name)

            if cookie_data:
                signed_data = cookie_data
                #the javascript api assumes a redirect uri of ''
                redirect_uri = ''
            if signed_data:
                logger.info('Got signed data from facebook')
                parsed_data = FacebookAuthorization.parse_signed_data(
                    signed_data)
                if parsed_data:
                    logger.info('Got parsed data from facebook')
                    #parsed data can fail because of signing issues
                    if 'oauth_token' in parsed_data:
                        logger.info('Got access_token from parsed data')
                        # we already have an active access token in the data
                        access_token = parsed_data['oauth_token']
                    else:
                        logger.info('Got code from parsed data')
                        # no access token, need to use this code to get one
                        code = parsed_data.get('code', None)

        if not access_token:
            if code:
                cache_key = 'convert_code_%s' % code
                access_token = cache.get(cache_key)
                if not access_token:
                    # exchange the code for an access token
                    # based on the php api
                    # https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php
                    # create a default for the redirect_uri
                    # when using the javascript sdk the default
                    # should be '' an empty string
                    # for other pages it should be the url
                    if not redirect_uri:
                        redirect_uri = ''

                    # we need to drop signed_request, code and state
                    redirect_uri = cleanup_oauth_url(redirect_uri)

                    try:
                        logger.info(
                            'trying to convert the code with redirect uri: %s',
                            redirect_uri)
                        #This is realy slow, that's why it's cached
                        token_response = FacebookAuthorization.convert_code(
                            code, redirect_uri=redirect_uri)
                        expires = token_response.get('expires')
                        access_token = token_response['access_token']
                        #would use cookies instead, but django's cookie setting
                        #is a bit of a mess
                        cache.set(cache_key, access_token, 60 * 60 * 2)
                    except open_facebook_exceptions.OAuthException, e:
                        # this sometimes fails, but it shouldnt raise because
                        # it happens when users remove your
                        # permissions and then try to reauthenticate
                        logger.warn('Error when trying to convert code %s',
                                    unicode(e))
                        if raise_:
                            raise
                        else:
                            return None
            elif request.user.is_authenticated():
                #support for offline access tokens stored in the users profile
                profile = get_fb_profile(request.user)
                access_token = getattr(profile, 'access_token', None)
                if not access_token:
                    if raise_:
                        message = 'Couldnt find an access token in the request or the users profile'
                        raise open_facebook_exceptions.OAuthException(message)
                    else:
                        return None
            else:
                if raise_:
                    message = 'Couldnt find an access token in the request or cookies'
                    raise open_facebook_exceptions.OAuthException(message)
                else:
                    return None
Exemplo n.º 12
0
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']
    user_dirty = profile_dirty = False
    profile = get_fb_profile(user)

    signals.facebook_pre_update.send(sender=get_profile_class(),
                                     profile=profile, facebook_data=facebook_data)

    profile_field_names = [f.name for f in profile._meta.fields]
    user_field_names = [f.name for f in user._meta.fields]

    #set the facebook id and make sure we are the only user with this id
    facebook_id_changed = facebook_data['facebook_id'] != profile.facebook_id
    overwrite_allowed = overwrite or not profile.facebook_id

    #update the facebook id and access token
    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(profile.facebook_id))
        profile.facebook_id = facebook_data['facebook_id']
        profile_dirty = True
        _remove_old_connections(profile.facebook_id, user.id)

    #update all fields on both user and profile
    for f in facebook_fields:
        facebook_value = facebook_data.get(f, False)
        if facebook_value:
            if (f in profile_field_names and hasattr(profile, f)):
                logger.debug('profile field %s changed from %s to %s', f,
                             getattr(profile, f), facebook_value)
                setattr(profile, f, facebook_value)
                profile_dirty = True
            elif (f in user_field_names and hasattr(user, f)):
                logger.debug('user field %s changed from %s to %s', f,
                             getattr(user, f), facebook_value)
                setattr(user, f, facebook_value)
                user_dirty = True

    #write the raw data in case we missed something
    if hasattr(profile, 'raw_data'):
        serialized_fb_data = json.dumps(facebook.facebook_profile_data())
        if profile.raw_data != serialized_fb_data:
            logger.debug('profile raw data changed from %s to %s',
                         profile.raw_data, serialized_fb_data)
            profile.raw_data = serialized_fb_data
            profile_dirty = True

    image_url = facebook_data['image']
    #update the image if we are allowed and have to
    if facebook_settings.FACEBOOK_STORE_LOCAL_IMAGE:
        if hasattr(profile, 'image') and not profile.image:
            profile_dirty = _update_image(profile, image_url)

    #save both models if they changed
    if user_dirty:
        user.save()
    if profile_dirty:
        profile.save()

    signals.facebook_post_update.send(sender=get_profile_class(),
                                      profile=profile, facebook_data=facebook_data)

    return user
 def post_registration_redirect(self, request, user):
     '''
     Handled by the Django Facebook app
     '''
     response = get_fb_profile(user).post_facebook_registration(request)
     return response