Exemple #1
0
def update_profile_settings(user_id,
                            about="",
                            hide_feed_images=False,
                            feed_size=25,
                            replies_size=25,
                            alerts_size=50,
                            reply_sort_order=-1,
                            homepage='',
                            location='',
                            upload=None,
                            permission=0):
    """Update all options on a users profile settings in MongoDB."""
    # Ensure the homepage URL is as valid as it can be
    if homepage != '':
        homepage = fix_url(homepage)

    avatar = None
    if upload:
        filename = process_upload(upload, image_size=(96, 96), thumbnail=False)
        if filename is not None:  # pragma: no cover
            avatar = filename

    update_dict = {
        'about': about,
        'hide_feed_images': hide_feed_images,
        'feed_pagination_size': int(feed_size),
        'replies_pagination_size': int(replies_size),
        'alerts_pagination_size': int(alerts_size),
        'reply_sort_order': reply_sort_order,
        'homepage': homepage,
        'location': location,
        'default_permission': int(permission)
    }

    if avatar is not None:
        update_dict['avatar'] = avatar

        user = get_user(user_id)

        if user.get('avatar'):
            # Clean up any old avatars
            # There is no update in GridFS
            delete_upload(user.get('avatar'))

    # Update the users profile
    m.db.users.update({'_id': user_id}, {'$set': update_dict})

    # Return the user object. We can update the current_user from this
    return get_user(user_id)
Exemple #2
0
def activate(token):
    """
    Activates the user account so long as the token is valid.
    """
    # Attempt to get the data from the token
    data = check_token(token)
    if data is not None and data.get('action') == 'activate':
        # Attempt to activate the users account
        user = get_user(data.get('uid'))
        # This does not need a branching check as it should never fail!
        # The check is there for safety. An auth token can not live longer
        # than a newly created user.
        if user is not None:  # pragma: no branch
            be_activate(user.get('_id'))
            # If we have got to this point. Send a welcome e-mail :)
            send_mail('Pjuu Account Notifcation - Welcome!',
                      [user.get('email')],
                      text_body=render_template('emails/welcome.txt'),
                      html_body=render_template('emails/welcome.html'))
            flash('Your account has now been activated', 'success')
            return redirect(url_for('auth.signin'))

    # The token is either out of date or has been tampered with
    flash('Invalid token', 'error')
    return redirect(url_for('auth.signin'))
Exemple #3
0
def forgot():
    """Allow users to get a password reset link"""
    form = ForgotForm(request.form)
    # We always go to /signin after a POST
    if request.method == 'POST':
        if form.validate():
            user = get_user(get_uid(form.username.data, non_active=True))
            if user is not None:
                # Only send e-mails to user which exist.
                token = generate_token({
                    'action': 'reset',
                    'uid': user.get('_id')
                })
                send_mail(
                    'Pjuu Account Notification - Password Reset',
                    [user.get('email')],
                    text_body=render_template('emails/forgot.txt',
                                              token=token),
                    html_body=render_template('emails/forgot.html',
                                              token=token)
                )
            flash('If we\'ve found your account we\'ve e-mailed you',
                  'information')
            return redirect(url_for('auth.signin'))
        else:
            flash('Please enter a username or e-mail address',
                  'error')

    return render_template('forgot.html', form=form)
Exemple #4
0
def activate(token):
    """
    Activates the user account so long as the token is valid.
    """
    # Attempt to get the data from the token
    data = check_token(token)
    if data is not None and data.get('action') == 'activate':
        # Attempt to activate the users account
        user = get_user(data.get('uid'))
        # This does not need a branching check as it should never fail!
        # The check is there for safety. An auth token can not live longer
        # than a newly created user.
        if user is not None:  # pragma: no branch
            be_activate(user.get('_id'))
            # If we have got to this point. Send a welcome e-mail :)
            send_mail(
                'Pjuu Account Notifcation - Welcome!',
                [user.get('email')],
                text_body=render_template('emails/welcome.txt'),
                html_body=render_template('emails/welcome.html')
            )
            flash('Your account has now been activated', 'success')
            return redirect(url_for('auth.signin'))

    # The token is either out of date or has been tampered with
    flash('Invalid token', 'error')
    return redirect(url_for('auth.signin'))
