Exemplo n.º 1
0
def subscribed(request, user_id):
    target_profile = get_user(user_id).profile

    is_subscribed = bool(request.user.is_authenticated
                         and request.user.profile.subscriptions.filter(
                             profile_id=target_profile.profile_id))

    if request.method == "GET":
        return api.succeed({"subscribed": is_subscribed})
    elif request.method == "PATCH":
        if not request.user.is_authenticated:
            return api.error("Not logged in.", status=401)

        data = json.loads(request.body)
        subscribed = data["subscribed"]

        if type(subscribed) is not bool:
            return api.error("Data for key 'subscribed' must be a boolean.")

        if not is_subscribed and data["subscribed"]:
            request.user.profile.subscriptions.add(target_profile)
        elif is_subscribed and not data["subscribed"]:
            request.user.profile.subscriptions.remove(target_profile)

        return api.succeed({"subscribed": data["subscribed"]})
Exemplo n.º 2
0
def subscribed(request, id):
    try:
        target_profile = Profile.objects.get(profile_id=id)
    except Profile.DoesNotExist:
        # If id doesn't match, we try username. If username doesn't, we throw an error caught by standardAPIErrors
        target_profile = User.objects.select_related('profile').get(username=id).profile

    is_subscribed = bool(request.user.is_authenticated and
        request.user.profile.subscriptions.filter(profile_id=target_profile.profile_id))

    if request.method == "GET":
        return api.succeed({ "subscribed": is_subscribed })
    elif request.method == "PATCH":
        if not request.user.is_authenticated:
            return api.error("Not logged in.", status=401)

        data = json.loads(request.body)
        subscribed = data["subscribed"]

        if type(subscribed) is not bool:
            return api.error("Data for key 'subscribed' must be a boolean.")

        if not is_subscribed and data["subscribed"]:
            request.user.profile.subscriptions.add(target_profile)
        elif is_subscribed and not data["subscribed"]:
            request.user.profile.subscriptions.remove(target_profile)

        return api.succeed({ "subscribed": data["subscribed"] })
Exemplo n.º 3
0
def user(request, user_id):
    requested_user = get_user(user_id)

    if request.method == "GET":
        user_data = {
            "username":
            requested_user.username,
            "id":
            requested_user.profile.profile_id,
            "displayName":
            requested_user.profile.display_name,
            "bio":
            requested_user.profile.bio,
            "programs":
            list(
                Program.objects.filter(user=requested_user).values_list(
                    "program_id", flat=True)),
            "joined":
            requested_user.date_joined.replace(microsecond=0).isoformat() + "Z"
        }
        return api.succeed(user_data)
    elif request.method == "PATCH":
        data = json.loads(request.body)

        if request.user != requested_user:
            return api.error("Not authorized.", status=401)

        if "displayName" in data:
            if len(data["displayName"]) > 45:
                return api.error(
                    "displayName length exceeds maximum characters.")

            requested_user.profile.display_name = data["displayName"]

        if "bio" in data:
            if len(data["bio"]) > 500:
                return api.error("bio length exceeds maximum characters.")

            requested_user.profile.bio = data["bio"]

        if "username" in data:
            if not check_username(data["username"], requested_user.username):
                return api.error("Invalid username.")

            requested_user.username = data["username"]

        requested_user.save()

        return api.succeed()
    elif request.method == "DELETE":
        if request.user != requested_user:
            return api.error("Not authorized.", status=401)

        requested_user.delete()

        return api.succeed()
