moving stuff around, cleaning up, adding mod area

This commit is contained in:
lza_menace 2022-08-31 23:02:31 -07:00
parent 0cab370e12
commit 143d08f2f7
20 changed files with 591 additions and 254 deletions

View file

@ -1,22 +1,8 @@
import json from flask import Flask
import click
import arrow
from math import ceil
from datetime import datetime, timedelta
from random import choice
from os import makedirs, path, remove
from flask import Flask, request, session, redirect
from flask import render_template, flash, url_for
from flask_session import Session from flask_session import Session
from suchwow import config
from suchwow.models import Post, Profile, Comment, Notification, db, Moderator, Ban from suchwow.routes import auth, comment, post, profile, leaderboard, api, mod, main
from suchwow.routes import auth, comment, post, profile, leaderboard, api from suchwow import filters, cli
from suchwow.utils.decorators import login_required, moderator_required
from suchwow.utils.helpers import post_webhook, get_latest_tipped_posts
from suchwow.utils.helpers import get_top_posters, get_top_posts
from suchwow.reddit import make_post
from suchwow.discord import post_discord_webhook
from suchwow import wownero, filters
app = Flask(__name__) app = Flask(__name__)
@ -30,186 +16,10 @@ app.register_blueprint(profile.bp)
app.register_blueprint(comment.bp) app.register_blueprint(comment.bp)
app.register_blueprint(leaderboard.bp) app.register_blueprint(leaderboard.bp)
app.register_blueprint(api.bp) app.register_blueprint(api.bp)
app.register_blueprint(mod.bp)
app.register_blueprint(main.bp)
app.register_blueprint(filters.bp) app.register_blueprint(filters.bp)
app.register_blueprint(cli.bp)
@app.route("/")
def index():
itp = 15
page = request.args.get("page", 1)
submitter = request.args.get("submitter", None)
content = request.args.get("content", None)
if content == 'latest_tipped':
posts = get_latest_tipped_posts()
return render_template(
"index.html",
posts=posts[0:30],
title="Latest Tipped Memes"
)
try:
page = int(page)
except:
flash("Wow, wtf hackerman. Cool it.", "is-danger")
page = 1
posts = Post.select().where(Post.approved==True).order_by(Post.timestamp.desc())
if submitter:
posts = posts.where(Post.submitter==submitter)
paginated_posts = posts.paginate(page, itp)
total_pages = ceil(posts.count() / itp)
return render_template(
"index.html",
posts=paginated_posts,
page=page,
total_pages=total_pages,
title="Latest Memes"
)
@app.route("/mod")
@moderator_required
def mod_queue():
posts = Post.select().where(Post.approved==False).order_by(Post.timestamp.asc())
return render_template("index.html", posts=posts)
@app.route("/about")
def about():
mods = Profile.select().join(Moderator, on=(Profile.username == Moderator.username))
return render_template("about.html", mods=mods)
@app.cli.command("init")
def init():
# create subdirs
for i in ["uploads", "db", "wallet"]:
makedirs(f"{config.DATA_FOLDER}/{i}", exist_ok=True)
# init db
db.create_tables([Post, Profile, Comment, Notification, Moderator, Ban])
@app.cli.command("post_reddit")
@click.argument('last_hours')
def post_reddit(last_hours):
posts = Post.select().where(
Post.approved==True,
Post.to_reddit==False
).order_by(Post.timestamp.asc())
for p in posts:
if p.hours_elapsed() < int(last_hours):
if not p.to_reddit:
_p = make_post(p)
if _p:
p.to_reddit = True
p.save()
return
@app.cli.command("create_accounts")
def create_accounts():
wallet = wownero.Wallet()
for post in Post.select():
if post.account_index not in wallet.accounts():
account = wallet.new_account()
print(f"Created account {account}")
@app.cli.command("payout_users")
def payout_users():
wallet = wownero.Wallet()
_fa = wownero.from_atomic
_aw = wownero.as_wownero
for post in Post.select():
try:
submitter = Profile.get(username=post.submitter)
balances = wallet.balances(post.account_index)
url = url_for('post.read', id=post.id, _external=True)
if balances[1] > 0.05:
print(f"Post #{post.id} has {balances[1]} funds unlocked and ready to send. Sweeping all funds to user's address ({submitter.address}).")
sweep = wallet.sweep_all(account=post.account_index, dest_address=submitter.address)
print(sweep)
if "tx_hash_list" in sweep:
amount = 0
for amt in sweep["amount_list"]:
amount += int(amt)
except Exception as e:
print(f"Failed because: {e}")
@app.cli.command("add_admin")
@click.argument("username")
def add_admin(username):
if not Moderator.filter(username=username):
m = Moderator(username=username)
m.save()
print(f"Added {username}")
post_webhook(f"Moderator `{username}` added :ship_it_parrot: by console :black_flag:")
else:
print("That moderator already exists")
@app.cli.command("remove_admin")
@click.argument("username")
def remove_admin(username):
m = Moderator.filter(username=username).first()
if m:
m.delete_instance()
print(f"Deleted {username}")
post_webhook(f"Moderator `{username}` removed :excuseme: by console :black_flag:")
else:
print("That moderator doesn't exist")
@app.cli.command("ban_user")
@click.argument("username")
@click.argument("reason", nargs=-1)
def ban_user(username, reason):
u = Profile.filter(username=username).first()
b = Ban.filter(user=u).first()
if b:
print("User already banned")
return False
if u:
b = Ban(user=u, reason=' '.join(reason))
b.save()
print(f"Banned {username}")
return
else:
print("That user doesn't exist")
return
@app.cli.command("unban_user")
@click.argument("username")
def ban_user(username):
u = Profile.filter(username=username).first()
if not u:
print("That user doesn't exist")
return False
b = Ban.filter(user=u).first()
if b:
b.delete_instance()
print(f"Unbanned {username}")
return True
else:
print("That user isn't banned")
return False
@app.cli.command("show")
@click.argument("post_id")
def post_id(post_id):
p = Post.filter(id=post_id).first()
if p:
print(p.show())
else:
print("That post doesn't exist")
@app.cli.command("load_cache")
def load_cache():
app.logger.info('loading top posters into cache')
get_top_posters()
app.logger.info('done')
app.logger.info('loading latest tipped into cache')
get_latest_tipped_posts()
app.logger.info('done')
for i in [1, 3, 7, 30, 9999]:
app.logger.info(f'loading top posts last {i} days into cache')
get_top_posts(i)
app.logger.info('done')
if __name__ == "__main__": if __name__ == "__main__":

