def test_user_deletes_other_comments(test_app):
    user_a = fixture_add_user(u"chris_a")
    user_b = fixture_add_user(u"chris_b")

    media_a = fixture_media_entry(uploader=user_a.id,
                                  save=False,
                                  expunge=False,
                                  fake_upload=False)
    media_b = fixture_media_entry(uploader=user_b.id,
                                  save=False,
                                  expunge=False,
                                  fake_upload=False)
    Session.add(media_a)
    Session.add(media_b)
    Session.flush()

    # Create all 4 possible comments:
    for u_id in (user_a.id, user_b.id):
        for m_id in (media_a.id, media_b.id):
            cmt = MediaComment()
            cmt.media_entry = m_id
            cmt.author = u_id
            cmt.content = u"Some Comment"
            Session.add(cmt)

    Session.flush()

    usr_cnt1 = User.query.count()
    med_cnt1 = MediaEntry.query.count()
    cmt_cnt1 = MediaComment.query.count()

    User.query.get(user_a.id).delete(commit=False)

    usr_cnt2 = User.query.count()
    med_cnt2 = MediaEntry.query.count()
    cmt_cnt2 = MediaComment.query.count()

    # One user deleted
    assert usr_cnt2 == usr_cnt1 - 1
    # One media gone
    assert med_cnt2 == med_cnt1 - 1
    # Three of four comments gone.
    assert cmt_cnt2 == cmt_cnt1 - 3

    User.query.get(user_b.id).delete()

    usr_cnt2 = User.query.count()
    med_cnt2 = MediaEntry.query.count()
    cmt_cnt2 = MediaComment.query.count()

    # All users gone
    assert usr_cnt2 == usr_cnt1 - 2
    # All media gone
    assert med_cnt2 == med_cnt1 - 2
    # All comments gone
    assert cmt_cnt2 == cmt_cnt1 - 4
def test_user_deletes_other_comments(test_app):
    user_a = fixture_add_user(u"chris_a")
    user_b = fixture_add_user(u"chris_b")

    media_a = fixture_media_entry(uploader=user_a.id, save=False,
                                  expunge=False, fake_upload=False)
    media_b = fixture_media_entry(uploader=user_b.id, save=False,
                                  expunge=False, fake_upload=False)
    Session.add(media_a)
    Session.add(media_b)
    Session.flush()

    # Create all 4 possible comments:
    for u_id in (user_a.id, user_b.id):
        for m_id in (media_a.id, media_b.id):
            cmt = MediaComment()
            cmt.media_entry = m_id
            cmt.author = u_id
            cmt.content = u"Some Comment"
            Session.add(cmt)

    Session.flush()

    usr_cnt1 = User.query.count()
    med_cnt1 = MediaEntry.query.count()
    cmt_cnt1 = MediaComment.query.count()

    User.query.get(user_a.id).delete(commit=False)

    usr_cnt2 = User.query.count()
    med_cnt2 = MediaEntry.query.count()
    cmt_cnt2 = MediaComment.query.count()

    # One user deleted
    assert usr_cnt2 == usr_cnt1 - 1
    # One media gone
    assert med_cnt2 == med_cnt1 - 1
    # Three of four comments gone.
    assert cmt_cnt2 == cmt_cnt1 - 3

    User.query.get(user_b.id).delete()

    usr_cnt2 = User.query.count()
    med_cnt2 = MediaEntry.query.count()
    cmt_cnt2 = MediaComment.query.count()

    # All users gone
    assert usr_cnt2 == usr_cnt1 - 2
    # All media gone
    assert med_cnt2 == med_cnt1 - 2
    # All comments gone
    assert cmt_cnt2 == cmt_cnt1 - 4
def fixture_add_comment(author=None, media_entry=None, comment=None):
    if author is None:
        author = fixture_add_user().id

    if media_entry is None:
        media_entry = fixture_media_entry().id

    if comment is None:
        comment = \
            'Auto-generated test comment by user #{0} on media #{0}'.format(
                author, media_entry)

    comment = MediaComment(author=author,
                      media_entry=media_entry,
                      content=comment)

    comment.save()

    Session.expunge(comment)

    return comment