Exemplo n.º 4
0
def user(request, id):
    try:
        requested_user = Profile.objects.select_related('user').get(profile_id=id).user
    except Profile.DoesNotExist:
        #If id doesn't match, we try username. If username doesn't, we throw an error caught by standardAPIErrors
        requested_user = User.objects.select_related('profile').get(username=id)

    if request.method == "GET":
        user_data = {
            "username": requested_user.username,
            "id": requested_user.profile.profile_id,
            "displayName": requested_user.profile.display_name,
            "bio": requested_user.profile.bio,
            "programs": list(Program.objects.filter(user=requested_user).values_list("program_id", flat=True)),
            "joined": requested_user.date_joined.replace(microsecond=0).isoformat() + "Z"
        }
        return api.succeed(user_data)
    elif request.method == "PATCH":
        data = json.loads(request.body)

        if request.user != requested_user:
            return api.error("Not authorized.", status=401)

        if "displayName" in data:
            if len(data["displayName"]) > 45:
                return api.error("displayName length exceeds maximum characters.")
            else:
                requested_user.profile.display_name = data["displayName"]

        if "bio" in data:
            if len(data["bio"]) > 500:
                return api.error("bio length exceeds maximum characters.")
            else:
                requested_user.profile.bio = data["bio"]

        if "username" in data:
            if  (not check_username(data["username"], requested_user.username)):
                return api.error("Invalid username.")
            else:
                requested_user.username = data["username"]

        requested_user.save()

        return api.succeed()
    elif (request.method == "DELETE"):
        if request.user != requested_user:
            return api.error("Not authorized.", status=401)

        requested_user.delete()

        return api.succeed()
Exemplo n.º 5
0
def program(request, program_id):
    requested_program = Program.objects.get(program_id=program_id)
    if (request.method == "GET"):
        return api.succeed(requested_program.to_dict())
    elif (request.method == "PATCH"):
        data = json.loads(request.body)
        return_data = {}

        if request.user != requested_program.user:
            return api.error("Not authorized.", status=401)

        valid_props = ["html", "js", "css", "title"]

        if "title" in data and len(data["title"]) > 45:
            return api.error("Title length exceeds maximum characters.", status=400)

        if "publishedMessage" in data and len(data["publishedMessage"]) > 250:
                return api.error("Publish message can't exceed 250 characters")

        for prop in valid_props:
            if prop in data:
                setattr(requested_program, prop, data[prop])

        if "publishedMessage" in data:
            requested_program.published_message = data["publishedMessage"];
            requested_program.last_published = datetime.datetime.now()

            return_data["lastPublished"] = requested_program.last_published.replace(microsecond=0).isoformat() + "Z"

            # Create notification for subscribers
            subscribers = requested_program.user.profile.profile_set.all()
            for subscriber in subscribers:
                Notif.objects.create(
                    target_user = subscriber.user,
                    link = "/program/" + requested_program.program_id,
                    description = "<strong>{0}</strong> just published a new program, <strong>{1}</strong>".format(
                        escape(request.user.profile.display_name), escape(requested_program.title)),
                    source_program = requested_program
                )

        requested_program.save()

        return api.succeed(return_data)
    elif (request.method == "DELETE"):
        if request.user != requested_program.user:
            return api.error("Not authorized.", status=401)

        requested_program.delete()

        return api.succeed()
Exemplo n.º 6
0
def program_list(request, user_id, sort):
    if request.method == "POST":
        # It seems intutive that POSTing here would make a new program. However, that is not the case
        return api.error("Make a new program by posting to /api/program/new")

    requested_user = get_user(user_id, and_profile=False)

    offset = get_as_int(request.GET, "offset", 0)
    limit = get_as_int(request.GET, "limit", 20)

    if (limit > 20 or limit <= 0):
        limit = 20

    try:
        programs = get_programs(sort,
                                Q(user=requested_user),
                                offset=offset,
                                limit=limit,
                                published_only=False)
    except ValueError as err:
        return api.error(str(err))

    program_dicts = [p.to_dict(include_code=False) for p in programs]

    return api.succeed({"sort": sort, "programs": program_dicts})
Exemplo n.º 7
0
def forks(request, program_id):
    if (request.method == "POST"):
        if (not request.user.is_authenticated):
            return api.error("Not logged in.", status=401)

        parent_program = Program.objects.get(program_id=program_id)

        data = json.loads(request.body)
        if (len(data["title"]) > 45):
            return api.error("Title length exceeds maximum characters.")

        program = Program.objects.create(
            user = request.user,
            parent = parent_program,
            title = data["title"],
            html = data["html"],
            js = data["js"],
            css = data["css"],
        )

        if (parent_program.user != program.user):
            Notif.objects.create(
                target_user = parent_program.user,
                link = "/program/" + program.program_id,
                description = "<strong>{0}</strong> created a fork of your program, <strong>{1}</strong>".format(
                    escape(request.user.profile.display_name), escape(parent_program.title)),
            )

        response = api.succeed({ "id": program.program_id }, status=201)
        response["Location"] = "/program/" + program.program_id
        return response