95
suchwow/cli.py Normal file
View file

@ -0,0 +1,95 @@
from os import makedirs
import click
from flask import Blueprint, url_for, current_app
from suchwow.models import Post, Profile, Comment, Notification, db, Moderator, Ban
from suchwow.utils.helpers import get_latest_tipped_posts
from suchwow.utils.helpers import get_top_posters, get_top_posts
from suchwow.reddit import make_post
from suchwow import wownero
from suchwow import config
bp = Blueprint('cli', 'cli', cli_group=None)
@bp.cli.command("init")
def init():
# create subdirs
for i in ["uploads", "db", "wallet"]:
makedirs(f"{config.DATA_FOLDER}/{i}", exist_ok=True)
# init db
db.create_tables([Post, Profile, Comment, Notification, Moderator, Ban])
@bp.cli.command("post_reddit")
@click.argument('last_hours')
def post_reddit(last_hours):
posts = Post.select().where(
Post.approved==True,
Post.to_reddit==False
).order_by(Post.timestamp.asc())
for p in posts:
if p.hours_elapsed() < int(last_hours):
if not p.to_reddit:
_p = make_post(p)
if _p:
p.to_reddit = True
p.save()
return
@bp.cli.command("create_accounts")
def create_accounts():
wallet = wownero.Wallet()
for post in Post.select():
if post.account_index not in wallet.accounts():
account = wallet.new_account()
print(f"Created account {account}")
@bp.cli.command("payout_users")
def payout_users():
wallet = wownero.Wallet()
_fa = wownero.from_atomic
_aw = wownero.as_wownero
for post in Post.select():
try:
submitter = Profile.get(username=post.submitter)
balances = wallet.balances(post.account_index)
url = url_for('post.read', id=post.id, _external=True)
if balances[1] > 0.05:
print(f"Post #{post.id} has {balances[1]} funds unlocked and ready to send. Sweeping all funds to user's address ({submitter.address}).")
sweep = wallet.sweep_all(account=post.account_index, dest_address=submitter.address)
print(sweep)
if "tx_hash_list" in sweep:
amount = 0
for amt in sweep["amount_list"]:
amount += int(amt)
except Exception as e:
print(f"Failed because: {e}")
@bp.cli.command("show")
@click.argument("post_id")
def post_id(post_id):
p = Post.filter(id=post_id).first()
if p:
print(p.show())
else:
print("That post doesn't exist")
@bp.cli.command("load_cache")
def load_cache():
current_app.logger.info('loading top posters into cache')
get_top_posters()
current_app.logger.info('done')
current_app.logger.info('loading latest tipped into cache')
get_latest_tipped_posts()
current_app.logger.info('done')
for i in [1, 3, 7, 30, 9999]:
current_app.logger.info(f'loading top posts last {i} days into cache')
get_top_posts(i)
current_app.logger.info('done')