示例#4
0
def fixture_add_comment(author=None, media_entry=None, comment=None):
    if author is None:
        author = fixture_add_user().id

    if media_entry is None:
        media_entry = fixture_media_entry().id

    if comment is None:
        comment = \
            'Auto-generated test comment by user #{0} on media #{0}'.format(
                author, media_entry)

    comment = MediaComment(author=author,
                           media_entry=media_entry,
                           content=comment)

    comment.save()

    Session.expunge(comment)

    return comment
示例#5
0
def feed(request):
    """ Handles the user's outbox - /api/user/<username>/feed """
    user = request.matchdict["username"]
    requested_user = User.query.filter_by(username=user).first()

    # check if the user exists
    if requested_user is None:
        return json_error("No such 'user' with id '{0}'".format(user), 404)

    if request.data:
        data = json.loads(request.data)
    else:
        data = {"verb": None, "object": {}}

    # We need to check that the user they're posting to is
    # the person that they are.
    if request.method in ["POST", "PUT"] and \
        requested_user.id != request.user.id:
        
        return json_error(
            "Not able to post to another users feed.",
            status=403
        )

    if request.method == "POST" and data["verb"] == "post":
        obj = data.get("object", None)
        if obj is None:
            return json_error("Could not find 'object' element.")

        if obj.get("objectType", None) == "comment":
            # post a comment
            if not request.user.has_privilege(u'commenter'):
                return json_error(
                    "Privilege 'commenter' required to comment.",
                    status=403
                )

            comment = MediaComment(author=request.user.id)
            comment.unserialize(data["object"])
            comment.save()
            data = {"verb": "post", "object": comment.serialize(request)}
            return json_response(data)

        elif obj.get("objectType", None) == "image":
            # Posting an image to the feed
            media_id = int(data["object"]["id"])
            media = MediaEntry.query.filter_by(id=media_id).first()
            if media is None:
                return json_response(
                    "No such 'image' with id '{0}'".format(id=media_id),
                    status=404
                )

            if not media.unserialize(data["object"]):
                return json_error(
                    "Invalid 'image' with id '{0}'".format(media_id)
                )

            media.save()
            api_add_to_feed(request, media)

            return json_response({
                "verb": "post",
                "object": media.serialize(request)
            })

        elif obj.get("objectType", None) is None:
            # They need to tell us what type of object they're giving us.
            return json_error("No objectType specified.")
        else:
            # Oh no! We don't know about this type of object (yet)
            object_type = obj.get("objectType", None)
            return json_error("Unknown object type '{0}'.".format(object_type))

    elif request.method in ["PUT", "POST"] and data["verb"] == "update":
        # Check we've got a valid object
        obj = data.get("object", None)

        if obj is None:
            return json_error("Could not find 'object' element.")

        if "objectType" not in obj:
            return json_error("No objectType specified.")

        if "id" not in obj:
            return json_error("Object ID has not been specified.")

        obj_id = obj["id"]

        # Now try and find object
        if obj["objectType"] == "comment":
            if not request.user.has_privilege(u'commenter'):
                return json_error(
                    "Privilege 'commenter' required to comment.",
                    status=403
                )

            comment = MediaComment.query.filter_by(id=obj_id).first()
            if comment is None:
                return json_error(
                    "No such 'comment' with id '{0}'.".format(obj_id)
                )

            # Check that the person trying to update the comment is
            # the author of the comment.
            if comment.author != request.user.id:
                return json_error(
                    "Only author of comment is able to update comment.",
                    status=403
                )

            if not comment.unserialize(data["object"]):
                return json_error(
                    "Invalid 'comment' with id '{0}'".format(obj_id)
                )

            comment.save()

            activity = {
                "verb": "update",
                "object": comment.serialize(request),
            }
            return json_response(activity)

        elif obj["objectType"] == "image":
            image = MediaEntry.query.filter_by(id=obj_id).first()
            if image is None:
                return json_error(
                    "No such 'image' with the id '{0}'.".format(obj_id)
                )

            # Check that the person trying to update the comment is
            # the author of the comment.
            if image.uploader != request.user.id:
                return json_error(
                    "Only uploader of image is able to update image.",
                    status=403
                )

            if not image.unserialize(obj):
                return json_error(
                    "Invalid 'image' with id '{0}'".format(obj_id)
                )
            image.save()

            activity = {
                "verb": "update",
                "object": image.serialize(request),
            }
            return json_response(activity)

    elif request.method != "GET":
        return json_error(
            "Unsupported HTTP method {0}".format(request.method),
            status=501
        )

    feed_url = request.urlgen(
        "mediagoblin.federation.feed",
        username=request.user.username,
        qualified=True
    )

    feed = {
        "displayName": "Activities by {user}@{host}".format(
            user=request.user.username,
            host=request.host
        ),
        "objectTypes": ["activity"],
        "url": feed_url,
        "links": {
            "first": {
                "href": feed_url,
            },
            "self": {
                "href": request.url,
            },
            "prev": {
                "href": feed_url,
            },
            "next": {
                "href": feed_url,
            }
        },
        "author": request.user.serialize(request),
        "items": [],
    }


    # Look up all the media to put in the feed (this will be changed
    # when we get real feeds/inboxes/outboxes/activites)
    for media in MediaEntry.query.all():
        item = {
            "verb": "post",
            "object": media.serialize(request),
            "actor": request.user.serialize(request),
            "content": "{0} posted a picture".format(request.user.username),
            "id": 1,
        }
        item["updated"] = item["object"]["updated"]
        item["published"] = item["object"]["published"]
        item["url"] = item["object"]["url"]
        feed["items"].append(item)
    feed["totalItems"] = len(feed["items"])

    return json_response(feed)
