Merge branch 'frost' into upgrade-to-sqlalchemy2

This commit is contained in:
justcool393 2023-08-09 00:30:15 -07:00 committed by GitHub
commit 3e66d8a7bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 71 additions and 202 deletions

View file

@ -55,7 +55,7 @@ class ModAction(CreatedDateTimeBase):
@property @property
@lazy @lazy
def string(self): def string(self):
output = self.lookup_action_type()["str"].format(self=self, cc=CC_TITLE) output = self.lookup_action_type()["str"].format(self=self)
if not self.note: return output if not self.note: return output
output += f" <i>({self.note})</i>" output += f" <i>({self.note})</i>"
return output return output
@ -65,7 +65,6 @@ class ModAction(CreatedDateTimeBase):
def target_link(self): def target_link(self):
if self.target_user: return f'<a href="{self.target_user.url}">{self.target_user.username}</a>' if self.target_user: return f'<a href="{self.target_user.url}">{self.target_user.username}</a>'
elif self.target_post: elif self.target_post:
if self.target_post.club: return f'<a href="{self.target_post.permalink}">{CC} ONLY</a>'
return f'<a href="{self.target_post.permalink}">{self.target_post.title_html}</a>' return f'<a href="{self.target_post.permalink}">{self.target_post.title_html}</a>'
elif self.target_comment_id: return f'<a href="/comment/{self.target_comment_id}?context=8#context">comment</a>' elif self.target_comment_id: return f'<a href="/comment/{self.target_comment_id}?context=8#context">comment</a>'
@ -140,16 +139,6 @@ ACTIONTYPES = {
"icon": 'fa-badge-check', "icon": 'fa-badge-check',
"color": 'bg-success' "color": 'bg-success'
}, },
'club_allow': {
"str": 'allowed user {self.target_link} into the {cc}',
"icon": 'fa-golf-club',
"color": 'bg-success'
},
'club_ban': {
"str": 'disallowed user {self.target_link} from the {cc}',
"icon": 'fa-golf-club',
"color": 'bg-danger'
},
'delete_report': { 'delete_report': {
"str": 'deleted report on {self.target_link}', "str": 'deleted report on {self.target_link}',
"icon": 'fa-flag', "icon": 'fa-flag',

View file

@ -34,7 +34,6 @@ class Submission(CreatedBase):
stickied_utc: Mapped[int | None] = mapped_column(Integer) stickied_utc: Mapped[int | None] = mapped_column(Integer)
is_pinned: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) is_pinned: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
private: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) private: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
club = Column(Boolean, default=False, nullable=False)
comment_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) comment_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
over_18: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) over_18: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
is_bot: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) is_bot: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
@ -172,7 +171,6 @@ class Submission(CreatedBase):
@lazy @lazy
def shortlink(self): def shortlink(self):
link = f"/post/{self.id}" link = f"/post/{self.id}"
if self.club: return link + '/-'
output = title_regex.sub('', self.title.lower()) output = title_regex.sub('', self.title.lower())
output = output.split()[:6] output = output.split()[:6]
@ -253,7 +251,6 @@ class Submission(CreatedBase):
'distinguish_level': self.distinguish_level, 'distinguish_level': self.distinguish_level,
'voted': self.voted if hasattr(self, 'voted') else 0, 'voted': self.voted if hasattr(self, 'voted') else 0,
'flags': flags, 'flags': flags,
'club': self.club,
} }
return data return data

View file