Exemple #5
0
def update_profile_settings(user_id, about="", hide_feed_images=False,
                            feed_size=25, replies_size=25, alerts_size=50,
                            reply_sort_order=-1, homepage='', location='',
                            upload=None, permission=0):
    """Update all options on a users profile settings in MongoDB."""
    # Ensure the homepage URL is as valid as it can be
    if homepage != '':
        homepage = fix_url(homepage)

    avatar = None
    if upload:
        filename, _ = process_upload(upload, image_size=(96, 96),
                                     thumbnail=False)
        if filename is not None:  # pragma: no cover
            avatar = filename

    update_dict = {
        'about': about,
        'hide_feed_images': hide_feed_images,
        'feed_pagination_size': int(feed_size),
        'replies_pagination_size': int(replies_size),
        'alerts_pagination_size': int(alerts_size),
        'reply_sort_order': reply_sort_order,
        'homepage': homepage,
        'location': location,
        'default_permission': int(permission)
    }

    if avatar is not None:
        update_dict['avatar'] = avatar

        user = get_user(user_id)

        if user.get('avatar'):
            # Clean up any old avatars
            # There is no update in GridFS
            delete_upload(user.get('avatar'))

    # Update the users profile
    m.db.users.update({'_id': user_id}, {'$set': update_dict})

    # Return the user object. We can update the current_user from this
    return get_user(user_id)
Exemple #6
0
def avatar(username):
    """Return the users avatar image or the dafault."""
    # Get the user
    user = get_user(get_uid_username(username))

    # If the user has an avatar set then get it from GridFS
    if user.get('avatar') is not None:
        return get_upload(user.get('avatar'), cache_for=0,
                          collection='avatars')

    # The user doesn't have one send them the default
    return send_file('static/img/otter_avatar.png', cache_timeout=0)
Exemple #7
0
def _load_user():
    """Get the currently logged in user as a `dict` and store on the
    application context. This will be `None` if the user is not logged in.

    """
    user = None
    if 'user_id' in session:
        # Fetch the user object from MongoDB
        user = get_user(session.get('user_id'))
        # Remove the uid from the session if the user is not logged in
        if not user:
            session.pop('user_id', None)
    _app_ctx_stack.top.user = user
Exemple #8
0
def _load_user():
    """Get the currently logged in user as a `dict` and store on the
    application context. This will be `None` if the user is not logged in.

    """
    user = None
    if 'user_id' in session:
        # Fetch the user object from MongoDB
        user = get_user(session.get('user_id'))
        # Remove the uid from the session if the user is not logged in
        if not user:
            session.pop('user_id', None)
    _app_ctx_stack.top.user = user
Exemple #9
0
def new_alerts(user_id):
    """Checks too see if user has any new alerts since they last got the them.

    """
    # Get the stamp since last check from Redis
    # If this has not been called before make it 0
    alerts_last_checked = get_user(user_id).get('alerts_last_checked', 0)

    # Do the check. This will just see if there is anything returned from the
    # sorted set newer than the last_checked timestamp, SIMPLES.
    #
    # Note: zrevrangebyscore has max and min the wrong way round :P
    return bool(r.zrevrangebyscore(k.USER_ALERTS.format(user_id), '+inf',
                alerts_last_checked, start=0, num=1))
Exemple #10
0
def new_alerts(user_id):
    """Checks too see if user has any new alerts since they last got the them.

    """
    # Get the stamp since last check from Redis
    # If this has not been called before make it 0
    alerts_last_checked = get_user(user_id).get('alerts_last_checked', 0)

    # Do the check. This will just see if there is anything returned from the
    # sorted set newer than the last_checked timestamp, SIMPLES.
    #
    # Note: zrevrangebyscore has max and min the wrong way round :P
    return len(
        r.zrevrangebyscore(k.USER_ALERTS.format(user_id), '+inf',
                           alerts_last_checked))
Exemple #11
0
def get_following(uid, page=1):
    """Returns a list of users uid is following as a pagination object."""
    per_page = app.config.get('PROFILE_ITEMS_PER_PAGE')
    total = r.zcard(k.USER_FOLLOWING.format(uid))
    fids = r.zrevrange(k.USER_FOLLOWING.format(uid), (page - 1) * per_page,
                       (page * per_page) - 1)
    users = []
    for fid in fids:
        user = get_user(fid)
        if user:
            users.append(user)
        else:
            # Self cleaning sorted sets
            r.zrem(k.USER_FOLLOWING.format(uid), fid)
            total = r.zcard(k.USER_FOLLOWING.format(id))

    return Pagination(users, total, page, per_page)