View file

@ -14,6 +14,7 @@ OIDC_REDIRECT_URL = getenv('OIDC_REDIRECT_URL', 'http://localhost:5000/auth')
SECRET_KEY = getenv('SECRET_KEY', 'yyyyyyyyyyyyy') # whatever you want it to be SECRET_KEY = getenv('SECRET_KEY', 'yyyyyyyyyyyyy') # whatever you want it to be
DATA_FOLDER = getenv('DATA_FOLDER', '/path/to/uploads') # some stable storage path DATA_FOLDER = getenv('DATA_FOLDER', '/path/to/uploads') # some stable storage path
SERVER_NAME = getenv('SERVER_NAME', 'localhost') # name of your DNS resolvable site (.com) SERVER_NAME = getenv('SERVER_NAME', 'localhost') # name of your DNS resolvable site (.com)
SUPER_ADMIN = getenv('SUPER_ADMIN', 'lza_menace') # top dawg you cannot delete
WALLET_HOST = getenv('WALLET_HOST', 'localhost') # WALLET_HOST = getenv('WALLET_HOST', 'localhost') #
WALLET_PORT = int(getenv('WALLET_PORT', 8888)) # WALLET_PORT = int(getenv('WALLET_PORT', 8888)) #
WALLET_PROTO = getenv('WALLET_PROTO', 'http') # WALLET_PROTO = getenv('WALLET_PROTO', 'http') #

View file

@ -1,6 +1,8 @@
from flask import Blueprint, current_app from flask import Blueprint, current_app
from arrow import get as arrow_get from arrow import get as arrow_get
from suchwow.models import Moderator
bp = Blueprint('filters', 'filters') bp = Blueprint('filters', 'filters')
@ -13,3 +15,10 @@ def shorten_address(a):
@bp.app_template_filter('humanize') @bp.app_template_filter('humanize')
def humanize(d): def humanize(d):
return arrow_get(d).humanize() return arrow_get(d).humanize()
@bp.app_template_filter('is_moderator')
def is_moderator(s):
m = Moderator.select().where(Moderator.username == s)
if m:
return True
return False

View file

@ -1,4 +1,5 @@
from peewee import * from peewee import *
from random import choice
from os import path from os import path
from datetime import datetime from datetime import datetime
from PIL import Image from PIL import Image
@ -8,6 +9,16 @@ from suchwow import config
db = SqliteDatabase(f"{config.DATA_FOLDER}/sqlite.db") db = SqliteDatabase(f"{config.DATA_FOLDER}/sqlite.db")
ban_reasons = [
'you smell bad',
'didnt pass the vibe check, homie',
'your memes are bad and you should feel bad',
'i just dont like you'
]
def get_ban_reason():
return choice(ban_reasons)
class Post(Model): class Post(Model):
id = AutoField() id = AutoField()
title = CharField() title = CharField()

View file

@ -21,7 +21,7 @@ def login():
@bp.route("/logout") @bp.route("/logout")
def logout(): def logout():
session["auth"] = None session["auth"] = None
return redirect(url_for("index")) return redirect(url_for("main.index"))
@bp.route("/auth/") @bp.route("/auth/")
def auth(): def auth():
@ -66,4 +66,4 @@ def auth():
# user can now visit /secret # user can now visit /secret
session["auth"] = user_profile session["auth"] = user_profile
return redirect(url_for("index")) return redirect(url_for("main.index"))

View file