Exemplo n.º 8
0
def new_user(request):
    data = json.loads(request.body)

    username = data['username']
    if "email" in data:
        email = data['email']
    else:
        email = ''
    password = data['password']
    display_name = data['displayName']

    if (not check_username(username, "")):
        return api.error("Invalid username")
    if (password == ""):
        return api.error("Password cannot be blank")
    if (display_name == "" or len(display_name) > 45):
        return api.error("Invalid display name")
    if (not re.match(r"^([\w.+-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+)?$", email)):
        return api.error("Invalid email")

    user = User.objects.create_user(
        username,
        email,
        password,
    )
    user.profile.display_name = display_name
    user.save()

    auth.login(request, user)

    return api.succeed({
        "id": user.profile.profile_id,
        "username": user.username
    }, status=200)
Exemplo n.º 9
0
def login(request):
    data = json.loads(request.body)
    user = auth.authenticate(username=data["username"],
                             password=data["password"])

    if user is not None:
        auth.login(request, user)
        return api.succeed({"username": user.username})
    else:
        # At this point, error should be handled by Javascript
        return api.error("Username or password incorrect.", status=401)
Exemplo n.º 10
0
def comment(request, *args):
    if (len(args) == 1):
        comment_id = args[0]

        requested_comment = Comment.objects.get(comment_id=comment_id)
    elif (len(args) == 2):
        program_id = args[0]
        comment_id = args[1]

        requested_comment = Comment.objects.get(program_id=program_id,
                                                comment_id=comment_id)

    if (request.method == "GET"):
        return api.succeed(requested_comment.to_dict())

    # Comment editing!
    elif (request.method == "PATCH"):
        data = json.loads(request.body)

        if request.user != requested_comment.user:
            return api.error("Not authorized.", status=401)

        requested_comment.content = data["content"]
        requested_comment.edited = datetime.datetime.now()

        requested_comment.save()

        return api.succeed()

    elif (request.method == "DELETE"):
        if request.user != requested_comment.user:
            return api.error("Not authorized.", status=401)

        parent = requested_comment.parent
        if (parent is not None):
            parent.reply_count -= 1
            parent.save()

        requested_comment.delete()

        return api.succeed()
Exemplo n.º 11
0
def program_vote(request, program_id):
    vote_type = parse_qs(request.META["QUERY_STRING"])["type"][0]

    if vote_type not in vote_types:
        return api.error("Invalid vote type.")
    try:
        voted_program = Program.objects.get(program_id=program_id)
        orig_votes = getattr(voted_program, vote_type + "_votes")

        # Look for a vote of the same type cast by the user before
        orig_vote = Vote.objects.get(voted_object_id=program_id,
                                     vote_type=vote_type,
                                     user_id=request.user.id)

        # If the vote already exists and:
        if request.method == "DELETE":
            setattr(voted_program, vote_type + "_votes", orig_votes - 1)
            voted_program.save()

            orig_vote.delete()
            return api.succeed()
        # If the specific vote already exists, and you're posting it again
        elif request.method == "POST":
            # 403 Forbidden, is used here beacuse there is no authentication that would allow the request
            return api.error("Already voted.", status=403)
    except Vote.DoesNotExist:
        # If the vote doesn't already exist
        if request.method == "POST":
            Vote.objects.create(user_id=request.user.id,
                                vote_type=vote_type,
                                voted_object_id=program_id)

            setattr(voted_program, vote_type + "_votes", orig_votes + 1)
            voted_program.save()

            # Return with 201, created
            return api.succeed({}, status=201)
        elif request.method == "DELETE":
            return api.error("Vote not found.", 404)