Exemple #12
0
def forgot():
    """Allow users to get a password reset link"""
    form = ForgotForm(request.form)
    # We always go to /signin after a POST
    if request.method == 'POST':
        user = get_user(get_uid(form.username.data, non_active=True))
        if user is not None:
            # Only send e-mails to user which exist.
            token = generate_token({'action': 'reset', 'uid': user.get('_id')})
            send_mail('Pjuu Account Notification - Password Reset',
                      [user.get('email')],
                      text_body=render_template('emails/forgot.txt',
                                                token=token),
                      html_body=render_template('emails/forgot.html',
                                                token=token))
        flash('If we\'ve found your account we\'ve e-mailed you',
              'information')
        return redirect(url_for('auth.signin'))
    return render_template('forgot.html', form=form)
Exemple #13
0
def update_profile_settings(user_id, about="", hide_feed_images=False,
                            feed_size=25, replies_size=25, alerts_size=50,
                            homepage='', location='', upload=None):
    """Update all options on a users profile settings in MongoDB."""
    # Ensure the homepage URL is as valid as it can be
    if homepage != '':
        homepage = fix_url(homepage)

    avatar = None
    if upload:
        filename = process_upload(user_id, upload, collection='avatars',
                                  image_size=(96, 96))
        if filename is not None:  # pragma: no cover
            avatar = filename

    update_dict = {
        'about': about,
        'hide_feed_images': hide_feed_images,
        'feed_pagination_size': int(feed_size),
        'replies_pagination_size': int(replies_size),
        'alerts_pagination_size': int(alerts_size),
        'homepage': homepage,
        'location': location,
    }

    if avatar is not None:
        # Add the avatar to the dict
        update_dict['avatar'] = avatar

        # Clean up any old avatars
        # There is no update in GridFS
        grid = gridfs.GridFS(m.db, collection='avatars')
        # Get all old ones
        cursor = grid.find(
            {'filename': avatar}).sort('uploadDate', -1).skip(1)
        for f in cursor:
            grid.delete(f._id)

    # Update the users profile
    m.db.users.update({'_id': user_id}, {'$set': update_dict})

    # Return the user object. We can update the current_user from this
    return get_user(user_id)
Exemple #14
0
def get_following(uid, page=1, per_page=None):
    """Returns a list of users uid is following as a pagination object."""
    if per_page is None:
        per_page = app.config.get('FEED_ITEMS_PER_PAGE')

    total = r.zcard(k.USER_FOLLOWING.format(uid))
    fids = r.zrevrange(k.USER_FOLLOWING.format(uid), (page - 1) * per_page,
                       (page * per_page) - 1)
    users = []
    for fid in fids:
        user = get_user(fid)
        if user:
            users.append(user)
        else:
            # Self cleaning sorted sets
            r.zrem(k.USER_FOLLOWING.format(uid), fid)
            total = r.zcard(k.USER_FOLLOWING.format(id))

    return Pagination(users, total, page, per_page)
Exemple #15
0
def get_followers(uid, page=1, per_page=None):
    """Returns a list of users who follow user with uid as a pagination object.

    """
    if per_page is None:
        per_page = app.config.get('FEED_ITEMS_PER_PAGE')

    total = r.zcard(k.USER_FOLLOWERS.format(uid))
    fids = r.zrevrange(k.USER_FOLLOWERS.format(uid), (page - 1) * per_page,
                       (page * per_page) - 1)
    users = []
    for fid in fids:
        user = get_user(fid)
        if user:
            users.append(user)
        else:
            # Self cleaning sorted sets
            r.zrem(k.USER_FOLLOWERS.format(uid), fid)
            total = r.zcard(k.USER_FOLLOWERS.format(uid))

    return Pagination(users, total, page, per_page)