示例#6
0
def feed_endpoint(request, outbox=None):
    """ Handles the user's outbox - /api/user/<username>/feed """
    username = request.matchdict["username"]
    requested_user = User.query.filter_by(username=username).first()

    # check if the user exists
    if requested_user is None:
        return json_error("No such 'user' with id '{0}'".format(username), 404)

    if request.data:
        data = json.loads(request.data.decode())
    else:
        data = {"verb": None, "object": {}}

    if request.method in ["POST", "PUT"]:
        # Validate that the activity is valid
        if "verb" not in data or "object" not in data:
            return json_error("Invalid activity provided.")

        # Check that the verb is valid
        if data["verb"] not in ["post", "update", "delete"]:
            return json_error("Verb not yet implemented", 501)

        # We need to check that the user they're posting to is
        # the person that they are.
        if requested_user.id != request.user.id:
            return json_error("Not able to post to another users feed.",
                              status=403)

        # Handle new posts
        if data["verb"] == "post":
            obj = data.get("object", None)
            if obj is None:
                return json_error("Could not find 'object' element.")

            if obj.get("objectType", None) == "comment":
                # post a comment
                if not request.user.has_privilege(u'commenter'):
                    return json_error(
                        "Privilege 'commenter' required to comment.",
                        status=403)

                comment = MediaComment(author=request.user.id)
                comment.unserialize(data["object"], request)
                comment.save()

                # Create activity for comment
                generator = create_generator(request)
                activity = create_activity(verb="post",
                                           actor=request.user,
                                           obj=comment,
                                           target=comment.get_entry,
                                           generator=generator)

                return json_response(activity.serialize(request))

            elif obj.get("objectType", None) == "image":
                # Posting an image to the feed
                media_id = int(
                    extract_url_arguments(url=data["object"]["id"],
                                          urlmap=request.app.url_map)["id"])

                media = MediaEntry.query.filter_by(id=media_id).first()

                if media is None:
                    return json_response(
                        "No such 'image' with id '{0}'".format(media_id),
                        status=404)

                if media.uploader != request.user.id:
                    return json_error(
                        "Privilege 'commenter' required to comment.",
                        status=403)

                if not media.unserialize(data["object"]):
                    return json_error(
                        "Invalid 'image' with id '{0}'".format(media_id))

                # Add location if one exists
                if "location" in data:
                    Location.create(data["location"], self)

                media.save()
                activity = api_add_to_feed(request, media)

                return json_response(activity.serialize(request))

            elif obj.get("objectType", None) is None:
                # They need to tell us what type of object they're giving us.
                return json_error("No objectType specified.")
            else:
                # Oh no! We don't know about this type of object (yet)
                object_type = obj.get("objectType", None)
                return json_error(
                    "Unknown object type '{0}'.".format(object_type))

        # Updating existing objects
        if data["verb"] == "update":
            # Check we've got a valid object
            obj = data.get("object", None)

            if obj is None:
                return json_error("Could not find 'object' element.")

            if "objectType" not in obj:
                return json_error("No objectType specified.")

            if "id" not in obj:
                return json_error("Object ID has not been specified.")

            obj_id = int(
                extract_url_arguments(url=obj["id"],
                                      urlmap=request.app.url_map)["id"])

            # Now try and find object
            if obj["objectType"] == "comment":
                if not request.user.has_privilege(u'commenter'):
                    return json_error(
                        "Privilege 'commenter' required to comment.",
                        status=403)

                comment = MediaComment.query.filter_by(id=obj_id).first()
                if comment is None:
                    return json_error(
                        "No such 'comment' with id '{0}'.".format(obj_id))

                # Check that the person trying to update the comment is
                # the author of the comment.
                if comment.author != request.user.id:
                    return json_error(
                        "Only author of comment is able to update comment.",
                        status=403)

                if not comment.unserialize(data["object"], request):
                    return json_error("Invalid 'comment' with id '{0}'".format(
                        obj["id"]))

                comment.save()

                # Create an update activity
                generator = create_generator(request)
                activity = create_activity(verb="update",
                                           actor=request.user,
                                           obj=comment,
                                           generator=generator)

                return json_response(activity.serialize(request))

            elif obj["objectType"] == "image":
                image = MediaEntry.query.filter_by(id=obj_id).first()
                if image is None:
                    return json_error(
                        "No such 'image' with the id '{0}'.".format(obj["id"]))

                # Check that the person trying to update the comment is
                # the author of the comment.
                if image.uploader != request.user.id:
                    return json_error(
                        "Only uploader of image is able to update image.",
                        status=403)

                if not image.unserialize(obj):
                    return json_error(
                        "Invalid 'image' with id '{0}'".format(obj_id))
                image.generate_slug()
                image.save()

                # Create an update activity
                generator = create_generator(request)
                activity = create_activity(verb="update",
                                           actor=request.user,
                                           obj=image,
                                           generator=generator)

                return json_response(activity.serialize(request))
            elif obj["objectType"] == "person":
                # check this is the same user
                if "id" not in obj or obj["id"] != requested_user.id:
                    return json_error("Incorrect user id, unable to update")

                requested_user.unserialize(obj)
                requested_user.save()

                generator = create_generator(request)
                activity = create_activity(verb="update",
                                           actor=request.user,
                                           obj=requested_user,
                                           generator=generator)

                return json_response(activity.serialize(request))

        elif data["verb"] == "delete":
            obj = data.get("object", None)
            if obj is None:
                return json_error("Could not find 'object' element.")

            if "objectType" not in obj:
                return json_error("No objectType specified.")

            if "id" not in obj:
                return json_error("Object ID has not been specified.")

            # Parse out the object ID
            obj_id = int(
                extract_url_arguments(url=obj["id"],
                                      urlmap=request.app.url_map)["id"])

            if obj.get("objectType", None) == "comment":
                # Find the comment asked for
                comment = MediaComment.query.filter_by(
                    id=obj_id, author=request.user.id).first()

                if comment is None:
                    return json_error(
                        "No such 'comment' with id '{0}'.".format(obj_id))

                # Make a delete activity
                generator = create_generator(request)
                activity = create_activity(verb="delete",
                                           actor=request.user,
                                           obj=comment,
                                           generator=generator)

                # Unfortunately this has to be done while hard deletion exists
                context = activity.serialize(request)

                # now we can delete the comment
                comment.delete()

                return json_response(context)

            if obj.get("objectType", None) == "image":
                # Find the image
                entry = MediaEntry.query.filter_by(
                    id=obj_id, uploader=request.user.id).first()

                if entry is None:
                    return json_error(
                        "No such 'image' with id '{0}'.".format(obj_id))

                # Make the delete activity
                generator = create_generator(request)
                activity = create_activity(verb="delete",
                                           actor=request.user,
                                           obj=entry,
                                           generator=generator)

                # This is because we have hard deletion
                context = activity.serialize(request)

                # Now we can delete the image
                entry.delete()

                return json_response(context)

    elif request.method != "GET":
        return json_error("Unsupported HTTP method {0}".format(request.method),
                          status=501)

    feed = {
        "displayName":
        "Activities by {user}@{host}".format(user=request.user.username,
                                             host=request.host),
        "objectTypes": ["activity"],
        "url":
        request.base_url,
        "links": {
            "self": {
                "href": request.url
            }
        },
        "author":
        request.user.serialize(request),
        "items": [],
    }

    # Create outbox
    if outbox is None:
        outbox = Activity.query.filter_by(actor=request.user.id)
    else:
        outbox = outbox.filter_by(actor=request.user.id)

    # We want the newest things at the top (issue: #1055)
    outbox = outbox.order_by(Activity.published.desc())

    # Limit by the "count" (default: 20)
    limit = request.args.get("count", 20)

    try:
        limit = int(limit)
    except ValueError:
        limit = 20

    # The upper most limit should be 200
    limit = limit if limit < 200 else 200

    # apply the limit
    outbox = outbox.limit(limit)

    # Offset (default: no offset - first <count>  result)
    outbox = outbox.offset(request.args.get("offset", 0))

    # Build feed.
    for activity in outbox:
        try:
            feed["items"].append(activity.serialize(request))
        except AttributeError:
            # This occurs because of how we hard-deletion and the object
            # no longer existing anymore. We want to keep the Activity
            # in case someone wishes to look it up but we shouldn't display
            # it in the feed.
            pass
    feed["totalItems"] = len(feed["items"])

    return json_response(feed)