@ -11,7 +11,7 @@ bp = Blueprint("comment", "comment")
def create(post_id): def create(post_id):
if not Post.filter(id=post_id): if not Post.filter(id=post_id):
flash("WTF, that post doesn't exist. Stop it, hackerman.", "is-danger") flash("WTF, that post doesn't exist. Stop it, hackerman.", "is-danger")
return redirect(url_for("index")) return redirect(url_for("main.index"))
if request.method == "POST": if request.method == "POST":
comment_text = request.form.get("comment") comment_text = request.form.get("comment")

50
suchwow/routes/main.py Normal file
View file

@ -0,0 +1,50 @@
from math import ceil
from flask import Blueprint, request, render_template, flash
from suchwow.models import Post, Profile, Moderator
from suchwow.utils.helpers import get_latest_tipped_posts
bp = Blueprint('main', 'main')
@bp.route("/")
def index():
itp = 15
page = request.args.get("page", 1)
submitter = request.args.get("submitter", None)
content = request.args.get("content", None)
if content == 'latest_tipped':
posts = get_latest_tipped_posts()
return render_template(
"index.html",
posts=posts[0:30],
title="Latest Tipped Memes"
)
try:
page = int(page)
except:
flash("Wow, wtf hackerman. Cool it.", "is-danger")
page = 1
posts = Post.select().where(Post.approved==True).order_by(Post.timestamp.desc())
if submitter:
posts = posts.where(Post.submitter==submitter)
paginated_posts = posts.paginate(page, itp)
total_pages = ceil(posts.count() / itp)
return render_template(
"index.html",
posts=paginated_posts,
page=page,
total_pages=total_pages,
title="Latest Memes"
)
@bp.route("/about")
def about():
mods = Profile.select().join(Moderator, on=(Profile.username == Moderator.username))
return render_template("about.html", mods=mods)

106
suchwow/routes/mod.py Normal file
View file

@ -0,0 +1,106 @@
from flask import Blueprint, render_template, redirect, url_for, flash, request
from suchwow.models import Post, Profile, Moderator, Ban, get_ban_reason
from suchwow.utils.decorators import moderator_required
from suchwow.utils.helpers import get_session_user
from suchwow import config
bp = Blueprint("mod", "mod")
@bp.route('/mods')
@moderator_required
def main():
live_posts = Post.select().where(Post.approved == True).count()
pending_posts = Post.select().where(Post.approved == False).count()
active_posters = Profile.select().join(Post, on=Post.submitter == Profile.username).distinct().count()
mods = Moderator.select().count()
return render_template(
'mod/main.html',
live_posts=live_posts,
pending_posts=pending_posts,
active_posters=active_posters,
mods=mods
)
@bp.route('/mods/pending')
@moderator_required
def pending_posts():
posts = Post.select().where(Post.approved == False).order_by(Post.timestamp.asc())
if not posts:
flash('no posts pending', 'is-warning')
return redirect(url_for('mod.main'))
return render_template('mod/posts.html', posts=posts)
@bp.route('/mods/manage', methods=['GET', 'POST'])
@moderator_required
def manage_mods():
to_delete = request.args.get('delete')
if to_delete:
m = Moderator.select().where(Moderator.username == to_delete).first()
if not m:
flash('No moderator exists with that name', 'is-danger')
elif m.username == get_session_user():
flash('Cannot remove yourself.', 'is-danger')
elif m.username == config.SUPER_ADMIN:
flash('Cannot delete super admin you son-of-a-bitch.', 'is-danger')
else:
m.delete_instance()
flash(f'Removed {to_delete} from mods!', 'is-success')
return redirect(url_for('mod.manage_mods'))
if request.method == 'POST':
to_add = request.form.get('username', None)
if to_add:
u = Profile.select().where(Profile.username == to_add).first()
if not u:
flash('That user does not appear to exist (no profile setup yet)', 'is-danger')
elif Moderator.select().where(Moderator.username == to_add).first():
flash(f'{to_add} is already a mod, ya dingus.', 'is-warning')
else:
m = Moderator(username=to_add)
m.save()
flash(f'Added {to_add} to mods!', 'is-success')
mods = Profile.select().join(Moderator, on=(Profile.username == Moderator.username))
return render_template('mod/manage.html', mods=mods)
@bp.route('/mods/bans', methods=['GET', 'POST'])
@moderator_required
def manage_bans():
to_delete = request.args.get('delete')
if to_delete:
ban = Ban.select().join(Profile).where(Profile.username == to_delete).first()
if not ban:
flash('No ban exists for that user', 'is-danger')
elif ban.user == get_session_user():
flash('Cannot ban yourself.', 'is-danger')
elif ban.user == config.SUPER_ADMIN:
flash('Cannot ban super admin you son-of-a-bitch.', 'is-danger')
else:
ban.delete_instance()
flash(f'Unbanned {to_delete}!', 'is-success')
return redirect(url_for('mod.manage_bans'))
if request.method == 'POST':
to_add = request.form.get('username', None)
if to_add:
u = Profile.select().where(Profile.username == to_add).first()
if not u:
flash('That user does not appear to exist (no profile setup yet)', 'is-danger')
elif Ban.select().join(Profile).where(Profile.username == to_add).first():
flash(f'{to_add} is already banned, ya dingus.', 'is-warning')
elif to_add == config.SUPER_ADMIN:
flash('Cannot ban the super admin you son-of-a-bitch.', 'is-danger')
else:
reason = request.form.get('reason')
if not reason:
reason = get_ban_reason()
ban = Ban(user=u, reason=reason)
ban.save()
flash(f'Banned {to_add}!', 'is-success')
bans = Ban.select()
return render_template('mod/bans.html', bans=bans)
# audit trail of activity
# view wallet rpc logs