Exemplo n.º 12
0
def program_list(request, sort):
    offset = get_as_int(request.GET, "offset", 0)
    limit = get_as_int(request.GET, "limit", 20)

    if (limit > 20 or limit <= 0):
        limit = 20

    try:
        programs = get_programs(sort, offset=offset, limit=limit)
    except ValueError as err:
        return api.error(str(err))

    program_dicts = [p.to_dict(include_code=False) for p in programs]

    return api.succeed({"sort": sort, "programs": program_dicts})
Exemplo n.º 13
0
def new_program(request):
    data = json.loads(request.body)
    if (len(data["title"]) > 45):
        return api.error("Title length exceeds maximum characters.")

    program = Program.objects.create(
        user = request.user,
        title = data["title"],
        html = data["html"],
        js = data["js"],
        css = data["css"],
    )

    response = api.succeed({ "id": program.program_id }, status=201)
    response["Location"] = "/program/" + program.program_id
    return response
Exemplo n.º 14
0
def comment_comments(request, *args):
    if (len(args) == 1):
        comment_id = args[0]

        comments = Comment.objects.select_related("user__profile").filter(
            parent__comment_id=comment_id).order_by("created")
    elif (len(args) == 2):
        program_id = args[0]
        comment_id = args[1]

        comments = Comment.objects.select_related("user__profile").filter(
            program_id=program_id,
            parent__comment_id=comment_id).order_by("created")

    comments = list(comments)
    return api.succeed({"comments": [c.to_dict() for c in comments]})
Exemplo n.º 15
0
def notif(request, notif_id):
    notif = Notif.objects.get(notif_id=notif_id)

    if (request.method == "PATCH"):
        data = json.loads(request.body)
        read = data["isRead"]  #true or false

        if request.user != notif.target_user:
            return api.error("Not authorized", status=401)

        if (read != True and read != False):
            return api.error("Invalid type for key \"isRead\"")

        notif.is_read = read
        notif.save()

        return api.succeed()
Exemplo n.º 16
0
def forgot_password(request):
    username = json.loads(request.body)["username"]
    try:
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        # Other DoesNotExist errors 404, but we need this one to 400
        return api.error("No user found with matching email and username.",
                         status=400)

    email = user.email

    timezone = int(json.loads(request.body)["timezone"])

    # Not perfect, but checks for a reasonable time value.
    if (timezone > 840 or timezone < -840):
        timezone = 0

    # Creates a time string in the form HH:MM AM|PM
    req_time = time.strftime("%I:%M %p",
                             time.localtime(time.time() - (timezone) * 60))

    token = token_generator.make_token(user)

    link = "{protocol}://{domain}/user/reset-password?{query_params}"
    link = link.format(protocol="https" if request.is_secure() else "http",
                       domain=Site.objects.get_current().domain,
                       query_params=urlencode({
                           "token":
                           token,
                           "user_id":
                           user.profile.profile_id
                       }))
    message = render_to_string("account/forgotPasswordEmail.html", {
        "link": link,
        "time": req_time,
    })

    if (email != ""):
        send_mail(subject="OurJSEditor Reset Password Request",
                  from_email="*****@*****.**",
                  recipient_list=[email],
                  message="Here's your password reset link: " + link,
                  html_message=message)

    return api.succeed({"user": {"id": user.profile.profile_id}})
Exemplo n.º 17
0
def program_list(request, sort):
    if (not sort):
        sort = "new" # Default sort. sort is actually passed in as None, so we can't use an argument default

    if (sort not in key_func_mapping):
        return api.error("Invalid sort type: \"{}\"".format(sort))

    key_func = key_func_mapping[sort]
    if (type(key_func) is unicode):
        key_func = lambda program: getattr(program, key_func_mapping[sort])

    programs = sorted(Program.objects.all(), reverse=True, key=key_func)[:20]

    program_dicts = []
    for program in programs:
        program = program.to_dict()
        del(program["css"])
        del(program["html"])
        del(program["js"])
        program_dicts.append(program)

    return api.succeed({"sort": sort, "programs": program_dicts})
