def settings_post(v): updated = False if request.form.get("new_password"): if request.form.get("new_password") != request.form.get( "cnf_password"): return render_template("settings.html", v=v, error="Passwords do not match.") if not v.verifyPass(request.form.get("old_password")): return render_template("settings.html", v=v, error="Incorrect password") v.passhash = v.hash_password(request.form.get("new_password")) updated = True if request.form.get("over18") != v.over_18: updated = True v.over_18 = bool(request.form.get("over18", None)) if updated: db.add(v) db.commit() return render_template("settings.html", v=v, msg="Your settings have been saved.") else: return render_template("settings.html", v=v, error="You didn't change anything.")
def badge_grant_post(v): user = get_user(request.form.get("username"), graceful=True) if not user: return redirect("/badge_grant?error=no_user") badge_id = int(request.form.get("badge_id")) if user.has_badge(badge_id): return redirect("/badge_grant?error=already_owned") badge = db.query(BadgeDef).filter_by(id=badge_id).first() if badge.kind != 3: abort(403) new_badge = Badge(badge_id=badge_id, user_id=user.id, created_utc=int(time.time())) desc = request.form.get("description") if desc: new_badge.description = desc url = request.form.get("url") if url: new_badge.url = url db.add(new_badge) db.commit() badge_types = db.query(BadgeDef).filter_by(kind=3).order_by( BadgeDef.rank).all() return redirect(user.permalink)
def verify_username(self, username): #no reassignments allowed if self.username_verified: return render_template("settings.html", v=self, error="Your account has already validated its username.") #For use when verifying username with reddit #Set username. Randomize username of any other existing account with same try: existing = db.query(User).filter_by(username=username).all()[0] #No reassignments allowed if existing.username_verified: return render_template("settings.html", v=self, error="Another account has already validated that username.") # Rename new account to user_id # guaranteed to be unique existing.username=f"user_{existing.id}" db.add(existing) db.commit() except IndexError: pass self.username=username self.username_verified=True db.add(self) db.commit() return render_template("settings.html", v=self, msg="Your account name has been updated and validated.")
def activate(): token = request.args.get("token", "") email = request.args.get("email", "") id = request.args.get("id", "") time = request.args.get("time", "") user = db.query(User).filter(User.id == id).first() if not user: flash("User Does Not Exist") return redirect(url_for("index")) if user.is_activated == True: flash("Account is already activated") return redirect(url_for("login")) check_hash = validate_hash(f"{email}+{id}+{time}", token) if not check_hash: flash("Invalid Token") return redirect(url_for("index")) if user.is_activated == False: user.is_activated = True db.add(user) db.commit() flash("Account has been activated") return redirect(url_for("login"))
def edit_post(pid, v): p = get_post(pid) if not p.author_id == v.id: abort(403) if p.is_banned: abort(403) if p.board.has_ban(v): abort(403) body = request.form.get("body", "") with CustomRenderer() as renderer: body_md = renderer.render(mistletoe.Document(body)) body_html = sanitize(body_md, linkgen=True) p.body = body p.body_html = body_html p.edited_utc = int(time.time()) db.add(p) db.commit() return redirect(p.permalink)
def del_profile(self): aws.delete_file( name=f"users/{self.username}/profile-{self.profile_nonce}.png") self.has_profile = False db.add(self) db.commit()
def api_vote_post(post_id, x, v): if x not in ["-1", "0", "1"]: abort(400) x = int(x) post = get_post(post_id) if post.is_banned or post.is_deleted or post.is_archived: abort(403) #check for existing vote existing = db.query(Vote).filter_by(user_id=v.id, submission_id=post.id).first() if existing: existing.change_to(x) #print(f"Re-vote Event: @{v.username} vote {x} on post {post_id}") return "", 204 vote = Vote(user_id=v.id, vote_type=x, submission_id=base36decode(post_id)) db.add(vote) db.commit() #print(f"Vote Event: @{v.username} vote {x} on post {post_id}") return "", 204
def mod_toggle_post_pin(bid, pid, x, board, v): post = get_post(pid) if post.board_id != board.id: abort(422) try: x = bool(int(x)) except: abort(422) if x and not board.can_pin_another: return jsonify({ "error": f"+{board.name} already has the maximum number of pinned posts." }), 409 post.is_pinned = x cache.delete_memoized(Board.idlist, post.board) db.add(post) db.commit() return "", 204
def user_kick_pid(pid, v): #allows a user to yank their content back to +general if it was there previously post = get_post(pid) current_board = post.board if not post.author_id == v.id: abort(403) if post.board_id == post.original_board_id: abort(403) if post.board_id == 1: abort(400) #block further yanks to the same board new_rel = PostRelationship(post_id=post.id, board_id=post.board.id) db.add(new_rel) post.board_id = 1 post.guild_name = "general" post.is_pinned = False db.add(post) db.commit() #clear board's listing caches cache.delete_memoized(Board.idlist, current_board) return "", 204
def mod_approve_bid_user(bid, board, v): user=get_user(request.form.get("username"), graceful=True) if not user: return jsonify({"error":"That user doesn't exist."}), 404 if board.has_ban(user): return jsonify({"error":f"@{user.username} is exiled from +{board.name} and can't currently be approved."}), 409 if board.has_contributor(user): return jsonify({"error":f"@{user.username} is already an approved user."}) #check for an existing deactivated approval existing_contrib=db.query(ContributorRelationship).filter_by(user_id=user.id, board_id=board.id, is_active=False).first() if existing_contrib: existing_contrib.is_active=True existing_contrib.created_utc=int(time.time()) existing_contrib.approving_mod_id=v.id db.add(existing_contrib) else: new_contrib=ContributorRelationship(user_id=user.id, board_id=board.id, is_active=True, approving_mod_id=v.id) db.add(new_contrib) if user.id != v.id: text=f"You have added as an approved contributor to +{board.name}." send_notification(user, text) db.commit() return "", 204
def ban(self, admin, include_alts=True, days=0): if days > 0: ban_time = int(time.time()) + (days * 86400) self.unban_utc = ban_time else: #Takes care of all functions needed for account termination self.unban_utc = 0 self.del_banner() self.del_profile() self.is_banned = admin.id db.add(self) db.commit() if include_alts: for alt in self.alts: # suspend alts if days > 0: alt.ban(admin=admin, include_alts=False, days=days) # ban alts alt.ban(admin=admin, include_alts=False)
def edit_comment(v): comment_id = request.form.get("id") body = request.form.get("comment", "") with UserRenderer() as renderer: body_md=renderer.render(mistletoe.Document(body)) body_html = sanitize(body_md, linkgen=True) c = db.query(Comment).filter_by(id=base36decode(comment_id)).first() if not c: abort(404) if not c.author_id == v.id: abort(403) if c.is_banned or c.is_deleted: abort(403) c.body=body c.body_html=body_html c.edited_timestamp = time.time() db.add(c) db.commit()
def delete_post_pid(pid, v): post = get_post(pid) if not post.author_id == v.id: abort(403) post.is_deleted = True db.add(post) #clear cache cache.delete_memoized(User.userpagelisting, v, sort="new") cache.delete_memoized(Board.idlist, post.board) if post.age >= 3600 * 6: cache.delete_memoized(Board.idlist, post.board, sort="new") cache.delete_memoized(frontlist, sort="new") #delete i.ruqqus.com if post.domain == "i.ruqqus.com": segments = post.url.split("/") pid = segments[4] rand = segments[5] if pid == post.base36id: key = f"post/{pid}/{rand}" delete_file(key) post.is_image = False db.add(post) db.commit() return "", 204
def edit_comment(cid, v): c = db.query(Comment).filter_by(id=base36decode(cid)).first() if not c: abort(404) if not c.author_id == v.id: abort(403) if c.is_banned or c.is_deleted: abort(403) body = request.form.get("body", "") with UserRenderer() as renderer: body_md = renderer.render(mistletoe.Document(body)) body_html = sanitize(body_md, linkgen=True) c.body = body c.body_html = body_html c.edited_timestamp = int(time.time()) db.add(c) db.commit() path = request.form.get("current_page", "/") return redirect(f"{path}#comment-{c.base36id}")
def subscribe_board(boardname, v): board = get_guild(boardname) #check for existing subscription, canceled or otherwise sub = db.query(Subscription).filter_by(user_id=v.id, board_id=board.id).first() if sub: if sub.is_active: abort(409) else: #reactivate canceled sub sub.is_active = True db.add(sub) db.commit() return "", 204 new_sub = Subscription(user_id=v.id, board_id=board.id) db.add(new_sub) db.commit() #clear your cached guild listings cache.delete_memoized(User.idlist, v, kind="board") return "", 204
def del_profile(self): aws.delete_file( name=f"board/{self.name.lower()}/profile-{self.profile_nonce}.png") self.has_profile = False db.add(self) db.commit()
def del_banner(self): aws.delete_file( name=f"board/{self.name.lower()}/banner-{self.banner_nonce}.png") self.has_banner = False db.add(self) db.commit()
def ban_user(user_id, v): user=db.query(User).filter_by(id=user_id).first() # check for number of days for suspension days = int(request.form.get("days")) if request.form.get('days') else 0 reason = request.form.get("reason", "") if not user: abort(400) if days > 0: if reason: text = f"Your Ruqqus account has been suspended for {days} days. \n reason:\n\n{reason}" else: text = f"Your Ruqqus account has been suspended for {days} days due to a Terms of Service violation." user.ban(admin=v, days=days) else: if reason: text = f"Your Ruqqus account has been permanently suspended for the following reason:\n\n{reason}" else: text = "Your Ruqqus account has been permanently suspended due to a Terms of Service violation." user.ban(admin=v) send_notification(user, text) db.commit() return (redirect(user.url), user)
def notifications_page(self, page=1, include_read=False): page = int(page) notifications = self.notifications.filter_by(is_banned=False, is_deleted=False) if not include_read: notifications = notifications.filter_by(read=False) notifications = notifications.order_by( text("notifications.created_utc desc")).offset( 25 * (page - 1)).limit(25) comments = [n.comment for n in notifications] for n in notifications: if not n.read: n.read = True db.add(n) db.commit() return render_template("notifications.html", v=self, notifications=comments)
def edit_post(pid, v): p = db.query(Submission).filter_by(id=base36decode(pid)).first() if not p: abort(404) if not p.author_id == v.id: abort(403) if p.is_banned: abort(403) body = request.form.get("body", "") with UserRenderer() as renderer: body_md = renderer.render(mistletoe.Document(body)) body_html = sanitize(body_md, linkgen=True) p.body = body p.body_html = body_html p.edited_utc = int(time.time()) db.add(p) db.commit() return redirect(p.permalink)
def mod_invite_username(bid, board, v): username = request.form.get("username", '').lstrip('@') user = get_user(username) if not board.can_invite_mod(user): return jsonify({ "error": f"@{user.username} is already a mod or has already been invited." }), 409 if not user.can_join_gms: return jsonify( {"error": f"@{user.username} already leads enough guilds."}), 409 if not board.has_rescinded_invite(user): #notification text = f"You have been invited to join +{board.name} as a guildmaster. You can [click here]({board.permalink}/mod/mods) and accept this invitation. Or, if you weren't expecting this, you can ignore it." send_notification(user, text) new_mod = ModRelationship(user_id=user.id, board_id=board.id, accepted=False) db.add(new_mod) db.commit() return "", 204
def api_vote_comment(comment_id, x, v): if x not in ["-1", "0", "1"]: abort(400) x = int(x) comment = get_comment(comment_id) if comment.is_banned or comment.is_deleted or comment.post.is_archived: abort(403) #check for existing vote existing = db.query(CommentVote).filter_by(user_id=v.id, comment_id=comment.id).first() if existing: existing.change_to(x) #print(f"Re-vote Event: @{v.username} vote {x} on comment {comment_id}") return "", 204 vote = CommentVote(user_id=v.id, vote_type=x, comment_id=base36decode(comment_id)) db.add(vote) db.commit() #print(f"Vote Event: @{v.username} vote {x} on comment {comment_id}") return "", 204
def mod_edit_rule(bid, board, v): r = base36decode(request.form.get("rid")) r = db.query(Rules).filter_by(id=r) if not r: abort(500) if board.is_banned: abort(403) if board.has_ban(v): abort(403) body = request.form.get("body", "") with CustomRenderer() as renderer: body_md = renderer.render(mistletoe.Document(body)) body_html = sanitize(body_md, linkgen=True) r.rule_body = body r.rule_html = body_html r.edited_utc = int(time.time()) db.add(r) db.commit() return "", 204
def api_vote_comment(post_id, x, v): if x not in ["-1", "0", "1"]: abort(400) x = int(x) comment_id = base36decode(comment_id) comment = db.query(Comment).filter_by(id=comment_id).first() if not comment_id: abort(404) if comment_id.is_banned: abort(403) #check for existing vote existing = db.query(CommentVote).filter_by(user_id=v.id, comment_id=comment_id).first() if existing: existing.change_to(x) return "", 204 vote = Vote(user_id=v.id, vote_type=x, submission_id=post_id) db.add(vote) db.commit() return "", 204
def mod_board_color(bid, board, v): color = str(request.form.get("color", "")) if len(color) != 6: color = "603abb" r = color[0:1] g = color[2:3] b = color[4:5] try: if any([int(x, 16) > 255 for x in [r, g, b]]): color = "603abb" except ValueError: color = "603abb" board.color = color board.color_nonce += 1 db.add(board) db.commit() try: cache.delete_memoized(board_css, board.name) cache.delete_memoized(board_dark_css, board.name) except: pass return redirect(f"/+{board.name}/mod/appearance?msg=Success")
def mod_take_pid(pid, v): bid = request.form.get("board_id",None) if not bid: abort(400) board=get_board(bid) if board.is_banned: abort(403) if not board.has_mod(v): abort(403) post = get_post(pid) if not post.board_id==1: abort(422) if board.has_ban(post.author): abort(403) if not board.can_take(post): abort(403) post.board_id=board.id post.guild_name=board.name db.add(post) db.commit() #clear board's listing caches cache.delete_memoized(Board.idlist, board) return redirect(post.permalink)
def settings_new_feedkey(v): v.feed_nonce+=1 db.add(v) db.commit() return render_template("settings_profile.html", v=v, msg="Your new custom RSS Feed Token has been generated.")
def del_banner(self): aws.delete_file( name=f"users/{self.username}/banner-{self.banner_nonce}.png") self.has_banner = False db.add(self) db.commit()
def api_agree_tos(v): v.tos_agreed_utc=int(time.time()) db.add(v) db.commit() return redirect("/help/terms")
def mod_settings_toggle_banner(bid, board, v): #toggle show/hide banner board.hide_banner_data = bool(request.form.get("hidebanner", False) == 'true') db.add(board) db.commit() return "", 204