Exemple #16
0
def view_post(username, post_id):
    """Displays a post along with its comments paginated. I am not sure if this
    should be here or in the 'posts' app.

    .. note: Viewable to the public if the post is public!
    """
    if not check_post(get_uid(username), post_id):
        return abort(404)

    # Get post and comments for the current page
    _post = get_post(post_id)

    # Stop a reply from ever being shown here
    if 'reply_to' in _post:
        return abort(404)

    _user = get_user(get_uid(username))

    # Only get the permission if the post is not owned by the current user
    if current_user:
        current_user_id = current_user.get('_id')
    else:
        current_user_id = None

    permission = get_user_permission(_user.get('_id'), current_user_id)

    if permission < _post.get('permission', k.PERM_PUBLIC):
        return abort(403)

    # Pagination
    page = handle_page(request)

    # Handle explicit sort order
    # Fall back to user default else default
    sort = request.args.get('sort', None)
    if sort is None:
        if current_user:
            sort = current_user.get('reply_sort_order', -1)
        else:
            sort = -1
    else:
        try:
            sort = 1 if int(sort) > 0 else -1
        except ValueError:
            if current_user:
                sort = current_user.get('reply_sort_order', -1)
            else:
                sort = -1

    # Get the page sizes taking in to account non-logged in users
    if current_user:
        page_size = current_user.get(
            'replies_pagination_size',
            app.config.get('REPLIES_ITEMS_PER_PAGE', 25)
        )
    else:
        page_size = app.config.get('REPLIES_ITEMS_PER_PAGE', 25)

    pagination = get_replies(post_id, page, page_size, sort)

    post_form = PostForm()
    return render_template('view_post.html', post=_post,
                           pagination=pagination, post_form=post_form,
                           sort=sort)
Exemple #17
0
    def test_votes(self):
        """
        Test that the voting mechanism will adjust the relevant score counters
        on users, posts and comments, etc...
        """
        # Create three test users
        user1 = create_account('user1', '*****@*****.**', 'Password')
        user2 = create_account('user2', '*****@*****.**', 'Password')
        user3 = create_account('user3', '*****@*****.**', 'Password')

        # Create a post by user 1
        post1 = create_post(user1, 'user1', 'Test post')

        # Get user 3 to downvote, this will test that the user can not go
        # negative yet the post can
        self.assertIsNone(vote_post(user3, post1, amount=-1))
        # Ensure post score has been adjusted
        self.assertEqual(get_post(post1).get('score'), -1)
        # Ensure user score has NOT been adjusted
        self.assertEqual(get_user(user1).get('score'), 0)

        # Get user 2 to upvote
        self.assertIsNone(vote_post(user2, post1))
        # Ensure post score has been adjusted
        self.assertEqual(get_post(post1).get('score'), 0)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), 1)

        # Ensure user 1 can not vote on there own post
        self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, post1))
        # Ensure the scores have not been adjusted
        self.assertEqual(get_post(post1).get('score'), 0)
        self.assertEqual(get_user(user1).get('score'), 1)

        # Check to see if a user can vote twice on a post
        self.assertRaises(AlreadyVoted, lambda: vote_post(user2, post1))
        # Ensure the scores have not been adjusted
        self.assertEqual(get_post(post1).get('score'), 0)
        self.assertEqual(get_user(user1).get('score'), 1)

        # Repeat the same tests on a comment
        # Create a comment by user 1
        comment1 = create_post(user1, 'user1', 'Test comment', post1)

        # Let's cheat and set user1's score back to 0 so we can check it will
        # not be lowered in the user3 downvote
        m.db.users.update({'_id': user1},
                          {'$set': {'score': 0}})

        # Get user 3 to downvote
        self.assertIsNone(vote_post(user3, comment1, amount=-1))
        # Ensure post score has been adjusted
        self.assertEqual(get_post(comment1)['score'], -1)
        # Ensure user score has NOT been adjusted
        self.assertEqual(get_user(user1)['score'], 0)

        # Get user 2 to upvote
        self.assertIsNone(vote_post(user2, comment1))
        # Ensure post score has been adjusted
        self.assertEqual(get_post(comment1).get('score'), 0)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), 1)

        # Ensure user 1 can not vote on there own comment
        self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, comment1))
        # Ensure post score has been adjusted
        self.assertEqual(get_post(comment1).get('score'), 0)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), 1)

        # Check to see if a user can vote twice on a comment
        self.assertRaises(AlreadyVoted, lambda: vote_post(user2, comment1))
        # Ensure post score has been adjusted
        self.assertEqual(get_post(comment1).get('score'), 0)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), 1)
