Example #1
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()

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

    text_comment = TextComment(
        actor=author,
        content=comment
    )
    text_comment.save()

    comment_link = Comment()
    comment_link.target = media_entry
    comment_link.comment = text_comment
    comment_link.save()

    Session.expunge(comment_link)

    return text_comment
Example #2
0
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 in (user_a, user_b):
        for m in (media_a, media_b):
            cmt = TextComment()
            cmt.actor = u.id
            cmt.content = u"Some Comment"
            Session.add(cmt)
            # think i need this to get the command ID
            Session.flush()

            link = Comment()
            link.target = m
            link.comment = cmt
            Session.add(link)

    Session.flush()

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

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

    usr_cnt2 = User.query.count()
    med_cnt2 = MediaEntry.query.count()
    cmt_cnt2 = Comment.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 = Comment.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
Example #3
0
def test_comments_removed_when_graveyarded(test_app):
    """ Checks comments which are tombstones are removed from collection """
    user = fixture_add_user()
    media = fixture_media_entry(uploader=user.id,
                                expunge=False,
                                fake_upload=False)

    # Add the TextComment
    comment = TextComment()
    comment.actor = user.id
    comment.content = u"This is a comment that will be deleted."
    comment.save()

    # Add a link for the comment
    link = Comment()
    link.target = media
    link.comment = comment
    link.save()

    # First double check it's there and all is well...
    assert Comment.query.filter_by(
        target_id=link.target_id).first() is not None

    # Now delete the comment.
    comment.delete()

    # Verify this also deleted the Comment link, ergo there is no comment left.
    assert Comment.query.filter_by(target_id=link.target_id).first() is None
Example #4
0
def test_comments_removed_when_graveyarded(test_app):
    """ Checks comments which are tombstones are removed from collection """
    user = fixture_add_user()
    media = fixture_media_entry(
        uploader=user.id,
        expunge=False,
        fake_upload=False
    )
    
    # Add the TextComment
    comment = TextComment()
    comment.actor = user.id
    comment.content = u"This is a comment that will be deleted."
    comment.save()

    # Add a link for the comment
    link = Comment()
    link.target = media
    link.comment = comment
    link.save()

    # First double check it's there and all is well...
    assert Comment.query.filter_by(target_id=link.target_id).first() is not None

    # Now delete the comment.
    comment.delete()

    # Verify this also deleted the Comment link, ergo there is no comment left.
    assert Comment.query.filter_by(target_id=link.target_id).first() is None
Example #5
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()

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

    text_comment = TextComment(actor=author, content=comment)
    text_comment.save()

    comment_link = Comment()
    comment_link.target = media_entry
    comment_link.comment = text_comment
    comment_link.save()

    Session.expunge(comment_link)

    return text_comment
Example #6
0
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 in (user_a, user_b):
        for m in (media_a, media_b):
            cmt = TextComment()
            cmt.actor = u.id
            cmt.content = u"Some Comment"
            Session.add(cmt)
            # think i need this to get the command ID
            Session.flush()

            link = Comment()
            link.target = m
            link.comment = cmt
            Session.add(link)

    Session.flush()

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

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

    usr_cnt2 = User.query.count()
    med_cnt2 = MediaEntry.query.count()
    cmt_cnt2 = Comment.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 = Comment.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
Example #7
0
def feed_endpoint(request, outbox=None):
    """ Handles the user's outbox - /api/user/<username>/feed """
    username = request.matchdict["username"]
    requested_user = LocalUser.query.filter(
        LocalUser.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 = TextComment(actor=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_reply_to(),
                                           generator=generator)

                return json_response(activity.serialize(request))

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

                # Build public_id
                public_id = request.urlgen("mediagoblin.api.object",
                                           object_type=obj["objectType"],
                                           id=media_id,
                                           qualified=True)

                media = MediaEntry.query.filter_by(public_id=public_id).first()

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

                if media.actor != 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 = extract_url_arguments(url=obj["id"],
                                           urlmap=request.app.url_map)["id"]

            public_id = request.urlgen("mediagoblin.api.object",
                                       object_type=obj["objectType"],
                                       id=obj_id,
                                       qualified=True)

            # 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 = TextComment.query.filter_by(
                    public_id=public_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.actor != 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(public_id=public_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.actor != 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 = extract_url_arguments(url=obj["id"],
                                           urlmap=request.app.url_map)["id"]

            public_id = request.urlgen("mediagoblin.api.object",
                                       object_type=obj["objectType"],
                                       id=obj_id,
                                       qualified=True)

            if obj.get("objectType", None) == "comment":
                # Find the comment asked for
                comment = TextComment.query.filter_by(
                    public_id=public_id, actor=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(
                    public_id=public_id, actor=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=requested_user.id)
    else:
        outbox = outbox.filter_by(actor=requested_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)
    offset = request.args.get("offset", 0)
    try:
        offset = int(offset)
    except ValueError:
        offset = 0
    outbox = outbox.offset(offset)

    # 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)
Example #8
0
def feed_endpoint(request, outbox=None):
    """ Handles the user's outbox - /api/user/<username>/feed """
    username = request.matchdict["username"]
    requested_user = LocalUser.query.filter(LocalUser.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 = TextComment(actor=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_reply_to(),
                    generator=generator
                )

                return json_response(activity.serialize(request))

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

                # Build public_id
                public_id = request.urlgen(
                    "mediagoblin.api.object",
                    object_type=obj["objectType"],
                    id=media_id,
                    qualified=True
                )

                media = MediaEntry.query.filter_by(
                    public_id=public_id
                ).first()

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

                if media.actor != 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 = extract_url_arguments(
                url=obj["id"],
                urlmap=request.app.url_map
            )["id"]

            public_id = request.urlgen(
                "mediagoblin.api.object",
                object_type=obj["objectType"],
                id=obj_id,
                qualified=True
            )

            # 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 = TextComment.query.filter_by(
                    public_id=public_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.actor != 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(
                    public_id=public_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.actor != 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 = extract_url_arguments(
                url=obj["id"],
                urlmap=request.app.url_map
            )["id"]

            public_id = request.urlgen(
                "mediagoblin.api.object",
                object_type=obj["objectType"],
                id=obj_id,
                qualified=True
            )

            if obj.get("objectType", None) == "comment":
                # Find the comment asked for
                comment = TextComment.query.filter_by(
                    public_id=public_id,
                    actor=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(
                    public_id=public_id,
                    actor=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)