def api_add_to_feed(request, entry): """ Add media to Feed """ feed_url = request.urlgen('mediagoblin.user_pages.atom_feed', qualified=True, user=request.user.username) add_comment_subscription(request.user, entry) # Create activity activity = create_activity(verb="post", obj=entry, actor=entry.actor, generator=create_generator(request)) entry.save() run_process_media(entry, feed_url) return activity
def api_add_to_feed(request, entry): """ Add media to Feed """ feed_url = request.urlgen( 'mediagoblin.user_pages.atom_feed', qualified=True, user=request.user.username ) add_comment_subscription(request.user, entry) # Create activity activity = create_activity( verb="post", obj=entry, actor=entry.uploader, generator=create_generator(request) ) entry.save() run_process_media(entry, feed_url) return activity
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)
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)