Exemple #18
0
def delete_account(user_id):
    """Will delete a users account.

    This **REMOVES ALL** details, posts, replies, etc. Not votes though.

    .. note: Ensure the user has authenticated this request. This is going to
             be the most *expensive* task in Pjuu, be warned.

    :param user_id: The `user_id` of the user to delete
    :type user_id: str

    """
    # Get the user object we will need this to remove the avatar
    user = get_user(user_id)

    # Delete the user from MongoDB
    m.db.users.remove({'_id': user_id})

    # If the user has an avatar remove it
    if user.get('avatar'):
        delete_upload(user.get('avatar'))

    # Remove all posts a user has ever made. This includes all votes
    # on the posts and all comments of the posts.
    # This calls the backend function from posts to do the deed
    posts_cursor = m.db.posts.find({'user_id': user_id}, {})
    for post in posts_cursor:
        delete_post(post.get('_id'))

    # Remove all the following relationships from Redis

    # Delete all references to followers of the user.
    # This will remove the user from the other users following list

    # TODO Replace with ZSCAN
    follower_cursor = r.zrange(k.USER_FOLLOWERS.format(user_id), 0, -1)

    for follower_id in follower_cursor:
        # Clear the followers following list of the uid
        r.zrem(k.USER_FOLLOWING.format(follower_id), user_id)
    # Delete the followers list
    r.delete(k.USER_FOLLOWERS.format(user_id))

    # Delete all references to the users the user is following
    # This will remove the user from the others users followers list

    # TODO Replace with ZSCAN
    followee_cursor = r.zrange(k.USER_FOLLOWING.format(user_id), 0, -1)

    for followee_id in followee_cursor:
        # Clear the followers list of people uid is following
        r.zrem(k.USER_FOLLOWERS.format(followee_id), user_id)
    # Delete the following list
    r.delete(k.USER_FOLLOWING.format(user_id))

    # Delete the users feed, this may have been added too during this process.
    # Probably not but let's be on the safe side
    r.delete(k.USER_FEED.format(user_id))

    # Delete the users alert list
    # DO NOT DELETE ANY ALERTS AS THESE ARE GENERIC
    r.delete(k.USER_ALERTS.format(user_id))