示例#7
0
def feed_endpoint(request, outbox=None):
    """ Handles the user's outbox - /api/user/<username>/feed """
    username = request.matchdict["username"]
    requested_user = User.query.filter_by(username=username).first()

    # check if the user exists
    if requested_user is None:
        return json_error("No such 'user' with id '{0}'".format(username), 404)

    if request.data:
        data = json.loads(request.data.decode())
    else:
        data = {"verb": None, "object": {}}

    if request.method in ["POST", "PUT"]:
        # Validate that the activity is valid
        if "verb" not in data or "object" not in data:
            return json_error("Invalid activity provided.")

        # Check that the verb is valid
        if data["verb"] not in ["post", "update", "delete"]:
            return json_error("Verb not yet implemented", 501)

        # We need to check that the user they're posting to is
        # the person that they are.
        if requested_user.id != request.user.id:
            return json_error("Not able to post to another users feed.", status=403)

        # Handle new posts
        if data["verb"] == "post":
            obj = data.get("object", None)
            if obj is None:
                return json_error("Could not find 'object' element.")

            if obj.get("objectType", None) == "comment":
                # post a comment
                if not request.user.has_privilege(u"commenter"):
                    return json_error("Privilege 'commenter' required to comment.", status=403)

                comment = MediaComment(author=request.user.id)
                comment.unserialize(data["object"], request)
                comment.save()

                # Create activity for comment
                generator = create_generator(request)
                activity = create_activity(
                    verb="post", actor=request.user, obj=comment, target=comment.get_entry, generator=generator
                )

                return json_response(activity.serialize(request))

            elif obj.get("objectType", None) == "image":
                # Posting an image to the feed
                media_id = int(extract_url_arguments(url=data["object"]["id"], urlmap=request.app.url_map)["id"])

                media = MediaEntry.query.filter_by(id=media_id).first()

                if media is None:
                    return json_response("No such 'image' with id '{0}'".format(media_id), status=404)

                if media.uploader != request.user.id:
                    return json_error("Privilege 'commenter' required to comment.", status=403)

                if not media.unserialize(data["object"]):
                    return json_error("Invalid 'image' with id '{0}'".format(media_id))

                # Add location if one exists
                if "location" in data:
                    Location.create(data["location"], self)

                media.save()
                activity = api_add_to_feed(request, media)

                return json_response(activity.serialize(request))

            elif obj.get("objectType", None) is None:
                # They need to tell us what type of object they're giving us.
                return json_error("No objectType specified.")
            else:
                # Oh no! We don't know about this type of object (yet)
                object_type = obj.get("objectType", None)
                return json_error("Unknown object type '{0}'.".format(object_type))

        # Updating existing objects
        if data["verb"] == "update":
            # Check we've got a valid object
            obj = data.get("object", None)

            if obj is None:
                return json_error("Could not find 'object' element.")

            if "objectType" not in obj:
                return json_error("No objectType specified.")

            if "id" not in obj:
                return json_error("Object ID has not been specified.")

            obj_id = int(extract_url_arguments(url=obj["id"], urlmap=request.app.url_map)["id"])

            # Now try and find object
            if obj["objectType"] == "comment":
                if not request.user.has_privilege(u"commenter"):
                    return json_error("Privilege 'commenter' required to comment.", status=403)

                comment = MediaComment.query.filter_by(id=obj_id).first()
                if comment is None:
                    return json_error("No such 'comment' with id '{0}'.".format(obj_id))

                # Check that the person trying to update the comment is
                # the author of the comment.
                if comment.author != request.user.id:
                    return json_error("Only author of comment is able to update comment.", status=403)

                if not comment.unserialize(data["object"], request):
                    return json_error("Invalid 'comment' with id '{0}'".format(obj["id"]))

                comment.save()

                # Create an update activity
                generator = create_generator(request)
                activity = create_activity(verb="update", actor=request.user, obj=comment, generator=generator)

                return json_response(activity.serialize(request))

            elif obj["objectType"] == "image":
                image = MediaEntry.query.filter_by(id=obj_id).first()
                if image is None:
                    return json_error("No such 'image' with the id '{0}'.".format(obj["id"]))

                # Check that the person trying to update the comment is
                # the author of the comment.
                if image.uploader != request.user.id:
                    return json_error("Only uploader of image is able to update image.", status=403)

                if not image.unserialize(obj):
                    return json_error("Invalid 'image' with id '{0}'".format(obj_id))
                image.generate_slug()
                image.save()

                # Create an update activity
                generator = create_generator(request)
                activity = create_activity(verb="update", actor=request.user, obj=image, generator=generator)

                return json_response(activity.serialize(request))
            elif obj["objectType"] == "person":
                # check this is the same user
                if "id" not in obj or obj["id"] != requested_user.id:
                    return json_error("Incorrect user id, unable to update")

                requested_user.unserialize(obj)
                requested_user.save()

                generator = create_generator(request)
                activity = create_activity(verb="update", actor=request.user, obj=requested_user, generator=generator)

                return json_response(activity.serialize(request))

        elif data["verb"] == "delete":
            obj = data.get("object", None)
            if obj is None:
                return json_error("Could not find 'object' element.")

            if "objectType" not in obj:
                return json_error("No objectType specified.")

            if "id" not in obj:
                return json_error("Object ID has not been specified.")

            # Parse out the object ID
            obj_id = int(extract_url_arguments(url=obj["id"], urlmap=request.app.url_map)["id"])

            if obj.get("objectType", None) == "comment":
                # Find the comment asked for
                comment = MediaComment.query.filter_by(id=obj_id, author=request.user.id).first()

                if comment is None:
                    return json_error("No such 'comment' with id '{0}'.".format(obj_id))

                # Make a delete activity
                generator = create_generator(request)
                activity = create_activity(verb="delete", actor=request.user, obj=comment, generator=generator)

                # Unfortunately this has to be done while hard deletion exists
                context = activity.serialize(request)

                # now we can delete the comment
                comment.delete()

                return json_response(context)

            if obj.get("objectType", None) == "image":
                # Find the image
                entry = MediaEntry.query.filter_by(id=obj_id, uploader=request.user.id).first()

                if entry is None:
                    return json_error("No such 'image' with id '{0}'.".format(obj_id))

                # Make the delete activity
                generator = create_generator(request)
                activity = create_activity(verb="delete", actor=request.user, obj=entry, generator=generator)

                # This is because we have hard deletion
                context = activity.serialize(request)

                # Now we can delete the image
                entry.delete()

                return json_response(context)

    elif request.method != "GET":
        return json_error("Unsupported HTTP method {0}".format(request.method), status=501)

    feed = {
        "displayName": "Activities by {user}@{host}".format(user=request.user.username, host=request.host),
        "objectTypes": ["activity"],
        "url": request.base_url,
        "links": {"self": {"href": request.url}},
        "author": request.user.serialize(request),
        "items": [],
    }

    # Create outbox
    if outbox is None:
        outbox = Activity.query.filter_by(actor=request.user.id)
    else:
        outbox = outbox.filter_by(actor=request.user.id)

    # We want the newest things at the top (issue: #1055)
    outbox = outbox.order_by(Activity.published.desc())

    # Limit by the "count" (default: 20)
    limit = request.args.get("count", 20)

    try:
        limit = int(limit)
    except ValueError:
        limit = 20

    # The upper most limit should be 200
    limit = limit if limit < 200 else 200

    # apply the limit
    outbox = outbox.limit(limit)

    # Offset (default: no offset - first <count>  result)
    outbox = outbox.offset(request.args.get("offset", 0))

    # Build feed.
    for activity in outbox:
        try:
            feed["items"].append(activity.serialize(request))
        except AttributeError:
            # This occurs because of how we hard-deletion and the object
            # no longer existing anymore. We want to keep the Activity
            # in case someone wishes to look it up but we shouldn't display
            # it in the feed.
            pass
    feed["totalItems"] = len(feed["items"])

    return json_response(feed)