@ -23,9 +23,7 @@ from files.classes.userblock import UserBlock
from files.classes.visstate import StateMod from files.classes.visstate import StateMod
from files.helpers.assetcache import assetcache_path from files.helpers.assetcache import assetcache_path
from files.helpers.config.const import * from files.helpers.config.const import *
from files.helpers.config.environment import (CARD_VIEW, from files.helpers.config.environment import (CARD_VIEW, DEFAULT_COLOR,
CLUB_TRUESCORE_MINIMUM,
DEFAULT_COLOR,
DEFAULT_TIME_FILTER, SITE_FULL, DEFAULT_TIME_FILTER, SITE_FULL,
SITE_ID) SITE_ID)
from files.helpers.security import * from files.helpers.security import *
@ -96,7 +94,6 @@ class User(CreatedBase):
is_banned = mapped_column(Integer, default=0, nullable=False) is_banned = mapped_column(Integer, default=0, nullable=False)
unban_utc = mapped_column(Integer, default=0, nullable=False) unban_utc = mapped_column(Integer, default=0, nullable=False)
ban_reason = deferred(mapped_column(String)) ban_reason = deferred(mapped_column(String))
club_allowed = mapped_column(Boolean)
login_nonce = mapped_column(Integer, default=0, nullable=False) login_nonce = mapped_column(Integer, default=0, nullable=False)
reserved = deferred(mapped_column(String)) reserved = deferred(mapped_column(String))
coins = mapped_column(Integer, default=0, nullable=False) coins = mapped_column(Integer, default=0, nullable=False)
@ -219,11 +216,6 @@ class User(CreatedBase):
def is_blocking(self, target): def is_blocking(self, target):
return g.db.query(UserBlock).filter_by(user_id=self.id, target_id=target.id).one_or_none() return g.db.query(UserBlock).filter_by(user_id=self.id, target_id=target.id).one_or_none()
@property
@lazy
def paid_dues(self):
return not self.shadowbanned and not (self.is_banned and not self.unban_utc) and (self.admin_level or self.club_allowed or (self.club_allowed != False and self.truescore > CLUB_TRUESCORE_MINIMUM))
@lazy @lazy
def any_block_exists(self, other): def any_block_exists(self, other):
return g.db.query(UserBlock).filter( return g.db.query(UserBlock).filter(

View file

@ -93,7 +93,6 @@ def seed_db_worker(num_users = 900, num_posts = 40, num_toplevel_comments = 1000
user = users[int(len(users) * detrand())] user = users[int(len(users) * detrand())]
post = Submission( post = Submission(
private=False, private=False,
club=None,
author_id=user.id, author_id=user.id,
over_18=False, over_18=False,
app_id=None, app_id=None,

View file

@ -43,9 +43,6 @@ class Service(IntEnum):
def enable_services(self) -> bool: def enable_services(self) -> bool:
return self not in {self.CRON, self.MIGRATION} return self not in {self.CRON, self.MIGRATION}
CC = "COUNTRY CLUB"
CC_TITLE = CC.title()
NOTIFICATIONS_ID = 1 NOTIFICATIONS_ID = 1
AUTOJANNY_ID = 2 AUTOJANNY_ID = 2
MODMAIL_ID = 2 MODMAIL_ID = 2

View file

@ -93,8 +93,6 @@ CARD_VIEW = bool_from_string(environ.get("CARD_VIEW", True))
FINGERPRINT_TOKEN = environ.get("FP", None) FINGERPRINT_TOKEN = environ.get("FP", None)
# other stuff from const.py that aren't constants # other stuff from const.py that aren't constants
CLUB_TRUESCORE_MINIMUM = int(environ.get("DUES").strip())
IMGUR_KEY = environ.get("IMGUR_KEY", "").strip() IMGUR_KEY = environ.get("IMGUR_KEY", "").strip()
PUSHER_ID = environ.get("PUSHER_ID", "").strip() PUSHER_ID = environ.get("PUSHER_ID", "").strip()
PUSHER_KEY = environ.get("PUSHER_KEY", "").strip() PUSHER_KEY = environ.get("PUSHER_KEY", "").strip()

View file

@ -65,8 +65,6 @@ def inject_constants():
"NOTIFICATIONS_ID":NOTIFICATIONS_ID, "NOTIFICATIONS_ID":NOTIFICATIONS_ID,
"MODMAIL_ID":MODMAIL_ID, "MODMAIL_ID":MODMAIL_ID,
"PUSHER_ID":PUSHER_ID, "PUSHER_ID":PUSHER_ID,
"CC":CC,
"CC_TITLE":CC_TITLE,
"listdir":listdir, "listdir":listdir,
"config":app.config.get, "config":app.config.get,
"ENABLE_DOWNVOTES": ENABLE_DOWNVOTES, "ENABLE_DOWNVOTES": ENABLE_DOWNVOTES,

View file

@ -23,7 +23,7 @@ USERPAGELISTING_TIMEOUT_SECS: Final[int] = 86400
CHANGELOGLIST_TIMEOUT_SECS: Final[int] = 86400 CHANGELOGLIST_TIMEOUT_SECS: Final[int] = 86400
@cache.memoize(timeout=FRONTLIST_TIMEOUT_SECS) @cache.memoize(timeout=FRONTLIST_TIMEOUT_SECS)
def frontlist(v=None, sort='new', page=1, t="all", ids_only=True, ccmode="false", filter_words='', gt=0, lt=0): def frontlist(v=None, sort='new', page=1, t="all", ids_only=True, filter_words='', gt=0, lt=0):
posts = g.db.query(Submission) posts = g.db.query(Submission)
if v and v.hidevotedon: if v and v.hidevotedon:
@ -42,14 +42,8 @@ def frontlist(v=None, sort='new', page=1, t="all", ids_only=True, ccmode="false"
if not gt and not lt: if not gt and not lt:
posts = apply_time_filter(posts, t, Submission) posts = apply_time_filter(posts, t, Submission)
if (ccmode == "true"):
posts = posts.filter(Submission.club == True)
posts = posts.filter_by(private=False, state_user_deleted_utc=None) posts = posts.filter_by(private=False, state_user_deleted_utc=None)
if ccmode == "false" and not gt and not lt:
posts = posts.filter_by(stickied=None)
if v and v.admin_level < 2: if v and v.admin_level < 2:
posts = posts.filter(Submission.author_id.notin_(v.userblocks)) posts = posts.filter(Submission.author_id.notin_(v.userblocks))
@ -77,7 +71,7 @@ def frontlist(v=None, sort='new', page=1, t="all", ids_only=True, ccmode="false"
posts = posts[:size] posts = posts[:size]
if page == 1 and ccmode == "false" and not gt and not lt: if page == 1 and not gt and not lt:
pins = g.db.query(Submission).filter(Submission.stickied != None, Submission.state_mod == StateMod.VISIBLE) pins = g.db.query(Submission).filter(Submission.stickied != None, Submission.state_mod == StateMod.VISIBLE)
if v: if v:
if v.admin_level < 2: if v.admin_level < 2:

View file

@ -163,62 +163,6 @@ def revert_actions(v, username):
return {"message": "Admin actions reverted!"} return {"message": "Admin actions reverted!"}
@app.post("/@<username>/club_allow")
@limiter.exempt
@admin_level_required(2)
def club_allow(v, username):
u = get_user(username, v=v)
if not u: abort(404)
if u.admin_level >= v.admin_level:
abort(403, "Can't target users with admin level higher than you")
u.club_allowed = True
g.db.add(u)
for x in u.alts_unique:
x.club_allowed = True
g.db.add(x)
ma = ModAction(
kind="club_allow",
user_id=v.id,
target_user_id=u.id
)
g.db.add(ma)
g.db.commit()
return {"message": f"@{username} has been allowed into the {CC_TITLE}!"}
@app.post("/@<username>/club_ban")
@limiter.exempt
@admin_level_required(2)
def club_ban(v, username):
u = get_user(username, v=v)
if not u: abort(404)
if u.admin_level >= v.admin_level:
abort(403, "Can't target users with admin level higher than you")
u.club_allowed = False
for x in u.alts_unique:
u.club_allowed = False
g.db.add(x)
ma = ModAction(
kind="club_ban",
user_id=v.id,
target_user_id=u.id
)
g.db.add(ma)
g.db.commit()
return {"message": f"@{username} has been kicked from the {CC_TITLE}. Deserved."}
@app.get("/admin/shadowbanned") @app.get("/admin/shadowbanned")
@limiter.exempt @limiter.exempt
@auth_required @auth_required

View file

@ -23,8 +23,6 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None):
g.db.add(notif) g.db.add(notif)
g.db.commit() g.db.commit()
if comment.post and comment.post.club and not (v and (v.paid_dues or v.id in [comment.author_id, comment.post.author_id])): abort(403)
if comment.post and comment.post.private and not (v and (v.admin_level >= 2 or v.id == comment.post.author.id)): abort(403) if comment.post and comment.post.private and not (v and (v.admin_level >= 2 or v.id == comment.post.author.id)): abort(403)
if not comment.parent_submission and not (v and (comment.author.id == v.id or comment.sentto == v.id)) and not (v and v.admin_level >= 2) : abort(403) if not comment.parent_submission and not (v and (comment.author.id == v.id or comment.sentto == v.id)) and not (v and v.admin_level >= 2) : abort(403)

View file

@ -207,7 +207,6 @@ def front_all(v, subdomain=None):
sort=request.values.get("sort", defaultsorting) sort=request.values.get("sort", defaultsorting)
t=request.values.get('t', defaulttime) t=request.values.get('t', defaulttime)
ccmode=request.values.get('ccmode', "false").lower()
if sort == 'bump': t='all' if sort == 'bump': t='all'
@ -221,7 +220,6 @@ def front_all(v, subdomain=None):
page=page, page=page,
t=t, t=t,
v=v, v=v,
ccmode=ccmode,
filter_words=v.filter_words if v else [], filter_words=v.filter_words if v else [],
gt=gt, gt=gt,
lt=lt, lt=lt,
@ -247,7 +245,7 @@ def front_all(v, subdomain=None):
g.db.commit() g.db.commit()
if request.headers.get("Authorization"): return {"data": [x.json for x in posts], "next_exists": next_exists} if request.headers.get("Authorization"): return {"data": [x.json for x in posts], "next_exists": next_exists}
return render_template("home.html", v=v, listing=posts, next_exists=next_exists, sort=sort, t=t, page=page, ccmode=ccmode, home=True) return render_template("home.html", v=v, listing=posts, next_exists=next_exists, sort=sort, t=t, page=page, home=True)
@app.get("/changelog") @app.get("/changelog")

View file

@ -35,21 +35,6 @@ MAX_TITLE_LENGTH = 500
MAX_URL_LENGTH = 2048 MAX_URL_LENGTH = 2048
@app.post("/toggle_club/<pid>")
@auth_required
def toggle_club(pid, v):
post = get_post(pid)
if post.author_id != v.id and v.admin_level < 2: abort(403)
post.club = not post.club
g.db.add(post)
g.db.commit()
if post.club: return {"message": "Post has been marked as club-only!"}
else: return {"message": "Post has been unmarked as club-only!"}
@app.post("/publish/<pid>") @app.post("/publish/<pid>")
@limiter.limit("1/second;30/minute;200/hour;1000/day") @limiter.limit("1/second;30/minute;200/hour;1000/day")
@auth_required @auth_required
@ -85,8 +70,6 @@ def post_id(pid, anything=None, v=None):
else: defaultsortingcomments = "new" else: defaultsortingcomments = "new"
sort = request.values.get("sort", defaultsortingcomments) sort = request.values.get("sort", defaultsortingcomments)
if post.club and not (v and (v.paid_dues or v.id == post.author_id)): abort(403)
limit = app.config['RESULTS_PER_PAGE_COMMENTS'] limit = app.config['RESULTS_PER_PAGE_COMMENTS']
offset = 0 offset = 0
@ -130,8 +113,6 @@ def post_id(pid, anything=None, v=None):
@auth_desired @auth_desired
def viewmore(v, pid, sort, offset): def viewmore(v, pid, sort, offset):
post = get_post(pid, v=v) post = get_post(pid, v=v)
if post.club and not (v and (v.paid_dues or v.id == post.author_id)): abort(403)
offset_prev = int(offset) offset_prev = int(offset)
try: ids = set(int(x) for x in request.values.get("ids").split(',')) try: ids = set(int(x) for x in request.values.get("ids").split(','))
except: abort(400) except: abort(400)
@ -557,7 +538,6 @@ def submit_post(v):
_do_antispam_submission_check(v, validated_post) _do_antispam_submission_check(v, validated_post)
club = bool(request.values.get("club",""))
is_bot = bool(request.headers.get("Authorization")) is_bot = bool(request.headers.get("Authorization"))
# Invariant: these values are guarded and obey the length bound # Invariant: these values are guarded and obey the length bound
@ -566,7 +546,6 @@ def submit_post(v):
post = Submission( post = Submission(
private=bool(request.values.get("private","")), private=bool(request.values.get("private","")),
club=club,
author_id=v.id, author_id=v.id,
over_18=bool(request.values.get("over_18","")), over_18=bool(request.values.get("over_18","")),
app_id=v.client.application.id if v.client else None, app_id=v.client.application.id if v.client else None,

View file

@ -42,8 +42,6 @@ def searchposts(v):
posts = g.db.query(Submission.id) posts = g.db.query(Submission.id)
if not (v and v.paid_dues): posts = posts.filter_by(club=False)
if v and v.admin_level < 2: if v and v.admin_level < 2:
posts = posts.filter(Submission.state_user_deleted_utc == None, Submission.state_mod == StateMod.VISIBLE, Submission.private == False, Submission.author_id.notin_(v.userblocks)) posts = posts.filter(Submission.state_user_deleted_utc == None, Submission.state_mod == StateMod.VISIBLE, Submission.private == False, Submission.author_id.notin_(v.userblocks))
elif not v: elif not v:
@ -174,15 +172,8 @@ def searchcomments(v):
private = [x[0] for x in g.db.query(Submission.id).filter(Submission.private == True).all()] private = [x[0] for x in g.db.query(Submission.id).filter(Submission.private == True).all()]
comments = comments.filter(Comment.state_mod == StateMod.VISIBLE, Comment.state_user_deleted_utc == None, Comment.parent_submission.notin_(private)) comments = comments.filter(Comment.state_mod == StateMod.VISIBLE, Comment.state_user_deleted_utc == None, Comment.parent_submission.notin_(private))
if not (v and v.paid_dues):
club = [x[0] for x in g.db.query(Submission.id).filter(Submission.club == True).all()]
comments = comments.filter(Comment.parent_submission.notin_(club))
comments = sort_objects(comments, sort, Comment) comments = sort_objects(comments, sort, Comment)
total = comments.count() total = comments.count()
comments = comments.offset(25 * (page - 1)).limit(26).all() comments = comments.offset(25 * (page - 1)).limit(26).all()
ids = [x[0] for x in comments] ids = [x[0] for x in comments]

View file

@ -4,7 +4,7 @@ from files.__main__ import app
from files.classes.comment import Comment from files.classes.comment import Comment
from files.classes.flags import CommentFlag from files.classes.flags import CommentFlag
from files.classes.user import User from files.classes.user import User
from files.classes.visstate import StateReport from files.classes.visstate import StateReport, StateMod
from files.classes.volunteer_janitor import VolunteerJanitorRecord, VolunteerJanitorResult from files.classes.volunteer_janitor import VolunteerJanitorRecord, VolunteerJanitorResult
from files.helpers.volunteer_janitor import update_comment_badness from files.helpers.volunteer_janitor import update_comment_badness
from files.routes.volunteer_common import VolunteerDuty from files.routes.volunteer_common import VolunteerDuty
@ -12,6 +12,7 @@ from flask import g
import pprint import pprint
import random import random
import sqlalchemy import sqlalchemy
from sqlalchemy.orm import aliased
class VolunteerDutyJanitor(VolunteerDuty): class VolunteerDutyJanitor(VolunteerDuty):
@ -42,11 +43,15 @@ def get_duty(u: User) -> Optional[VolunteerDutyJanitor]:
# these could probably be combined into one query somehow # these could probably be combined into one query somehow
# find reported not-deleted comments not made by the current user # find reported visible comments not made by the current user or in reply to the current user
ParentComment = aliased(Comment)
reported_comments = g.db.query(Comment) \ reported_comments = g.db.query(Comment) \
.where(Comment.state_report == StateReport.REPORTED) \ .where(Comment.state_report == StateReport.REPORTED) \
.where(Comment.state_mod == StateMod.VISIBLE) \
.where(Comment.state_user_deleted_utc == None) \ .where(Comment.state_user_deleted_utc == None) \
.where(Comment.author_id != u.id) \ .where(Comment.author_id != u.id) \
.outerjoin(ParentComment, ParentComment.id == Comment.parent_comment_id) \
.where(sqlalchemy.or_(ParentComment.author_id != u.id, ParentComment.author_id == None)) \
.with_entities(Comment.id) .with_entities(Comment.id)
reported_ids = [reported.id for reported in reported_comments] reported_ids = [reported.id for reported in reported_comments]

View file

@ -53,11 +53,6 @@
<a id="unpin-{{p.id}}" class="dropdown-item {% if not p.stickied %}d-none{% endif %} list-inline-item text-info" role="button" onclick="postToastSwitch(this,'/unsticky/{{p.id}}','POST','pin-{{p.id}}','unpin-{{p.id}}');"><i class="fas fa-thumbtack fa-rotate--45"></i>Unpin</a> <a id="unpin-{{p.id}}" class="dropdown-item {% if not p.stickied %}d-none{% endif %} list-inline-item text-info" role="button" onclick="postToastSwitch(this,'/unsticky/{{p.id}}','POST','pin-{{p.id}}','unpin-{{p.id}}');"><i class="fas fa-thumbtack fa-rotate--45"></i>Unpin</a>
{% endif %} {% endif %}
{# {% if v.admin_level > 1 or v.id == p.author_id %}
<a id="club-{{p.id}}" class="dropdown-item {% if p.club %}d-none{% endif %} list-inline-item text-info" role="button" onclick="postToastSwitch(this,'/toggle_club/{{p.id}}','POST','club-{{p.id}}','unclub-{{p.id}}');"><i class="fas fa-eye-slash"></i>Mark club</a>
<a id="unclub-{{p.id}}" class="dropdown-item {% if not p.club %}d-none{% endif %} list-inline-item text-info" role="button" onclick="postToastSwitch(this,'/toggle_club/{{p.id}}','POST','club-{{p.id}}','unclub-{{p.id}}');"><i class="fas fa-eye"></i>Unmark club</a>
{% endif %} #}
{%- if v and v.admin_level >= PERMS['POST_COMMENT_MODERATION'] -%} {%- if v and v.admin_level >= PERMS['POST_COMMENT_MODERATION'] -%}
{%- set show_approve = p.state_mod != StateMod.VISIBLE or "/reported/" in request.path -%} {%- set show_approve = p.state_mod != StateMod.VISIBLE or "/reported/" in request.path -%}
{%- set show_remove = p.state_mod == StateMod.VISIBLE -%} {%- set show_remove = p.state_mod == StateMod.VISIBLE -%}

View file

@ -13,10 +13,7 @@
<button class="nobackground btn btn-link btn-block btn-lg text-left text-muted" data-bs-dismiss="modal" onclick="togglePostEdit('{{p.id}}')"><i class="far fa-edit text-center text-muted mr-3"></i>Edit</button> <button class="nobackground btn btn-link btn-block btn-lg text-left text-muted" data-bs-dismiss="modal" onclick="togglePostEdit('{{p.id}}')"><i class="far fa-edit text-center text-muted mr-3"></i>Edit</button>
{% endif %} {% endif %}
{# <button id="club2-{{p.id}}" class="{% if p.club %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-info text-left" role="button" onclick="postToastSwitch(this,'/toggle_club/{{p.id}}','POST','club2-{{p.id}}','unclub2-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-eye-slash mr-3"></i>Mark club</button> {#<button id="distinguish2-{{p.id}}" class="{% if p.distinguish_level %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" role="button" onclick="postToastSwitch(this,'/distinguish/{{p.id}}','POST','distinguish2-{{p.id}}','undistinguish2-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-crown text-center text-primary mr-3"></i>Distinguish</button>
<button id="unclub2-{{p.id}}" class="{% if not p.club %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-info text-left" role="button" onclick="postToastSwitch(this,'/toggle_club/{{p.id}}','POST','club2-{{p.id}}','unclub2-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-eye mr-3"></i>Unmark club</button>
<button id="distinguish2-{{p.id}}" class="{% if p.distinguish_level %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" role="button" onclick="postToastSwitch(this,'/distinguish/{{p.id}}','POST','distinguish2-{{p.id}}','undistinguish2-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-crown text-center text-primary mr-3"></i>Distinguish</button>
<button id="undistinguish2-{{p.id}}" class="{% if not p.distinguish_level %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" role="button" onclick="postToastSwitch(this,'/distinguish/{{p.id}}','POST','distinguish2-{{p.id}}','undistinguish2-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-crown text-center text-primary mr-3"></i>Undistinguish</button> #} <button id="undistinguish2-{{p.id}}" class="{% if not p.distinguish_level %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" role="button" onclick="postToastSwitch(this,'/distinguish/{{p.id}}','POST','distinguish2-{{p.id}}','undistinguish2-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-crown text-center text-primary mr-3"></i>Undistinguish</button> #}
<button id="pin2-{{p.id}}" class="{% if p.stickied %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" role="button" onclick="postToastSwitch(this,'/sticky/{{p.id}}','POST','pin2-{{p.id}}','unpin2-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center text-primary mr-3"></i>Pin</button> <button id="pin2-{{p.id}}" class="{% if p.stickied %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" role="button" onclick="postToastSwitch(this,'/sticky/{{p.id}}','POST','pin2-{{p.id}}','unpin2-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center text-primary mr-3"></i>Pin</button>

View file

@ -31,15 +31,9 @@
<button id="unpin-profile2-{{p.id}}" class="{% if not p.is_pinned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-muted text-left" role="button" onclick="postToastSwitch(this,'/pin/{{p.id}}','POST','pin-profile2-{{p.id}}','unpin-profile2-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center mr-3"></i>Unpin from profile</button> <button id="unpin-profile2-{{p.id}}" class="{% if not p.is_pinned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-muted text-left" role="button" onclick="postToastSwitch(this,'/pin/{{p.id}}','POST','pin-profile2-{{p.id}}','unpin-profile2-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center mr-3"></i>Unpin from profile</button>
{% endif %} {% endif %}
<button id="undelete-{{p.id}}" class="{% if not p.state_user_deleted_utc %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" role="button" onclick="postToastSwitch(this,'/undelete_post/{{p.id}}','POST','delete-{{p.id}}','undelete-{{p.id}}');document.getElementById('post-{{p.id}}').classList.remove('deleted');" data-bs-dismiss="modal"><i class="far fa-trash-alt text-center mr-3"></i>Undelete</button> <button id="undelete-{{p.id}}" class="{% if not p.state_user_deleted_utc %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" role="button" onclick="postToastSwitch(this,'/undelete_post/{{p.id}}','POST','delete-{{p.id}}','undelete-{{p.id}}');document.getElementById('post-{{p.id}}').classList.remove('deleted');" data-bs-dismiss="modal"><i class="far fa-trash-alt text-center mr-3"></i>Undelete</button>
<button id="delete-{{p.id}}" class="{% if p.state_user_deleted_utc %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" data-bs-toggle="modal" data-bs-dismiss="modal" data-bs-target="#deletePostModal" onclick="delete_postModal('{{p.id}}')"><i class="far fa-trash-alt mr-3"></i>Delete</button> <button id="delete-{{p.id}}" class="{% if p.state_user_deleted_utc %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" data-bs-toggle="modal" data-bs-dismiss="modal" data-bs-target="#deletePostModal" onclick="delete_postModal('{{p.id}}')"><i class="far fa-trash-alt mr-3"></i>Delete</button>
{# <button id="club3-{{p.id}}" class="{% if p.club %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-danger text-left" role="button" onclick="postToastSwitch(this,'/toggle_club/{{p.id}}','POST','club3-{{p.id}}','unclub3-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-eye-slash mr-3"></i>Mark club</button>
<button id="unclub3-{{p.id}}" class="{% if not p.club %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-success text-left" role="button" onclick="postToastSwitch(this,'/toggle_club/{{p.id}}','POST','club3-{{p.id}}','unclub3-{{p.id}}');" data-bs-dismiss="modal"><i class="fas fa-eye mr-3"></i>Unmark club</button> #}
<button id="mark3-{{p.id}}" class="{% if p.over_18 %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" onclick="postToastSwitch(this,'/toggle_post_nsfw/{{p.id}}','POST','mark3-{{p.id}}','unmark3-{{p.id}}');" data-bs-dismiss="modal"><i class="far fa-eye-evil text-center mr-3"></i>Mark +18</button> <button id="mark3-{{p.id}}" class="{% if p.over_18 %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" onclick="postToastSwitch(this,'/toggle_post_nsfw/{{p.id}}','POST','mark3-{{p.id}}','unmark3-{{p.id}}');" data-bs-dismiss="modal"><i class="far fa-eye-evil text-center mr-3"></i>Mark +18</button>
<button id="unmark3-{{p.id}}" class="{% if not p.over_18 %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" onclick="postToastSwitch(this,'/toggle_post_nsfw/{{p.id}}','POST','mark3-{{p.id}}','unmark3-{{p.id}}');" data-bs-dismiss="modal"><i class="far fa-eye-evil text-center mr-3"></i>Unmark +18</button> <button id="unmark3-{{p.id}}" class="{% if not p.over_18 %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" onclick="postToastSwitch(this,'/toggle_post_nsfw/{{p.id}}','POST','mark3-{{p.id}}','unmark3-{{p.id}}');" data-bs-dismiss="modal"><i class="far fa-eye-evil text-center mr-3"></i>Unmark +18</button>
{% else %} {% else %}

View file

@ -54,16 +54,15 @@
{{t | capitalize}} {{t | capitalize}}
</button> </button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 31px, 0px);"> <div class="dropdown-menu" aria-labelledby="dropdownMenuButton" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 31px, 0px);">
{% if t != "hour" %}<a class="dropdown-item" href="?sort={{sort}}&t=hour&ccmode={{ccmode}}"><i class="fas fa-clock mr-2 "></i>Hour</a>{% endif %} {% if t != "hour" %}<a class="dropdown-item" href="?sort={{sort}}&t=hour"><i class="fas fa-clock mr-2 "></i>Hour</a>{% endif %}
{% if t != "day" %}<a class="dropdown-item" href="?sort={{sort}}&t=day&ccmode={{ccmode}}"><i class="fas fa-calendar-day mr-2 "></i>Day</a>{% endif %} {% if t != "day" %}<a class="dropdown-item" href="?sort={{sort}}&t=day"><i class="fas fa-calendar-day mr-2 "></i>Day</a>{% endif %}
{% if t != "week" %}<a class="dropdown-item" href="?sort={{sort}}&t=week&ccmode={{ccmode}}"><i class="fas fa-calendar-week mr-2 "></i>Week</a>{% endif %} {% if t != "week" %}<a class="dropdown-item" href="?sort={{sort}}&t=week"><i class="fas fa-calendar-week mr-2 "></i>Week</a>{% endif %}
{% if t != "month" %}<a class="dropdown-item" href="?sort={{sort}}&t=month&ccmode={{ccmode}}"><i class="fas fa-calendar-alt mr-2 "></i>Month</a>{% endif %} {% if t != "month" %}<a class="dropdown-item" href="?sort={{sort}}&t=month"><i class="fas fa-calendar-alt mr-2 "></i>Month</a>{% endif %}
{% if t != "year" %}<a class="dropdown-item" href="?sort={{sort}}&t=year&ccmode={{ccmode}}"><i class="fas fa-calendar mr-2 "></i>Year</a>{% endif %} {% if t != "year" %}<a class="dropdown-item" href="?sort={{sort}}&t=year"><i class="fas fa-calendar mr-2 "></i>Year</a>{% endif %}
{% if t != "all" %}<a class="dropdown-item" href="?sort={{sort}}&t=all&ccmode={{ccmode}}"><i class="fas fa-infinity mr-2 "></i>All</a>{% endif %} {% if t != "all" %}<a class="dropdown-item" href="?sort={{sort}}&t=all"><i class="fas fa-infinity mr-2 "></i>All</a>{% endif %}
</div> </div>
</div> </div>
{% set ccmode_text = 'ccmode=' ~ ccmode %} {{sorting_time.sort_dropdown(sort, t, SORTS_POSTS)}}
{{sorting_time.sort_dropdown(sort, t, SORTS_POSTS, ccmode_text)}}
</div> </div>
{% endblock %} {% endblock %}
</div> </div>
@ -87,14 +86,14 @@
<ul class="pagination pagination-sm mb-0"> <ul class="pagination pagination-sm mb-0">
{% if page>1 %} {% if page>1 %}
<li class="page-item"> <li class="page-item">
<small><a class="page-link" href="?sort={{sort}}&page={{page-1}}&t={{t}}&ccmode={{ccmode}}" tabindex="-1">Prev</a></small> <small><a class="page-link" href="?sort={{sort}}&page={{page-1}}&t={{t}}" tabindex="-1">Prev</a></small>
</li> </li>
{% else %} {% else %}
<li class="page-item disabled"><span class="page-link">Prev</span></li> <li class="page-item disabled"><span class="page-link">Prev</span></li>
{% endif %} {% endif %}
{% if next_exists %} {% if next_exists %}
<li class="page-item"> <li class="page-item">
<small><a class="page-link" href="?sort={{sort}}&page={{page+1}}&t={{t}}&ccmode={{ccmode}}">Next</a></small> <small><a class="page-link" href="?sort={{sort}}&page={{page+1}}&t={{t}}">Next</a></small>
</li> </li>
{% else %} {% else %}
<li class="page-item disabled"><span class="page-link">Next</span></li> <li class="page-item disabled"><span class="page-link">Next</span></li>

View file

@ -127,9 +127,6 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% set cc='COUNTRY CLUB' %}
<div class="row mb-3" style="background-color:var(--gray-600)"> <div class="row mb-3" style="background-color:var(--gray-600)">
<div id="post-root" class="col-12"> <div id="post-root" class="col-12">
<div class="card border-0 mt-3{% if p.state_mod == StateMod.REMOVED %} removed{% endif %}{% if p.state_mod == StateMod.FILTERED %} filtered{% endif %}{% if p.stickied %} stickied{% endif %}{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}"> <div class="card border-0 mt-3{% if p.state_mod == StateMod.REMOVED %} removed{% endif %}{% if p.state_mod == StateMod.FILTERED %} filtered{% endif %}{% if p.stickied %} stickied{% endif %}{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}">
@ -199,13 +196,11 @@
{% endif %} {% endif %}
{% if p.realurl(v) %} {% if p.realurl(v) %}
<h1 id="post-title" class="card-title post-title text-left mb-md-3"><a {% if not v or v.newtabexternal %}target="_blank"{% endif %} rel="nofollow noopener noreferrer" href="{{p.realurl(v)}}"> <h1 id="post-title" class="card-title post-title text-left mb-md-3"><a {% if not v or v.newtabexternal %}target="_blank"{% endif %} rel="nofollow noopener noreferrer" href="{{p.realurl(v)}}">
{% if p.club %}<span class="patron font-weight-bolder mr-1" style="background-color:red; font-size:12px; line-height:2;">{{CC}}</span>{% endif %}
{% if p.flair %}<span class="patron font-weight-bolder mr-1" style="background-color:var(--primary); font-size:12px; line-height:2;">{{p.flair | safe}}</span>{% endif %} {% if p.flair %}<span class="patron font-weight-bolder mr-1" style="background-color:var(--primary); font-size:12px; line-height:2;">{{p.flair | safe}}</span>{% endif %}
{{p.realtitle(v) | safe}} {{p.realtitle(v) | safe}}
</a></h1> </a></h1>
{% else %} {% else %}
<h1 id="post-title" class="card-title post-title text-left mb-md-3"> <h1 id="post-title" class="card-title post-title text-left mb-md-3">
{% if p.club %}<span class="patron font-weight-bolder mr-1" style="background-color:red; font-size:12px; line-height:2;">{{CC}}</span>{% endif %}
{% if p.flair %}<span class="patron font-weight-bolder mr-1" style="background-color:var(--primary); font-size:12px; line-height:2;">{{p.flair | safe}}</span>{% endif %} {% if p.flair %}<span class="patron font-weight-bolder mr-1" style="background-color:var(--primary); font-size:12px; line-height:2;">{{p.flair | safe}}</span>{% endif %}
{{p.realtitle(v) | safe}} {{p.realtitle(v) | safe}}
</h1> </h1>

View file

@ -1,5 +1,3 @@
{% set cc='COUNTRY CLUB' %}
{% if not v or v.highlightcomments %} {% if not v or v.highlightcomments %}
<script src="{{ 'js/new_comments_count.js' | asset }}"></script> <script src="{{ 'js/new_comments_count.js' | asset }}"></script>
{% endif %} {% endif %}
@ -148,7 +146,6 @@
<div class="post-meta text-left mb-md-2"> <div class="post-meta text-left mb-md-2">
<h5 class="card-title post-title text-left mb-0 pb-0 pb-md-1"> <h5 class="card-title post-title text-left mb-0 pb-0 pb-md-1">
<a id="{{p.id}}-title" {% if v and v.newtab and not g.webview %}target="_blank"{% endif %} href="{{p.permalink}}"> <a id="{{p.id}}-title" {% if v and v.newtab and not g.webview %}target="_blank"{% endif %} href="{{p.permalink}}">
{% if p.club %}<span class="patron font-weight-bolder mr-1" style="background-color:red; font-size:12px; line-height:2;">{{CC}}</span>{% endif %}
{% if p.flair %}<span class="patron font-weight-bolder mr-1" style="background-color:var(--primary); font-size:12px; line-height:2;">{{p.flair | safe}}</span>{% endif %} {% if p.flair %}<span class="patron font-weight-bolder mr-1" style="background-color:var(--primary); font-size:12px; line-height:2;">{{p.flair | safe}}</span>{% endif %}
{{p.realtitle(v) | safe}} {{p.realtitle(v) | safe}}
</a></h5> </a></h5>
@ -305,30 +302,28 @@
{% include "component/post/actions_admin_mobile.html" %} {% include "component/post/actions_admin_mobile.html" %}
{% endif %} {% endif %}
{% if not p.club or v and (v.paid_dues or v.id == p.author_id) %} {% if p.realbody(v) %}
{% if p.realbody(v) %} <div class="d-none card rounded border pt-3 pb-2 my-2" id="post-text-{{p.id}}">
<div class="d-none card rounded border pt-3 pb-2 my-2" id="post-text-{{p.id}}"> {{p.realbody(v) | safe}}
{{p.realbody(v) | safe}} </div>
</div> {% endif %}
{% endif %}
{% if p.is_image and not p.over_18 and ((v and v.cardview) or (not v and CARD_VIEW)) %} {% if p.is_image and not p.over_18 and ((v and v.cardview) or (not v and CARD_VIEW)) %}
<div style="text-align: center" class="mt-3 mb-4"> <div style="text-align: center" class="mt-3 mb-4">
<a {% if v and v.newtab and not g.webview %}target="_blank"{% endif %} rel="nofollow noopener noreferrer" href="{{p.realurl(v)}}"> <a {% if v and v.newtab and not g.webview %}target="_blank"{% endif %} rel="nofollow noopener noreferrer" href="{{p.realurl(v)}}">
<img loading="lazy" data-src="{{p.realurl(v)}}" src="/assets/images/loading.webp" class="img-fluid" style="max-height:20rem" alt="Unable to load image"> <img loading="lazy" data-src="{{p.realurl(v)}}" src="/assets/images/loading.webp" class="img-fluid" style="max-height:20rem" alt="Unable to load image">
</a> </a>
</div> </div>
{% elif p.is_video %} {% elif p.is_video %}
<div id="video-{{p.id}}" style="text-align: center" class="{% if p.over_18 or not ((v and v.cardview) or (not v and CARD_VIEW)) %}d-none{% endif %} mt-4"> <div id="video-{{p.id}}" style="text-align: center" class="{% if p.over_18 or not ((v and v.cardview) or (not v and CARD_VIEW)) %}d-none{% endif %} mt-4">
<video id="video2-{{p.id}}" controls preload="none" class="vid"> <video id="video2-{{p.id}}" controls preload="none" class="vid">
<source src="{{p.realurl(v)}}" type="video/mp4"> <source src="{{p.realurl(v)}}" type="video/mp4">
</video> </video>
</div> </div>
{% elif p.is_youtube %} {% elif p.is_youtube %}
<div id="video-{{p.id}}" class="{% if p.over_18 or not ((v and v.cardview) or (not v and CARD_VIEW)) %}d-none{% endif %} mt-3 mb-4 youtube_embed"> <div id="video-{{p.id}}" class="{% if p.over_18 or not ((v and v.cardview) or (not v and CARD_VIEW)) %}d-none{% endif %} mt-3 mb-4 youtube_embed">
{{p.embed_url | safe}} {{p.embed_url | safe}}
</div> </div>
{% endif %}
{% endif %} {% endif %}
</div> </div>

View file

@ -246,9 +246,6 @@
<input type="hidden" name="user" value="{{u.username}}"> <input type="hidden" name="user" value="{{u.username}}">
<input type="submit" class="btn btn-danger" value="Remove User's Content"> <input type="submit" class="btn btn-danger" value="Remove User's Content">
</form> </form>
<pre></pre>
<button id="grant2" class="{% if u.paid_dues %}d-none{% endif %} btn btn-success" onclick="postToastSwitch(this,'/@{{u.username}}/club_allow','POST','grant2','bar2');">Grant club access</button>
<button id="bar2" class="{% if u.club_allowed == False %}d-none{% endif %} btn btn-danger" onclick="postToastSwitch(this,'/@{{u.username}}/club_ban','POST','grant2','bar2');">Bar from club</button>
{% endif %} {% endif %}
{% if v and v.admin_level >= PERMS['USER_SET_PROFILE_PRIVACY'] %} {% if v and v.admin_level >= PERMS['USER_SET_PROFILE_PRIVACY'] %}
<a id="private-desktop" class="{% if u.is_private %}d-none{% endif %} btn btn-danger" role="button" onclick="postToastSwitch(this,'/id/{{u.id}}/private/1','POST','private-desktop','unprivate-desktop');">Set Private Mode</a> <a id="private-desktop" class="{% if u.is_private %}d-none{% endif %} btn btn-danger" role="button" onclick="postToastSwitch(this,'/id/{{u.id}}/private/1','POST','private-desktop','unprivate-desktop');">Set Private Mode</a>
@ -425,15 +422,8 @@
<div id="message-preview-mobile" class="preview my-3"></div> <div id="message-preview-mobile" class="preview my-3"></div>
{% if v and v.admin_level >= 2 %} {% if v and v.admin_level >= 2 %}
<button id="grant" class="{% if u.paid_dues %}d-none{% endif %} btn btn-success" onclick="postToastSwitch(this,'/@{{u.username}}/club_allow','POST','grant','bar');">Grant club access</button>
<button id="bar" class="{% if u.club_allowed == False %}d-none{% endif %} btn btn-danger" onclick="postToastSwitch(this,'/@{{u.username}}/club_ban','POST','grant','bar');">Bar from club</button>
<br><br>
<div class="body d-lg-flex border-bottom"> <div class="body d-lg-flex border-bottom">
<div class="w-lg-100"> <div class="w-lg-100">
<form action="/admin/title_change/{{u.id}}" method="post"> <form action="/admin/title_change/{{u.id}}" method="post">
{{forms.formkey(v)}} {{forms.formkey(v)}}
<input maxlength=100 autocomplete="off" id="customtitlebody-mobile" type="text" name="title" class="form-control" placeholder='Enter a flair here' value="{% if u.customtitleplain %}{{u.customtitleplain}}{% endif %}"> <input maxlength=100 autocomplete="off" id="customtitlebody-mobile" type="text" name="title" class="form-control" placeholder='Enter a flair here' value="{% if u.customtitleplain %}{{u.customtitleplain}}{% endif %}">
@ -447,7 +437,6 @@
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<pre></pre> <pre></pre>
{% if u.is_suspended %} {% if u.is_suspended %}

View file

@ -192,7 +192,6 @@ def test_bulk_update_descendant_count_quick(accounts, submissions, comments):
for i in range(2): for i in range(2):
post = Submission(**{ post = Submission(**{
'private': False, 'private': False,
'club': None,
'author_id': alice.id, 'author_id': alice.id,
'over_18': False, 'over_18': False,
'app_id': None, 'app_id': None,

View file

@ -0,0 +1,27 @@
"""excise country club
Revision ID: d08833c2adc7
Revises: 85dad82a4a67
Create Date: 2023-08-08 14:36:38.797624+00:00
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'd08833c2adc7'
down_revision = '85dad82a4a67'
branch_labels = None
depends_on = None
def upgrade():
op.drop_column('submissions', 'club')
op.drop_column('users', 'club_allowed')
def downgrade():
op.add_column('users', sa.Column('club_allowed', sa.BOOLEAN(), autoincrement=False, nullable=True))
op.add_column('submissions', sa.Column('club', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))
op.add_column('commentvotes', sa.Column('created_utc', sa.INTEGER(), autoincrement=False, nullable=False))