Exemple #19
0
    def test_votes(self):
        """
        Test that the voting mechanism will adjust the relevant score counters
        on users, posts and comments, etc...
        """
        # Create three test users
        user1 = create_account('user1', '*****@*****.**', 'Password')
        user2 = create_account('user2', '*****@*****.**', 'Password')
        user3 = create_account('user3', '*****@*****.**', 'Password')

        # Create a post by user 1
        post1 = create_post(user1, 'user1', 'Test post')

        # Get user 3 to downvote
        self.assertEqual(vote_post(user3, post1, amount=-1), -1)
        # Ensure post score has been adjusted
        self.assertEqual(get_post(post1).get('score'), -1)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), -1)

        # Check that a user can reverse their vote within TIMEOUT
        self.assertEqual(vote_post(user3, post1, amount=-1), 0)
        self.assertEqual(get_post(post1).get('score'), 0)
        self.assertEqual(get_user(user1).get('score'), 0)

        # Get user 2 to upvote
        self.assertEqual(vote_post(user2, post1), 1)
        # Ensure post score has been adjusted
        self.assertEqual(get_post(post1).get('score'), 1)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), 1)

        # Ensure user 1 can not vote on there own post
        self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, post1))
        # Ensure the scores have not been adjusted
        self.assertEqual(get_post(post1).get('score'), 1)
        self.assertEqual(get_user(user1).get('score'), 1)

        # Ensure the user has voted
        self.assertTrue(has_voted(user2, post1))

        # Check that a user can reverse their vote within TIMEOUT
        self.assertEqual(vote_post(user2, post1), 0)
        self.assertEqual(get_post(post1).get('score'), 0)
        self.assertEqual(get_user(user1).get('score'), 0)

        # Ensure the user has voted
        self.assertFalse(has_voted(user2, post1))

        # Check that the score reflects an opposite vote within TIMEOUT
        self.assertEqual(vote_post(user2, post1, 1), 1)
        self.assertEqual(get_post(post1).get('score'), 1)
        self.assertEqual(get_user(user1).get('score'), 1)

        self.assertTrue(has_voted(user2, post1))

        self.assertEqual(vote_post(user2, post1, -1), -1)
        self.assertEqual(get_post(post1).get('score'), -1)
        self.assertEqual(get_user(user1).get('score'), -1)

        self.assertTrue(has_voted(user2, post1))

        # Check that a user can not reverse there vote after the TIMEOUT
        self.assertRaises(AlreadyVoted,
                          lambda: vote_post(user2, post1, -1,
                                            timestamp() + K.VOTE_TIMEOUT + 1))

        self.assertRaises(AlreadyVoted,
                          lambda: vote_post(user2, post1, 1,
                                            timestamp() + K.VOTE_TIMEOUT + 1))

        # Repeat the same tests on a comment
        # Create a comment by user 1
        comment1 = create_post(user1, 'user1', 'Test comment', post1)

        # Let's cheat and set user1's score back to 0
        m.db.users.update({'_id': user1},
                          {'$set': {'score': 0}})

        # Get user 3 to downvote
        self.assertEqual(vote_post(user3, comment1, amount=-1), -1)
        # Ensure post score has been adjusted
        self.assertEqual(get_post(comment1).get('score'), -1)
        self.assertEqual(get_user(user1).get('score'), -1)

        # Reverse user3's vote just so it's not confusing
        self.assertEqual(vote_post(user3, comment1, amount=-1), 0)
        self.assertEqual(get_post(comment1).get('score'), 0)
        self.assertEqual(get_user(user1).get('score'), 0)

        # Ensure user 1 can not vote on there own comment
        self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, comment1))
        # Ensure post score has been adjusted
        self.assertEqual(get_post(comment1).get('score'), 0)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), 0)

        # Get user 2 to upvote
        self.assertEqual(vote_post(user2, comment1), 1)
        # Ensure post score has been adjusted
        self.assertEqual(get_post(comment1).get('score'), 1)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), 1)

        self.assertTrue(has_voted(user2, comment1))

        # Check that a user can reverse their vote within TIMEOUT
        self.assertEqual(vote_post(user2, comment1), 0)
        self.assertEqual(get_post(comment1).get('score'), 0)
        self.assertEqual(get_user(user1).get('score'), 0)

        self.assertFalse(has_voted(user2, comment1))

        # Check that the score reflects an opposite vote within TIMEOUT
        self.assertEqual(vote_post(user2, comment1, -1), -1)
        self.assertEqual(get_post(comment1).get('score'), -1)
        self.assertEqual(get_user(user1).get('score'), -1)

        self.assertTrue(has_voted(user2, comment1))

        self.assertEqual(vote_post(user2, comment1, 1), 1)
        self.assertEqual(get_post(comment1).get('score'), 1)
        self.assertEqual(get_user(user1).get('score'), 1)

        self.assertTrue(has_voted(user2, comment1))

        # Check that a user can not reverse there vote after the TIMEOUT
        self.assertRaises(AlreadyVoted,
                          lambda: vote_post(user2, comment1, -1,
                                            timestamp() + K.VOTE_TIMEOUT + 1))

        self.assertRaises(AlreadyVoted,
                          lambda: vote_post(user2, comment1, 1,
                                            timestamp() + K.VOTE_TIMEOUT + 1))