View file

@ -48,7 +48,7 @@ def read(id):
) )
else: else:
flash("No meme there, brah", "is-warning") flash("No meme there, brah", "is-warning")
return redirect(url_for("index")) return redirect(url_for("main.index"))
@bp.route("/post/create", methods=["GET", "POST"]) @bp.route("/post/create", methods=["GET", "POST"])
@login_required @login_required
@ -107,7 +107,7 @@ def create():
url = url_for('post.read', id=post.id, _external=True) url = url_for('post.read', id=post.id, _external=True)
post_webhook(f"New post :doge2: [{post.id}]({url}) by `{submitter}` :neckbeard:") post_webhook(f"New post :doge2: [{post.id}]({url}) by `{submitter}` :neckbeard:")
flash("New post created and pending approval!", "is-success") flash("New post created and pending approval!", "is-success")
return redirect(url_for("index")) return redirect(url_for("main.index"))
return render_template("post/create.html") return render_template("post/create.html")
@bp.route("/post/<id>/approve") @bp.route("/post/<id>/approve")
@ -122,22 +122,12 @@ def approve(id):
post_webhook(f"Post [{post.id}]({url}) approved :white_check_mark: by `{get_session_user()}` :fieri_parrot:") post_webhook(f"Post [{post.id}]({url}) approved :white_check_mark: by `{get_session_user()}` :fieri_parrot:")
flash("Approved", "is-success") flash("Approved", "is-success")
if current_app.config["DEBUG"] is False: if current_app.config["DEBUG"] is False:
# _r = None
# _d = None
# if not post.to_reddit:
# _r = make_post(post)
# if not post.to_discord:
# _d = post_discord_webhook(post)
# if _r and _d:
# post_webhook(f"Post [{post.id}]({url}) submitted :dab_parrot: to Reddit and Discord.")
# else:
# post_webhook(f"Post [{post.id}]({url}) failed :this-is-fine-fire: to post to socials...Reddit: {_r} - Discord: {_d}")
post_discord_webhook(post) post_discord_webhook(post)
post_webhook(f"Post [{post.id}]({url}) submitted :dab_parrot: to Discord.") post_webhook(f"Post [{post.id}]({url}) submitted :dab_parrot: to Discord.")
return redirect(url_for("mod_queue")) return redirect(url_for("mod.pending_posts"))
else: else:
flash("You can't approve this", "is-success") flash("You can't approve this", "is-success")
return redirect(url_for("index")) return redirect(url_for("main.index"))
@bp.route("/post/<id>/delete") @bp.route("/post/<id>/delete")
@login_required @login_required
@ -155,15 +145,15 @@ def delete(id):
post_webhook(f"Post {post.id} deleted :dumpsterfire: by `{user}` :godmode:") post_webhook(f"Post {post.id} deleted :dumpsterfire: by `{user}` :godmode:")
flash("Deleted that shit, brah!", "is-success") flash("Deleted that shit, brah!", "is-success")
if is_mod: if is_mod:
return redirect(url_for("mod_queue")) return redirect(url_for("mod.pending_posts"))
else: else:
return redirect(url_for("index")) return redirect(url_for("main.index"))
else: else:
flash("You can't delete a meme you don't own, brah", "is-warning") flash("You can't delete a meme you don't own, brah", "is-warning")
return redirect(url_for("post.read", id=post.id)) return redirect(url_for("post.read", id=post.id))
else: else:
flash("No meme there, brah", "is-warning") flash("No meme there, brah", "is-warning")
return redirect(url_for("index")) return redirect(url_for("main.index"))
@bp.route("/uploads/<path:filename>") @bp.route("/uploads/<path:filename>")
def uploaded_file(filename): def uploaded_file(filename):