Exemplo n.º 18
0
def collaborators(request, program_id):
    requested_program = Program.objects.get(program_id=program_id)

    # Any collaborator can add or remove other collaborators
    if not requested_program.can_user_edit(request.user):
        return api.error("Not authorized.", status=401)

    # Payload:
    # {user:{id:""}}
    # {user:{username:""}}
    # TODO: Allow getting a user by username *or* id?
    requested_user_identifier = json.loads(request.body)["user"]
    try:
        user = Profile.objects.get(profile_id=requested_user_identifier["id"]).user
    except KeyError:
        user = User.objects.get(username=requested_user_identifier["username"])

    if request.method == "POST":
        # Check if collaborators is already a collab
        # or is the author
        if requested_program.can_user_edit(user):
            return api.error("That user can already edit this program.")

        # Send a notification to the program owner, if it wasn't the author who did the adding
        if request.user != requested_program.user:
            Notif.objects.create(
                target_user=requested_program.user,
                link="/program/" + requested_program.program_id,
                description="<strong>{0}</strong> added <strong>{1}</strong> to the list of collaborators on your program, <strong>{2}</strong>.".format(
                    escape(request.user.profile.display_name), escape(user.profile.display_name), escape(requested_program.title)),
            )

        # Send a notification to the user who has been added
        # if (request.user != user):
        Notif.objects.create(
            target_user=user,
            link="/program/" + requested_program.program_id,
            description="<strong>{0}</strong> added you as a collaborator on the program, <strong>{1}</strong>.".format(
                escape(request.user.profile.display_name), escape(requested_program.title)),
        )

        requested_program.collaborators.add(user)
        return api.succeed({"username": user.username, "id": user.profile.profile_id})

    elif request.method == "DELETE":
        if requested_program.collaborators.filter(id=user.id).exists():
            # Send a notification to the program owner, if it wasn't the author who did the removing
            if request.user != requested_program.user:
                Notif.objects.create(
                    target_user=requested_program.user, # Program author
                    link="/program/" + requested_program.program_id,
                    description="<strong>{0}</strong> removed <strong>{1}</strong> from the list of collaborators on your program, <strong>{2}</strong>.".format(
                        escape(request.user.profile.display_name), escape(user.profile.display_name), escape(requested_program.title)),
                )

            # Send a notification to the user who was removed, unless they removed themselves
            if request.user != user:
                Notif.objects.create(
                    target_user=user,
                    link="/program/" + requested_program.program_id,
                    description="<strong>{0}</strong> removed you from the list of collaborators on the program, <strong>{1}</strong>.".format(
                        escape(request.user.profile.display_name), escape(requested_program.title)),
                )

            # Other, 3rd-party, collaborators don't get notifications

            requested_program.collaborators.remove(user)
            return api.succeed()
        return api.error("User isn't a collaborator on this program.")
Exemplo n.º 19
0
def username_valid(request, username):
    return api.succeed({ "usernameValid": check_username(username, "") })
Exemplo n.º 20
0
def program_comments(request, program_id):
    comments = list(
        Comment.objects.select_related("user__profile").filter(
            program_id=program_id, depth=0).order_by("-created"))
    return api.succeed({"comments": [c.to_dict() for c in comments]})