Exemple #20
0
def view_post(username, post_id):
    """Displays a post along with its comments paginated. I am not sure if this
    should be here or in the 'posts' app.

    .. note: Viewable to the public if the post is public!
    """
    if not check_post(get_uid(username), post_id):
        return abort(404)

    # Get post and comments for the current page
    _post = get_post(post_id)

    # Stop a reply from ever being shown here
    if 'reply_to' in _post:
        return abort(404)

    _user = get_user(get_uid(username))

    # Only get the permission if the post is not owned by the current user
    if current_user:
        current_user_id = current_user.get('_id')
    else:
        current_user_id = None

    permission = get_user_permission(_user.get('_id'), current_user_id)

    if permission < _post.get('permission', k.PERM_PUBLIC):
        return abort(403)

    # Pagination
    page = handle_page(request)

    # Handle explicit sort order
    # Fall back to user default else default
    sort = request.args.get('sort', None)
    if sort is None:
        if current_user:
            sort = current_user.get('reply_sort_order', -1)
        else:
            sort = -1
    else:
        try:
            sort = 1 if int(sort) > 0 else -1
        except ValueError:
            if current_user:
                sort = current_user.get('reply_sort_order', -1)
            else:
                sort = -1

    # Get the page sizes taking in to account non-logged in users
    if current_user:
        page_size = current_user.get(
            'replies_pagination_size',
            app.config.get('REPLIES_ITEMS_PER_PAGE', 25))
    else:
        page_size = app.config.get('REPLIES_ITEMS_PER_PAGE', 25)

    pagination = get_replies(post_id, page, page_size, sort)

    post_form = PostForm()
    return render_template('view_post.html',
                           post=_post,
                           pagination=pagination,
                           post_form=post_form,
                           sort=sort)
Exemple #21
0
    def test_votes(self):
        """
        Test that the voting mechanism will adjust the relevant score counters
        on users, posts and comments, etc...
        """
        # Create three test users
        user1 = create_account('user1', '*****@*****.**', 'Password')
        user2 = create_account('user2', '*****@*****.**', 'Password')
        user3 = create_account('user3', '*****@*****.**', 'Password')

        # Create a post by user 1
        post1 = create_post(user1, 'user1', 'Test post')

        # Get user 3 to downvote
        self.assertEqual(vote_post(user3, post1, amount=-1), -1)
        # Ensure post score has been adjusted
        self.assertEqual(get_post(post1).get('score'), -1)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), -1)

        # Check that a user can reverse their vote within TIMEOUT
        self.assertEqual(vote_post(user3, post1, amount=-1), 0)
        self.assertEqual(get_post(post1).get('score'), 0)
        self.assertEqual(get_user(user1).get('score'), 0)

        # Get user 2 to upvote
        self.assertEqual(vote_post(user2, post1), 1)
        # Ensure post score has been adjusted
        self.assertEqual(get_post(post1).get('score'), 1)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), 1)

        # Ensure user 1 can not vote on there own post
        self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, post1))
        # Ensure the scores have not been adjusted
        self.assertEqual(get_post(post1).get('score'), 1)
        self.assertEqual(get_user(user1).get('score'), 1)

        # Ensure the user has voted
        self.assertTrue(has_voted(user2, post1))

        # Check that a user can reverse their vote within TIMEOUT
        self.assertEqual(vote_post(user2, post1), 0)
        self.assertEqual(get_post(post1).get('score'), 0)
        self.assertEqual(get_user(user1).get('score'), 0)

        # Ensure the user has voted
        self.assertFalse(has_voted(user2, post1))

        # Check that the score reflects an opposite vote within TIMEOUT
        self.assertEqual(vote_post(user2, post1, 1), 1)
        self.assertEqual(get_post(post1).get('score'), 1)
        self.assertEqual(get_user(user1).get('score'), 1)

        self.assertTrue(has_voted(user2, post1))

        self.assertEqual(vote_post(user2, post1, -1), -1)
        self.assertEqual(get_post(post1).get('score'), -1)
        self.assertEqual(get_user(user1).get('score'), -1)

        self.assertTrue(has_voted(user2, post1))

        # Check that a user can not reverse there vote after the TIMEOUT
        self.assertRaises(
            AlreadyVoted, lambda: vote_post(user2, post1, -1,
                                            timestamp() + K.VOTE_TIMEOUT + 1))

        self.assertRaises(
            AlreadyVoted, lambda: vote_post(user2, post1, 1,
                                            timestamp() + K.VOTE_TIMEOUT + 1))

        # Repeat the same tests on a comment
        # Create a comment by user 1
        comment1 = create_post(user1, 'user1', 'Test comment', post1)

        # Let's cheat and set user1's score back to 0
        m.db.users.update({'_id': user1}, {'$set': {'score': 0}})

        # Get user 3 to downvote
        self.assertEqual(vote_post(user3, comment1, amount=-1), -1)
        # Ensure post score has been adjusted
        self.assertEqual(get_post(comment1).get('score'), -1)
        self.assertEqual(get_user(user1).get('score'), -1)

        # Reverse user3's vote just so it's not confusing
        self.assertEqual(vote_post(user3, comment1, amount=-1), 0)
        self.assertEqual(get_post(comment1).get('score'), 0)
        self.assertEqual(get_user(user1).get('score'), 0)

        # Ensure user 1 can not vote on there own comment
        self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, comment1))
        # Ensure post score has been adjusted
        self.assertEqual(get_post(comment1).get('score'), 0)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), 0)

        # Get user 2 to upvote
        self.assertEqual(vote_post(user2, comment1), 1)
        # Ensure post score has been adjusted
        self.assertEqual(get_post(comment1).get('score'), 1)
        # Ensure user score has been adjusted
        self.assertEqual(get_user(user1).get('score'), 1)

        self.assertTrue(has_voted(user2, comment1))

        # Check that a user can reverse their vote within TIMEOUT
        self.assertEqual(vote_post(user2, comment1), 0)
        self.assertEqual(get_post(comment1).get('score'), 0)
        self.assertEqual(get_user(user1).get('score'), 0)

        self.assertFalse(has_voted(user2, comment1))

        # Check that the score reflects an opposite vote within TIMEOUT
        self.assertEqual(vote_post(user2, comment1, -1), -1)
        self.assertEqual(get_post(comment1).get('score'), -1)
        self.assertEqual(get_user(user1).get('score'), -1)

        self.assertTrue(has_voted(user2, comment1))

        self.assertEqual(vote_post(user2, comment1, 1), 1)
        self.assertEqual(get_post(comment1).get('score'), 1)
        self.assertEqual(get_user(user1).get('score'), 1)

        self.assertTrue(has_voted(user2, comment1))

        # Check that a user can not reverse there vote after the TIMEOUT
        self.assertRaises(
            AlreadyVoted, lambda: vote_post(user2, comment1, -1,
                                            timestamp() + K.VOTE_TIMEOUT + 1))

        self.assertRaises(
            AlreadyVoted, lambda: vote_post(user2, comment1, 1,
                                            timestamp() + K.VOTE_TIMEOUT + 1))