def feed_endpoint(request):
    """ Handles the user's outbox - /api/user/<username>/feed """
    username = request.matchdict["username"]
    requested_user = User.query.filter_by(username=username).first()

    # check if the user exists
    if requested_user is None:
        return json_error("No such 'user' with id '{0}'".format(username), 404)

    if request.data:
        data = json.loads(request.data.decode())
    else:
        data = {"verb": None, "object": {}}


    if request.method in ["POST", "PUT"]:
        # Validate that the activity is valid
        if "verb" not in data or "object" not in data:
            return json_error("Invalid activity provided.")

        # Check that the verb is valid
        if data["verb"] not in ["post", "update"]:
            return json_error("Verb not yet implemented", 501)

        # We need to check that the user they're posting to is
        # the person that they are.
        if requested_user.id != request.user.id:
            return json_error(
                "Not able to post to another users feed.",
                status=403
            )

        # Handle new posts
        if data["verb"] == "post":
            obj = data.get("object", None)
            if obj is None:
                return json_error("Could not find 'object' element.")

            if obj.get("objectType", None) == "comment":
                # post a comment
                if not request.user.has_privilege(u'commenter'):
                    return json_error(
                        "Privilege 'commenter' required to comment.",
                        status=403
                    )

                comment = MediaComment(author=request.user.id)
                comment.unserialize(data["object"])
                comment.save()
                data = {
                    "verb": "post",
                    "object": comment.serialize(request)
                }
                return json_response(data)

            elif obj.get("objectType", None) == "image":
                # Posting an image to the feed
                media_id = int(data["object"]["id"])
                media = MediaEntry.query.filter_by(id=media_id).first()

                if media is None:
                    return json_response(
                        "No such 'image' with id '{0}'".format(media_id),
                        status=404
                    )

                if media.uploader != request.user.id:
                    return json_error(
                        "Privilege 'commenter' required to comment.",
                        status=403
                    )


                if not media.unserialize(data["object"]):
                    return json_error(
                        "Invalid 'image' with id '{0}'".format(media_id)
                    )


                # Add location if one exists
                if "location" in data:
                    Location.create(data["location"], self)

                media.save()
                api_add_to_feed(request, media)

                return json_response({
                    "verb": "post",
                    "object": media.serialize(request)
                })

            elif obj.get("objectType", None) is None:
                # They need to tell us what type of object they're giving us.
                return json_error("No objectType specified.")
            else:
                # Oh no! We don't know about this type of object (yet)
                object_type = obj.get("objectType", None)
                return json_error(
                    "Unknown object type '{0}'.".format(object_type)
                )

        # Updating existing objects
        if data["verb"] == "update":
            # Check we've got a valid object
            obj = data.get("object", None)

            if obj is None:
                return json_error("Could not find 'object' element.")

            if "objectType" not in obj:
                return json_error("No objectType specified.")

            if "id" not in obj:
                return json_error("Object ID has not been specified.")

            obj_id = obj["id"]

            # Now try and find object
            if obj["objectType"] == "comment":
                if not request.user.has_privilege(u'commenter'):
                    return json_error(
                        "Privilege 'commenter' required to comment.",
                        status=403
                    )

                comment = MediaComment.query.filter_by(id=obj_id).first()
                if comment is None:
                    return json_error(
                        "No such 'comment' with id '{0}'.".format(obj_id)
                    )

                # Check that the person trying to update the comment is
                # the author of the comment.
                if comment.author != request.user.id:
                    return json_error(
                        "Only author of comment is able to update comment.",
                        status=403
                    )

                if not comment.unserialize(data["object"]):
                    return json_error(
                        "Invalid 'comment' with id '{0}'".format(obj_id)
                    )

                comment.save()

                activity = {
                    "verb": "update",
                    "object": comment.serialize(request),
                }
                return json_response(activity)

            elif obj["objectType"] == "image":
                image = MediaEntry.query.filter_by(id=obj_id).first()
                if image is None:
                    return json_error(
                        "No such 'image' with the id '{0}'.".format(obj_id)
                    )

                # Check that the person trying to update the comment is
                # the author of the comment.
                if image.uploader != request.user.id:
                    return json_error(
                        "Only uploader of image is able to update image.",
                        status=403
                    )

                if not image.unserialize(obj):
                    return json_error(
                        "Invalid 'image' with id '{0}'".format(obj_id)
                    )
                image.generate_slug()
                image.save()

                activity = {
                    "verb": "update",
                    "object": image.serialize(request),
                }
                return json_response(activity)
            elif obj["objectType"] == "person":
                # check this is the same user
                if "id" not in obj or obj["id"] != requested_user.id:
                    return json_error(
                        "Incorrect user id, unable to update"
                    )

                requested_user.unserialize(obj)
                requested_user.save()

    elif request.method != "GET":
        return json_error(
            "Unsupported HTTP method {0}".format(request.method),
            status=501
        )

    feed_url = request.urlgen(
        "mediagoblin.federation.feed",
        username=request.user.username,
        qualified=True
    )

    feed = {
        "displayName": "Activities by {user}@{host}".format(
            user=request.user.username,
            host=request.host
        ),
        "objectTypes": ["activity"],
        "url": feed_url,
        "links": {
            "first": {
                "href": feed_url,
            },
            "self": {
                "href": request.url,
            },
            "prev": {
                "href": feed_url,
            },
            "next": {
                "href": feed_url,
            }
        },
        "author": request.user.serialize(request),
        "items": [],
    }

    for activity in Activity.query.filter_by(actor=request.user.id):
        feed["items"].append(activity.serialize(request))
    feed["totalItems"] = len(feed["items"])

    return json_response(feed)