forked from MarseyWorld/MarseyWorld
add form to submit banners and sidebar images
parent
b781bbeced
commit
5b9dd4a8c4
|
@ -28,8 +28,10 @@ RUN mkdir /temp_songs
|
||||||
RUN mkdir /videos
|
RUN mkdir /videos
|
||||||
RUN mkdir /audio
|
RUN mkdir /audio
|
||||||
RUN mkdir /asset_submissions
|
RUN mkdir /asset_submissions
|
||||||
|
RUN mkdir /asset_submissions/art
|
||||||
RUN mkdir /asset_submissions/emojis
|
RUN mkdir /asset_submissions/emojis
|
||||||
RUN mkdir /asset_submissions/hats
|
RUN mkdir /asset_submissions/hats
|
||||||
|
RUN mkdir /asset_submissions/art/original
|
||||||
RUN mkdir /asset_submissions/emojis/original
|
RUN mkdir /asset_submissions/emojis/original
|
||||||
RUN mkdir /asset_submissions/hats/original
|
RUN mkdir /asset_submissions/hats/original
|
||||||
RUN mkdir /var/log/rdrama
|
RUN mkdir /var/log/rdrama
|
||||||
|
|
|
@ -173,7 +173,7 @@
|
||||||
.fa-bell-slash:before{content:"\f1f6"}
|
.fa-bell-slash:before{content:"\f1f6"}
|
||||||
.fa-chart-network:before{content:"\f78a"}
|
.fa-chart-network:before{content:"\f78a"}
|
||||||
.fa-square-share-nodes:before{content:"\f1e1"}
|
.fa-square-share-nodes:before{content:"\f1e1"}
|
||||||
.fa-sidebar:before{content:"\e24e"}
|
.fa-sidebar-flip:before{content:"\e24f"}
|
||||||
.fa-landscape:before{content:"\e1b5"}
|
.fa-landscape:before{content:"\e1b5"}
|
||||||
.fa-external-link:before{content:"\f08e"}
|
.fa-external-link:before{content:"\f08e"}
|
||||||
.fa-circle-info:before{content:"\f05a"}
|
.fa-circle-info:before{content:"\f05a"}
|
||||||
|
@ -3690,7 +3690,7 @@ ol > li::before {
|
||||||
}
|
}
|
||||||
.settings-nav > .nav-item > .nav-link.active, .settings-nav > .nav-item > .nav-link:focus, .settings-nav > .nav-item > .nav-link:hover {
|
.settings-nav > .nav-item > .nav-link.active, .settings-nav > .nav-item > .nav-link:focus, .settings-nav > .nav-item > .nav-link:hover {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
box-shadow: inset 0 -2px 0 var(--primary);
|
border-bottom: 3px solid var(--primary);
|
||||||
}
|
}
|
||||||
.post-nav .nav-link:hover, .post-nav .nav-link:focus, .settings-nav .nav-link:hover, .settings-nav .nav-link:focus {
|
.post-nav .nav-link:hover, .post-nav .nav-link:focus, .settings-nav .nav-link:hover, .settings-nav .nav-link:focus {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
|
@ -3708,7 +3708,7 @@ ol > li::before {
|
||||||
}
|
}
|
||||||
.settings-nav > .nav-item > .nav-link.active {
|
.settings-nav > .nav-item > .nav-link.active {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
box-shadow: inset 0 -2px 0 var(--primary);
|
border-bottom: 3px solid var(--primary);
|
||||||
}
|
}
|
||||||
.settings-nav > .nav-item > .nav-link.active .fa, .settings-nav > .nav-item > .nav-link.active .fas, .settings-nav > .nav-item > .nav-link.active .far, .settings-nav > .nav-item > .nav-link.active .fab {
|
.settings-nav > .nav-item > .nav-link.active .fa, .settings-nav > .nav-item > .nav-link.active .fas, .settings-nav > .nav-item > .nav-link.active .far, .settings-nav > .nav-item > .nav-link.active .fab {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
function approve_art(t, id) {
|
||||||
|
postToast(t, `/admin/approve/art/${id}`,
|
||||||
|
{
|
||||||
|
"comment": document.getElementById(`${id}-comment`).value,
|
||||||
|
"author": document.getElementById(`${id}-author`).value,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
document.getElementById(`${id}-art`).remove()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_art(t, id) {
|
||||||
|
postToast(t, `/remove/art/${id}`,
|
||||||
|
{
|
||||||
|
"comment": document.getElementById(`${id}-comment`).value,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
document.getElementById(`${id}-art`).remove()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
import time
|
||||||
|
|
||||||
|
from sqlalchemy import Column, ForeignKey
|
||||||
|
from sqlalchemy.sql.sqltypes import *
|
||||||
|
from files.classes import Base
|
||||||
|
from files.helpers.lazy import lazy
|
||||||
|
|
||||||
|
sidebar_hashes = {}
|
||||||
|
banner_hashes = {}
|
||||||
|
|
||||||
|
class ArtSubmission(Base):
|
||||||
|
__tablename__ = "art_submissions"
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
kind = Column(String)
|
||||||
|
author_id = Column(Integer, ForeignKey("users.id"))
|
||||||
|
submitter_id = Column(Integer, ForeignKey("users.id"))
|
||||||
|
created_utc = Column(Integer)
|
||||||
|
approved = Column(Boolean, default=False)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<{self.__class__.__name__}(id={self.id})>"
|
||||||
|
|
||||||
|
@property
|
||||||
|
@lazy
|
||||||
|
def badge_id(self):
|
||||||
|
return 99 if self.kind == "sidebar" else 101
|
||||||
|
|
||||||
|
@property
|
||||||
|
@lazy
|
||||||
|
def resize(self):
|
||||||
|
return 600 if self.kind == "sidebar" else 1600
|
||||||
|
|
||||||
|
@property
|
||||||
|
@lazy
|
||||||
|
def location_kind(self):
|
||||||
|
return 'sidebar' if self.kind == "sidebar" else 'banners'
|
||||||
|
|
||||||
|
@property
|
||||||
|
@lazy
|
||||||
|
def formatted_kind(self):
|
||||||
|
return 'sidebar image' if self.kind == "sidebar" else 'banner'
|
||||||
|
|
||||||
|
@property
|
||||||
|
@lazy
|
||||||
|
def msg_kind(self):
|
||||||
|
return 'Sidebar image' if self.kind == "sidebar" else 'Banner'
|
||||||
|
|
||||||
|
@property
|
||||||
|
@lazy
|
||||||
|
def hashes(self):
|
||||||
|
return sidebar_hashes if self.kind == "sidebar" else banner_hashes
|
|
@ -246,6 +246,7 @@ FEATURES = {
|
||||||
'PATRON_ICONS': False,
|
'PATRON_ICONS': False,
|
||||||
'EMOJI_SUBMISSIONS': True,
|
'EMOJI_SUBMISSIONS': True,
|
||||||
'HAT_SUBMISSIONS': True,
|
'HAT_SUBMISSIONS': True,
|
||||||
|
'ART_SUBMISSIONS': True,
|
||||||
'NSFW_MARKING': True,
|
'NSFW_MARKING': True,
|
||||||
'PING_GROUPS': True,
|
'PING_GROUPS': True,
|
||||||
'IP_LOGGING': False,
|
'IP_LOGGING': False,
|
||||||
|
@ -589,16 +590,11 @@ LOTTERY_DURATION = 60 * 60 * 24 * 7
|
||||||
|
|
||||||
BUG_THREAD = 0
|
BUG_THREAD = 0
|
||||||
|
|
||||||
SIDEBAR_THREAD = 0
|
|
||||||
BANNER_THREAD = 0
|
|
||||||
BADGE_THREAD = 0
|
BADGE_THREAD = 0
|
||||||
SNAPPY_THREAD = 0
|
SNAPPY_THREAD = 0
|
||||||
CHANGELOG_THREAD = 0
|
CHANGELOG_THREAD = 0
|
||||||
POLL_THREAD = 0
|
POLL_THREAD = 0
|
||||||
ADMIGGER_THREADS = {SIDEBAR_THREAD, BANNER_THREAD, BADGE_THREAD, SNAPPY_THREAD, CHANGELOG_THREAD, POLL_THREAD}
|
ADMIGGER_THREADS = {BADGE_THREAD, SNAPPY_THREAD, CHANGELOG_THREAD, POLL_THREAD}
|
||||||
|
|
||||||
SIDEBAR_REQUEST_THREAD = 0
|
|
||||||
BANNER_REQUEST_THREAD = 0
|
|
||||||
|
|
||||||
MAX_IMAGE_SIZE_BANNER_RESIZED_MB = 2
|
MAX_IMAGE_SIZE_BANNER_RESIZED_MB = 2
|
||||||
MAX_IMAGE_AUDIO_SIZE_MB = 8
|
MAX_IMAGE_AUDIO_SIZE_MB = 8
|
||||||
|
@ -654,16 +650,11 @@ if SITE in {'rdrama.net', 'staging.rdrama.net'}:
|
||||||
|
|
||||||
BUG_THREAD = 18459
|
BUG_THREAD = 18459
|
||||||
|
|
||||||
SIDEBAR_THREAD = 37696
|
|
||||||
BANNER_THREAD = 37697
|
|
||||||
BADGE_THREAD = 37833
|
BADGE_THREAD = 37833
|
||||||
SNAPPY_THREAD = 37749
|
SNAPPY_THREAD = 37749
|
||||||
CHANGELOG_THREAD = 165657
|
CHANGELOG_THREAD = 165657
|
||||||
POLL_THREAD = 79285
|
POLL_THREAD = 79285
|
||||||
ADMIGGER_THREADS = {SIDEBAR_THREAD, BANNER_THREAD, BADGE_THREAD, SNAPPY_THREAD, CHANGELOG_THREAD, POLL_THREAD, 166300, 187078}
|
ADMIGGER_THREADS = {BADGE_THREAD, SNAPPY_THREAD, CHANGELOG_THREAD, POLL_THREAD, 166300, 187078}
|
||||||
|
|
||||||
SIDEBAR_REQUEST_THREAD = 75878
|
|
||||||
BANNER_REQUEST_THREAD = 35835
|
|
||||||
|
|
||||||
TRUESCORE_MINIMUM = 10
|
TRUESCORE_MINIMUM = 10
|
||||||
|
|
||||||
|
@ -767,16 +758,11 @@ elif SITE in {'watchpeopledie.tv', 'marsey.world'}:
|
||||||
|
|
||||||
BUG_THREAD = 61549
|
BUG_THREAD = 61549
|
||||||
|
|
||||||
SIDEBAR_THREAD = 5403
|
|
||||||
BANNER_THREAD = 9869
|
|
||||||
BADGE_THREAD = 52519
|
BADGE_THREAD = 52519
|
||||||
SNAPPY_THREAD = 67186
|
SNAPPY_THREAD = 67186
|
||||||
CHANGELOG_THREAD = 56363
|
CHANGELOG_THREAD = 56363
|
||||||
POLL_THREAD = 22937
|
POLL_THREAD = 22937
|
||||||
ADMIGGER_THREADS = {SIDEBAR_THREAD, BANNER_THREAD, BADGE_THREAD, SNAPPY_THREAD, CHANGELOG_THREAD, POLL_THREAD, 106665}
|
ADMIGGER_THREADS = {BADGE_THREAD, SNAPPY_THREAD, CHANGELOG_THREAD, POLL_THREAD, 106665}
|
||||||
|
|
||||||
SIDEBAR_REQUEST_THREAD = 29630
|
|
||||||
BANNER_REQUEST_THREAD = 29629
|
|
||||||
|
|
||||||
MAX_VIDEO_SIZE_MB = 500
|
MAX_VIDEO_SIZE_MB = 500
|
||||||
MAX_VIDEO_SIZE_MB_PATRON = 500
|
MAX_VIDEO_SIZE_MB_PATRON = 500
|
||||||
|
|
|
@ -416,26 +416,46 @@ MODACTION_TYPES = {
|
||||||
"icon": 'fa-cat',
|
"icon": 'fa-cat',
|
||||||
"color": 'bg-success'
|
"color": 'bg-success'
|
||||||
},
|
},
|
||||||
'approve_hat': {
|
'approve_sidebar': {
|
||||||
"str": 'approved hat',
|
"str": 'approved a sidebar image made by {self.target_link}',
|
||||||
"icon": 'fa-hat-cowboy',
|
"icon": 'fa-sidebar-flip',
|
||||||
"color": 'bg-success'
|
"color": 'bg-success'
|
||||||
},
|
},
|
||||||
'reject_hat': {
|
'reject_sidebar': {
|
||||||
"str": 'rejected hat',
|
"str": 'rejected a sidebar image made by {self.target_link}',
|
||||||
"icon": 'fa-hat-cowboy',
|
"icon": 'fa-sidebar-flip',
|
||||||
|
"color": 'bg-danger'
|
||||||
|
},
|
||||||
|
'approve_banner': {
|
||||||
|
"str": 'approved a banner made by {self.target_link}',
|
||||||
|
"icon": 'fa-landscape',
|
||||||
|
"color": 'bg-success'
|
||||||
|
},
|
||||||
|
'reject_banner': {
|
||||||
|
"str": 'rejected a banner made by {self.target_link}',
|
||||||
|
"icon": 'fa-landscape',
|
||||||
"color": 'bg-danger'
|
"color": 'bg-danger'
|
||||||
},
|
},
|
||||||
'approve_emoji': {
|
'approve_emoji': {
|
||||||
"str": 'approved emoji',
|
"str": 'approved an emoji made by {self.target_link}',
|
||||||
"icon": 'fa-cat',
|
"icon": 'fa-cat',
|
||||||
"color": 'bg-success'
|
"color": 'bg-success'
|
||||||
},
|
},
|
||||||
'reject_emoji': {
|
'reject_emoji': {
|
||||||
"str": 'rejected emoji',
|
"str": 'rejected an emoji made by {self.target_link}',
|
||||||
"icon": 'fa-cat',
|
"icon": 'fa-cat',
|
||||||
"color": 'bg-danger'
|
"color": 'bg-danger'
|
||||||
},
|
},
|
||||||
|
'approve_hat': {
|
||||||
|
"str": 'approved hat made by {self.target_link}',
|
||||||
|
"icon": 'fa-hat-cowboy',
|
||||||
|
"color": 'bg-success'
|
||||||
|
},
|
||||||
|
'reject_hat': {
|
||||||
|
"str": 'rejected hat made by {self.target_link}',
|
||||||
|
"icon": 'fa-hat-cowboy',
|
||||||
|
"color": 'bg-danger'
|
||||||
|
},
|
||||||
'reset_password': {
|
'reset_password': {
|
||||||
"str": 'reset the password of {self.target_link}',
|
"str": 'reset the password of {self.target_link}',
|
||||||
"icon": 'fa-lock',
|
"icon": 'fa-lock',
|
||||||
|
|
|
@ -7,7 +7,6 @@ import json
|
||||||
import requests
|
import requests
|
||||||
import ffmpeg
|
import ffmpeg
|
||||||
import gevent
|
import gevent
|
||||||
import imagehash
|
|
||||||
from flask import abort, g, has_request_context, request
|
from flask import abort, g, has_request_context, request
|
||||||
from mimetypes import guess_extension
|
from mimetypes import guess_extension
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
@ -47,7 +46,7 @@ def media_ratelimit(v):
|
||||||
print(STARS, flush=True)
|
print(STARS, flush=True)
|
||||||
abort(500)
|
abort(500)
|
||||||
|
|
||||||
def process_files(files, v, body, is_dm=False, dm_user=None, admigger_thread=None, comment_body=None):
|
def process_files(files, v, body, is_dm=False, dm_user=None, is_badge_thread=False, comment_body=None):
|
||||||
if g.is_tor or not files.get("file"): return body
|
if g.is_tor or not files.get("file"): return body
|
||||||
files = files.getlist('file')[:20]
|
files = files.getlist('file')[:20]
|
||||||
|
|
||||||
|
@ -63,8 +62,8 @@ def process_files(files, v, body, is_dm=False, dm_user=None, admigger_thread=Non
|
||||||
name = f'/images/{time.time()}'.replace('.','') + '.webp'
|
name = f'/images/{time.time()}'.replace('.','') + '.webp'
|
||||||
file.save(name)
|
file.save(name)
|
||||||
url = process_image(name, v)
|
url = process_image(name, v)
|
||||||
if admigger_thread:
|
if is_badge_thread:
|
||||||
process_admigger_entry(name, v, admigger_thread, comment_body)
|
process_badge_entry(name, v, comment_body)
|
||||||
elif file.content_type.startswith('video/'):
|
elif file.content_type.startswith('video/'):
|
||||||
url = process_video(file, v)
|
url = process_video(file, v)
|
||||||
elif file.content_type.startswith('audio/'):
|
elif file.content_type.startswith('audio/'):
|
||||||
|
@ -257,30 +256,6 @@ def process_image(filename, v, resize=0, trim=False, uploader_id=None):
|
||||||
abort(413, f"Max size for site assets is {MAX_IMAGE_SIZE_BANNER_RESIZED_MB} MB")
|
abort(413, f"Max size for site assets is {MAX_IMAGE_SIZE_BANNER_RESIZED_MB} MB")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if filename.startswith('files/assets/images/'):
|
|
||||||
path = filename.rsplit('/', 1)[0]
|
|
||||||
kind = path.split('/')[-1]
|
|
||||||
|
|
||||||
if kind in {'banners','sidebar'}:
|
|
||||||
hashes = {}
|
|
||||||
|
|
||||||
for img in os.listdir(path):
|
|
||||||
img_path = f'{path}/{img}'
|
|
||||||
if img_path == filename: continue
|
|
||||||
|
|
||||||
with Image.open(img_path) as i:
|
|
||||||
i_hash = str(imagehash.phash(i))
|
|
||||||
|
|
||||||
if i_hash not in hashes.keys():
|
|
||||||
hashes[i_hash] = img_path
|
|
||||||
|
|
||||||
with Image.open(filename) as i:
|
|
||||||
i_hash = str(imagehash.phash(i))
|
|
||||||
|
|
||||||
if i_hash in hashes.keys():
|
|
||||||
os.remove(filename)
|
|
||||||
return None
|
|
||||||
|
|
||||||
media = g.db.query(Media).filter_by(filename=filename, kind='image').one_or_none()
|
media = g.db.query(Media).filter_by(filename=filename, kind='image').one_or_none()
|
||||||
if media: g.db.delete(media)
|
if media: g.db.delete(media)
|
||||||
|
|
||||||
|
@ -306,41 +281,27 @@ def send_file(filename):
|
||||||
rclone.copy(filename, 'no:/videos', ignore_existing=True, show_progress=False)
|
rclone.copy(filename, 'no:/videos', ignore_existing=True, show_progress=False)
|
||||||
|
|
||||||
|
|
||||||
def process_sidebar_or_banner(oldname, v, type, resize):
|
def process_badge_entry(oldname, v, comment_body):
|
||||||
li = sorted(os.listdir(f'files/assets/images/{SITE_NAME}/{type}'),
|
try:
|
||||||
key=lambda e: int(e.split('.webp')[0]))[-1]
|
json_body = '{' + comment_body.split('{')[1].split('}')[0] + '}'
|
||||||
num = int(li.split('.webp')[0]) + 1
|
badge_def = json.loads(json_body)
|
||||||
filename = f'files/assets/images/{SITE_NAME}/{type}/{num}.webp'
|
name = badge_def["name"]
|
||||||
copyfile(oldname, filename)
|
|
||||||
process_image(filename, v, resize=resize)
|
|
||||||
|
|
||||||
def process_admigger_entry(oldname, v, admigger_thread, comment_body):
|
if len(name) > 50:
|
||||||
if admigger_thread == SIDEBAR_THREAD:
|
abort(400, "Badge name is too long (max 50 characters)")
|
||||||
process_sidebar_or_banner(oldname, v, 'sidebar', 600)
|
|
||||||
elif admigger_thread == BANNER_THREAD:
|
|
||||||
banner_width = 1600
|
|
||||||
process_sidebar_or_banner(oldname, v, 'banners', banner_width)
|
|
||||||
elif admigger_thread == BADGE_THREAD:
|
|
||||||
try:
|
|
||||||
json_body = '{' + comment_body.split('{')[1].split('}')[0] + '}'
|
|
||||||
badge_def = json.loads(json_body)
|
|
||||||
name = badge_def["name"]
|
|
||||||
|
|
||||||
if len(name) > 50:
|
if not badge_name_regex.fullmatch(name):
|
||||||
abort(400, "Badge name is too long (max 50 characters)")
|
abort(400, "Invalid badge name!")
|
||||||
|
|
||||||
if not badge_name_regex.fullmatch(name):
|
existing = g.db.query(BadgeDef).filter_by(name=name).one_or_none()
|
||||||
abort(400, "Invalid badge name!")
|
if existing: abort(409, "A badge with this name already exists!")
|
||||||
|
|
||||||
existing = g.db.query(BadgeDef).filter_by(name=name).one_or_none()
|
badge = BadgeDef(name=name, description=badge_def["description"])
|
||||||
if existing: abort(409, "A badge with this name already exists!")
|
g.db.add(badge)
|
||||||
|
g.db.flush()
|
||||||
badge = BadgeDef(name=name, description=badge_def["description"])
|
filename = f'files/assets/images/{SITE_NAME}/badges/{badge.id}.webp'
|
||||||
g.db.add(badge)
|
copyfile(oldname, filename)
|
||||||
g.db.flush()
|
process_image(filename, v, resize=300, trim=True)
|
||||||
filename = f'files/assets/images/{SITE_NAME}/badges/{badge.id}.webp'
|
purge_files_in_cloudflare_cache(f"{SITE_FULL_IMAGES}/i/{SITE_NAME}/badges/{badge.id}.webp")
|
||||||
copyfile(oldname, filename)
|
except Exception as e:
|
||||||
process_image(filename, v, resize=300, trim=True)
|
abort(400, str(e))
|
||||||
purge_files_in_cloudflare_cache(f"{SITE_FULL_IMAGES}/i/{SITE_NAME}/badges/{badge.id}.webp")
|
|
||||||
except Exception as e:
|
|
||||||
abort(400, str(e))
|
|
||||||
|
|
|
@ -47,6 +47,8 @@ if FEATURES['HATS']:
|
||||||
from .hats import *
|
from .hats import *
|
||||||
if FEATURES['EMOJI_SUBMISSIONS'] or FEATURES['HAT_SUBMISSIONS']:
|
if FEATURES['EMOJI_SUBMISSIONS'] or FEATURES['HAT_SUBMISSIONS']:
|
||||||
from .asset_submissions import *
|
from .asset_submissions import *
|
||||||
|
if FEATURES['ART_SUBMISSIONS']:
|
||||||
|
from .art_submissions import *
|
||||||
from .special import *
|
from .special import *
|
||||||
from .push_notifs import *
|
from .push_notifs import *
|
||||||
if FEATURES['PING_GROUPS']:
|
if FEATURES['PING_GROUPS']:
|
||||||
|
|
|
@ -0,0 +1,190 @@
|
||||||
|
from shutil import copyfile, move
|
||||||
|
import imagehash
|
||||||
|
|
||||||
|
from files.classes.art_submissions import *
|
||||||
|
from files.helpers.config.const import *
|
||||||
|
from files.helpers.media import *
|
||||||
|
from files.helpers.useractions import badge_grant
|
||||||
|
from files.routes.wrappers import *
|
||||||
|
from files.__main__ import app, limiter
|
||||||
|
|
||||||
|
@app.get("/submit/sidebar")
|
||||||
|
@app.get("/submit/banners")
|
||||||
|
@feature_required('ART_SUBMISSIONS')
|
||||||
|
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
|
||||||
|
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
|
||||||
|
@auth_required
|
||||||
|
def submit_art(v):
|
||||||
|
kind = request.path.split('/')[-1].rstrip('s')
|
||||||
|
|
||||||
|
entries = g.db.query(ArtSubmission).filter(
|
||||||
|
ArtSubmission.kind == kind,
|
||||||
|
ArtSubmission.approved == False,
|
||||||
|
).order_by(ArtSubmission.id.desc()).all()
|
||||||
|
|
||||||
|
for entry in entries:
|
||||||
|
entry.author = g.db.query(User.username).filter_by(id=entry.author_id).one()[0]
|
||||||
|
entry.submitter = g.db.query(User.username).filter_by(id=entry.submitter_id).one()[0]
|
||||||
|
|
||||||
|
return render_template("submit_art.html", v=v, entries=entries, kind=kind.title())
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/submit/art")
|
||||||
|
@feature_required('ART_SUBMISSIONS')
|
||||||
|
@limiter.limit('1/second', scope=rpath)
|
||||||
|
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
|
||||||
|
@limiter.limit("20/day", deduct_when=lambda response: response.status_code < 400)
|
||||||
|
@limiter.limit("20/day", deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
|
||||||
|
@auth_required
|
||||||
|
def submit_art_post(v):
|
||||||
|
if g.is_tor:
|
||||||
|
abort(400, "File uploads are not allowed through TOR!")
|
||||||
|
|
||||||
|
file = request.files["image"]
|
||||||
|
if not file or not file.content_type.startswith('image/'):
|
||||||
|
abort(400, "You need to submit an image!")
|
||||||
|
|
||||||
|
username = request.values.get('author', '').lower().strip()
|
||||||
|
author = get_user(username, v=v)
|
||||||
|
|
||||||
|
kind = request.values.get('kind', '').strip()
|
||||||
|
if kind not in {"sidebar", "banner"}:
|
||||||
|
abort(400, "Invalid kind!")
|
||||||
|
|
||||||
|
print(kind, flush=True)
|
||||||
|
entry = ArtSubmission(
|
||||||
|
kind=kind,
|
||||||
|
author_id=author.id,
|
||||||
|
submitter_id=v.id,
|
||||||
|
)
|
||||||
|
g.db.add(entry)
|
||||||
|
g.db.flush()
|
||||||
|
|
||||||
|
highquality = f'/asset_submissions/art/{entry.id}.webp'
|
||||||
|
file.save(highquality)
|
||||||
|
process_image(highquality, v) #to ensure not malware
|
||||||
|
|
||||||
|
path = f"files/assets/images/{SITE_NAME}/{entry.location_kind}"
|
||||||
|
if not entry.hashes:
|
||||||
|
for img in os.listdir(path):
|
||||||
|
img_path = f'{path}/{img}'
|
||||||
|
with Image.open(img_path) as i:
|
||||||
|
i_hash = str(imagehash.phash(i))
|
||||||
|
if i_hash not in entry.hashes.keys():
|
||||||
|
entry.hashes[i_hash] = img_path
|
||||||
|
|
||||||
|
with Image.open(highquality) as i:
|
||||||
|
i_hash = str(imagehash.phash(i))
|
||||||
|
if i_hash in entry.hashes.keys():
|
||||||
|
os.remove(highquality)
|
||||||
|
abort(400, f"Image already exists as a {entry.formatted_kind}!")
|
||||||
|
|
||||||
|
|
||||||
|
return {"message": f"{entry.msg_kind} submitted successfully!"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/admin/approve/art/<int:id>")
|
||||||
|
@feature_required('ART_SUBMISSIONS')
|
||||||
|
@limiter.limit('1/second', scope=rpath)
|
||||||
|
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
|
||||||
|
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
|
||||||
|
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
|
||||||
|
@admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'])
|
||||||
|
def approve_art(v, id):
|
||||||
|
entry = g.db.get(ArtSubmission, id)
|
||||||
|
if not entry:
|
||||||
|
abort(404, "Art submission not found!")
|
||||||
|
|
||||||
|
old = f'/asset_submissions/art/{entry.id}.webp'
|
||||||
|
copyfile(old, f"/asset_submissions/art/original/{entry.id}.webp")
|
||||||
|
|
||||||
|
filename = f"files/assets/images/{SITE_NAME}/{entry.location_kind}/{entry.id}.webp"
|
||||||
|
move(old, filename)
|
||||||
|
process_image(filename, v, resize=entry.resize, trim=True)
|
||||||
|
|
||||||
|
entry_url = filename.replace('files', SITE_FULL_IMAGES)
|
||||||
|
|
||||||
|
|
||||||
|
author = request.values.get('author').strip()
|
||||||
|
author = get_user(author)
|
||||||
|
entry.author_id = author.id
|
||||||
|
g.db.add(entry)
|
||||||
|
badge_grant(author, entry.badge_id)
|
||||||
|
|
||||||
|
|
||||||
|
if v.id != author.id:
|
||||||
|
msg = f"@{v.username} (a site admin) has approved a {entry.formatted_kind} you made:\n{entry_url}"
|
||||||
|
|
||||||
|
comment = request.values.get("comment")
|
||||||
|
if comment:
|
||||||
|
msg += f"\nComment: `{comment}`"
|
||||||
|
|
||||||
|
send_repeatable_notification(author.id, msg)
|
||||||
|
|
||||||
|
if v.id != entry.submitter_id and author.id != entry.submitter_id:
|
||||||
|
msg = f"@{v.username} (a site admin) has approved a {entry.formatted_kind} you submitted:\n{entry_url}"
|
||||||
|
|
||||||
|
comment = request.values.get("comment")
|
||||||
|
if comment:
|
||||||
|
msg += f"\nComment: `{comment}`"
|
||||||
|
|
||||||
|
send_repeatable_notification(entry.submitter_id, msg)
|
||||||
|
|
||||||
|
|
||||||
|
note = entry_url
|
||||||
|
if comment:
|
||||||
|
note += f' - Comment: "{comment}"'
|
||||||
|
|
||||||
|
ma = ModAction(
|
||||||
|
kind=f"approve_{entry.kind}",
|
||||||
|
user_id=v.id,
|
||||||
|
target_user_id=entry.author_id,
|
||||||
|
_note=filter_emojis_only(note, link=True),
|
||||||
|
)
|
||||||
|
g.db.add(ma)
|
||||||
|
|
||||||
|
|
||||||
|
return {"message": f"{entry.msg_kind} approved!"}
|
||||||
|
|
||||||
|
@app.post("/remove/art/<int:id>")
|
||||||
|
@feature_required('ART_SUBMISSIONS')
|
||||||
|
@limiter.limit('1/second', scope=rpath)
|
||||||
|
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
|
||||||
|
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
|
||||||
|
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
|
||||||
|
@auth_required
|
||||||
|
def remove_art(v, id):
|
||||||
|
entry = g.db.get(ArtSubmission, id)
|
||||||
|
if not entry:
|
||||||
|
abort(404, "Art submission not found!")
|
||||||
|
|
||||||
|
if v.id != entry.submitter_id and v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS']:
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
if v.id != entry.submitter_id:
|
||||||
|
entry_url = f'{SITE_FULL_IMAGES}/asset_submissions/art/{entry.id}.webp'
|
||||||
|
msg = f"@{v.username} (a site admin) has rejected a {entry.formatted_kind} you submitted:\n{entry_url}"
|
||||||
|
|
||||||
|
comment = request.values.get("comment")
|
||||||
|
if comment:
|
||||||
|
msg += f"\nComment: `{comment}`"
|
||||||
|
|
||||||
|
send_repeatable_notification(entry.submitter_id, msg)
|
||||||
|
|
||||||
|
note = entry_url
|
||||||
|
if comment:
|
||||||
|
note += f' - Comment: "{comment}"'
|
||||||
|
|
||||||
|
ma = ModAction(
|
||||||
|
kind=f"reject_{entry.kind}",
|
||||||
|
user_id=v.id,
|
||||||
|
target_user_id=entry.author_id,
|
||||||
|
_note=filter_emojis_only(note, link=True),
|
||||||
|
)
|
||||||
|
g.db.add(ma)
|
||||||
|
|
||||||
|
os.remove(f'/asset_submissions/art/{entry.id}.webp')
|
||||||
|
msg = f"{entry.msg_kind} removed!"
|
||||||
|
g.db.delete(entry)
|
||||||
|
|
||||||
|
return {"message": msg}
|
|
@ -248,10 +248,16 @@ def approve_emoji(v, name):
|
||||||
|
|
||||||
emoji.submitter_id = None
|
emoji.submitter_id = None
|
||||||
|
|
||||||
|
|
||||||
|
note = f':{emoji.name}:'
|
||||||
|
if comment:
|
||||||
|
note += f' - Comment: "{comment}"'
|
||||||
|
|
||||||
ma = ModAction(
|
ma = ModAction(
|
||||||
kind="approve_emoji",
|
kind="approve_emoji",
|
||||||
user_id=v.id,
|
user_id=v.id,
|
||||||
_note=f'<img loading="lazy" data-bs-toggle="tooltip" alt=":{emoji.name}:" title=":{emoji.name}:" src="{SITE_FULL_IMAGES}/e/{emoji.name}.webp">'
|
target_user_id=emoji.author_id,
|
||||||
|
_note=filter_emojis_only(note, link=True),
|
||||||
)
|
)
|
||||||
g.db.add(ma)
|
g.db.add(ma)
|
||||||
|
|
||||||
|
@ -300,10 +306,15 @@ def remove_asset(cls, type_name, v, name):
|
||||||
|
|
||||||
send_repeatable_notification(asset.submitter_id, msg)
|
send_repeatable_notification(asset.submitter_id, msg)
|
||||||
|
|
||||||
|
note = name
|
||||||
|
if comment:
|
||||||
|
note += f' - Comment: "{comment}"'
|
||||||
|
|
||||||
ma = ModAction(
|
ma = ModAction(
|
||||||
kind=f"reject_{type_name}",
|
kind=f"reject_{type_name}",
|
||||||
user_id=v.id,
|
user_id=v.id,
|
||||||
_note=name
|
target_user_id=asset.author_id,
|
||||||
|
_note=filter_emojis_only(note, link=True),
|
||||||
)
|
)
|
||||||
g.db.add(ma)
|
g.db.add(ma)
|
||||||
|
|
||||||
|
@ -464,10 +475,15 @@ def approve_hat(v, name):
|
||||||
new_path = f'/asset_submissions/hats/original/{hat.name}.{i.format.lower()}'
|
new_path = f'/asset_submissions/hats/original/{hat.name}.{i.format.lower()}'
|
||||||
rename(highquality, new_path)
|
rename(highquality, new_path)
|
||||||
|
|
||||||
|
note = f'[{hat.name}]({SITE_FULL_IMAGES}/i/hats/{hat.name}.webp)'
|
||||||
|
if comment:
|
||||||
|
note += f' - Comment: "{comment}"'
|
||||||
|
|
||||||
ma = ModAction(
|
ma = ModAction(
|
||||||
kind="approve_hat",
|
kind="approve_hat",
|
||||||
user_id=v.id,
|
user_id=v.id,
|
||||||
_note=f'<a href="{SITE_FULL_IMAGES}/i/hats/{hat.name}.webp">{hat.name}</a>'
|
target_user_id=hat.author_id,
|
||||||
|
_note=filter_emojis_only(note, link=True),
|
||||||
)
|
)
|
||||||
g.db.add(ma)
|
g.db.add(ma)
|
||||||
|
|
||||||
|
|
|
@ -181,14 +181,14 @@ def comment(v):
|
||||||
if parent_user.has_blocked(v) or parent_user.has_muted(v):
|
if parent_user.has_blocked(v) or parent_user.has_muted(v):
|
||||||
notify_op = False
|
notify_op = False
|
||||||
|
|
||||||
if posting_to_post and v.admin_level >= PERMS['USE_ADMIGGER_THREADS'] and post_target.id in {SIDEBAR_THREAD, BANNER_THREAD, BADGE_THREAD}:
|
if posting_to_post and v.admin_level >= PERMS['USE_ADMIGGER_THREADS'] and post_target.id == BADGE_THREAD:
|
||||||
admigger_thread = post_target.id
|
is_badge_thread = True
|
||||||
comment_body = body
|
comment_body = body
|
||||||
else:
|
else:
|
||||||
admigger_thread = None
|
is_badge_thread = False
|
||||||
comment_body = None
|
comment_body = None
|
||||||
|
|
||||||
body = process_files(request.files, v, body, admigger_thread=admigger_thread, comment_body=comment_body)
|
body = process_files(request.files, v, body, is_badge_thread=is_badge_thread, comment_body=comment_body)
|
||||||
if len(body) > COMMENT_BODY_LENGTH_LIMIT:
|
if len(body) > COMMENT_BODY_LENGTH_LIMIT:
|
||||||
abort(400, f'Comment body is too long (max {COMMENT_BODY_LENGTH_LIMIT} characters)')
|
abort(400, f'Comment body is too long (max {COMMENT_BODY_LENGTH_LIMIT} characters)')
|
||||||
|
|
||||||
|
|
|
@ -164,8 +164,7 @@ def inject_constants():
|
||||||
"DEFAULT_THEME":DEFAULT_THEME, "DESCRIPTION":DESCRIPTION,
|
"DEFAULT_THEME":DEFAULT_THEME, "DESCRIPTION":DESCRIPTION,
|
||||||
"has_sidebar":has_sidebar, "has_logo":has_logo,
|
"has_sidebar":has_sidebar, "has_logo":has_logo,
|
||||||
"patron":patron, "get_setting": get_setting,
|
"patron":patron, "get_setting": get_setting,
|
||||||
"SIDEBAR_THREAD":SIDEBAR_THREAD, "BANNER_THREAD":BANNER_THREAD, "BUG_THREAD":BUG_THREAD,
|
"BUG_THREAD":BUG_THREAD, "BADGE_THREAD":BADGE_THREAD, "SNAPPY_THREAD":SNAPPY_THREAD, "CHANGELOG_THREAD":CHANGELOG_THREAD,
|
||||||
"BADGE_THREAD":BADGE_THREAD, "SNAPPY_THREAD":SNAPPY_THREAD, "CHANGELOG_THREAD":CHANGELOG_THREAD,
|
|
||||||
"approved_embed_hosts":approved_embed_hosts, "POST_BODY_LENGTH_LIMIT":POST_BODY_LENGTH_LIMIT,
|
"approved_embed_hosts":approved_embed_hosts, "POST_BODY_LENGTH_LIMIT":POST_BODY_LENGTH_LIMIT,
|
||||||
"SITE_SETTINGS":get_settings(), "EMAIL":EMAIL, "max": max, "min": min, "can_see":can_see,
|
"SITE_SETTINGS":get_settings(), "EMAIL":EMAIL, "max": max, "min": min, "can_see":can_see,
|
||||||
"TELEGRAM_ID":TELEGRAM_ID, "TWITTER_ID":TWITTER_ID, "TRUESCORE_MINIMUM":TRUESCORE_MINIMUM, "TRUESCORE_DONATE_MINIMUM":TRUESCORE_DONATE_MINIMUM,
|
"TELEGRAM_ID":TELEGRAM_ID, "TWITTER_ID":TWITTER_ID, "TRUESCORE_MINIMUM":TRUESCORE_MINIMUM, "TRUESCORE_DONATE_MINIMUM":TRUESCORE_DONATE_MINIMUM,
|
||||||
|
@ -185,6 +184,6 @@ def inject_constants():
|
||||||
"MAX_VIDEO_SIZE_MB":MAX_VIDEO_SIZE_MB, "MAX_VIDEO_SIZE_MB_PATRON":MAX_VIDEO_SIZE_MB_PATRON,
|
"MAX_VIDEO_SIZE_MB":MAX_VIDEO_SIZE_MB, "MAX_VIDEO_SIZE_MB_PATRON":MAX_VIDEO_SIZE_MB_PATRON,
|
||||||
"CURSORMARSEY_DEFAULT":CURSORMARSEY_DEFAULT, "SNAPPY_ID":SNAPPY_ID, "ZOZBOT_ID":ZOZBOT_ID, "get_running_orgy":get_running_orgy,
|
"CURSORMARSEY_DEFAULT":CURSORMARSEY_DEFAULT, "SNAPPY_ID":SNAPPY_ID, "ZOZBOT_ID":ZOZBOT_ID, "get_running_orgy":get_running_orgy,
|
||||||
"bar_position":bar_position, "datetime":datetime, "CSS_LENGTH_LIMIT":CSS_LENGTH_LIMIT, "cache":cache, "emoji_count":emoji_count, "HOLE_SIDEBAR_COLUMN_LENGTH":HOLE_SIDEBAR_COLUMN_LENGTH, "HOLE_SNAPPY_QUOTES_LENGTH":HOLE_SNAPPY_QUOTES_LENGTH,
|
"bar_position":bar_position, "datetime":datetime, "CSS_LENGTH_LIMIT":CSS_LENGTH_LIMIT, "cache":cache, "emoji_count":emoji_count, "HOLE_SIDEBAR_COLUMN_LENGTH":HOLE_SIDEBAR_COLUMN_LENGTH, "HOLE_SNAPPY_QUOTES_LENGTH":HOLE_SNAPPY_QUOTES_LENGTH,
|
||||||
"SIDEBAR_REQUEST_THREAD":SIDEBAR_REQUEST_THREAD, "BANNER_REQUEST_THREAD":BANNER_REQUEST_THREAD, "top_poster_of_the_day":top_poster_of_the_day,
|
"top_poster_of_the_day":top_poster_of_the_day,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,16 +18,10 @@
|
||||||
</ul>
|
</ul>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{% if v.admin_level >= PERMS['USE_ADMIGGER_THREADS'] and (SITE_NAME == 'rDrama' or SIDEBAR_THREAD or BANNER_THREAD or BADGE_THREAD or SNAPPY_THREAD) %}
|
{% if v.admin_level >= PERMS['USE_ADMIGGER_THREADS'] and (SITE_NAME == 'rDrama' or BADGE_THREAD or SNAPPY_THREAD) %}
|
||||||
<h4>Add Stuff</h4>
|
<h4>Add Stuff</h4>
|
||||||
<ul>
|
<ul>
|
||||||
{% if v.admin_level >= PERMS['USE_ADMIGGER_THREADS'] %}
|
{% if v.admin_level >= PERMS['USE_ADMIGGER_THREADS'] %}
|
||||||
{% if SIDEBAR_THREAD %}
|
|
||||||
<li><a href="/post/{{SIDEBAR_THREAD}}">Add Sidebar Images</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if BANNER_THREAD %}
|
|
||||||
<li><a href="/post/{{BANNER_THREAD}}">Add Banners</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if BADGE_THREAD %}
|
{% if BADGE_THREAD %}
|
||||||
<li><a href="/post/{{BADGE_THREAD}}">Add Badges</a></li>
|
<li><a href="/post/{{BADGE_THREAD}}">Add Badges</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -37,6 +31,10 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if v.admin_level >= PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}
|
{% if v.admin_level >= PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}
|
||||||
|
{% if FEATURES['ART_SUBMISSIONS'] -%}
|
||||||
|
<li><a href="/submit/sidebar">Approve or Reject Sidebar Images</a></li>
|
||||||
|
<li><a href="/submit/banners">Approve or Reject Banners</a></li>
|
||||||
|
{% endif %}
|
||||||
{% if FEATURES['EMOJI_SUBMISSIONS'] -%}
|
{% if FEATURES['EMOJI_SUBMISSIONS'] -%}
|
||||||
<li><a href="/submit/emojis">Approve or Reject Emojis</a></li>
|
<li><a href="/submit/emojis">Approve or Reject Emojis</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -3,103 +3,98 @@
|
||||||
{% block pagetype %}directory{% endblock %}
|
{% block pagetype %}directory{% endblock %}
|
||||||
|
|
||||||
{%- set DIRECTORY = [
|
{%- set DIRECTORY = [
|
||||||
(
|
(
|
||||||
'Donate',
|
'Donate',
|
||||||
'',
|
'',
|
||||||
'fa-dollar-sign', 'green',
|
'fa-dollar-sign', 'green',
|
||||||
'/donate',
|
'/donate',
|
||||||
),
|
True,
|
||||||
(
|
),
|
||||||
'Bugs / Suggestions',
|
(
|
||||||
'Something broken? Improvements?',
|
'Bugs / Suggestions',
|
||||||
'fa-bug', '#6b8e23',
|
'Something broken? Improvements?',
|
||||||
'/post/' ~ BUG_THREAD,
|
'fa-bug', '#6b8e23',
|
||||||
),
|
'/post/' ~ BUG_THREAD,
|
||||||
(
|
BUG_THREAD,
|
||||||
'Changelog',
|
),
|
||||||
'Log of all site changes.',
|
(
|
||||||
'fa-clipboard', '#ffffff',
|
'Changelog',
|
||||||
'/post/' ~ CHANGELOG_THREAD,
|
'Log of all site changes.',
|
||||||
),
|
'fa-clipboard', '#ffffff',
|
||||||
(
|
'/post/' ~ CHANGELOG_THREAD,
|
||||||
'Submit Emojis',
|
CHANGELOG_THREAD,
|
||||||
'Submit new emojis for the site.',
|
),
|
||||||
'fa-smile-beam', '#fec83c',
|
(
|
||||||
'/submit/emojis',
|
'Submit Sidebar Art',
|
||||||
),
|
'Original ' ~ SITE_NAME ~ '-themed works.',
|
||||||
(
|
'fa-sidebar-flip', '#f5fffa',
|
||||||
'Submit Sidebar Art',
|
'/submit/sidebar',
|
||||||
'Original ' ~ SITE_NAME ~ '-themed works.',
|
FEATURES['ART_SUBMISSIONS'],
|
||||||
'fa-sidebar', '#f5fffa',
|
),
|
||||||
'/post/' ~ SIDEBAR_REQUEST_THREAD,
|
(
|
||||||
),
|
'Submit Banner Art',
|
||||||
(
|
'Original ' ~ SITE_NAME ~ '-themed works.',
|
||||||
'Submit Banner Art',
|
'fa-landscape', '#87cefa',
|
||||||
'Original ' ~ SITE_NAME ~ '-themed works.',
|
'/submit/banners',
|
||||||
'fa-landscape', '#87cefa',
|
FEATURES['ART_SUBMISSIONS'],
|
||||||
'/post/' ~ BANNER_REQUEST_THREAD,
|
),
|
||||||
),
|
(
|
||||||
(
|
'Submit Emojis',
|
||||||
'View All Emojis',
|
'Submit new emojis for the site.',
|
||||||
'',
|
'fa-smile-beam', '#fec83c',
|
||||||
'fa-smile-beam', '#fec83c',
|
'/submit/emojis',
|
||||||
'/emojis/marsey',
|
FEATURES['EMOJI_SUBMISSIONS'],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'View All Sidebar Pictures',
|
'Submit Hats',
|
||||||
'',
|
'Submit a Hat to be added.',
|
||||||
'fa-sidebar', '#f5fffa',
|
'fa-hat-cowboy', '#7c603e',
|
||||||
'/post/' ~ SIDEBAR_THREAD,
|
'/submit/hats',
|
||||||
),
|
FEATURES['HAT_SUBMISSIONS'],
|
||||||
(
|
),
|
||||||
'View All Banner Pictures',
|
(
|
||||||
'',
|
'Submit Snappy Quotes',
|
||||||
'fa-landscape', '#87cefa',
|
'Sentient.',
|
||||||
'/post/' ~ BANNER_THREAD,
|
'fa-robot', '#adff2f',
|
||||||
),
|
'/post/33652',
|
||||||
(
|
SITE == 'rdrama.net',
|
||||||
'View All Snappy Quotes',
|
),
|
||||||
'',
|
(
|
||||||
'fa-robot', '#adff2f',
|
'Update Emojis or Hats',
|
||||||
'/post/' ~ SNAPPY_THREAD,
|
'Ask for an emoji or a hat to be updated with a better version or for emoji tags to be changed.',
|
||||||
),
|
'fa-smile-beam', '#fec83c',
|
||||||
] -%}
|
'/post/103085',
|
||||||
|
SITE == 'rdrama.net',
|
||||||
|
),
|
||||||
{%- if SITE_NAME == 'rDrama' -%}
|
(
|
||||||
{%- do DIRECTORY.extend([
|
'View All Emojis',
|
||||||
(
|
'',
|
||||||
'Submit Hats',
|
'fa-smile-beam', '#fec83c',
|
||||||
'Submit a Hat to be added.',
|
'/emojis/marsey',
|
||||||
'fa-hat-cowboy', '#7c603e',
|
True,
|
||||||
'/submit/hats',
|
),
|
||||||
),
|
(
|
||||||
(
|
'View All Snappy Quotes',
|
||||||
'Update Emojis or Hats',
|
'',
|
||||||
'Ask for an emoji or a hat to be updated with a better version or for emoji tags to be changed.',
|
'fa-robot', '#adff2f',
|
||||||
'fa-smile-beam', '#fec83c',
|
'/post/' ~ SNAPPY_THREAD,
|
||||||
'/post/103085',
|
SNAPPY_THREAD,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'Submit Snappy Quotes',
|
'Emoji Commissions',
|
||||||
'Sentient.',
|
'Request an Emoji to be made.',
|
||||||
'fa-robot', '#adff2f',
|
'fa-coins', '#ffd700',
|
||||||
'/post/33652',
|
'/post/37677',
|
||||||
),
|
SITE == 'rdrama.net',
|
||||||
(
|
),
|
||||||
'Emoji Commissions',
|
(
|
||||||
'Request an Emoji to be made.',
|
'Reportmaxxxing Bounties',
|
||||||
'fa-coins', '#ffd700',
|
'Request a redditor be banned.',
|
||||||
'/post/37677',
|
'fa-gavel', '#dc3545',
|
||||||
),
|
'/post/215970',
|
||||||
(
|
SITE == 'rdrama.net',
|
||||||
'Reportmaxxxing Bounties',
|
),
|
||||||
'Request a redditor be banned.',
|
] -%}
|
||||||
'fa-gavel', '#dc3545',
|
|
||||||
'/post/215970',
|
|
||||||
),
|
|
||||||
])-%}
|
|
||||||
{%- endif -%}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="directory--wrapper">
|
<div id="directory--wrapper">
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
{% extends "submit_navbar.html" %}
|
||||||
|
{% block pagetitle %}Submit {{kind}}{% if kind == 'Sidebar' %} Images{% else %}s{% endif %}{% endblock %}
|
||||||
|
{% block pagetype %}message{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<h2 class="mt-3 mt-md-5">Submit {{kind}} {% if kind == 'Sidebar' %}Image{% endif %}</h2>
|
||||||
|
<div class="settings-section rounded">
|
||||||
|
<div class="d-lg-flex">
|
||||||
|
<div class="body w-lg-100">
|
||||||
|
<form action="/submit/art" method="post" enctype="multipart/form-data" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
|
||||||
|
<input hidden name="formkey" value="{{v|formkey}}" class="notranslate" translate="no">
|
||||||
|
<input hidden name="kind" value="{{kind.lower()}}" class="notranslate" translate="no">
|
||||||
|
|
||||||
|
<div id="image-upload-block">
|
||||||
|
<div><label class="mt-3">Image</label></div>
|
||||||
|
|
||||||
|
<img loading="lazy" id="image-preview" class="d-none" style="max-width:50%;border:5px white solid">
|
||||||
|
<label class="btn btn-secondary m-0" for="file-upload">
|
||||||
|
<div>Select Image</div>
|
||||||
|
<input autocomplete="off" id="file-upload" accept="image/*" type="file" name="image" {% if g.is_tor %}disabled{% endif %} hidden>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="mt-3" for="author">Author</label>
|
||||||
|
<input autocomplete="off" type="text" id="author" class="form-control" name="author" maxlength="30" pattern='[a-zA-Z0-9_\-]{1,30}|\?{3}' placeholder="Required" value="{{username}}" required>
|
||||||
|
|
||||||
|
<div class="footer mt-5">
|
||||||
|
<div class="d-flex">
|
||||||
|
<input id="submit-btn" disabled type="submit" class="btn btn-primary ml-auto" value="Submit {{kind}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="mt-5 mx-1">Pending Approval</h2>
|
||||||
|
<div class="mt-5">
|
||||||
|
<div class="col px-0">
|
||||||
|
<div class="settings">
|
||||||
|
{% for entry in entries %}
|
||||||
|
<div id="{{entry.id}}-art" class="settings-section rounded">
|
||||||
|
<div class="d-lg-flex">
|
||||||
|
<div class="body w-lg-100">
|
||||||
|
<input hidden value="{{v|formkey}}" class="notranslate" translate="no">
|
||||||
|
|
||||||
|
<div><label class="mt-3">Image</label></div>
|
||||||
|
<img loading="lazy" src="{{SITE_FULL_IMAGES}}/asset_submissions/art/{{entry.id}}.webp?s={{range(1, 10000000)|random}}" style="max-width:50%;border:5px white solid">
|
||||||
|
|
||||||
|
<div><label class="mt-3" for="{{entry.id}}-submitter">Submitter</label></div>
|
||||||
|
<input autocomplete="off" type="text" id="{{entry.id}}-submitter" class="form-control" maxlength="30" value="{{entry.submitter}}" readonly>
|
||||||
|
|
||||||
|
<label class="mt-3" for="{{entry.id}}-author">Author</label>
|
||||||
|
<input autocomplete="off" type="text" id="{{entry.id}}-author" class="form-control" maxlength="30" value="{{entry.author}}" pattern='[a-zA-Z0-9_\-]{1,30}|\?{3}' placeholder="Required" required {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}readonly{% endif %}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if v.admin_level >= PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] or v.id == entry.submitter_id %}
|
||||||
|
<div class="d-flex my-4 mx-3">
|
||||||
|
<input autocomplete="off" type="text" id="{{entry.id}}-comment" class="form-control mr-4" placeholder="Comment..." maxlength="500" {% if v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}hidden{% endif %}>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-danger ml-auto" data-nonce="{{g.nonce}}" data-onclick="remove_art(this, '{{entry.id}}')">Remove</button>
|
||||||
|
{% if v.admin_level >= PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] %}
|
||||||
|
<button type="button" class="btn btn-success ml-3 mr-1" data-nonce="{{g.nonce}}" data-onclick="approve_art(this, '{{entry.id}}')">Approve</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script defer src="{{'js/submit_art.js' | asset}}"></script>
|
||||||
|
{% endblock %}
|
|
@ -1,68 +1,66 @@
|
||||||
{% extends "default.html" %}
|
{% extends "submit_navbar.html" %}
|
||||||
{% block pagetitle %}Submit Emojis{% endblock %}
|
{% block pagetitle %}Submit Emojis{% endblock %}
|
||||||
{% block pagetype %}message{% endblock %}
|
{% block pagetype %}message{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="mx-4">
|
<h2 class="mt-3 mt-md-5">Submit Emoji</h2>
|
||||||
<h2 class="mt-5">Submit Emoji</h2>
|
<div class="settings-section rounded">
|
||||||
<div class="settings-section rounded">
|
<div class="d-lg-flex">
|
||||||
<div class="d-lg-flex">
|
<div class="body w-lg-100">
|
||||||
<div class="body w-lg-100">
|
<form action="/submit/emojis" method="post" enctype="multipart/form-data" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
|
||||||
<form action="/submit/emojis" method="post" enctype="multipart/form-data" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHR(this)">
|
<input hidden name="formkey" value="{{v|formkey}}" class="notranslate" translate="no">
|
||||||
<input hidden name="formkey" value="{{v|formkey}}" class="notranslate" translate="no">
|
|
||||||
|
|
||||||
<div id="image-upload-block">
|
<div id="image-upload-block">
|
||||||
<div><label class="mt-3">Image</label></div>
|
<div><label class="mt-3">Image</label></div>
|
||||||
|
|
||||||
<img loading="lazy" id="image-preview" class="d-none" style="max-width:50%;border:5px white solid">
|
<img loading="lazy" id="image-preview" class="d-none" style="max-width:50%;border:5px white solid">
|
||||||
<label class="btn btn-secondary m-0" for="file-upload">
|
<label class="btn btn-secondary m-0" for="file-upload">
|
||||||
<div>Select Image</div>
|
<div>Select Image</div>
|
||||||
<input autocomplete="off" id="file-upload" accept="image/*" type="file" name="image" {% if g.is_tor %}disabled{% endif %} hidden>
|
<input autocomplete="off" id="file-upload" accept="image/*" type="file" name="image" {% if g.is_tor %}disabled{% endif %} hidden>
|
||||||
</label>
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="mt-3" for="kind">Kind</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<select autocomplete="off" id='kind' class="form-control" name="kind" required>
|
||||||
|
{% if not kind %}
|
||||||
|
<option hidden disabled selected value>-- select an option --</option>
|
||||||
|
{% endif %}
|
||||||
|
{% for entry in EMOJI_KINDS %}
|
||||||
|
<option value="{{entry}}" {% if entry == kind %}selected{% endif %}>
|
||||||
|
{{entry}}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="mt-3" for="name">Emoji Name</label>
|
||||||
|
<input autocomplete="off" type="text" id="name" class="form-control" name="name" maxlength="30" pattern='[a-zA-Z0-9]{1,30}' placeholder="Required" value="{{name}}" required>
|
||||||
|
|
||||||
|
<label class="mt-3" for="author">Author</label>
|
||||||
|
<input autocomplete="off" type="text" id="author" class="form-control" name="author" maxlength="30" pattern='[a-zA-Z0-9_\-]{1,30}|\?{3}' placeholder="Required" value="{{username}}" required>
|
||||||
|
|
||||||
|
<label class="mt-3" for="tags">Tags (must be separated by spaces)</label>
|
||||||
|
<input autocomplete="off" type="text" id="tags" class="form-control" name="tags" maxlength="200" pattern='[a-zA-Z0-9: ]{1,200}' placeholder="Required" value="{{tags}}" required>
|
||||||
|
|
||||||
|
{% if FEATURES['NSFW_MARKING'] %}
|
||||||
|
<div class="custom-control custom-checkbox mt-4 pt-1 ml-1">
|
||||||
|
<input autocomplete="off" type="checkbox" class="custom-control-input" id="emoji-nsfw" name="nsfw">
|
||||||
|
<label class="custom-control-label" for="emoji-nsfw">NSFW</label>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<label class="mt-3" for="kind">Kind</label>
|
<div class="footer mt-5">
|
||||||
<div class="input-group">
|
<div class="d-flex">
|
||||||
<select autocomplete="off" id='kind' class="form-control" name="kind" required>
|
<input id="submit-btn" disabled type="submit" class="btn btn-primary ml-auto" value="Submit Emoji">
|
||||||
{% if not kind %}
|
|
||||||
<option hidden disabled selected value>-- select an option --</option>
|
|
||||||
{% endif %}
|
|
||||||
{% for entry in EMOJI_KINDS %}
|
|
||||||
<option value="{{entry}}" {% if entry == kind %}selected{% endif %}>
|
|
||||||
{{entry}}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<label class="mt-3" for="name">Emoji Name</label>
|
</form>
|
||||||
<input autocomplete="off" type="text" id="name" class="form-control" name="name" maxlength="30" pattern='[a-zA-Z0-9]{1,30}' placeholder="Required" value="{{name}}" required>
|
|
||||||
|
|
||||||
<label class="mt-3" for="author">Author</label>
|
|
||||||
<input autocomplete="off" type="text" id="author" class="form-control" name="author" maxlength="30" pattern='[a-zA-Z0-9_\-]{1,30}|\?{3}' placeholder="Required" value="{{username}}" required>
|
|
||||||
|
|
||||||
<label class="mt-3" for="tags">Tags (must be separated by spaces)</label>
|
|
||||||
<input autocomplete="off" type="text" id="tags" class="form-control" name="tags" maxlength="200" pattern='[a-zA-Z0-9: ]{1,200}' placeholder="Required" value="{{tags}}" required>
|
|
||||||
|
|
||||||
{% if FEATURES['NSFW_MARKING'] %}
|
|
||||||
<div class="custom-control custom-checkbox mt-4 pt-1 ml-1">
|
|
||||||
<input autocomplete="off" type="checkbox" class="custom-control-input" id="emoji-nsfw" name="nsfw">
|
|
||||||
<label class="custom-control-label" for="emoji-nsfw">NSFW</label>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="footer mt-5">
|
|
||||||
<div class="d-flex">
|
|
||||||
<input id="submit-btn" disabled type="submit" class="btn btn-primary ml-auto" value="Submit Emoji">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="mt-5 mx-4">Pending Approval</h2>
|
<h2 class="mt-5 mx-1">Pending Approval</h2>
|
||||||
<div class="row mt-5 mx-4">
|
<div class="mt-5">
|
||||||
<div class="col px-0">
|
<div class="col px-0">
|
||||||
<div class="settings">
|
<div class="settings">
|
||||||
{% for emoji in emojis %}
|
{% for emoji in emojis %}
|
||||||
|
|
|
@ -1,51 +1,49 @@
|
||||||
{% extends "default.html" %}
|
{% extends "submit_navbar.html" %}
|
||||||
{% block pagetitle %}Submit Hats{% endblock %}
|
{% block pagetitle %}Submit Hats{% endblock %}
|
||||||
{% block pagetype %}message{% endblock %}
|
{% block pagetype %}message{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="mx-4">
|
<h2 class="mt-3 mt-md-5">Submit Hat</h2>
|
||||||
<h2 class="mt-5">Submit Hat</h2>
|
<div class="settings-section rounded">
|
||||||
<div class="settings-section rounded">
|
<div class="d-lg-flex">
|
||||||
<div class="d-lg-flex">
|
<div class="body w-lg-100">
|
||||||
<div class="body w-lg-100">
|
<form action="/submit/hats" method="post" enctype="multipart/form-data" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
|
||||||
<form action="/submit/hats" method="post" enctype="multipart/form-data" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
|
<input hidden name="formkey" value="{{v|formkey}}" class="notranslate" translate="no">
|
||||||
<input hidden name="formkey" value="{{v|formkey}}" class="notranslate" translate="no">
|
|
||||||
|
|
||||||
<div id="image-upload-block">
|
<div id="image-upload-block">
|
||||||
<div><label class="mt-3">Image</label></div>
|
<div><label class="mt-3">Image</label></div>
|
||||||
|
|
||||||
<img loading="lazy" id="image-preview" class="d-none" style="max-width:50%;border:5px white solid">
|
<img loading="lazy" id="image-preview" class="d-none" style="max-width:50%;border:5px white solid">
|
||||||
<label class="btn btn-secondary m-0" for="file-upload">
|
<label class="btn btn-secondary m-0" for="file-upload">
|
||||||
<div>Select Image</div>
|
<div>Select Image</div>
|
||||||
<input autocomplete="off" id="file-upload" accept="image/*" type="file" name="image" {% if g.is_tor %}disabled{% endif %} hidden>
|
<input autocomplete="off" id="file-upload" accept="image/*" type="file" name="image" {% if g.is_tor %}disabled{% endif %} hidden>
|
||||||
</label>
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="hat-design-reference-block" class="mt-3">
|
||||||
|
<a href="{{SITE_FULL_IMAGES}}/i/hat-template.png" class="font-weight-bold">Hat Template</a> — 100x130px (do not resize), circle is profile picture, do not include circle in final submission.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="mt-3" for="name">Hat Name</label>
|
||||||
|
<input autocomplete="off" type="text" id="name" class="form-control" name="name" maxlength="50" placeholder="Required" value="{{name}}" required>
|
||||||
|
|
||||||
|
<label class="mt-3" for="author">Author</label>
|
||||||
|
<input autocomplete="off" type="text" id="author" class="form-control" name="author" maxlength="30" pattern='[a-zA-Z0-9_\-]{1,30}' placeholder="Required" value="{{username}}" required>
|
||||||
|
|
||||||
|
<label class="mt-3" for="description">Description</label>
|
||||||
|
<input autocomplete="off" type="text" id="description" class="form-control" name="description" maxlength="300" pattern='[^<>&\n\t]{1,300}' placeholder="Required" value="{{description}}" required>
|
||||||
|
|
||||||
|
<div class="footer mt-5">
|
||||||
|
<div class="d-flex">
|
||||||
|
<input id="submit-btn" disabled type="submit" class="btn btn-primary ml-auto" value="Submit Hat">
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div id="hat-design-reference-block" class="mt-3">
|
</form>
|
||||||
<a href="{{SITE_FULL_IMAGES}}/i/hat-template.png" class="font-weight-bold">Hat Template</a> — 100x130px (do not resize), circle is profile picture, do not include circle in final submission.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label class="mt-3" for="name">Hat Name</label>
|
|
||||||
<input autocomplete="off" type="text" id="name" class="form-control" name="name" maxlength="50" placeholder="Required" value="{{name}}" required>
|
|
||||||
|
|
||||||
<label class="mt-3" for="author">Author</label>
|
|
||||||
<input autocomplete="off" type="text" id="author" class="form-control" name="author" maxlength="30" pattern='[a-zA-Z0-9_\-]{1,30}' placeholder="Required" value="{{username}}" required>
|
|
||||||
|
|
||||||
<label class="mt-3" for="description">Description</label>
|
|
||||||
<input autocomplete="off" type="text" id="description" class="form-control" name="description" maxlength="300" pattern='[^<>&\n\t]{1,300}' placeholder="Required" value="{{description}}" required>
|
|
||||||
|
|
||||||
<div class="footer mt-5">
|
|
||||||
<div class="d-flex">
|
|
||||||
<input id="submit-btn" disabled type="submit" class="btn btn-primary ml-auto" value="Submit Hat">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="mt-5 mx-4">Pending Approval</h2>
|
<h2 class="mt-5 mx-1">Pending Approval</h2>
|
||||||
<div class="row mt-5 mx-4">
|
<div class="mt-5">
|
||||||
<div class="col px-0">
|
<div class="col px-0">
|
||||||
<div class="settings">
|
<div class="settings">
|
||||||
{% for hat in hats %}
|
{% for hat in hats %}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
{%- extends 'root.html' -%}
|
||||||
|
{% block pagetitle %}User List{% endblock %}
|
||||||
|
{% block pagetype %}meta_navbar{% endblock %}
|
||||||
|
{% block body_attributes %}class="has_header"{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
{% include "header.html" %}
|
||||||
|
{% block subNav %}
|
||||||
|
<div class="container-fluid bg-white sticky ml-2 d-mob-none" style="padding-top: 50px; padding-bottom: 0 !important">
|
||||||
|
<div class="row box-shadow-bottom">
|
||||||
|
<div class="col">
|
||||||
|
<div class="container" style="padding-bottom: 0">
|
||||||
|
<div class="row box-shadow-bottom bg-white">
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex flex-row-reverse justify-content-end">
|
||||||
|
<ul class="nav settings-nav" style="margin-left: -15px">
|
||||||
|
{% if FEATURES['ART_SUBMISSIONS'] %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link{% if request.path == '/submit/sidebar' %} active{% endif %}" href="/submit/sidebar">
|
||||||
|
<i class="fas fa-sidebar-flip pr-2"></i>Sidebar Images
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link{% if request.path == '/submit/banners' %} active{% endif %}" href="/submit/banners">
|
||||||
|
<i class="fas fa-landscape pr-2"></i>Banners
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if FEATURES['EMOJI_SUBMISSIONS'] %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link{% if request.path == '/submit/emojis' %} active{% endif %}" href="/submit/emojis">
|
||||||
|
<i class="fas fa-smile-beam pr-2"></i>Emojis
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if FEATURES['HAT_SUBMISSIONS'] %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link{% if request.path == '/submit/hats' %} active{% endif %}" href="/submit/hats">
|
||||||
|
<i class="fas fa-hat-cowboy pr-2"></i>Hats
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if SITE == 'rdrama.net' %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/post/33652">
|
||||||
|
<i class="fas fa-robot pr-2"></i>Snappy Quotes
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container-fluid bg-white sticky d-md-none" style="padding-top: 20px; padding-bottom: 0">
|
||||||
|
<div class="row box-shadow-bottom">
|
||||||
|
<div class="col px-0">
|
||||||
|
<div class="d-flex flex-row-reverse justify-content-center">
|
||||||
|
<ul class="nav settings-nav">
|
||||||
|
{% if FEATURES['ART_SUBMISSIONS'] %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a style="padding: 0.75rem 1.5rem" class="nav-link{% if request.path == '/submit/sidebar' %} active{% endif %}" href="/submit/sidebar"><i class="fas fa-sidebar-flip text-lg mr-0"></i></a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a style="padding: 0.75rem 1.5rem" class="nav-link{% if request.path == '/submit/banners' %} active{% endif %}" href="/submit/banners"><i class="fas fa-landscape text-lg mr-0"></i></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if FEATURES['EMOJI_SUBMISSIONS'] %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a style="padding: 0.75rem 1.5rem" class="nav-link{% if request.path == '/submit/emojis' %} active{% endif %}" href="/submit/emojis"><i class="fas fa-smile-beam text-lg mr-0"></i></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if FEATURES['HAT_SUBMISSIONS'] %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a style="padding: 0.75rem 1.5rem" class="nav-link{% if request.path == '/submit/hats' %} active{% endif %}" href="/submit/hats"><i class="fas fa-hat-cowboy text-lg mr-0"></i></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if SITE == 'rdrama.net' %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a style="padding: 0.75rem 1.5rem" class="nav-link{% if request.path == '/post/33652' %} active{% endif %}" href="/post/33652"><i class="fas fa-robot text-lg mr-0"></i></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<div class="pt-3 container">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,35 @@
|
||||||
|
create table art_submissions (
|
||||||
|
id integer primary key,
|
||||||
|
kind varchar(7) not null,
|
||||||
|
author_id integer not null,
|
||||||
|
submitter_id integer not null,
|
||||||
|
created_utc integer not null,
|
||||||
|
approved bool not null
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.art_submissions_id_seq
|
||||||
|
AS integer
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.art_submissions_id_seq OWNED BY public.art_submissions.id;
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.art_submissions ALTER COLUMN id SET DEFAULT nextval('public.art_submissions_id_seq'::regclass);
|
||||||
|
|
||||||
|
rdrama:
|
||||||
|
SELECT pg_catalog.setval('public.art_submissions_id_seq', 1477, true);
|
||||||
|
|
||||||
|
wpd:
|
||||||
|
SELECT pg_catalog.setval('public.art_submissions_id_seq', 157, true);
|
||||||
|
|
||||||
|
alter table only art_submissions
|
||||||
|
add constraint art_submissions_author_fkey foreign key (author_id) references public.users(id);
|
||||||
|
|
||||||
|
alter table only art_submissions
|
||||||
|
add constraint art_submissions_submitter_fkey foreign key (submitter_id) references public.users(id);
|
||||||
|
|
||||||
|
|
||||||
|
delete from modactions where kind in ('approve_emoji', 'reject_emoji', 'approve_hat', 'reject_hat');
|
Loading…
Reference in New Issue