Exemple #22
0
def delete_account(user_id):
    """Will delete a users account.

    This **REMOVES ALL** details, posts, replies, etc. Not votes though.

    .. note: Ensure the user has authenticated this request. This is going to
             be the most *expensive* task in Pjuu, be warned.

    :param user_id: The `user_id` of the user to delete
    :type user_id: str

    """
    # Get the user object we will need this to remove the avatar
    user = get_user(user_id)

    # Delete the user from MongoDB
    m.db.users.remove({'_id': user_id})

    # If the user has an avatar remove it
    if user.get('avatar'):
        delete_upload(user.get('avatar'))

    # Remove all posts a user has ever made. This includes all votes
    # on the posts and all comments of the posts.
    # This calls the backend function from posts to do the deed
    posts_cursor = m.db.posts.find({'user_id': user_id}, {})
    for post in posts_cursor:
        delete_post(post.get('_id'))

    # Remove all the following relationships from Redis

    # Delete all references to followers of the user.
    # This will remove the user from the other users following list

    # TODO Replace with ZSCAN
    follower_cursor = r.zrange(k.USER_FOLLOWERS.format(user_id), 0, -1)

    for follower_id in follower_cursor:
        # Clear the followers following list of the uid
        r.zrem(k.USER_FOLLOWING.format(follower_id), user_id)
    # Delete the followers list
    r.delete(k.USER_FOLLOWERS.format(user_id))

    # Delete all references to the users the user is following
    # This will remove the user from the others users followers list

    # TODO Replace with ZSCAN
    followee_cursor = r.zrange(k.USER_FOLLOWING.format(user_id), 0, -1)

    for followee_id in followee_cursor:
        # Clear the followers list of people uid is following
        r.zrem(k.USER_FOLLOWERS.format(followee_id), user_id)
    # Delete the following list
    r.delete(k.USER_FOLLOWING.format(user_id))

    # Delete the users feed, this may have been added too during this process.
    # Probably not but let's be on the safe side
    r.delete(k.USER_FEED.format(user_id))

    # Delete the users alert list
    # DO NOT DELETE ANY ALERTS AS THESE ARE GENERIC
    r.delete(k.USER_ALERTS.format(user_id))