View file

@ -70,13 +70,17 @@
<!-- Page Content --> <!-- Page Content -->
{% block content %} {% endblock %} {% block content %} {% endblock %}
<div style="padding-bottom:10em;"></div>
<!-- Footer --> <!-- Footer -->
{% block footer %}
<footer class="footer"> <footer class="footer">
<div class="content has-text-centered"> <div class="content has-text-centered">
<p><strong>SuchWow</strong> by <a href="https://lzahq.tech" target="_blank">lza_menace</a></p> <p><strong>SuchWow</strong> by <a href="https://lzahq.tech" target="_blank">lza_menace</a></p>
<p><a href="https://wownero.org/" target="_blank">Learn more about the infamous shitcoin, Wownero</a>.</p> <p><a href="https://wownero.org/" target="_blank">Learn more about the infamous shitcoin, Wownero</a>.</p>
</div> </div>
</footer> </footer>
{% endblock %}
<!-- JS --> <!-- JS -->
<script> <script>

View file

@ -1,19 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<div class="container" style="width:40%;">
<div class="submit">
<h1>Leave a Comment</h1>
<form method=post enctype=multipart/form-data class="form-horizontal">
<div class="form-group">
<label class="sr-only" for="inlineFormInput">Text</label>
<input type="text" class="form-control mb-2 mr-sm-2 mb-sm-0" id="inlineFormInput" placeholder="Comment text (max 300 chars)" name="comment">
</div>
<div class="form-group">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</form>
</div>
</div>
{% endblock %}

View file

@ -26,7 +26,7 @@
{% endif %} {% endif %}
<hr> <hr>
<a href="{{ url_for('index') }}"><button class="btn btn-warning">Go Home</button></a> <a href="{{ url_for('main.index') }}"><button class="btn btn-warning">Go Home</button></a>
</div> </div>

View file

@ -0,0 +1,57 @@
{% extends 'base.html' %}
{% block content %}
<div class="container" style="text-align:center;">
<h1 class="title">Manage Mods</h1>
<nav class="breadcrumb is-centered" aria-label="breadcrumbs">
<ul>
<li><a href="/">Home</a></li>
<li><a href="{{ url_for('mod.main') }}">Mods</a></li>
</ul>
</nav>
<section class="section">
{% for ban in bans %}
<article class="message" style="width: 30%; margin: 2em auto;">
<div class="message-header">
<p>{{ ban.user.username }}</p>
<a href="?delete={{ ban.user.username }}" class="delete"></a>
</div>
<div class="message-body">
{{ ban.reason }}
</div>
</article>
{% endfor %}
</section>
<div class="container" style="text-align:left; width: 30%;">
<form method="post">
<div class="field">
<label class="label">Ban User</label>
<div class="control">
<input class="input" type="text" placeholder="Username" name="username">
</div>
</div>
<div class="field">
<label class="label">Reason</label>
<div class="control">
<textarea class="textarea" placeholder="Reason for ban" name="reason"></textarea>
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link">Submit</button>
</div>
<div class="control">
<button class="button is-link is-light">Cancel</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
{% block footer %}{% endblock %}

View file

