def undo_trash_res(): res_id = request.form.get("res_id") user_id = session["user_id"] cur = conn.cursor() # Undo single resource if res_id != "*": cur.execute( ("""INSERT INTO resources SELECT * FROM trash WHERE user_id = %s and re_id = %s""" ), (user_id, res_id), ) cur.execute( ("""DELETE FROM trash WHERE user_id = %s and re_id = %s"""), (user_id, res_id), ) # Undo all resources else: cur.execute( ("""INSERT INTO RESOURCES SELECT * FROM trash WHERE user_id = %s""" ), (user_id, ), ) cur.execute(("""DELETE FROM trash WHERE user_id = %s"""), (user_id, )) cur.close() conn.commit() return redirect(url_for("deleted_res"))
def renametag(): tag = request.form.get("tag") replacement = request.form.get("replacement") print(replacement) if tag: if helpers.characters_valid( replacement ) and not helpers.list_contains_duplicates(replacement): cur = conn.cursor() cur.execute( "UPDATE resources SET tags = array_replace(tags,%s,%s)", (tag, replacement), ) cur.close() conn.commit() flash("Tag renamed successfully.", "success") else: flash( "Invalid tags were given. Check for invalid characters or duplicate tags.", "danger", ) return redirect(url_for("options"))
def fildel(): # Check if user is logged in if helpers.logged_in(): tags_to_del = request.form.get("tags") user_id = session["user_id"] tags_array = "{" + tags_to_del + "}" cur = conn.cursor() # Add to trash cur.execute( ("""INSERT INTO trash SELECT * FROM resources WHERE user_id = %s AND tags @> %s""" ), (user_id, tags_array), ) # Then Delete cur.execute( ("""DELETE FROM resources WHERE user_id = %s AND tags @> %s"""), (user_id, tags_array), ) cur.close() conn.commit() flash("Resources deleted successfully.", "danger") return redirect(url_for("options")) else: flash("You are not logged in.", "danger") return redirect(url_for("login"))
def register(): form = forms.RegisterForm(request.form) if request.method == "POST" and form.validate(): username = form.username.data email = form.email.data # Encrypt the password using sha256 password = sha256_crypt.encrypt(str(form.password.data)) timestamp = datetime.datetime.fromtimestamp( time()).strftime("%Y-%m-%d %H:%M:%S") if not conn: flash("Could not connect to database.", "error") else: cur = conn.cursor() cur.execute( ("""INSERT INTO users(email,username,password,date_of_reg) VALUES (%s,%s,%s,%s)""" ), (email, username, password, timestamp), ) cur.close() conn.commit() flash("You are now registered.", "success") return redirect(url_for("login")) return render_template("register.html", form=form)
def delacc(): # Check if user is logged in if helpers.logged_in(): # Extract user_id from the current session user_id = session["user_id"] cur = conn.cursor() try: # First delete from `resources` so as not to violate foreign key constraints cur.execute("DELETE FROM resources WHERE user_id = %s", (user_id, )) # Delete from `trash` as not to violate foreign key constraints cur.execute("DELETE FROM trash WHERE user_id = %s", (user_id, )) # Finally, remove the user from `users` cur.execute("DELETE FROM users WHERE id = %s", (user_id, )) cur.close() conn.commit() except DatabaseError: cur.rollback() session.clear() flash("Account deleted. Sad to see you go :(", "danger") return redirect("/") else: flash("You are not logged in.", "danger") return redirect(url_for("login"))
def add_resource(): form = forms.ResourceForm(request.form) if request.method == "POST" and form.validate(): # Escape user input using Markup title = Markup.escape(form.title.data) link = urllib.parse.unquote(Markup.escape(form.link.data)) note = Markup.escape(form.note.data) timestamp = datetime.datetime.fromtimestamp( time()).strftime("%Y-%m-%d %H:%M:%S") tags = Markup.escape(form.tags.data) # If not empty format for proper insertion into postgresql if tags: tags = "{" + str(tags).lower() + "}" else: tags = None user_id = session["user_id"] cur = conn.cursor() cur.execute( ("""INSERT INTO resources(user_id,title,link,note,tags,date_of_posting) VALUES (%s,%s,%s,%s,%s,%s)""" ), (user_id, title, link, note, tags, timestamp), ) cur.close() conn.commit() flash("Resource created successfully.", "success") return redirect(url_for("resources")) else: user_id = session["user_id"] cur = conn.cursor() cur.execute( ("""SELECT DISTINCT unnest(tags) FROM resources WHERE user_id = %s""" ), (user_id, ), ) tags_raw = cur.fetchall() # 'Unpack' tags_raw into one array all_tags = [] for tag_arr in tags_raw: all_tags.append(tag_arr[0]) cur.close() return render_template("add_resource.html", form=form, tags=all_tags)
def remtag(): tags = request.form.get("tags") if tags: tags_to_rem = tags.split(",") for tag in tags_to_rem: cur = conn.cursor() cur.execute("UPDATE resources SET tags = array_remove(tags, %s )", (tag, )) cur.close() conn.commit() flash("Tag(s) removed successfully.", "danger") return redirect(url_for("options"))
def chpass(): form = forms.ChangePassForm(request.form) if request.method == "POST" and form.validate(): new_password = sha256_crypt.encrypt(str(form.password.data)) user_id = session["user_id"] cur = conn.cursor() cur.execute( ("""UPDATE users SET password = %s WHERE id = %s"""), (new_password, user_id), ) cur.close() conn.commit() flash("Password changed successfully.", "success") return redirect(url_for("options")) return render_template("chng_password.html", form=form)
def delete_res(user_id, re_id): if helpers.logged_in(user_id): cur = conn.cursor() # First add resource to trash bin cur.execute( ("""INSERT INTO trash SELECT * FROM resources WHERE user_id = %s and re_id = %s""" ), (user_id, re_id), ) # And then delete it cur.execute( ("""DELETE FROM resources WHERE user_id = %s and re_id = %s"""), (user_id, re_id), ) cur.close() conn.commit() return redirect(url_for("resources"))
def reset_w_token(token): # Get email from token try: pwd_reset_serializer = URLSafeTimedSerializer(app.config["SECRET_KEY"]) email = pwd_reset_serializer.loads(token, salt="password-reset-salt", max_age=3600) except: flash("The password reset link is invalid or has expired.", "danger") return redirect(url_for("login")) form = forms.ChangePassForm(request.form) if request.method == "POST" and form.validate(): cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) cur.execute(("""SELECT * FROM users WHERE email=%s"""), (email, )) user = cur.fetchall()[0] if not user: flash("Invalid email address!", "danger") return redirect(url_for("login")) new_password = sha256_crypt.encrypt(str(form.password.data)) cur.execute( ("""UPDATE users SET password = %s WHERE email = %s"""), (new_password, email), ) cur.close() conn.commit() flash("Password changed successfully.", "success") return redirect(url_for("login")) return render_template("chng_password.html", form=form)
def delall(): # Check if user is logged in if helpers.logged_in(): user_id = session.get("user_id") cur = conn.cursor() # Add to trash cur.execute( """INSERT INTO trash SELECT * FROM resources WHERE user_id = %s""", ([user_id]), ) # Then Delete cur.execute("""DELETE FROM resources WHERE user_id = %s""", ([user_id])) cur.close() conn.commit() flash("All resources were deleted successfully.", "danger") return redirect(url_for("resources")) else: flash("You are not logged in.", "danger") return redirect(url_for("login"))
def import_resources(): cur = conn.cursor() # res = cc.BaseResource def add_res_to_db(res): # Transform tags to all lowercase tags = [tag.lower() for tag in res.tags] link = urllib.parse.unquote(res.link) if res.title: title = res.title[0:99] else: title = res.link[0:50] + "..." timestamp = datetime.datetime.fromtimestamp( time()).strftime("%Y-%m-%d %H:%M:%S") user_id = session["user_id"] try: cur.execute( ("""INSERT INTO resources(user_id,title,link,tags,date_of_posting) VALUES (%s,%s,%s,%s,%s)""" ), (user_id, title, link, tags, timestamp), ) except DatabaseError: conn.rollback() def search_and_insert(filters=None, incl=None): tags = [] prev_was_res = False if filters: filters = [f.lower() for f in filters] # Transform into all lowercase for cur_el in soup.find_all(): # Detect folders, aka <DT><H3> {folder name} </H3> if cur_el.name == "h3": if prev_was_res and tags: tags.pop() tags.append(cur_el.string.lower()) # Detect resources/links aka <DT><A {href}> {title} </A> if cur_el.name == "a": new_resource = cc.BaseResource(cur_el.string, cur_el.get("href"), tags) if ((not incl and not filters) or (incl and any(f in tags for f in filters)) or (not incl and all(f not in tags for f in filters))): add_res_to_db(new_resource) if not prev_was_res: prev_was_res = True if request.method == "POST": if "file" not in request.files: flash("No file selected.", "warning") return redirect(request.url) else: file = request.files["file"] if file.filename == "": flash("No file selected.", "warning") return redirect(request.url) if file: soup = BeautifulSoup(file, "html.parser") incl = request.form.get("incl") excl = request.form.get("excl") if not incl and not excl: # Default import search_and_insert() elif incl and not excl: # Include only incl_items = incl.split(",") search_and_insert(incl_items, incl=True) elif not incl and excl: # Exclude only excl_items = excl.split(",") search_and_insert(excl_items, incl=False) cur.close() conn.commit() flash("Resources imported successfully.", "success") return redirect(url_for("resources"))
def edit_res(user_id, re_id): if request.method == "GET": if helpers.logged_in(user_id): # Fetch the data we want to edit cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) cur.execute( ("""SELECT * FROM resources WHERE user_id = %s and re_id = %s""" ), (user_id, re_id), ) data = cur.fetchall() # Get the tags to suggest to the user cur.execute( ("""SELECT DISTINCT unnest(tags) FROM resources WHERE user_id = %s""" ), (user_id, ), ) tags_raw = cur.fetchall() # 'Unpack' tags_raw into one array all_tags = [] for tag_arr in tags_raw: all_tags.append(Markup.escape(tag_arr[0])) cur.close() conn.commit() # Paranoid Mode: On. Escape user input even after we retrieve it from the database. # Fill the form with the data form = forms.ResourceForm() form.title.data = Markup.escape(data[0]["title"]) form.link.data = Markup.escape(data[0]["link"]) form.note.data = Markup.escape(data[0]["note"]) if form.note.data: form.note.data = form.note.data.replace( "</br>", "\n") # Else the </br> tags will display as text if data[0]["tags"]: form.tags.data = ",".join(data[0]["tags"]) # Array to string form.tags.data = Markup.escape(form.tags.data.lower()) else: form.tags.data = "" return render_template("edit_resource.html", title=data[0]["title"], form=form, tags=all_tags) elif request.method == "POST": form = forms.ResourceForm(request.form) if form.validate(): # Grab the new form and its data title = form.title.data link = Markup.escape(form.link.data) note = Markup.escape(form.note.data) tags = Markup.escape(form.tags.data) # If not empty format for proper insertion into postgresql if tags: tags = "{" + str(tags).lower() + "}" else: tags = None # Update the row - keep date_of_posting, re_id and user_id the same cur = conn.cursor() cur.execute( ("""UPDATE resources SET title=%s,link=%s,note=%s,tags=%s WHERE user_id=%s AND re_id=%s""" ), (title, link, note, tags, user_id, re_id), ) cur.close() conn.commit() flash("Resource edited successfully.", "success") return redirect(url_for("resources")) else: return render_template("edit_resource.html", form=form) return redirect(url_for("resources"))
def resources(): if not helpers.logged_in(): flash("You must be logged in to access your resources page.", "warning") return redirect(url_for("login")) else: user_id = session["user_id"] cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) sort = request.cookies.get("sort") criteria = request.cookies.get("criteria") if criteria == "title": if sort == "asc" or not sort: cur.execute( ("""SELECT * FROM resources WHERE user_id = %s ORDER BY title ASC""" ), (user_id, ), ) else: cur.execute( ("""SELECT * FROM resources WHERE user_id = %s ORDER BY title DESC""" ), (user_id, ), ) elif criteria == "time" or not criteria: if sort == "asc" or not sort: cur.execute( ("""SELECT * FROM resources WHERE user_id = %s ORDER BY date_of_posting ASC""" ), (user_id, ), ) else: cur.execute( ("""SELECT * FROM resources WHERE user_id = %s ORDER BY date_of_posting DESC""" ), (user_id, ), ) data = cur.fetchall() try: cur.execute( ("""SELECT DISTINCT unnest(tags) FROM resources WHERE user_id = %s""" ), (user_id, ), ) except DatabaseError: conn.rollbal() tags_raw = cur.fetchall() # 'Unpack' tags_raw into one array all_tags = [] for tag_arr in tags_raw: all_tags.append(tag_arr[0]) cur.close() conn.commit() for res in data: res["title"] = unescape(res["title"]) view = request.cookies.get("view") if view == "full": return render_template( "resources.html", resources=data, tags=all_tags, view=view ) # Pass 'view' attribute to use the correct .css file else: return render_template("resources_cmpct.html", resources=data, tags=all_tags, view=view) return render_template("resources.html")
def open_share(token): use_filters = False all_tags = False try: serializer = URLSafeTimedSerializer(app.config["SECRET_KEY"]) data = serializer.loads(token, salt="share-salt", max_age=259200) # 3 days user_id = data[0] if data[1] == "*": all_tags = True tags = "*" else: tags = "{" + data[1] + "}" if data[2]: use_filters = True filters = "{" + data[2] + "}" except: flash("The share link is invalid or has expired.", "danger") return render_template("home.html") cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) if use_filters: if all_tags: cur.execute( """SELECT * FROM resources WHERE user_id= %s AND NOT tags @> %s AND tags IS NOT NULL ORDER BY date_of_posting DESC""", (user_id, filters), ) else: cur.execute( """SELECT * FROM resources WHERE user_id= %s AND tags @> %s AND NOT tags @> %s AND tags IS NOT NULL ORDER BY date_of_posting DESC""", (user_id, tags, filters), ) else: if all_tags: cur.execute( """SELECT * FROM resources WHERE user_id= %s ORDER BY date_of_posting DESC""", (user_id, ), ) else: cur.execute( """SELECT * FROM resources WHERE user_id= %s AND tags @> %s ORDER BY date_of_posting DESC""", (user_id, tags), ) resources = cur.fetchall() cur.close() conn.commit() return render_template("resources_public.html", resources=resources, tags=tags, view="full")
def share(): if request.method == "POST": search_str = request.form.get("tags") search_str = search_str.lower() filters = [] filters_str = "" use_filters = False # Assuming standard form <tag1,tag2 -tag4,tag6> we separate filters from tags if "-" in search_str: use_filters = True filters_str = search_str[search_str.find("-") + 1:].strip() filters = [f.lower() for f in filters_str.split(",")] tags = [ t.lower() for t in search_str[:search_str.find("-") - 1].split(",") ] else: tags = [t.lower() for t in search_str.split(",")] tags_str = ",".join(tags).strip() if not tags or not tags_str or tags_str == "": flash(f"No tags selected. Can't share.", "danger") return redirect(url_for("resources")) if use_filters and (filters == tags or all(fil in tags for fil in filters)): flash("Tags and filters are the same. Can't share.", "danger") return redirect(url_for("resources")) cur = conn.cursor() cur.execute( ("""SELECT DISTINCT unnest(tags) FROM resources WHERE user_id = %s""" ), (session["user_id"], ), ) tags_used = cur.fetchall() # 'Unpack' tags_raw into one array tags_used_clean = ["*"] for tag_arr in tags_used: tags_used_clean.append(tag_arr[0]) cur.close() conn.commit() # If filters or search tags don't contain any actually used (aka existing) tags if any(tag not in tags_used_clean or fil not in tags_used_clean for tag in tags for fil in filters): flash("No such tag exists. Can't share.", "danger") return redirect(url_for("resources")) serializer = URLSafeTimedSerializer(app.config["SECRET_KEY"]) share_url = url_for( "open_share", token=serializer.dumps([session["user_id"], tags_str, filters_str], salt="share-salt"), _external=True, ) if use_filters: message = Markup(" \ Resources containing {" + tags_str + " -" + filters_str + "} can be publicly accessed for 3 days via the \ following link: <a href=" + share_url + ">Link</a>") else: message = Markup("Resources containing {" + tags_str + "} can be publicly accessed for 3 days via the \ following link: <a href=" + share_url + ">Link</a>") flash(message, "info") return redirect(url_for("resources")) return redirect(url_for("resources"))