Exemplo n.º 21
0
def new_comment(request, program_id):
    data = json.loads(request.body)

    # A JSON of `null` gets parsed into a Python of `None`
    # If parent isn't passed, KeyError, caught by standardAPIErrors
    parent_comment = data["parent"]
    if (parent_comment is None):
        depth = 0
    else:
        try:
            parent_comment = Comment.objects.get(comment_id=parent_comment,
                                                 program_id=program_id)
        except Comment.DoesNotExist:
            return api.error("Invalid comment parent")
        depth = parent_comment.depth + 1

    if (depth > 1):
        return api.error("Comments have gone too deep!")

    if (parent_comment is not None):
        parent_comment.reply_count += 1
        parent_comment.save()

    program = Program.objects.get(program_id=program_id)

    comment = Comment.objects.create(
        user=request.user,
        program=program,
        parent=parent_comment,
        depth=depth,
        content=data["content"],
        original_content=data["content"],
    )

    link = "/program/{0}#comment-{1}".format(comment.program.program_id,
                                             comment.comment_id)

    if (depth == 0):
        if (program.user != comment.user):
            Notif.objects.create(
                target_user=program.user,
                link=link,
                description=
                "<strong>{0}</strong> left a comment on your program, <strong>{1}</strong>"
                .format(escape(request.user.profile.display_name),
                        escape(program.title)),
                source_comment=comment)
    else:
        #Create a list of everyone in the comment thread and spam them all
        to_notify = set(
            Comment.objects.filter(parent=parent_comment).exclude(
                user=parent_comment.user
            )  #Not the thread starter, they get a different message
            .exclude(user=comment.user)  #Not the user who posted this comment
            .values_list("user", flat=True))

        for user in to_notify:
            Notif.objects.create(
                target_user_id=user,
                link=link,
                description=
                "<strong>{0}</strong> commented on a thread on <strong>{1}</strong>"
                .format(escape(request.user.profile.display_name),
                        escape(program.title)),
                source_comment=comment)

        #Notify the original comment creator seperately
        Notif.objects.create(
            target_user=parent_comment.user,
            link=link,
            description=
            "<strong>{0}</strong> replied to your comment on <strong>{1}</strong>"
            .format(escape(request.user.profile.display_name),
                    escape(program.title)),
            source_comment=comment)

    response = api.succeed({"id": comment.comment_id}, status=201)
    response["Location"] = link
    return response
Exemplo n.º 22
0
def program(request, program_id):
    requested_program = Program.objects.get(program_id=program_id)
    if (request.method == "GET"):
        return api.succeed(requested_program.to_dict())
    elif (request.method == "PATCH"):
        data = json.loads(request.body)
        return_data = {}

        if request.user != requested_program.user:
            return api.error("Not authorized.", status=401)

        if "title" in data and len(data["title"]) > 45:
            return api.error("Title length exceeds maximum characters.", status=400)

        if "publishedMessage" in data and len(data["publishedMessage"]) > 250:
            return api.error("Publish message can't exceed 250 characters")

        if "publishedMessage" in data:
            # Should it be possible to publish without an image or update the image without publishing

            try:
                image = base64toImageFile(data["imageData"], "{}.png".format(requested_program.program_id))
            except TypeError as err:
                if (err.args[0] == "Image isn't a PNG"):
                    return api.error("Image must be a PNG")
                raise
            except:
                return api.error("Invalid Image")

            requested_program.image = image

            requested_program.published_message = data["publishedMessage"]
            requested_program.last_published = datetime.datetime.now()

            return_data["lastPublished"] = requested_program.last_published.replace(microsecond=0).isoformat() + "Z"

            # Create notification for subscribers
            subscribers = requested_program.user.profile.profile_set.all()
            for subscriber in subscribers:
                Notif.objects.create(
                    target_user = subscriber.user,
                    link = "/program/" + requested_program.program_id,
                    description = "<strong>{0}</strong> just published a new program, <strong>{1}</strong>".format(
                        escape(request.user.profile.display_name), escape(requested_program.title)),
                    source_program = requested_program
                )

        valid_props = ["html", "js", "css", "title"]

        for prop in valid_props:
            if prop in data:
                setattr(requested_program, prop, data[prop])

        requested_program.save()

        return api.succeed(return_data)
    elif (request.method == "DELETE"):
        if (request.user != requested_program.user):
            return api.error("Not authorized.", status=401)

        if (requested_program.image.name != "program/nophoto.png"):
            requested_program.image.delete()

        requested_program.delete()

        return api.succeed()
Exemplo n.º 23
0
def notif_list(request):
    notifs = Notif.objects.filter(
        target_user=request.user).order_by("-created")
    notifs = map(lambda n: n.to_dict(), notifs)

    return api.succeed({"notifs": notifs})
Exemplo n.º 24
0
def notif_count(request):
    return api.succeed({
        "notifCount":
        Notif.objects.filter(target_user=request.user, is_read=False).count()
    })
Exemplo n.º 25
0
def notif_list(request):
    notifs = Notif.objects.filter(
        target_user=request.user).order_by("-created")
    notifs = [n.to_dict() for n in notifs]

    return api.succeed({"notifs": notifs})