@ -0,0 +1,72 @@
{% extends 'base.html' %}
{% block content %}
<div class="container content" style="padding-top: 4em;">
<nav class="level">
<div class="level-item has-text-centered">
<div>
<p class="heading">Live Memes</p>
<p class="title">{{ live_posts }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Pending Memes</p>
<p class="title">{{ pending_posts }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Active Posters</p>
<p class="title">{{ active_posters }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Mods</p>
<p class="title">{{ mods }}</p>
</div>
</div>
</nav>
<div class="tabs is-toggle is-fullwidth">
<ul>
<li>
<a href="{{ url_for('mod.pending_posts') }}">
<span class="icon is-small"></span>
<span>Manage Queue</span>
</a>
</li>
<li>
<a href="{{ url_for('mod.manage_bans') }}">
<span class="icon is-small"></span>
<span>Manage Bans</span>
</a>
</li>
<li>
<a href="{{ url_for('mod.manage_mods') }}">
<span class="icon is-small"></span>
<span>Manage Mods</span>
</a>
</li>
<li>
<a>
<span class="icon is-small"></span>
<span>View Logs</span>
</a>
</li>
</ul>
</div>
<figure>
<img src="https://suchwow.xyz/uploads/pg1VwHJWeKT5dWXy-wowcomfysemifinal.gif">
</figure>
<div style="padding-bottom:10em;"></div>
</div>
{% endblock %}
{% block footer %}{% endblock %}

View file

@ -0,0 +1,48 @@
{% extends 'base.html' %}
{% block content %}
<div class="container" style="text-align:center;">
<h1 class="title">Manage Mods</h1>
<nav class="breadcrumb is-centered" aria-label="breadcrumbs">
<ul>
<li><a href="/">Home</a></li>
<li><a href="{{ url_for('mod.main') }}">Mods</a></li>
</ul>
</nav>
<section class="section">
{% for mod in mods %}
<div class="block">
<span class="tag is-large">
{{ mod.username }}
<a href="?delete={{ mod.username }}" class="delete"></a>
</span>
</div>
{% endfor %}
</section>
<div class="container" style="text-align:left; width: 30%;">
<form method="post">
<div class="field">
<label class="label">Add New Mod</label>
<div class="control">
<input class="input" type="text" placeholder="Username" name="username">
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link">Submit</button>
</div>
<div class="control">
<button class="button is-link is-light">Cancel</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
{% block footer %}{% endblock %}

View file

@ -0,0 +1,65 @@
{% extends 'base.html' %}
{% block content %}
<div class="container" style="text-align:center;">
<h1 class="title">{% if title %}{{ title }}{% else %}Pending Posts{% endif %}</h1>
<nav class="breadcrumb is-centered" aria-label="breadcrumbs">
<ul>
<li><a href="/">Home</a></li>
<li><a href="{{ url_for('mod.main') }}">Mods</a></li>
</ul>
</nav>
<section class="section">
{% if posts %}
{% for row in posts | batch(4) %}
<div class="columns">
{% for p in row %}
{% set post = p.show() %}
<div class="column">
<div class="card">
<div class="card-image">
{% if p.get_image_path().endswith('mp4') %}
<video style="max-height: 100vh!important;" controls>
<source src="{{ url_for('post.uploaded_file', filename=p.image_name) }}" type="video/mp4">
Your browser does not support the video tag.
</video>
{% else %}
<img alt="SuchWow #{{ post.id }} - {{ post.title }} by {{ post.submitter }}" src="{{ url_for('post.uploaded_file', filename=post.thumbnail_name) }}" />
{% endif %}
</div>
<div class="card-content">
<div class="media">
<div class="media-content">
<p class="title is-4">
{{ post.title }}
</p>
<p class="subtitle is-6"><a href="/?submitter={{ post.submitter }}">{{ post.submitter }}</a></p>
</div>
</div>
<div class="content">
{{ post.text | truncate(60) }}
<time datetime="2016-1-1">{{ post.timestamp.year }}-{{ post.timestamp.month }}-{{ post.timestamp.day }} {{ post.timestamp.hour }}:{{ post.timestamp.minute }} UTC</time>
<p>({{ post.timestamp | humanize }})</p>
</div>
</div>
<footer class="card-footer">
<a href="{{ url_for('post.approve', id=post.id) }}" class="card-footer-item" style="color:green;"><strong>Approve</strong></a>
<a href="{{ url_for('post.delete', id=post.id) }}" class="card-footer-item" style="color:red;"><strong>Deny</strong></a>
</footer>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
{% else %}
<p>No posts pending!</p>
{% endif %}
</section>
</div>
{% endblock %}
{% block footer %}{% endblock %}

View file

@ -13,7 +13,7 @@
<div id="navbarBasicExample" class="navbar-menu"> <div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start"> <div class="navbar-start">
<a class="navbar-item" href="{{ url_for('index') }}">Home</a> <a class="navbar-item" href="{{ url_for('main.index') }}">Home</a>
<div class="navbar-item has-dropdown is-hoverable"> <div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">Leaderboards</a> <a class="navbar-link">Leaderboards</a>
@ -26,10 +26,10 @@
<a class="navbar-item" href="{{ url_for('leaderboard.top_posts') }}?days=9999">Top Memes All Time</a> <a class="navbar-item" href="{{ url_for('leaderboard.top_posts') }}?days=9999">Top Memes All Time</a>
</div> </div>
</div> </div>
<a class="navbar-item" href="{{ url_for('about') }}">About</a> <a class="navbar-item" href="{{ url_for('main.about') }}">About</a>
<a class="navbar-item" href="{{ url_for('post.create') }}">Submit</a> <a class="navbar-item" href="{{ url_for('post.create') }}">Submit</a>
{% if session.auth %} {% if session.auth.preferred_username | is_moderator %}
<a class="navbar-item" style="color: orange;" href="{{ url_for('mod_queue') }}">Mods!</a> <a class="navbar-item" style="color: orange;" href="{{ url_for('mod.main') }}">Mods!</a>
{% endif %} {% endif %}
</div> </div>
@ -70,7 +70,7 @@
<a class="nav-link" href="{{ url_for('post.create') }}">Submit</a> <a class="nav-link" href="{{ url_for('post.create') }}">Submit</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ url_for('about') }}">About</a> <a class="nav-link" href="{{ url_for('main.about') }}">About</a>
</li> </li>
{% if session.auth == None %} {% if session.auth == None %}
<li class="nav-item"> <li class="nav-item">

View file

@ -9,25 +9,63 @@
<ol> <ol>
<li>no super low effort memes</li> <li>no super low effort memes</li>
<li>no nsfl and nsfw</li> <li>no nsfl and nsfw</li>
<li>must pertain to wownero or crypto in general</li> <!-- <li>must pertain to wownero or crypto in general</li> -->
</ol> </ol>
<form method=post enctype=multipart/form-data class="form-horizontal"> <form method=post enctype=multipart/form-data class="form-horizontal">
<div class="form-group"> <div class="field">
<label class="sr-only" for="inlineFormInput">Title</label> <label class="label">Title</label>
<input type="text" class="form-control mb-2 mr-sm-2 mb-sm-0" id="inlineFormInput" placeholder="Title" name="title"> <div class="control">
<input class="input" type="text" placeholder="So there I was..." name="title">
</div> </div>
<div class="form-group">
<label class="sr-only" for="inlineFormInput">Text</label>
<input type="text" class="form-control mb-2 mr-sm-2 mb-sm-0" id="inlineFormInput" placeholder="Text (optional)" name="text">
</div> </div>
<div class="form-group"> <div class="field">
<input type=file name=file> <label class="label">Text</label>
<div class="control">
<textarea class="textarea" placeholder="balls deep in a jar of mayonnaise" name="text"></textarea>
</div>
</div>
<div id="file-js-example" class="file has-name">
<label class="file-label">
<input class="file-input" type="file" name="file">
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
<span class="file-label">
Upload a spicy meme
</span>
</span>
<span class="file-name">
No file uploaded
</span>
</label>
</div>
<div class="field mt-4">
<div class="control">
<label class="checkbox">
<input type="checkbox">
I agree to be hella based
</label>
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link">Submit</button>
</div> </div>
<div class="form-group">
<button type="submit" class="btn btn-success">Submit</button>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<script>
const fileInput = document.querySelector('#file-js-example input[type=file]');
fileInput.onchange = () => {
if (fileInput.files.length > 0) {
const fileName = document.querySelector('#file-js-example .file-name');
fileName.textContent = fileInput.files[0].name;
}
}
</script>
{% endblock %} {% endblock %}

View file

@ -21,7 +21,7 @@ def moderator_required(f):
return f(*args, **kwargs) return f(*args, **kwargs)
else: else:
flash("You are not a moderator", "is-warning") flash("You are not a moderator", "is-warning")
return redirect(url_for("index")) return redirect(url_for("main.index"))
return decorated_function return decorated_function
def profile_required(f): def profile_required(f):