Rejigger the Mod/Report state system.

This commit is contained in:
Ben Rog-Wilhelm 2023-06-25 20:46:24 -05:00
parent 31ebdd0213
commit 7266c7887c
36 changed files with 490 additions and 222 deletions

View file

@ -174,6 +174,9 @@ div.deleted {
div.deleted.banned { div.deleted.banned {
background-color: var(--gray) !important; background-color: var(--gray) !important;
} }
div.deleted.removed {
background-color: var(--gray) !important;
}
.comment-anchor:target, .unread { .comment-anchor:target, .unread {
background: #2280B310 !important; background: #2280B310 !important;
} }

View file

@ -4336,6 +4336,9 @@ div.deleted {
div.deleted.banned { div.deleted.banned {
background-color: #964000 !important; background-color: #964000 !important;
} }
div.deleted.removed {
background-color: #964000 !important;
}
.text-admin { .text-admin {
color: var(--primary); color: var(--primary);
} }

View file

@ -7,6 +7,7 @@ from sqlalchemy import *
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from files.classes.base import CreatedBase from files.classes.base import CreatedBase
from files.classes.visstate import StateMod, StateReport
from files.helpers.config.const import * from files.helpers.config.const import *
from files.helpers.config.environment import SCORE_HIDING_TIME_HOURS, SITE_FULL from files.helpers.config.environment import SCORE_HIDING_TIME_HOURS, SITE_FULL
from files.helpers.content import (ModerationState, body_displayed, from files.helpers.content import (ModerationState, body_displayed,
@ -27,11 +28,9 @@ class Comment(CreatedBase):
author_id = Column(Integer, ForeignKey("users.id"), nullable=False) author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
parent_submission = Column(Integer, ForeignKey("submissions.id")) parent_submission = Column(Integer, ForeignKey("submissions.id"))
edited_utc = Column(Integer, default=0, nullable=False) edited_utc = Column(Integer, default=0, nullable=False)
is_banned = Column(Boolean, default=False, nullable=False)
ghost = Column(Boolean, default=False, nullable=False) ghost = Column(Boolean, default=False, nullable=False)
bannedfor = Column(Boolean) bannedfor = Column(Boolean)
distinguish_level = Column(Integer, default=0, nullable=False) distinguish_level = Column(Integer, default=0, nullable=False)
is_approved = Column(Integer, ForeignKey("users.id"))
level = Column(Integer, default=1, nullable=False) level = Column(Integer, default=1, nullable=False)
parent_comment_id = Column(Integer, ForeignKey("comments.id")) parent_comment_id = Column(Integer, ForeignKey("comments.id"))
top_comment_id = Column(Integer) top_comment_id = Column(Integer)
@ -47,20 +46,17 @@ class Comment(CreatedBase):
descendant_count = Column(Integer, default=0, nullable=False) descendant_count = Column(Integer, default=0, nullable=False)
body = Column(Text) body = Column(Text)
body_html = Column(Text, nullable=False) body_html = Column(Text, nullable=False)
ban_reason = Column(String)
filter_state = Column(String, nullable=False)
volunteer_janitor_badness = Column(Float, default=0.5, nullable=False) volunteer_janitor_badness = Column(Float, default=0.5, nullable=False)
# Visibility states here # Visibility states here
state_user_deleted_utc = Column(DateTime(timezone=True), nullable=True) # null if it hasn't been deleted by the user state_user_deleted_utc = Column(DateTime(timezone=True), nullable=True) # null if it hasn't been deleted by the user
# TBD: state_mod state_mod = Column(Enum(StateMod), default=StateMod.Filtered, nullable=False) # default to Filtered just to partially neuter possible exploits
# TBD: state_mod_set_by state_mod_set_by = Column(String, nullable=True) # This should *really* be a User.id, but I don't want to mess with the required refactoring at the moment - it's extra hard because it could potentially be a lot of extra either data or queries
# TBD: state_report state_report = Column(Enum(StateReport), default=StateReport.Unreported, nullable=False)
Index('comment_parent_index', parent_comment_id) Index('comment_parent_index', parent_comment_id)
Index('comment_post_id_index', parent_submission) Index('comment_post_id_index', parent_submission)
Index('comments_user_index', author_id) Index('comments_user_index', author_id)
Index('fki_comment_approver_fkey', is_approved)
Index('fki_comment_sentto_fkey', sentto) Index('fki_comment_sentto_fkey', sentto)
oauth_app = relationship("OauthApp", viewonly=True) oauth_app = relationship("OauthApp", viewonly=True)
@ -79,11 +75,6 @@ class Comment(CreatedBase):
viewonly=True) viewonly=True)
notes = relationship("UserNote", back_populates="comment") notes = relationship("UserNote", back_populates="comment")
def __init__(self, *args, **kwargs):
if 'filter_state' not in kwargs:
kwargs['filter_state'] = 'normal'
super().__init__(*args, **kwargs)
def __repr__(self): def __repr__(self):
return f"<{self.__class__.__name__}(id={self.id})>" return f"<{self.__class__.__name__}(id={self.id})>"
@ -171,13 +162,13 @@ class Comment(CreatedBase):
if not self.parent_submission: if not self.parent_submission:
return sorted((x for x in self.child_comments return sorted((x for x in self.child_comments
if x.author if x.author
and (x.filter_state not in ('filtered', 'removed') or x.author_id == author_id) and (x.state_mod == StateMod.Visible or x.author_id == author_id)
and not x.author.shadowbanned), and not x.author.shadowbanned),
key=lambda x: x.created_utc) key=lambda x: x.created_utc)
return sorted((x for x in self.child_comments return sorted((x for x in self.child_comments
if x.author if x.author
and not x.author.shadowbanned and not x.author.shadowbanned
and (x.filter_state not in ('filtered', 'removed') or x.author_id == author_id)), and (x.state_mod == StateMod.Visible or x.author_id == author_id)),
key=lambda x: x.created_utc, reverse=True) key=lambda x: x.created_utc, reverse=True)
@property @property
@ -234,8 +225,6 @@ class Comment(CreatedBase):
'is_bot': self.is_bot, 'is_bot': self.is_bot,
'created_utc': self.created_utc, 'created_utc': self.created_utc,
'edited_utc': self.edited_utc or 0, 'edited_utc': self.edited_utc or 0,
'is_banned': bool(self.is_banned),
'state_user_deleted_utc': self.state_user_deleted_utc,
'is_nsfw': self.over_18, 'is_nsfw': self.over_18,
'permalink': f'/comment/{self.id}', 'permalink': f'/comment/{self.id}',
'is_pinned': self.is_pinned, 'is_pinned': self.is_pinned,
@ -248,21 +237,21 @@ class Comment(CreatedBase):
'flags': flags, 'flags': flags,
} }
if self.ban_reason:
data["ban_reason"]=self.ban_reason
return data return data
def award_count(self, kind): def award_count(self, kind):
if not FEATURES['AWARDS']: return 0 if not FEATURES['AWARDS']: return 0
return len([x for x in self.awards if x.kind == kind]) return len([x for x in self.awards if x.kind == kind])
def is_visible(self):
return self.state_mod == StateMod.Visible
@property @property
@lazy @lazy
def json_core(self): def json_core(self):
if self.is_banned: if self.state_mod != StateMod.Visible:
data = {'is_banned': True, data = {'state_mod_not_visible': True,
'ban_reason': self.ban_reason, 'state_mod_set_by': self.state_mod_set_by,
'id': self.id, 'id': self.id,
'post': self.post.id if self.post else 0, 'post': self.post.id if self.post else 0,
'level': self.level, 'level': self.level,
@ -287,7 +276,7 @@ class Comment(CreatedBase):
@lazy @lazy
def json(self): def json(self):
data = self.json_core data = self.json_core
if self.state_user_deleted_utc or self.is_banned: return data if self.state_user_deleted_utc or self.state_mod != StateMod.Visible: return data
data["author"] = '👻' if self.ghost else self.author.json_core data["author"] = '👻' if self.ghost else self.author.json_core
data["post"] = self.post.json_core if self.post else '' data["post"] = self.post.json_core if self.post else ''
return data return data
@ -317,7 +306,7 @@ class Comment(CreatedBase):
if v and self.author_id == v.id: return False if v and self.author_id == v.id: return False
if path == '/admin/removed/comments': return False if path == '/admin/removed/comments': return False
if self.over_18 and not (v and v.over_18) and not (self.post and self.post.over_18): return True if self.over_18 and not (v and v.over_18) and not (self.post and self.post.over_18): return True
if self.is_banned: return True # we no longer collapse removed things; the mods want to see them, non-mods see a placeholder anyway
if v and v.filter_words and self.body and any(x in self.body for x in v.filter_words): return True if v and v.filter_words and self.body and any(x in self.body for x in v.filter_words): return True
return False return False

View file

@ -8,6 +8,7 @@ from sqlalchemy.sql.sqltypes import Boolean, Integer, String, Text
from files.classes.cron.tasks import (RepeatableTask, ScheduledTaskType, from files.classes.cron.tasks import (RepeatableTask, ScheduledTaskType,
TaskRunContext) TaskRunContext)
from files.classes.submission import Submission from files.classes.submission import Submission
from files.classes.visstate import StateMod
from files.helpers.config.const import SUBMISSION_TITLE_LENGTH_MAXIMUM from files.helpers.config.const import SUBMISSION_TITLE_LENGTH_MAXIMUM
from files.helpers.content import ModerationState, body_displayed from files.helpers.content import ModerationState, body_displayed
from files.helpers.lazy import lazy from files.helpers.lazy import lazy
@ -68,7 +69,7 @@ class ScheduledSubmissionTask(RepeatableTask):
body_html=self.body_html, body_html=self.body_html,
flair=self.flair, flair=self.flair,
ghost=self.ghost, ghost=self.ghost,
filter_state='normal', state_mod=StateMod.Visible,
embed_url=self.embed_url, embed_url=self.embed_url,
task_id=self.id, task_id=self.id,
) )
@ -127,8 +128,8 @@ class ScheduledSubmissionTask(RepeatableTask):
return 0 return 0
@property @property
def filter_state(self) -> str: def state_mod(self) -> StateMod:
return 'normal' return StateMod.Visible
def award_count(self, kind): def award_count(self, kind):
return 0 return 0

View file

@ -6,6 +6,7 @@ from sqlalchemy.orm import Session, declared_attr, deferred, relationship
from files.classes.base import CreatedBase from files.classes.base import CreatedBase
from files.classes.flags import Flag from files.classes.flags import Flag
from files.classes.visstate import StateMod, StateReport
from files.classes.votes import Vote from files.classes.votes import Vote
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 *
@ -23,7 +24,6 @@ class Submission(CreatedBase):
author_id = Column(Integer, ForeignKey("users.id"), nullable=False) author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
edited_utc = Column(Integer, default=0, nullable=False) edited_utc = Column(Integer, default=0, nullable=False)
thumburl = Column(String) thumburl = Column(String)
is_banned = Column(Boolean, default=False, nullable=False)
bannedfor = Column(Boolean) bannedfor = Column(Boolean)
ghost = Column(Boolean, default=False, nullable=False) ghost = Column(Boolean, default=False, nullable=False)
views = Column(Integer, default=0, nullable=False) views = Column(Integer, default=0, nullable=False)
@ -34,7 +34,6 @@ class Submission(CreatedBase):
private = Column(Boolean, default=False, nullable=False) private = Column(Boolean, default=False, nullable=False)
club = Column(Boolean, default=False, nullable=False) club = Column(Boolean, default=False, nullable=False)
comment_count = Column(Integer, default=0, nullable=False) comment_count = Column(Integer, default=0, nullable=False)
is_approved = Column(Integer, ForeignKey("users.id"))
over_18 = Column(Boolean, default=False, nullable=False) over_18 = Column(Boolean, default=False, nullable=False)
is_bot = Column(Boolean, default=False, nullable=False) is_bot = Column(Boolean, default=False, nullable=False)
upvotes = Column(Integer, default=1, nullable=False) upvotes = Column(Integer, default=1, nullable=False)
@ -47,26 +46,23 @@ class Submission(CreatedBase):
body = Column(Text) body = Column(Text)
body_html = Column(Text) body_html = Column(Text)
flair = Column(String) flair = Column(String)
ban_reason = Column(String)
embed_url = Column(String) embed_url = Column(String)
filter_state = Column(String, nullable=False)
task_id = Column(Integer, ForeignKey("tasks_repeatable_scheduled_submissions.id")) task_id = Column(Integer, ForeignKey("tasks_repeatable_scheduled_submissions.id"))
# Visibility states here # Visibility states here
state_user_deleted_utc = Column(DateTime(timezone=True), nullable=True) # null if it hasn't been deleted by the user state_user_deleted_utc = Column(DateTime(timezone=True), nullable=True) # null if it hasn't been deleted by the user
# TBD: state_mod state_mod = Column(Enum(StateMod), default=StateMod.Filtered, nullable=False) # default to Filtered just to partially neuter possible exploits
# TBD: state_mod_set_by state_mod_set_by = Column(String, nullable=True) # This should *really* be a User.id, but I don't want to mess with the required refactoring at the moment - it's extra hard because it could potentially be a lot of extra either data or queries
# TBD: state_report state_report = Column(Enum(StateReport), default=StateReport.Unreported, nullable=False)
Index('fki_submissions_approver_fkey', is_approved)
Index('post_app_id_idx', app_id) Index('post_app_id_idx', app_id)
Index('subimssion_binary_group_idx', is_banned, state_user_deleted_utc, over_18) Index('subimssion_binary_group_idx', state_mod, state_user_deleted_utc, over_18)
Index('submission_isbanned_idx', is_banned) Index('submission_state_mod_idx', state_mod)
Index('submission_isdeleted_idx', state_user_deleted_utc) Index('submission_isdeleted_idx', state_user_deleted_utc)
@declared_attr @declared_attr
def submission_new_sort_idx(self): def submission_new_sort_idx(self):
return Index('submission_new_sort_idx', self.is_banned, self.state_user_deleted_utc, self.created_utc.desc(), self.over_18) return Index('submission_new_sort_idx', self.state_mod, self.state_user_deleted_utc, self.created_utc.desc(), self.over_18)
Index('submission_pinned_idx', is_pinned) Index('submission_pinned_idx', is_pinned)
Index('submissions_author_index', author_id) Index('submissions_author_index', author_id)
@ -83,7 +79,6 @@ class Submission(CreatedBase):
author = relationship("User", primaryjoin="Submission.author_id==User.id") author = relationship("User", primaryjoin="Submission.author_id==User.id")
oauth_app = relationship("OauthApp", viewonly=True) oauth_app = relationship("OauthApp", viewonly=True)
approved_by = relationship("User", uselist=False, primaryjoin="Submission.is_approved==User.id", viewonly=True)
awards = relationship("AwardRelationship", viewonly=True) awards = relationship("AwardRelationship", viewonly=True)
reports = relationship("Flag", viewonly=True) reports = relationship("Flag", viewonly=True)
comments = relationship("Comment", primaryjoin="Comment.parent_submission==Submission.id") comments = relationship("Comment", primaryjoin="Comment.parent_submission==Submission.id")
@ -107,7 +102,7 @@ class Submission(CreatedBase):
author = self.author author = self.author
author.post_count = db.query(Submission.id).filter_by( author.post_count = db.query(Submission.id).filter_by(
author_id=self.author_id, author_id=self.author_id,
is_banned=False, state_mod=StateMod.Visible,
state_user_deleted_utc=None).count() state_user_deleted_utc=None).count()
db.add(author) db.add(author)
@ -235,8 +230,6 @@ class Submission(CreatedBase):
data = {'author_name': self.author_name if self.author else '', data = {'author_name': self.author_name if self.author else '',
'permalink': self.permalink, 'permalink': self.permalink,
'shortlink': self.shortlink, 'shortlink': self.shortlink,
'is_banned': bool(self.is_banned),
'state_user_deleted_utc': self.state_user_deleted_utc,
'created_utc': self.created_utc, 'created_utc': self.created_utc,
'id': self.id, 'id': self.id,
'title': self.title, 'title': self.title,
@ -261,24 +254,21 @@ class Submission(CreatedBase):
'club': self.club, 'club': self.club,
} }
if self.ban_reason:
data["ban_reason"]=self.ban_reason
return data return data
@property @property
@lazy @lazy
def json_core(self): def json_core(self):
if self.is_banned: if self.state_mod != StateMod.Visible:
return {'is_banned': True, return {'state_mod_not_visible': True,
'state_user_deleted_utc': self.state_user_deleted_utc, 'state_user_deleted_utc': self.state_user_deleted_utc,
'ban_reason': self.ban_reason, 'state_mod_set_by': self.state_mod_set_by,
'id': self.id, 'id': self.id,
'title': self.title, 'title': self.title,
'permalink': self.permalink, 'permalink': self.permalink,
} }
elif self.state_user_deleted_utc: elif self.state_user_deleted_utc:
return {'is_banned': bool(self.is_banned), return {'state_mod_not_visible': False,
'state_user_deleted_utc': self.state_user_deleted_utc, 'state_user_deleted_utc': self.state_user_deleted_utc,
'id': self.id, 'id': self.id,
'title': self.title, 'title': self.title,
@ -292,7 +282,7 @@ class Submission(CreatedBase):
def json(self): def json(self):
data=self.json_core data=self.json_core
if self.state_user_deleted_utc or self.is_banned: if self.state_user_deleted_utc or self.state_mod != StateMod.Visible:
return data return data
data["author"]='👻' if self.ghost else self.author.json_core data["author"]='👻' if self.ghost else self.author.json_core

View file

@ -18,6 +18,7 @@ from files.classes.notifications import Notification
from files.classes.saves import CommentSaveRelationship, SaveRelationship from files.classes.saves import CommentSaveRelationship, SaveRelationship
from files.classes.subscriptions import Subscription from files.classes.subscriptions import Subscription
from files.classes.userblock import UserBlock from files.classes.userblock import UserBlock
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,
@ -335,7 +336,7 @@ class User(CreatedBase):
@property @property
@lazy @lazy
def notifications_count(self): def notifications_count(self):
notifs = g.db.query(Notification.user_id).join(Comment).filter(Notification.user_id == self.id, Notification.read == False, Comment.is_banned == False, Comment.state_user_deleted_utc == None) notifs = g.db.query(Notification.user_id).join(Comment).filter(Notification.user_id == self.id, Notification.read == False, Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None)
if not self.shadowbanned and self.admin_level < 3: if not self.shadowbanned and self.admin_level < 3:
notifs = notifs.join(User, User.id == Comment.author_id).filter(User.shadowbanned == None) notifs = notifs.join(User, User.id == Comment.author_id).filter(User.shadowbanned == None)
@ -350,7 +351,7 @@ class User(CreatedBase):
@property @property
@lazy @lazy
def reddit_notifications_count(self): def reddit_notifications_count(self):
return g.db.query(Notification.user_id).join(Comment).filter(Notification.user_id == self.id, Notification.read == False, Comment.is_banned == False, Comment.state_user_deleted_utc == None, Comment.body_html.like('%<p>New site mention: <a href="https://old.reddit.com/r/%'), Comment.parent_submission == None, Comment.author_id == NOTIFICATIONS_ID).count() return g.db.query(Notification.user_id).join(Comment).filter(Notification.user_id == self.id, Notification.read == False, Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None, Comment.body_html.like('%<p>New site mention: <a href="https://old.reddit.com/r/%'), Comment.parent_submission == None, Comment.author_id == NOTIFICATIONS_ID).count()
@property @property
@lazy @lazy
@ -528,7 +529,7 @@ class User(CreatedBase):
@lazy @lazy
def saved_idlist(self, page=1): def saved_idlist(self, page=1):
saved = [x[0] for x in g.db.query(SaveRelationship.submission_id).filter_by(user_id=self.id).all()] saved = [x[0] for x in g.db.query(SaveRelationship.submission_id).filter_by(user_id=self.id).all()]
posts = g.db.query(Submission.id).filter(Submission.id.in_(saved), Submission.is_banned == False, Submission.state_user_deleted_utc == None) posts = g.db.query(Submission.id).filter(Submission.id.in_(saved), Submission.state_mod == StateMod.Visible, Submission.state_user_deleted_utc == None)
if self.admin_level < 2: if self.admin_level < 2:
posts = posts.filter(Submission.author_id.notin_(self.userblocks)) posts = posts.filter(Submission.author_id.notin_(self.userblocks))
@ -538,7 +539,7 @@ class User(CreatedBase):
@lazy @lazy
def saved_comment_idlist(self, page=1): def saved_comment_idlist(self, page=1):
saved = [x[0] for x in g.db.query(CommentSaveRelationship.comment_id).filter_by(user_id=self.id).all()] saved = [x[0] for x in g.db.query(CommentSaveRelationship.comment_id).filter_by(user_id=self.id).all()]
comments = g.db.query(Comment.id).filter(Comment.id.in_(saved), Comment.is_banned == False, Comment.state_user_deleted_utc == None) comments = g.db.query(Comment.id).filter(Comment.id.in_(saved), Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None)
if self.admin_level < 2: if self.admin_level < 2:
comments = comments.filter(Comment.author_id.notin_(self.userblocks)) comments = comments.filter(Comment.author_id.notin_(self.userblocks))

13
files/classes/visstate.py Normal file
View file

@ -0,0 +1,13 @@
import enum
class StateMod(enum.Enum):
Visible = 0
Filtered = 1
Removed = 2
class StateReport(enum.Enum):
Unreported = 0
Resolved = 1
Reported = 2
Ignored = 3

View file

@ -8,6 +8,7 @@ from werkzeug.security import generate_password_hash
from files.__main__ import app, db_session from files.__main__ import app, db_session
from files.classes import Comment, CommentVote, Submission, User, Vote from files.classes import Comment, CommentVote, Submission, User, Vote
from files.classes.visstate import StateMod
from files.helpers.comments import bulk_recompute_descendant_counts from files.helpers.comments import bulk_recompute_descendant_counts
@ -104,7 +105,7 @@ def seed_db_worker(num_users = 900, num_posts = 40, num_toplevel_comments = 1000
title=f'Clever unique post title number {i}', title=f'Clever unique post title number {i}',
title_html=f'Clever unique post title number {i}', title_html=f'Clever unique post title number {i}',
ghost=False, ghost=False,
filter_state='normal' state_mod=StateMod.Visible,
) )
db.add(post) db.add(post)
posts.append(post) posts.append(post)
@ -127,7 +128,8 @@ def seed_db_worker(num_users = 900, num_posts = 40, num_toplevel_comments = 1000
app_id=None, app_id=None,
body_html=f'toplevel {i}', body_html=f'toplevel {i}',
body=f'toplevel {i}', body=f'toplevel {i}',
ghost=False ghost=False,
state_mod=StateMod.Visible,
) )
db.add(comment) db.add(comment)
comments.append(comment) comments.append(comment)
@ -156,7 +158,8 @@ def seed_db_worker(num_users = 900, num_posts = 40, num_toplevel_comments = 1000
app_id=None, app_id=None,
body_html=f'reply {i}', body_html=f'reply {i}',
body=f'reply {i}', body=f'reply {i}',
ghost=False ghost=False,
state_mod=StateMod.Visible,
) )
db.add(comment) db.add(comment)
comments.append(comment) comments.append(comment)

View file

@ -3,6 +3,7 @@ from flask import g
from .sanitize import * from .sanitize import *
from .config.const import * from .config.const import *
from files.classes.visstate import StateMod
def create_comment(text_html, autojanny=False): def create_comment(text_html, autojanny=False):
if autojanny: author_id = AUTOJANNY_ID if autojanny: author_id = AUTOJANNY_ID
@ -11,7 +12,8 @@ def create_comment(text_html, autojanny=False):
new_comment = Comment(author_id=author_id, new_comment = Comment(author_id=author_id,
parent_submission=None, parent_submission=None,
body_html=text_html, body_html=text_html,
distinguish_level=6) distinguish_level=6,
state_mod=StateMod.Visible,)
g.db.add(new_comment) g.db.add(new_comment)
g.db.flush() g.db.flush()

View file

@ -9,6 +9,7 @@ from sqlalchemy.orm import Query, aliased
from sqlalchemy.sql.expression import alias, func, text from sqlalchemy.sql.expression import alias, func, text
from files.classes import Comment, Notification, Subscription, User from files.classes import Comment, Notification, Subscription, User
from files.classes.visstate import StateMod
from files.helpers.alerts import NOTIFY_USERS from files.helpers.alerts import NOTIFY_USERS
from files.helpers.assetcache import assetcache_path from files.helpers.assetcache import assetcache_path
from files.helpers.config.environment import (PUSHER_ID, PUSHER_KEY, SITE_FULL, from files.helpers.config.environment import (PUSHER_ID, PUSHER_KEY, SITE_FULL,
@ -65,7 +66,7 @@ def update_author_comment_count(comment, delta):
comment.author.comment_count = g.db.query(Comment).filter( comment.author.comment_count = g.db.query(Comment).filter(
Comment.author_id == comment.author_id, Comment.author_id == comment.author_id,
Comment.parent_submission != None, Comment.parent_submission != None,
Comment.is_banned == False, Comment.state_mod == StateMod.Visible,
Comment.state_user_deleted_utc == None, Comment.state_user_deleted_utc == None,
).count() ).count()
g.db.add(comment.author) g.db.add(comment.author)
@ -218,7 +219,7 @@ def comment_on_publish(comment:Comment):
def comment_on_unpublish(comment:Comment): def comment_on_unpublish(comment:Comment):
""" """
Run when a comment becomes invisible: when a moderator makes the comment non-visible Run when a comment becomes invisible: when a moderator makes the comment non-visible
by changing the filter_state to "removed", or when the user deletes the comment. by changing the state_mod to "removed", or when the user deletes the comment.
Should be used to update stateful counters, notifications, etc. that Should be used to update stateful counters, notifications, etc. that
reflect the comments users will actually see. reflect the comments users will actually see.
""" """
@ -231,8 +232,7 @@ def comment_filter_moderated(q: Query, v: Optional[User]) -> Query:
.filter(User.shadowbanned == None) .filter(User.shadowbanned == None)
if not v or v.admin_level < 2: if not v or v.admin_level < 2:
q = q.filter( q = q.filter(
((Comment.filter_state != 'filtered') Comment.state_mod == StateMod.Visible
& (Comment.filter_state != 'removed'))
| (Comment.author_id == ((v and v.id) or 0)) | (Comment.author_id == ((v and v.id) or 0))
) )
return q return q

View file

@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, Any, Optional
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from files.helpers.config.const import PERMS from files.helpers.config.const import PERMS
from files.classes.visstate import StateMod, StateReport
if TYPE_CHECKING: if TYPE_CHECKING:
from files.classes import Comment, Submission, User from files.classes import Comment, Submission, User
@ -108,11 +109,11 @@ class ModerationState:
@classmethod @classmethod
def from_submittable(cls, target: Submittable) -> "ModerationState": def from_submittable(cls, target: Submittable) -> "ModerationState":
return cls( return cls(
removed=bool(target.is_banned or target.filter_state == 'removed'), removed=bool(target.state_mod != StateMod.Visible),
removed_by_name=target.ban_reason, # type: ignore removed_by_name=target.state_mod_set_by, # type: ignore
deleted=bool(target.state_user_deleted_utc != None), deleted=bool(target.state_user_deleted_utc != None),
reports_ignored=bool(target.filter_state == 'ignored'), reports_ignored=bool(target.state_report == StateReport.Ignored),
filtered=bool(target.filter_state == 'filtered'), filtered=bool(target.state_mod == StateMod.Filtered),
op_shadowbanned=bool(target.author.shadowbanned), op_shadowbanned=bool(target.author.shadowbanned),
op_id=target.author_id, # type: ignore op_id=target.author_id, # type: ignore
op_name_safe=target.author_name op_name_safe=target.author_name

View file

@ -5,6 +5,7 @@ from jinja2 import pass_context
from files.__main__ import app from files.__main__ import app
from files.classes.cron.tasks import ScheduledTaskType from files.classes.cron.tasks import ScheduledTaskType
from files.classes.visstate import StateMod, StateReport
from files.helpers.assetcache import assetcache_path from files.helpers.assetcache import assetcache_path
from files.helpers.config.environment import (CARD_VIEW, DEFAULT_COLOR, from files.helpers.config.environment import (CARD_VIEW, DEFAULT_COLOR,
ENABLE_DOWNVOTES, FINGERPRINT_TOKEN, PUSHER_ID, SITE, SITE_FULL, SITE_ID, ENABLE_DOWNVOTES, FINGERPRINT_TOKEN, PUSHER_ID, SITE, SITE_FULL, SITE_ID,
@ -84,6 +85,8 @@ def inject_constants():
"SORTS_POSTS":SORTS_POSTS, "SORTS_POSTS":SORTS_POSTS,
"CSS_LENGTH_MAXIMUM":CSS_LENGTH_MAXIMUM, "CSS_LENGTH_MAXIMUM":CSS_LENGTH_MAXIMUM,
"ScheduledTaskType":ScheduledTaskType, "ScheduledTaskType":ScheduledTaskType,
"StateMod": StateMod,
"StateReport": StateReport,
} }

View file

@ -12,6 +12,7 @@ from sqlalchemy import func
from files.__main__ import cache from files.__main__ import cache
from files.classes.submission import Submission from files.classes.submission import Submission
from files.classes.user import User from files.classes.user import User
from files.classes.visstate import StateMod
from files.classes.votes import Vote from files.classes.votes import Vote
from files.helpers.contentsorting import apply_time_filter, sort_objects from files.helpers.contentsorting import apply_time_filter, sort_objects
from files.helpers.strings import sql_ilike_clean from files.helpers.strings import sql_ilike_clean
@ -30,7 +31,7 @@ def frontlist(v=None, sort='new', page=1, t="all", ids_only=True, ccmode="false"
posts = posts.filter(Submission.id.notin_(voted)) posts = posts.filter(Submission.id.notin_(voted))
if not v or v.admin_level < 2: if not v or v.admin_level < 2:
filter_clause = (Submission.filter_state != 'filtered') & (Submission.filter_state != 'removed') filter_clause = Submission.state_mod == StateMod.Visible
if v: if v:
filter_clause = filter_clause | (Submission.author_id == v.id) filter_clause = filter_clause | (Submission.author_id == v.id)
posts = posts.filter(filter_clause) posts = posts.filter(filter_clause)
@ -44,7 +45,7 @@ def frontlist(v=None, sort='new', page=1, t="all", ids_only=True, ccmode="false"
if (ccmode == "true"): if (ccmode == "true"):
posts = posts.filter(Submission.club == True) posts = posts.filter(Submission.club == True)
posts = posts.filter_by(is_banned=False, private=False, state_user_deleted_utc=None) posts = posts.filter_by(state_mod=StateMod.Visible, private=False, state_user_deleted_utc=None)
if ccmode == "false" and not gt and not lt: if ccmode == "false" and not gt and not lt:
posts = posts.filter_by(stickied=None) posts = posts.filter_by(stickied=None)
@ -77,7 +78,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 ccmode == "false" and not gt and not lt:
pins = g.db.query(Submission).filter(Submission.stickied != None, Submission.is_banned == False) 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:
pins = pins.filter(Submission.author_id.notin_(v.userblocks)) pins = pins.filter(Submission.author_id.notin_(v.userblocks))
@ -107,7 +108,7 @@ def userpagelisting(u:User, v=None, page=1, sort="new", t="all"):
posts = g.db.query(Submission.id).filter_by(author_id=u.id, is_pinned=False) posts = g.db.query(Submission.id).filter_by(author_id=u.id, is_pinned=False)
if not (v and (v.admin_level >= 2 or v.id == u.id)): if not (v and (v.admin_level >= 2 or v.id == u.id)):
posts = posts.filter_by(state_user_deleted_utc=None, is_banned=False, private=False, ghost=False) posts = posts.filter_by(state_user_deleted_utc=None, state_mod=StateMod.Visible, private=False, ghost=False)
posts = apply_time_filter(posts, t, Submission) posts = apply_time_filter(posts, t, Submission)
posts = sort_objects(posts, sort, Submission) posts = sort_objects(posts, sort, Submission)
@ -119,7 +120,7 @@ def userpagelisting(u:User, v=None, page=1, sort="new", t="all"):
@cache.memoize(timeout=CHANGELOGLIST_TIMEOUT_SECS) @cache.memoize(timeout=CHANGELOGLIST_TIMEOUT_SECS)
def changeloglist(v=None, sort="new", page=1, t="all"): def changeloglist(v=None, sort="new", page=1, t="all"):
posts = g.db.query(Submission.id).filter_by(is_banned=False, private=False,).filter(Submission.state_user_deleted_utc == None) posts = g.db.query(Submission.id).filter_by(state_mod=StateMod.Visible, private=False,).filter(Submission.state_user_deleted_utc == None)
if v.admin_level < 2: if v.admin_level < 2:
posts = posts.filter(Submission.author_id.notin_(v.userblocks)) posts = posts.filter(Submission.author_id.notin_(v.userblocks))

View file

@ -5,6 +5,7 @@ from datetime import datetime
import requests import requests
from files.classes import * from files.classes import *
from files.classes.visstate import StateMod, StateReport
from files.helpers.alerts import * from files.helpers.alerts import *
from files.helpers.caching import invalidate_cache from files.helpers.caching import invalidate_cache
from files.helpers.comments import comment_on_publish, comment_on_unpublish from files.helpers.comments import comment_on_publish, comment_on_unpublish
@ -136,8 +137,8 @@ def revert_actions(v, username):
comments = g.db.query(Comment).filter(Comment.id.in_(comments)).all() comments = g.db.query(Comment).filter(Comment.id.in_(comments)).all()
for item in posts + comments: for item in posts + comments:
item.is_banned = False item.state_mod = StateMod.Visible
item.ban_reason = None item.state_mod_set_by = v.username
g.db.add(item) g.db.add(item)
users = (x[0] for x in g.db.query(ModAction.target_user_id).filter(ModAction.user_id == user.id, ModAction.created_utc > cutoff, ModAction.kind.in_(('shadowban', 'ban_user'))).all()) users = (x[0] for x in g.db.query(ModAction.target_user_id).filter(ModAction.user_id == user.id, ModAction.created_utc > cutoff, ModAction.kind.in_(('shadowban', 'ban_user'))).all())
@ -235,7 +236,7 @@ def filtered_submissions(v):
posts_just_ids = g.db.query(Submission) \ posts_just_ids = g.db.query(Submission) \
.order_by(Submission.id.desc()) \ .order_by(Submission.id.desc()) \
.filter(Submission.filter_state == 'filtered') \ .filter(Submission.state_mod == StateMod.Filtered) \
.limit(26) \ .limit(26) \
.offset(25 * (page - 1)) \ .offset(25 * (page - 1)) \
.with_entities(Submission.id) .with_entities(Submission.id)
@ -255,7 +256,7 @@ def filtered_comments(v):
comments_just_ids = g.db.query(Comment) \ comments_just_ids = g.db.query(Comment) \
.order_by(Comment.id.desc()) \ .order_by(Comment.id.desc()) \
.filter(Comment.filter_state == 'filtered') \ .filter(Comment.state_mod == StateMod.Filtered) \
.limit(26) \ .limit(26) \
.offset(25 * (page - 1)) \ .offset(25 * (page - 1)) \
.with_entities(Comment.id) .with_entities(Comment.id)
@ -266,6 +267,9 @@ def filtered_comments(v):
return render_template("admin/filtered_comments.html", v=v, listing=comments, next_exists=next_exists, page=page, sort="new") return render_template("admin/filtered_comments.html", v=v, listing=comments, next_exists=next_exists, page=page, sort="new")
# NOTE:
# This function is pretty grimy and should be rolled into the Remove/Unremove functions.
# (also rename Unremove to Approve, sigh)
@app.post("/admin/update_filter_status") @app.post("/admin/update_filter_status")
@limiter.exempt @limiter.exempt
@admin_level_required(2) @admin_level_required(2)
@ -277,29 +281,48 @@ def update_filter_status(v):
if new_status not in ['normal', 'removed', 'ignored']: if new_status not in ['normal', 'removed', 'ignored']:
return { 'result': f'Status of {new_status} is not permitted' } return { 'result': f'Status of {new_status} is not permitted' }
if new_status == 'normal':
state_mod_new = StateMod.Visible
state_report_new = StateReport.Resolved
elif new_status == 'removed':
state_mod_new = StateMod.Removed
state_report_new = StateReport.Resolved
elif new_status == 'ignored':
state_mod_new = None # we just leave this as-is
state_report_new = StateReport.Ignored
if post_id: if post_id:
target = g.db.get(Submission, post_id) target = g.db.get(Submission, post_id)
old_status = target.filter_state old_status = target.state_mod
rows_updated = g.db.query(Submission).where(Submission.id == post_id) \
.update({Submission.filter_state: new_status}) # this could totally be one query but it would be kinda ugly
if state_mod_new:
g.db.query(Submission).where(Submission.id == post_id) \
.update({Submission.state_mod: state_mod_new, Submission.state_mod_set_by: v.username})
g.db.query(Submission).where(Submission.id == post_id) \
.update({Submission.state_report: state_report_new})
elif comment_id: elif comment_id:
target = g.db.get(Comment, comment_id) target = g.db.get(Comment, comment_id)
old_status = target.filter_state old_status = target.state_mod
rows_updated = g.db.query(Comment).where(Comment.id == comment_id) \
.update({Comment.filter_state: new_status}) if state_mod_new:
g.db.query(Comment).where(Comment.id == comment_id) \
.update({Comment.state_mod: state_mod_new, Comment.state_mod_set_by: v.username})
g.db.query(Comment).where(Comment.id == comment_id) \
.update({Comment.state_report: state_report_new})
else: else:
return { 'result': f'No valid item ID provided' } return { 'result': f'No valid item ID provided' }
if rows_updated == 1: if target is not None:
# If comment now visible, update state to reflect publication. # If comment now visible, update state to reflect publication.
if (isinstance(target, Comment) if (isinstance(target, Comment)
and old_status in ['filtered', 'removed'] and old_status != StateMod.Visible
and new_status in ['normal', 'ignored']): and state_mod_new == StateMod.Visible):
comment_on_publish(target) # XXX: can cause discrepancies if removal state ≠ filter state comment_on_publish(target) # XXX: can cause discrepancies if removal state ≠ filter state
if (isinstance(target, Comment) if (isinstance(target, Comment)
and old_status in ['normal', 'ignored'] and old_status == StateMod.Visible
and new_status in ['filtered', 'removed']): and state_mod_new != StateMod.Visible and state_mod_new is not None):
comment_on_unpublish(target) # XXX: can cause discrepancies if removal state ≠ filter state comment_on_unpublish(target) # XXX: can cause discrepancies if removal state ≠ filter state
g.db.commit() g.db.commit()
@ -332,7 +355,7 @@ def reported_posts(v):
page = max(1, int(request.values.get("page", 1))) page = max(1, int(request.values.get("page", 1)))
subs_just_ids = g.db.query(Submission) \ subs_just_ids = g.db.query(Submission) \
.filter(Submission.filter_state == 'reported') \ .filter(Submission.state_report == StateReport.Reported) \
.order_by(Submission.id.desc()) \ .order_by(Submission.id.desc()) \
.offset(25 * (page - 1)) \ .offset(25 * (page - 1)) \
.limit(26) \ .limit(26) \
@ -351,13 +374,8 @@ def reported_posts(v):
def reported_comments(v): def reported_comments(v):
page = max(1, int(request.values.get("page", 1))) page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Comment
).filter_by(
is_approved=None,
is_banned=False
).join(Comment.reports).order_by(Comment.id.desc()).offset(25 * (page - 1)).limit(26).all()
comments_just_ids = g.db.query(Comment) \ comments_just_ids = g.db.query(Comment) \
.filter(Comment.filter_state == 'reported') \ .filter(Comment.state_report == StateReport.Reported) \
.order_by(Comment.id.desc()) \ .order_by(Comment.id.desc()) \
.offset(25 * (page - 1)) \ .offset(25 * (page - 1)) \
.limit(26) \ .limit(26) \
@ -415,7 +433,8 @@ def change_settings(v, setting):
level=1, level=1,
body_html=body_html, body_html=body_html,
sentto=MODMAIL_ID, sentto=MODMAIL_ID,
distinguish_level=6 distinguish_level=6,
state_mod=StateMod.Visible,
) )
g.db.add(new_comment) g.db.add(new_comment)
g.db.flush() g.db.flush()
@ -765,7 +784,7 @@ def admin_removed(v):
if page < 1: abort(400) if page < 1: abort(400)
ids = g.db.query(Submission.id).join(User, User.id == Submission.author_id).filter(or_(Submission.is_banned==True, User.shadowbanned != None)).order_by(Submission.id.desc()).offset(25 * (page - 1)).limit(26).all() ids = g.db.query(Submission.id).join(User, User.id == Submission.author_id).filter(or_(Submission.state_mod == StateMod.Removed, User.shadowbanned != None)).order_by(Submission.id.desc()).offset(25 * (page - 1)).limit(26).all()
ids=[x[0] for x in ids] ids=[x[0] for x in ids]
@ -790,7 +809,7 @@ def admin_removed_comments(v):
try: page = int(request.values.get("page", 1)) try: page = int(request.values.get("page", 1))
except: page = 1 except: page = 1
ids = g.db.query(Comment.id).join(User, User.id == Comment.author_id).filter(or_(Comment.is_banned==True, User.shadowbanned != None)).order_by(Comment.id.desc()).offset(25 * (page - 1)).limit(26).all() ids = g.db.query(Comment.id).join(User, User.id == Comment.author_id).filter(or_(Comment.state_mode == StateMod.Removed, User.shadowbanned != None)).order_by(Comment.id.desc()).offset(25 * (page - 1)).limit(26).all()
ids=[x[0] for x in ids] ids=[x[0] for x in ids]
@ -840,7 +859,8 @@ def shadowban(user_id, v):
parent_submission=None, parent_submission=None,
level=1, level=1,
body_html=body_html, body_html=body_html,
distinguish_level=6 distinguish_level=6,
state_mod=StateMod.Visible,
) )
g.db.add(new_comment) g.db.add(new_comment)
g.db.flush() g.db.flush()
@ -1025,7 +1045,8 @@ def ban_user(user_id, v):
parent_submission=None, parent_submission=None,
level=1, level=1,
body_html=body_html, body_html=body_html,
distinguish_level=6 distinguish_level=6,
state_mod=StateMod.Visible,
) )
g.db.add(new_comment) g.db.add(new_comment)
g.db.flush() g.db.flush()
@ -1088,11 +1109,10 @@ def remove_post(post_id, v):
if not post: if not post:
abort(400) abort(400)
post.is_banned = True post.state_mod = StateMod.Removed
post.is_approved = None post.state_mod_set_by = v.username
post.stickied = None post.stickied = None
post.is_pinned = False post.is_pinned = False
post.ban_reason = v.username
g.db.add(post) g.db.add(post)
@ -1125,7 +1145,7 @@ def unremove_post(post_id, v):
if not post: if not post:
abort(400) abort(400)
if post.is_banned: if post.state_mod != StateMod.Visible:
ma=ModAction( ma=ModAction(
kind="unremove_post", kind="unremove_post",
user_id=v.id, user_id=v.id,
@ -1133,9 +1153,8 @@ def unremove_post(post_id, v):
) )
g.db.add(ma) g.db.add(ma)
post.is_banned = False post.state_mod = StateMod.Visible
post.ban_reason = None post.state_mod_set_by = v.username
post.is_approved = v.id
g.db.add(post) g.db.add(post)
@ -1187,7 +1206,7 @@ def api_distinguish_post(post_id, v):
def sticky_post(post_id, v): def sticky_post(post_id, v):
post = g.db.query(Submission).filter_by(id=post_id).one_or_none() post = g.db.query(Submission).filter_by(id=post_id).one_or_none()
if post and not post.stickied: if post and not post.stickied:
pins = g.db.query(Submission.id).filter(Submission.stickied != None, Submission.is_banned == False).count() pins = g.db.query(Submission.id).filter(Submission.stickied != None, Submission.state_mod == StateMod.Visible).count()
if pins > 2: if pins > 2:
if v.admin_level >= 2: if v.admin_level >= 2:
post.stickied = v.username post.stickied = v.username
@ -1296,9 +1315,8 @@ def api_remove_comment(c_id, v):
if not comment: if not comment:
abort(404) abort(404)
comment.is_banned = True comment.state_mod = StateMod.Removed
comment.is_approved = None comment.state_mod_set_by = v.username
comment.ban_reason = v.username
comment_on_unpublish(comment) # XXX: can cause discrepancies if removal state ≠ filter state comment_on_unpublish(comment) # XXX: can cause discrepancies if removal state ≠ filter state
ma=ModAction( ma=ModAction(
kind="remove_comment", kind="remove_comment",
@ -1317,7 +1335,7 @@ def api_unremove_comment(c_id, v):
comment = g.db.query(Comment).filter_by(id=c_id).one_or_none() comment = g.db.query(Comment).filter_by(id=c_id).one_or_none()
if not comment: abort(404) if not comment: abort(404)
if comment.is_banned: if comment.state_mod == StateMod.Removed:
ma=ModAction( ma=ModAction(
kind="unremove_comment", kind="unremove_comment",
user_id=v.id, user_id=v.id,
@ -1325,9 +1343,8 @@ def api_unremove_comment(c_id, v):
) )
g.db.add(ma) g.db.add(ma)
comment.is_banned = False comment.state_mod = StateMod.Visible
comment.ban_reason = None comment.state_mod_set_by = v.username
comment.is_approved = v.id
comment_on_publish(comment) # XXX: can cause discrepancies if removal state ≠ filter state comment_on_publish(comment) # XXX: can cause discrepancies if removal state ≠ filter state
g.db.add(comment) g.db.add(comment)
@ -1426,24 +1443,24 @@ def admin_toggle_ban_domain(v):
@app.post("/admin/nuke_user") @app.post("/admin/nuke_user")
@limiter.exempt @limiter.exempt
@admin_level_required(2) @admin_level_required(3)
def admin_nuke_user(v): def admin_nuke_user(v):
user=get_user(request.values.get("user")) user=get_user(request.values.get("user"))
for post in g.db.query(Submission).filter_by(author_id=user.id).all(): for post in g.db.query(Submission).filter_by(author_id=user.id).all():
if post.is_banned: if post.state_mod != StateMod.Removed:
continue continue
post.is_banned = True post.state_mod == StateMod.Removed
post.ban_reason = v.username post.state_mod_set_by = v.username
g.db.add(post) g.db.add(post)
for comment in g.db.query(Comment).filter_by(author_id=user.id).all(): for comment in g.db.query(Comment).filter_by(author_id=user.id).all():
if comment.is_banned: if comment.state_mod != StateMod.Removed:
continue continue
comment.is_banned = True comment.state_mod == StateMod.Removed
comment.ban_reason = v.username comment.state_mod_set_by = v.username
g.db.add(comment) g.db.add(comment)
ma=ModAction( ma=ModAction(
@ -1460,24 +1477,24 @@ def admin_nuke_user(v):
@app.post("/admin/unnuke_user") @app.post("/admin/unnuke_user")
@limiter.exempt @limiter.exempt
@admin_level_required(2) @admin_level_required(3)
def admin_nunuke_user(v): def admin_nunuke_user(v):
user=get_user(request.values.get("user")) user=get_user(request.values.get("user"))
for post in g.db.query(Submission).filter_by(author_id=user.id).all(): for post in g.db.query(Submission).filter_by(author_id=user.id).all():
if not post.is_banned: if post.state_mod == StateMod.Visible:
continue continue
post.is_banned = False post.state_mod == StateMod.Visible
post.ban_reason = None post.state_mod_set_by = v.username
g.db.add(post) g.db.add(post)
for comment in g.db.query(Comment).filter_by(author_id=user.id).all(): for comment in g.db.query(Comment).filter_by(author_id=user.id).all():
if not comment.is_banned: if comment.state_mod == StateMod.Visible:
continue continue
comment.is_banned = False comment.state_mod == StateMod.Visible
comment.ban_reason = None comment.state_mod_set_by = v.username
g.db.add(comment) g.db.add(comment)
ma=ModAction( ma=ModAction(

View file

@ -1,5 +1,6 @@
from files.__main__ import app, limiter from files.__main__ import app, limiter
from files.classes import * from files.classes import *
from files.classes.visstate import StateMod
from files.helpers.alerts import * from files.helpers.alerts import *
from files.helpers.comments import comment_on_publish from files.helpers.comments import comment_on_publish
from files.helpers.config.const import * from files.helpers.config.const import *
@ -97,7 +98,7 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None):
if request.headers.get("Authorization"): return top_comment.json if request.headers.get("Authorization"): return top_comment.json
else: else:
if post.is_banned and not (v and (v.admin_level >= 2 or post.author_id == v.id)): template = "submission_banned.html" if post.state_mod != StateMod.Visible and not (v and (v.admin_level >= 2 or post.author_id == v.id)): template = "submission_banned.html"
else: template = "submission.html" else: template = "submission.html"
return render_template(template, v=v, p=post, sort=sort, comment_info=comment_info, render_replies=True) return render_template(template, v=v, p=post, sort=sort, comment_info=comment_info, render_replies=True)
@ -188,8 +189,8 @@ def api_comment(v):
days=1) days=1)
for comment in similar_comments: for comment in similar_comments:
comment.is_banned = True comment.state_mod = StateMod.Removed
comment.ban_reason = "AutoJanny" comment.state_mod_set_by = "AutoJanny"
g.db.add(comment) g.db.add(comment)
ma=ModAction( ma=ModAction(
user_id=AUTOJANNY_ID, user_id=AUTOJANNY_ID,
@ -213,7 +214,7 @@ def api_comment(v):
body_html=body_html, body_html=body_html,
body=body[:COMMENT_BODY_LENGTH_MAXIMUM], body=body[:COMMENT_BODY_LENGTH_MAXIMUM],
ghost=parent_post.ghost, ghost=parent_post.ghost,
filter_state='filtered' if is_filtered else 'normal' state_mod=StateMod.Filtered if is_filtered else StateMod.Visible,
) )
c.upvotes = 1 c.upvotes = 1
@ -287,8 +288,8 @@ def edit_comment(cid, v):
days=1) days=1)
for comment in similar_comments: for comment in similar_comments:
comment.is_banned = True comment.state_mod = StateMod.Removed
comment.ban_reason = "AutoJanny" comment.state_mod_set_by = "AutoJanny"
g.db.add(comment) g.db.add(comment)
abort(403, "Too much spam!") abort(403, "Too much spam!")
@ -313,7 +314,7 @@ def edit_comment(cid, v):
g.db.add(c) g.db.add(c)
if c.filter_state != 'filtered': if c.state_mod == StateMod.Visible:
notify_users = NOTIFY_USERS(body, v) notify_users = NOTIFY_USERS(body, v)
for x in notify_users: for x in notify_users:

View file

@ -3,6 +3,7 @@ from sqlalchemy.orm import Query
import files.helpers.listing as listing import files.helpers.listing as listing
from files.__main__ import app, limiter from files.__main__ import app, limiter
from files.classes.submission import Submission from files.classes.submission import Submission
from files.classes.visstate import StateMod
from files.helpers.comments import comment_filter_moderated from files.helpers.comments import comment_filter_moderated
from files.helpers.contentsorting import (apply_time_filter, from files.helpers.contentsorting import (apply_time_filter,
sort_comment_results, sort_objects) sort_comment_results, sort_objects)
@ -26,7 +27,7 @@ def unread(v):
listing = g.db.query(Notification, Comment).join(Comment, Notification.comment_id == Comment.id).filter( listing = g.db.query(Notification, Comment).join(Comment, Notification.comment_id == Comment.id).filter(
Notification.read == False, Notification.read == False,
Notification.user_id == v.id, Notification.user_id == v.id,
Comment.is_banned == False, Comment.state_mod == StateMod.Visible,
Comment.state_user_deleted_utc == None, Comment.state_user_deleted_utc == None,
Comment.author_id != AUTOJANNY_ID, Comment.author_id != AUTOJANNY_ID,
).order_by(Notification.created_utc.desc()).all() ).order_by(Notification.created_utc.desc()).all()
@ -100,7 +101,7 @@ def notifications(v):
else: else:
comments = g.db.query(Comment, Notification).join(Notification, Notification.comment_id == Comment.id).filter( comments = g.db.query(Comment, Notification).join(Notification, Notification.comment_id == Comment.id).filter(
Notification.user_id == v.id, Notification.user_id == v.id,
Comment.is_banned == False, Comment.state_mod == StateMod.Visible,
Comment.state_user_deleted_utc == None, Comment.state_user_deleted_utc == None,
Comment.author_id != AUTOJANNY_ID, Comment.author_id != AUTOJANNY_ID,
Comment.body_html.notlike('%<p>New site mention: <a href="https://old.reddit.com/r/%') Comment.body_html.notlike('%<p>New site mention: <a href="https://old.reddit.com/r/%')
@ -250,7 +251,7 @@ def changelog(v):
@app.get("/random_post") @app.get("/random_post")
def random_post(): def random_post():
p = g.db.query(Submission.id).filter(Submission.state_user_deleted_utc == None, Submission.is_banned == False, Submission.private == False).order_by(func.random()).first() p = g.db.query(Submission.id).filter(Submission.state_user_deleted_utc == None, Submission.state_mod == StateMod.Visible, Submission.private == False).order_by(func.random()).first()
if p: p = p[0] if p: p = p[0]
else: abort(404) else: abort(404)
@ -307,11 +308,10 @@ def get_comments_idlist(page=1, v=None, sort="new", t="all", gt=0, lt=0):
if v.admin_level < 2: if v.admin_level < 2:
comments = comments.filter( comments = comments.filter(
Comment.author_id.notin_(v.userblocks), Comment.author_id.notin_(v.userblocks),
Comment.is_banned == False, Comment.state_mod == StateMod.Visible,
Comment.state_user_deleted_utc == None, Comment.state_user_deleted_utc == None,
Submission.private == False, # comment parent post not private Submission.private == False, # comment parent post not private
User.shadowbanned == None, # comment author not shadowbanned User.shadowbanned == None, # comment author not shadowbanned
Comment.filter_state.notin_(('filtered', 'removed')),
) )
if gt: comments = comments.filter(Comment.created_utc > gt) if gt: comments = comments.filter(Comment.created_utc > gt)

View file

@ -62,7 +62,8 @@ def request_api_keys(v):
level=1, level=1,
body_html=body_html, body_html=body_html,
sentto=MODMAIL_ID, sentto=MODMAIL_ID,
distinguish_level=6 distinguish_level=6,
state_mod=StateMod.Visible,
) )
g.db.add(new_comment) g.db.add(new_comment)
g.db.flush() g.db.flush()

View file

@ -15,6 +15,7 @@ from sqlalchemy.orm import Query
import files.helpers.validators as validators import files.helpers.validators as validators
from files.__main__ import app, db_session, limiter from files.__main__ import app, db_session, limiter
from files.classes import * from files.classes import *
from files.classes.visstate import StateMod
from files.helpers.alerts import * from files.helpers.alerts import *
from files.helpers.caching import invalidate_cache from files.helpers.caching import invalidate_cache
from files.helpers.config.const import * from files.helpers.config.const import *
@ -120,7 +121,7 @@ def post_id(pid, anything=None, v=None):
if request.headers.get("Authorization"): return post.json if request.headers.get("Authorization"): return post.json
else: else:
if post.is_banned and not (v and (v.admin_level >= 2 or post.author_id == v.id)): template = "submission_banned.html" if post.state_mod != StateMod.Visible and not (v and (v.admin_level >= 2 or post.author_id == v.id)): template = "submission_banned.html"
else: template = "submission.html" else: template = "submission.html"
return render_template(template, v=v, p=post, ids=list(ids), sort=sort, render_replies=True, offset=offset) return render_template(template, v=v, p=post, ids=list(ids), sort=sort, render_replies=True, offset=offset)
@ -431,7 +432,7 @@ def api_is_repost():
repost = g.db.query(Submission).filter( repost = g.db.query(Submission).filter(
Submission.url.ilike(search_url), Submission.url.ilike(search_url),
Submission.state_user_deleted_utc == None, Submission.state_user_deleted_utc == None,
Submission.is_banned == False Submission.state_mod == StateMod.Visible
).first() ).first()
if repost: return {'permalink': repost.permalink} if repost: return {'permalink': repost.permalink}
else: return {'permalink': ''} else: return {'permalink': ''}
@ -468,9 +469,9 @@ def _do_antispam_submission_check(v:User, validated:validators.ValidatedSubmissi
v.ban(reason="Spamming.", days=1) v.ban(reason="Spamming.", days=1)
for post in similar_posts + similar_urls: for post in similar_posts + similar_urls:
post.is_banned = True post.state_mod = StateMod.Removed
post.state_mod_set_by = "AutoJanny"
post.is_pinned = False post.is_pinned = False
post.ban_reason = "AutoJanny"
g.db.add(post) g.db.add(post)
ma=ModAction( ma=ModAction(
user_id=AUTOJANNY_ID, user_id=AUTOJANNY_ID,
@ -497,7 +498,7 @@ def _duplicate_check(search_url:Optional[str]) -> Optional[werkzeug.wrappers.Res
repost = g.db.query(Submission).filter( repost = g.db.query(Submission).filter(
func.lower(Submission.url) == search_url.lower(), func.lower(Submission.url) == search_url.lower(),
Submission.state_user_deleted_utc == None, Submission.state_user_deleted_utc == None,
Submission.is_banned == False Submission.state_mod == StateMod.Visible
).first() ).first()
if repost and SITE != 'localhost': if repost and SITE != 'localhost':
return redirect(repost.permalink) return redirect(repost.permalink)
@ -577,7 +578,7 @@ def submit_post(v):
title=validated_post.title, title=validated_post.title,
title_html=validated_post.title_html, title_html=validated_post.title_html,
ghost=False, ghost=False,
filter_state='filtered' if v.admin_level == 0 and app.config['SETTINGS']['FilterNewPosts'] else 'normal', state_mod=StateMod.Filtered if v.admin_level == 0 and app.config['SETTINGS']['FilterNewPosts'] else StateMod.Visible,
thumburl=validated_post.thumburl thumburl=validated_post.thumburl
) )
post.submit(g.db) post.submit(g.db)

View file

@ -1,6 +1,7 @@
from flask import g from flask import g
from files.__main__ import app, limiter from files.__main__ import app, limiter
from files.classes.visstate import StateReport
from files.helpers.get import * from files.helpers.get import *
from files.helpers.sanitize import filter_emojis_only from files.helpers.sanitize import filter_emojis_only
from files.helpers.wrappers import * from files.helpers.wrappers import *
@ -31,8 +32,8 @@ def api_flag_post(pid, v):
# We only want to notify if the user is not permabanned # We only want to notify if the user is not permabanned
if not v.is_suspended_permanently: if not v.is_suspended_permanently:
g.db.query(Submission) \ g.db.query(Submission) \
.where(Submission.id == post.id, Submission.filter_state != 'ignored') \ .where(Submission.id == post.id, Submission.state_report != StateReport.Ignored) \
.update({Submission.filter_state: 'reported'}) .update({Submission.state_report: StateReport.Reported})
g.db.commit() g.db.commit()
@ -53,8 +54,9 @@ def api_flag_comment(cid, v):
# We only want to notify if the user is not permabanned # We only want to notify if the user is not permabanned
if not v.is_suspended_permanently: if not v.is_suspended_permanently:
g.db.query(Comment) \ g.db.query(Comment) \
.where(Comment.id == comment.id, Comment.filter_state != 'ignored') \ .where(Comment.id == comment.id, Comment.state_report != StateReport.Ignored) \
.update({Comment.filter_state: 'reported'}) .update({Comment.state_report: StateReport.Reported})
g.db.commit() g.db.commit()

View file

@ -1,6 +1,7 @@
from sqlalchemy import * from sqlalchemy import *
from files.__main__ import app from files.__main__ import app
from files.classes.visstate import StateMod
from files.helpers.contentsorting import apply_time_filter, sort_objects from files.helpers.contentsorting import apply_time_filter, sort_objects
from files.helpers.strings import sql_ilike_clean from files.helpers.strings import sql_ilike_clean
from files.helpers.wrappers import * from files.helpers.wrappers import *
@ -44,9 +45,9 @@ def searchposts(v):
if not (v and v.paid_dues): posts = posts.filter_by(club=False) 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.is_banned == False, 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:
posts = posts.filter(Submission.state_user_deleted_utc == None, Submission.is_banned == False, Submission.private == False) posts = posts.filter(Submission.state_user_deleted_utc == None, Submission.state_mod == StateMod.Visible, Submission.private == False)
if 'author' in criteria: if 'author' in criteria:
@ -168,10 +169,10 @@ def searchcomments(v):
if v and v.admin_level < 2: if v and v.admin_level < 2:
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.author_id.notin_(v.userblocks), Comment.is_banned==False, Comment.state_user_deleted_utc == None, Comment.parent_submission.notin_(private)) comments = comments.filter(Comment.author_id.notin_(v.userblocks), Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None, Comment.parent_submission.notin_(private))
elif not v: elif not 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.is_banned==False, 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): if not (v and v.paid_dues):

View file

@ -6,6 +6,7 @@ from sqlalchemy import func
from files.classes.award import AWARDS from files.classes.award import AWARDS
from files.classes.badges import BadgeDef from files.classes.badges import BadgeDef
from files.classes.mod_logs import ACTIONTYPES, ACTIONTYPES2 from files.classes.mod_logs import ACTIONTYPES, ACTIONTYPES2
from files.classes.visstate import StateMod
from files.helpers.alerts import * from files.helpers.alerts import *
from files.helpers.captcha import validate_captcha from files.helpers.captcha import validate_captcha
from files.helpers.config.const import * from files.helpers.config.const import *
@ -77,13 +78,13 @@ def participation_stats(v):
"signups last 24h": users.filter(User.created_utc > day).count(), "signups last 24h": users.filter(User.created_utc > day).count(),
"total posts": submissions.count(), "total posts": submissions.count(),
"posting users": g.db.query(Submission.author_id).distinct().count(), "posting users": g.db.query(Submission.author_id).distinct().count(),
"listed posts": submissions.filter_by(is_banned=False).filter(Submission.state_user_deleted_utc == None).count(), "listed posts": submissions.filter_by(Submission.state_mod == StateMod.Visible).filter(Submission.state_user_deleted_utc == None).count(),
"removed posts (by admins)": submissions.filter_by(is_banned=True).count(), "removed posts (by admins)": submissions.filter_by(Submission.state_mod != StateMod.Visible).count(),
"deleted posts (by author)": submissions.filter(Submission.state_user_deleted_utc != None).count(), "deleted posts (by author)": submissions.filter(Submission.state_user_deleted_utc != None).count(),
"posts last 24h": submissions.filter(Submission.created_utc > day).count(), "posts last 24h": submissions.filter(Submission.created_utc > day).count(),
"total comments": comments.filter(Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count(), "total comments": comments.filter(Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count(),
"commenting users": g.db.query(Comment.author_id).distinct().count(), "commenting users": g.db.query(Comment.author_id).distinct().count(),
"removed comments (by admins)": comments.filter_by(is_banned=True).count(), "removed comments (by admins)": comments.filter_by(Comment.state_mod != StateMod.Visible).count(),
"deleted comments (by author)": comments.filter(Comment.state_user_deleted_utc != None).count(), "deleted comments (by author)": comments.filter(Comment.state_user_deleted_utc != None).count(),
"comments last_24h": comments.filter(Comment.created_utc > day, Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count(), "comments last_24h": comments.filter(Comment.created_utc > day, Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count(),
"post votes": g.db.query(Vote.submission_id).count(), "post votes": g.db.query(Vote.submission_id).count(),
@ -142,9 +143,9 @@ def cached_chart(kind, site):
daily_signups = [g.db.query(User.id).filter(User.created_utc < day_cutoffs[i], User.created_utc > day_cutoffs[i + 1]).count() for i in range(len(day_cutoffs) - 1)][::-1] daily_signups = [g.db.query(User.id).filter(User.created_utc < day_cutoffs[i], User.created_utc > day_cutoffs[i + 1]).count() for i in range(len(day_cutoffs) - 1)][::-1]
post_stats = [g.db.query(Submission.id).filter(Submission.created_utc < day_cutoffs[i], Submission.created_utc > day_cutoffs[i + 1], Submission.is_banned == False).count() for i in range(len(day_cutoffs) - 1)][::-1] post_stats = [g.db.query(Submission.id).filter(Submission.created_utc < day_cutoffs[i], Submission.created_utc > day_cutoffs[i + 1], Submission.state_mod == StateMod.Visible).count() for i in range(len(day_cutoffs) - 1)][::-1]
comment_stats = [g.db.query(Comment.id).filter(Comment.created_utc < day_cutoffs[i], Comment.created_utc > day_cutoffs[i + 1],Comment.is_banned == False, Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count() for i in range(len(day_cutoffs) - 1)][::-1] comment_stats = [g.db.query(Comment.id).filter(Comment.created_utc < day_cutoffs[i], Comment.created_utc > day_cutoffs[i + 1],Comment.state_mod == StateMod.Visible, Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count() for i in range(len(day_cutoffs) - 1)][::-1]
plt.rcParams["figure.figsize"] = (30, 20) plt.rcParams["figure.figsize"] = (30, 20)
@ -310,6 +311,7 @@ def submit_contact(v: Optional[User]):
level=1, level=1,
body_html=html, body_html=html,
sentto=MODMAIL_ID, sentto=MODMAIL_ID,
state_mod=StateMod.Visible,
) )
g.db.add(new_comment) g.db.add(new_comment)
g.db.flush() g.db.flush()

View file

@ -12,6 +12,7 @@ from files.__main__ import app, cache, limiter
from files.classes.leaderboard import (BadgeMarseyLeaderboard, LeaderboardMeta, from files.classes.leaderboard import (BadgeMarseyLeaderboard, LeaderboardMeta,
SimpleLeaderboard, UserBlockLeaderboard) SimpleLeaderboard, UserBlockLeaderboard)
from files.classes.views import ViewerRelationship from files.classes.views import ViewerRelationship
from files.classes.visstate import StateMod
from files.helpers.alerts import * from files.helpers.alerts import *
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 *
@ -39,7 +40,7 @@ def upvoters_posts(v, username, uid):
page = max(1, int(request.values.get("page", 1))) page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Submission).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.is_banned == False, Submission.state_user_deleted_utc == None, Vote.vote_type==1, Submission.author_id==id, Vote.user_id==uid).order_by(Submission.created_utc.desc()).offset(25 * (page - 1)).limit(26).all() listing = g.db.query(Submission).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.state_mod == StateMod.Visible, Submission.state_user_deleted_utc == None, Vote.vote_type==1, Submission.author_id==id, Vote.user_id==uid).order_by(Submission.created_utc.desc()).offset(25 * (page - 1)).limit(26).all()
listing = [p.id for p in listing] listing = [p.id for p in listing]
next_exists = len(listing) > 25 next_exists = len(listing) > 25
@ -60,7 +61,7 @@ def upvoters_comments(v, username, uid):
page = max(1, int(request.values.get("page", 1))) page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Comment).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.is_banned == False, Comment.state_user_deleted_utc == None, CommentVote.vote_type==1, Comment.author_id==id, CommentVote.user_id==uid).order_by(Comment.created_utc.desc()).offset(25 * (page - 1)).limit(26).all() listing = g.db.query(Comment).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None, CommentVote.vote_type==1, Comment.author_id==id, CommentVote.user_id==uid).order_by(Comment.created_utc.desc()).offset(25 * (page - 1)).limit(26).all()
listing = [c.id for c in listing] listing = [c.id for c in listing]
next_exists = len(listing) > 25 next_exists = len(listing) > 25
@ -81,7 +82,7 @@ def downvoters_posts(v, username, uid):
page = max(1, int(request.values.get("page", 1))) page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Submission).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.is_banned == False, Submission.state_user_deleted_utc == None, Vote.vote_type==-1, Submission.author_id==id, Vote.user_id==uid).order_by(Submission.created_utc.desc()).offset(25 * (page - 1)).limit(26).all() listing = g.db.query(Submission).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.state_mod == StateMod.Visible, Submission.state_user_deleted_utc == None, Vote.vote_type==-1, Submission.author_id==id, Vote.user_id==uid).order_by(Submission.created_utc.desc()).offset(25 * (page - 1)).limit(26).all()
listing = [p.id for p in listing] listing = [p.id for p in listing]
next_exists = len(listing) > 25 next_exists = len(listing) > 25
@ -102,7 +103,7 @@ def downvoters_comments(v, username, uid):
page = max(1, int(request.values.get("page", 1))) page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Comment).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.is_banned == False, Comment.state_user_deleted_utc == None, CommentVote.vote_type==-1, Comment.author_id==id, CommentVote.user_id==uid).order_by(Comment.created_utc.desc()).offset(25 * (page - 1)).limit(26).all() listing = g.db.query(Comment).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None, CommentVote.vote_type==-1, Comment.author_id==id, CommentVote.user_id==uid).order_by(Comment.created_utc.desc()).offset(25 * (page - 1)).limit(26).all()
listing = [c.id for c in listing] listing = [c.id for c in listing]
next_exists = len(listing) > 25 next_exists = len(listing) > 25
@ -122,7 +123,7 @@ def upvoting_posts(v, username, uid):
page = max(1, int(request.values.get("page", 1))) page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Submission).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.is_banned == False, Submission.state_user_deleted_utc == None, Vote.vote_type==1, Vote.user_id==id, Submission.author_id==uid).order_by(Submission.created_utc.desc()).offset(25 * (page - 1)).limit(26).all() listing = g.db.query(Submission).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.state_mod == StateMod.Visible, Submission.state_user_deleted_utc == None, Vote.vote_type==1, Vote.user_id==id, Submission.author_id==uid).order_by(Submission.created_utc.desc()).offset(25 * (page - 1)).limit(26).all()
listing = [p.id for p in listing] listing = [p.id for p in listing]
next_exists = len(listing) > 25 next_exists = len(listing) > 25
@ -143,7 +144,7 @@ def upvoting_comments(v, username, uid):
page = max(1, int(request.values.get("page", 1))) page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Comment).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.is_banned == False, Comment.state_user_deleted_utc == None, CommentVote.vote_type==1, CommentVote.user_id==id, Comment.author_id==uid).order_by(Comment.created_utc.desc()).offset(25 * (page - 1)).limit(26).all() listing = g.db.query(Comment).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None, CommentVote.vote_type==1, CommentVote.user_id==id, Comment.author_id==uid).order_by(Comment.created_utc.desc()).offset(25 * (page - 1)).limit(26).all()
listing = [c.id for c in listing] listing = [c.id for c in listing]
next_exists = len(listing) > 25 next_exists = len(listing) > 25
@ -164,7 +165,7 @@ def downvoting_posts(v, username, uid):
page = max(1, int(request.values.get("page", 1))) page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Submission).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.is_banned == False, Submission.state_user_deleted_utc == None, Vote.vote_type==-1, Vote.user_id==id, Submission.author_id==uid).order_by(Submission.created_utc.desc()).offset(25 * (page - 1)).limit(26).all() listing = g.db.query(Submission).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.state_mod == StateMod.Visible, Submission.state_user_deleted_utc == None, Vote.vote_type==-1, Vote.user_id==id, Submission.author_id==uid).order_by(Submission.created_utc.desc()).offset(25 * (page - 1)).limit(26).all()
listing = [p.id for p in listing] listing = [p.id for p in listing]
next_exists = len(listing) > 25 next_exists = len(listing) > 25
@ -185,7 +186,7 @@ def downvoting_comments(v, username, uid):
page = max(1, int(request.values.get("page", 1))) page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Comment).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.is_banned == False, Comment.state_user_deleted_utc == None, CommentVote.vote_type==-1, CommentVote.user_id==id, Comment.author_id==uid).order_by(Comment.created_utc.desc()).offset(25 * (page - 1)).limit(26).all() listing = g.db.query(Comment).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None, CommentVote.vote_type==-1, CommentVote.user_id==id, Comment.author_id==uid).order_by(Comment.created_utc.desc()).offset(25 * (page - 1)).limit(26).all()
listing = [c.id for c in listing] listing = [c.id for c in listing]
next_exists = len(listing) > 25 next_exists = len(listing) > 25
@ -200,9 +201,9 @@ def downvoting_comments(v, username, uid):
def upvoters(v, username): def upvoters(v, username):
id = get_user(username).id id = get_user(username).id
votes = g.db.query(Vote.user_id, func.count(Vote.user_id)).join(Submission, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.is_banned == False, Submission.state_user_deleted_utc == None, Vote.vote_type==1, Submission.author_id==id).group_by(Vote.user_id).order_by(func.count(Vote.user_id).desc()).all() votes = g.db.query(Vote.user_id, func.count(Vote.user_id)).join(Submission, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.state_mod == StateMod.Visible, Submission.state_user_deleted_utc == None, Vote.vote_type==1, Submission.author_id==id).group_by(Vote.user_id).order_by(func.count(Vote.user_id).desc()).all()
votes2 = g.db.query(CommentVote.user_id, func.count(CommentVote.user_id)).join(Comment, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.is_banned == False, Comment.state_user_deleted_utc == None, CommentVote.vote_type==1, Comment.author_id==id).group_by(CommentVote.user_id).order_by(func.count(CommentVote.user_id).desc()).all() votes2 = g.db.query(CommentVote.user_id, func.count(CommentVote.user_id)).join(Comment, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None, CommentVote.vote_type==1, Comment.author_id==id).group_by(CommentVote.user_id).order_by(func.count(CommentVote.user_id).desc()).all()
votes = Counter(dict(votes)) + Counter(dict(votes2)) votes = Counter(dict(votes)) + Counter(dict(votes2))
@ -226,9 +227,9 @@ def upvoters(v, username):
def downvoters(v, username): def downvoters(v, username):
id = get_user(username).id id = get_user(username).id
votes = g.db.query(Vote.user_id, func.count(Vote.user_id)).join(Submission, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.is_banned == False, Submission.state_user_deleted_utc == None, Vote.vote_type==-1, Submission.author_id==id).group_by(Vote.user_id).order_by(func.count(Vote.user_id).desc()).all() votes = g.db.query(Vote.user_id, func.count(Vote.user_id)).join(Submission, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.state_mod == StateMod.Visible, Submission.state_user_deleted_utc == None, Vote.vote_type==-1, Submission.author_id==id).group_by(Vote.user_id).order_by(func.count(Vote.user_id).desc()).all()
votes2 = g.db.query(CommentVote.user_id, func.count(CommentVote.user_id)).join(Comment, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.is_banned == False, Comment.state_user_deleted_utc == None, CommentVote.vote_type==-1, Comment.author_id==id).group_by(CommentVote.user_id).order_by(func.count(CommentVote.user_id).desc()).all() votes2 = g.db.query(CommentVote.user_id, func.count(CommentVote.user_id)).join(Comment, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None, CommentVote.vote_type==-1, Comment.author_id==id).group_by(CommentVote.user_id).order_by(func.count(CommentVote.user_id).desc()).all()
votes = Counter(dict(votes)) + Counter(dict(votes2)) votes = Counter(dict(votes)) + Counter(dict(votes2))
@ -250,9 +251,9 @@ def downvoters(v, username):
def upvoting(v, username): def upvoting(v, username):
id = get_user(username).id id = get_user(username).id
votes = g.db.query(Submission.author_id, func.count(Submission.author_id)).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.is_banned == False, Submission.state_user_deleted_utc == None, Vote.vote_type==1, Vote.user_id==id).group_by(Submission.author_id).order_by(func.count(Submission.author_id).desc()).all() votes = g.db.query(Submission.author_id, func.count(Submission.author_id)).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.state_mod == StateMod.Visible, Submission.state_user_deleted_utc == None, Vote.vote_type==1, Vote.user_id==id).group_by(Submission.author_id).order_by(func.count(Submission.author_id).desc()).all()
votes2 = g.db.query(Comment.author_id, func.count(Comment.author_id)).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.is_banned == False, Comment.state_user_deleted_utc == None, CommentVote.vote_type==1, CommentVote.user_id==id).group_by(Comment.author_id).order_by(func.count(Comment.author_id).desc()).all() votes2 = g.db.query(Comment.author_id, func.count(Comment.author_id)).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None, CommentVote.vote_type==1, CommentVote.user_id==id).group_by(Comment.author_id).order_by(func.count(Comment.author_id).desc()).all()
votes = Counter(dict(votes)) + Counter(dict(votes2)) votes = Counter(dict(votes)) + Counter(dict(votes2))
@ -274,9 +275,9 @@ def upvoting(v, username):
def downvoting(v, username): def downvoting(v, username):
id = get_user(username).id id = get_user(username).id
votes = g.db.query(Submission.author_id, func.count(Submission.author_id)).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.is_banned == False, Submission.state_user_deleted_utc == None, Vote.vote_type==-1, Vote.user_id==id).group_by(Submission.author_id).order_by(func.count(Submission.author_id).desc()).all() votes = g.db.query(Submission.author_id, func.count(Submission.author_id)).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.state_mod == StateMod.Visible, Submission.state_user_deleted_utc == None, Vote.vote_type==-1, Vote.user_id==id).group_by(Submission.author_id).order_by(func.count(Submission.author_id).desc()).all()
votes2 = g.db.query(Comment.author_id, func.count(Comment.author_id)).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.is_banned == False, Comment.state_user_deleted_utc == None, CommentVote.vote_type==-1, CommentVote.user_id==id).group_by(Comment.author_id).order_by(func.count(Comment.author_id).desc()).all() votes2 = g.db.query(Comment.author_id, func.count(Comment.author_id)).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.state_mod == StateMod.Visible, Comment.state_user_deleted_utc == None, CommentVote.vote_type==-1, CommentVote.user_id==id).group_by(Comment.author_id).order_by(func.count(Comment.author_id).desc()).all()
votes = Counter(dict(votes)) + Counter(dict(votes2)) votes = Counter(dict(votes)) + Counter(dict(votes2))
@ -454,7 +455,8 @@ def message2(v, username):
parent_submission=None, parent_submission=None,
level=1, level=1,
sentto=user.id, sentto=user.id,
body_html=body_html body_html=body_html,
state_mod=StateMod.Visible,
) )
g.db.add(c) g.db.add(c)
g.db.flush() g.db.flush()
@ -512,6 +514,7 @@ def messagereply(v):
level=parent.level + 1, level=parent.level + 1,
sentto=user_id, sentto=user_id,
body_html=body_html, body_html=body_html,
state_mod=StateMod.Visible,
) )
g.db.add(c) g.db.add(c)
g.db.flush() g.db.flush()
@ -755,9 +758,8 @@ def u_username_comments(username, v=None):
if not v or (v.id != u.id and v.admin_level < 2): if not v or (v.id != u.id and v.admin_level < 2):
comments = comments.filter( comments = comments.filter(
Comment.state_user_deleted_utc == None, Comment.state_user_deleted_utc == None,
Comment.is_banned == False, Comment.state_mod == StateMod.Visible,
Comment.ghost == False, Comment.ghost == False,
(Comment.filter_state != 'filtered') & (Comment.filter_state != 'removed')
) )
comments = apply_time_filter(comments, t, Comment) comments = apply_time_filter(comments, t, Comment)

View file

@ -4,6 +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.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
@ -43,7 +44,7 @@ def get_duty(u: User) -> Optional[VolunteerDutyJanitor]:
# find reported not-deleted comments not made by the current user # find reported not-deleted comments not made by the current user
reported_comments = g.db.query(Comment) \ reported_comments = g.db.query(Comment) \
.where(Comment.filter_state == 'reported') \ .where(Comment.state_report == StateReport.Reported) \
.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) \
.with_entities(Comment.id) .with_entities(Comment.id)

View file

@ -81,10 +81,10 @@
{%- include 'component/comment/user_info.html' -%} {%- include 'component/comment/user_info.html' -%}
<div class="comment-body"> <div class="comment-body">
<div id="{% if comment_info and comment_info.id == c.id %}context{%else%}comment-{{c.id}}-only{% endif %}" class="{% if c.unread %}unread{% endif %} comment-{{c.id}}-only comment-anchor {% if comment_info and comment_info.id == c.id %}context{%endif%}{% if c.is_banned %} banned{% endif %}{% if c.state_user_deleted_utc %} deleted{% endif %}"> <div id="{% if comment_info and comment_info.id == c.id %}context{%else%}comment-{{c.id}}-only{% endif %}" class="{% if c.unread %}unread{% endif %} comment-{{c.id}}-only comment-anchor {% if comment_info and comment_info.id == c.id %}context{%endif%}{% if c.state_mod == StateMod.Removed %} removed{% endif %}{% if c.state_mod == StateMod.Filtered %} filtered{% endif %}{% if c.state_user_deleted_utc %} deleted{% endif %}">
{%- include 'component/comment/reports.html'-%} {%- include 'component/comment/reports.html'-%}
{% if c.is_banned and c.ban_reason %} {# TODO: shouldn't be visible. See #359 #} {% if c.state_mod == StateMod.Removed and c.state_mod_set_by and v and v.admin_level >= 2 %}
<div id="comment-banned-warning" class="comment-text text-removed mb-0">removed by @{{c.ban_reason}}</div> <div id="comment-banned-warning" class="comment-text text-removed mb-0">removed by @{{c.state_mod_set_by}}</div>
{% endif %} {% endif %}
<div id="comment-text-{{c.id}}" class="comment-text mb-0"> <div id="comment-text-{{c.id}}" class="comment-text mb-0">

View file

@ -19,7 +19,7 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if v and v.admin_level >= 2 and c.filter_state == 'filtered' %} {% if v and v.admin_level >= 2 and c.state_mod == StateMod.Filtered %}
<button class="btn" role="button" id="filter-approve" onclick="filter_new_comment_status({{ c.id }}, 'normal')"><span>Approve</span></button> <button class="btn" role="button" id="filter-approve" onclick="filter_new_comment_status({{ c.id }}, 'normal')"><span>Approve</span></button>
<button class="btn" role="button" id="filter-remove" onclick="filter_new_comment_status({{ c.id }}, 'removed')"><span>Remove</span></button> <button class="btn" role="button" id="filter-remove" onclick="filter_new_comment_status({{ c.id }}, 'removed')"><span>Remove</span></button>
{% endif %} {% endif %}
@ -50,8 +50,8 @@
<button class="dropdown-item list-inline-item text-success" onclick="approveCommentDesktop('{{c.id}}')"><i class="fas fa-check text-success fa-fw"></i>Approve</button> <button class="dropdown-item list-inline-item text-success" onclick="approveCommentDesktop('{{c.id}}')"><i class="fas fa-check text-success fa-fw"></i>Approve</button>
<button class="dropdown-item list-inline-item text-danger" onclick="removeCommentDesktop('{{c.id}}')"><i class="fas fa-ban text-danger fa-fw"></i>Remove</button> <button class="dropdown-item list-inline-item text-danger" onclick="removeCommentDesktop('{{c.id}}')"><i class="fas fa-ban text-danger fa-fw"></i>Remove</button>
{% else %} {% else %}
<button id="approve-{{c.id}}" class="dropdown-item list-inline-item d-none {% if c.is_banned %}d-md-block{% endif %} text-success" onclick="approveCommentDesktop('{{c.id}}','approve-{{c.id}}','remove-{{c.id}}')"><i class="fas fa-check text-success fa-fw"></i>Approve</button> <button id="approve-{{c.id}}" class="dropdown-item list-inline-item d-none {% if c.state_mod != StateMod.Visible %}d-md-block{% endif %} text-success" onclick="approveCommentDesktop('{{c.id}}','approve-{{c.id}}','remove-{{c.id}}')"><i class="fas fa-check text-success fa-fw"></i>Approve</button>
<button id="remove-{{c.id}}" class="dropdown-item list-inline-item d-none {% if not c.is_banned %}d-md-block{% endif %} text-danger" onclick="removeCommentDesktop('{{c.id}}','approve-{{c.id}}','remove-{{c.id}}')"><i class="fas fa-ban text-danger fa-fw"></i>Remove</button> <button id="remove-{{c.id}}" class="dropdown-item list-inline-item d-none {% if c.state_mod != StateMod.Removed %}d-md-block{% endif %} text-danger" onclick="removeCommentDesktop('{{c.id}}','approve-{{c.id}}','remove-{{c.id}}')"><i class="fas fa-ban text-danger fa-fw"></i>Remove</button>
{% endif %} {% endif %}
{% endif %} {% endif %}

View file

@ -31,8 +31,8 @@
<a class="list-group-item text-danger" role="button" onclick="removeCommentMobile('{{c.id}}')" data-bs-dismiss="modal"><i class="fas fa-ban text-danger mr-2"></i>Remove</a> <a class="list-group-item text-danger" role="button" onclick="removeCommentMobile('{{c.id}}')" data-bs-dismiss="modal"><i class="fas fa-ban text-danger mr-2"></i>Remove</a>
<a class="list-group-item text-success" role="button" onclick="approveCommentMobile('{{c.id}}')" data-bs-dismiss="modal"><i class="fas fa-check text-success mr-2"></i>Approve</a> <a class="list-group-item text-success" role="button" onclick="approveCommentMobile('{{c.id}}')" data-bs-dismiss="modal"><i class="fas fa-check text-success mr-2"></i>Approve</a>
{% else %} {% else %}
<a id="remove2-{{c.id}}" class="{% if c.is_banned %}d-none{% endif %} list-group-item text-danger" role="button" onclick="removeCommentMobile('{{c.id}}','approve2-{{c.id}}','remove2-{{c.id}}')" data-bs-dismiss="modal"><i class="fas fa-ban text-danger mr-2"></i>Remove</a> <a id="remove2-{{c.id}}" class="{% if c.state_mod != StateMod.Visible %}d-none{% endif %} list-group-item text-danger" role="button" onclick="removeCommentMobile('{{c.id}}','approve2-{{c.id}}','remove2-{{c.id}}')" data-bs-dismiss="modal"><i class="fas fa-ban text-danger mr-2"></i>Remove</a>
<a id="approve2-{{c.id}}" class="{% if not c.is_banned %}d-none{% endif %} list-group-item text-success" role="button" onclick="approveCommentMobile('{{c.id}}','approve2-{{c.id}}','remove2-{{c.id}}')" data-bs-dismiss="modal"><i class="fas fa-check text-success mr-2"></i>Approve</a> <a id="approve2-{{c.id}}" class="{% if c.state_mod != StateMod.Removed %}d-none{% endif %} list-group-item text-success" role="button" onclick="approveCommentMobile('{{c.id}}','approve2-{{c.id}}','remove2-{{c.id}}')" data-bs-dismiss="modal"><i class="fas fa-check text-success mr-2"></i>Approve</a>
{% endif %} {% endif %}
{% if c.oauth_app %} {% if c.oauth_app %}

View file

@ -1,4 +1,4 @@
{%- if v and c.filter_state == 'reported' and v.can_manage_reports() -%} {%- if v and c.state_report == StateReport.Reported and v.can_manage_reports() -%}
<div id="flaggers-{{c.id}}" class="flaggers d-none"> <div id="flaggers-{{c.id}}" class="flaggers d-none">
<strong><i class="far fa-fw fa-flag"></i> Reports:</strong> <strong><i class="far fa-fw fa-flag"></i> Reports:</strong>
<a class="btn btn-primary" style="margin:1px 5px" onclick="filter_new_comment_status({{c.id}}, 'normal')">Approve</a> <a class="btn btn-primary" style="margin:1px 5px" onclick="filter_new_comment_status({{c.id}}, 'normal')">Approve</a>

View file

@ -27,7 +27,7 @@
{% if c.bannedfor %} {% if c.bannedfor %}
<a role="button"><i class="fas fa-hammer-crash text-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="User was banned for this comment{% if c.author.banned_by %} by @{{c.author.banned_by.username}}{% endif %}"></i></a> <a role="button"><i class="fas fa-hammer-crash text-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="User was banned for this comment{% if c.author.banned_by %} by @{{c.author.banned_by.username}}{% endif %}"></i></a>
{% endif %} {% endif %}
{% if v and c.filter_state == 'reported' and v.can_manage_reports() %} {% if v and c.state_report == StateReport.Reported and v.can_manage_reports() %}
<a class="btn btn-primary" id="report-btn-{{c.id}}" style="padding:1px 5px; font-size:10px" role="button" onclick="document.getElementById('flaggers-{{c.id}}').classList.toggle('d-none')">{{c.active_flags(v)}} Reports</a> <a class="btn btn-primary" id="report-btn-{{c.id}}" style="padding:1px 5px; font-size:10px" role="button" onclick="document.getElementById('flaggers-{{c.id}}').classList.toggle('d-none')">{{c.active_flags(v)}} Reports</a>
<span class="volunteer_janitor_result_{{c.volunteer_janitor_css()}} volunteer_janitor_result"> <span class="volunteer_janitor_result_{{c.volunteer_janitor_css()}} volunteer_janitor_result">
{% if c.volunteer_janitor_is_unknown() %} {% if c.volunteer_janitor_is_unknown() %}

View file

@ -4,7 +4,7 @@
<span class="mr-2 arrow-up comment-{{c.id}}-up active"></span> <span class="mr-2 arrow-up comment-{{c.id}}-up active"></span>
{% endif %} {% endif %}
<span class="comment-mobile-score-{{c.id}} score comment-score-{{c.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if c.controversial %} controversial{% endif %}"{% if not c.is_banned %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span> <span class="comment-mobile-score-{{c.id}} score comment-score-{{c.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if c.controversial %} controversial{% endif %}"{% if c.state_mod == StateMod.Visible %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span>
{% if voted==-1 %} {% if voted==-1 %}
<span class="ml-2 my-0 arrow-down comment-{{c.id}}-down active"></span> <span class="ml-2 my-0 arrow-down comment-{{c.id}}-down active"></span>
{% endif %} {% endif %}
@ -12,7 +12,7 @@
{% elif v %} {% elif v %}
<li id="voting-{{c.id}}-mobile" class="voting list-inline-item d-md-none"> <li id="voting-{{c.id}}-mobile" class="voting list-inline-item d-md-none">
<span tabindex="0" role="button" onclick="vote('comment-mobile', '{{c.id}}', '1')" class="comment-mobile-{{c.id}}-up mx-0 pr-1 arrow-up upvote-button comment-{{c.id}}-up {% if voted==1 %}active{% endif %}"></span> <span tabindex="0" role="button" onclick="vote('comment-mobile', '{{c.id}}', '1')" class="comment-mobile-{{c.id}}-up mx-0 pr-1 arrow-up upvote-button comment-{{c.id}}-up {% if voted==1 %}active{% endif %}"></span>
<span class="comment-mobile-score-{{c.id}} score comment-score-{{c.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if c.controversial %} controversial{% endif %}"{% if not c.is_banned %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span> <span class="comment-mobile-score-{{c.id}} score comment-score-{{c.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if c.controversial %} controversial{% endif %}"{% if c.state_mod == StateMod.Visible %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span>
<span {% if not ENABLE_DOWNVOTES %}style="display: none!important"{% endif %} tabindex="0" role="button" onclick="vote('comment-mobile', '{{c.id}}', '-1')" class="comment-mobile-{{c.id}}-down mx-0 pl-1 my-0 arrow-down downvote-button comment-{{c.id}}-down {% if voted==-1 %}active{% endif %}"></span> <span {% if not ENABLE_DOWNVOTES %}style="display: none!important"{% endif %} tabindex="0" role="button" onclick="vote('comment-mobile', '{{c.id}}', '-1')" class="comment-mobile-{{c.id}}-down mx-0 pl-1 my-0 arrow-down downvote-button comment-{{c.id}}-down {% if voted==-1 %}active{% endif %}"></span>
</li> </li>
{% else %} {% else %}
@ -20,7 +20,7 @@
<span tabindex="0" class="arrow-{{c.id}}-mobile-up mx-0 pr-1 arrow-mobile-up" onclick="location.href='/login';"> <span tabindex="0" class="arrow-{{c.id}}-mobile-up mx-0 pr-1 arrow-mobile-up" onclick="location.href='/login';">
<i class="fas fa-arrow-alt-up mx-0" aria-hidden="true"></i> <i class="fas fa-arrow-alt-up mx-0" aria-hidden="true"></i>
</span> </span>
<span class="comment-mobile-score-{{c.id}} score{% if c.controversial %} controversial{% endif %}"{% if not c.is_banned %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span> <span class="comment-mobile-score-{{c.id}} score{% if c.controversial %} controversial{% endif %}"{% if c.state_mod == StateMod.Visible %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span>
<span tabindex="0" class="arrow-{{c.id}}-mobile-down arrow-mobile-down mx-0 pl-1 my-0" onclick="location.href='/login';"> <span tabindex="0" class="arrow-{{c.id}}-mobile-down arrow-mobile-down mx-0 pl-1 my-0" onclick="location.href='/login';">
<i class="fas fa-arrow-alt-down mx-0" aria-hidden="true"></i> <i class="fas fa-arrow-alt-down mx-0" aria-hidden="true"></i>
</span> </span>

View file

@ -35,7 +35,7 @@
<a id="delete2-{{p.id}}" class="{% if p.state_user_deleted_utc %}d-none{% endif %} list-inline-item" role="button" data-bs-toggle="modal" data-bs-dismiss="modal" data-bs-target="#deletePostModal" onclick="delete_postModal('{{p.id}}')"><i class="fas fa-trash-alt"></i>Delete</a> <a id="delete2-{{p.id}}" class="{% if p.state_user_deleted_utc %}d-none{% endif %} list-inline-item" role="button" data-bs-toggle="modal" data-bs-dismiss="modal" data-bs-target="#deletePostModal" onclick="delete_postModal('{{p.id}}')"><i class="fas fa-trash-alt"></i>Delete</a>
{% endif %} {% endif %}
{% if v and v.admin_level >= 2 and p.filter_state == 'filtered' %} {% if v and v.admin_level >= 2 and p.state_mod == StateMod.Filtered %}
<a id="filter-approve" class="list-inline-item" role="button" onclick="filter_new_status({{p.id}}, 'normal')">Approve</a> <a id="filter-approve" class="list-inline-item" role="button" onclick="filter_new_status({{p.id}}, 'normal')">Approve</a>
<a id="filter-remove" class="list-inline-item" role="button" onclick="filter_new_status({{p.id}}, 'removed')">Remove</a> <a id="filter-remove" class="list-inline-item" role="button" onclick="filter_new_status({{p.id}}, 'removed')">Remove</a>
{% endif %} {% endif %}
@ -63,8 +63,8 @@
{% if v.id != p.author.id %}<a class="dropdown-item list-inline-item text-danger" role="button" onclick="post_toast(this,'/remove_post/{{p.id}}')"><i class="fas fa-ban"></i>Remove</a>{% endif %} {% if v.id != p.author.id %}<a class="dropdown-item list-inline-item text-danger" role="button" onclick="post_toast(this,'/remove_post/{{p.id}}')"><i class="fas fa-ban"></i>Remove</a>{% endif %}
<a class="dropdown-item list-inline-item text-success" role="button" onclick="post_toast(this,'/unremove_post/{{p.id}}')"><i class="fas fa-check"></i>Approve</a> <a class="dropdown-item list-inline-item text-success" role="button" onclick="post_toast(this,'/unremove_post/{{p.id}}')"><i class="fas fa-check"></i>Approve</a>
{% else %} {% else %}
{% if v.id != p.author.id %}<a id="remove-{{p.id}}" class="dropdown-item {% if p.is_banned %}d-none{% endif %} list-inline-item text-danger" role="button" onclick="post_toast2(this,'/remove_post/{{p.id}}','remove-{{p.id}}','approve-{{p.id}}')"><i class="fas fa-ban"></i>Remove</a>{% endif %} {% if v.id != p.author.id %}<a id="remove-{{p.id}}" class="dropdown-item {% if p.state_mod == StateMod.Removed %}d-none{% endif %} list-inline-item text-danger" role="button" onclick="post_toast2(this,'/remove_post/{{p.id}}','remove-{{p.id}}','approve-{{p.id}}')"><i class="fas fa-ban"></i>Remove</a>{% endif %}
<a id="approve-{{p.id}}" class="dropdown-item {% if not p.is_banned %}d-none{% endif %} list-inline-item text-success" role="button" onclick="post_toast2(this,'/unremove_post/{{p.id}}','remove-{{p.id}}','approve-{{p.id}}')"><i class="fas fa-check"></i>Approve</a> <a id="approve-{{p.id}}" class="dropdown-item {% if p.state_mod == StateMod.Visible %}d-none{% endif %} list-inline-item text-success" role="button" onclick="post_toast2(this,'/unremove_post/{{p.id}}','remove-{{p.id}}','approve-{{p.id}}')"><i class="fas fa-check"></i>Approve</a>
{% endif %} {% endif %}
{% if p.oauth_app %} {% if p.oauth_app %}

View file

@ -27,11 +27,11 @@
<button class="nobackground btn btn-link btn-block btn-lg text-danger text-left" role="button" onclick="post_toast(this,'/remove_post/{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-ban text-center mr-3"></i>Remove</button> <button class="nobackground btn btn-link btn-block btn-lg text-danger text-left" role="button" onclick="post_toast(this,'/remove_post/{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-ban text-center mr-3"></i>Remove</button>
<button class="nobackground btn btn-link btn-block btn-lg text-success text-left" role="button" onclick="post_toast(this,'/unremove_post/{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-check text-center mr-3"></i>Approve</button> <button class="nobackground btn btn-link btn-block btn-lg text-success text-left" role="button" onclick="post_toast(this,'/unremove_post/{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-check text-center mr-3"></i>Approve</button>
{% else %} {% else %}
<button id="remove2-{{p.id}}" class="{% if p.is_banned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-danger text-left" role="button" onclick="post_toast2(this,'/remove_post/{{p.id}}','remove2-{{p.id}}','approve2-{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-ban text-center mr-3"></i>Remove</button> <button id="remove2-{{p.id}}" class="{% if p.state_mod == StateMod.Removed %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-danger text-left" role="button" onclick="post_toast2(this,'/remove_post/{{p.id}}','remove2-{{p.id}}','approve2-{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-ban text-center mr-3"></i>Remove</button>
<button id="approve2-{{p.id}}" class="{% if not p.is_banned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-success text-left" role="button" onclick="post_toast2(this,'/unremove_post/{{p.id}}','remove2-{{p.id}}','approve2-{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-check text-center mr-3"></i>Approve</button> <button id="approve2-{{p.id}}" class="{% if p.state_mod == StateMod.Visible %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-success text-left" role="button" onclick="post_toast2(this,'/unremove_post/{{p.id}}','remove2-{{p.id}}','approve2-{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-check text-center mr-3"></i>Approve</button>
{% endif %} {% endif %}
{% if v and v.admin_level >= 2 and p.filter_state == 'filtered' %} {% if v and v.admin_level >= 2 and p.state_mod == StateMod.Filtered %}
<button id="mobile-filter-approve-{{p.id}}" class="nobackground btn btn-link btn-block btn-lg text-success text-left" role="button" onclick="filter_new_status({{p.id}}, 'normal')"><i class="far fa-check text-center mr-3"></i>Filter Approve</a> <button id="mobile-filter-approve-{{p.id}}" class="nobackground btn btn-link btn-block btn-lg text-success text-left" role="button" onclick="filter_new_status({{p.id}}, 'normal')"><i class="far fa-check text-center mr-3"></i>Filter Approve</a>
<button id="mobile-filter-remove-{{p.id}}" class="nobackground btn btn-link btn-block btn-lg text-danger text-left" role="button" onclick="filter_new_status({{p.id}}, 'removed')"><i class="far fa-ban text-center mr-3"></i>Filter Remove</a> <button id="mobile-filter-remove-{{p.id}}" class="nobackground btn btn-link btn-block btn-lg text-danger text-left" role="button" onclick="filter_new_status({{p.id}}, 'removed')"><i class="far fa-ban text-center mr-3"></i>Filter Remove</a>
{% endif %} {% endif %}

View file

@ -38,7 +38,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta property="og:type" content="article"> <meta property="og:type" content="article">
{% if comment_info and not comment_info.is_banned and not comment_info.state_user_deleted_utc %} {% if comment_info and comment_info.state_mod == StateMod.Visible and not comment_info.state_user_deleted_utc %}
<title>{{'@'+comment_info.author_name}} comments on "{{p.plaintitle(v)}} - {{SITE_TITLE}}"</title> <title>{{'@'+comment_info.author_name}} comments on "{{p.plaintitle(v)}} - {{SITE_TITLE}}"</title>
@ -132,7 +132,7 @@
<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.is_banned %} banned{% 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 %}">
<div id="post-{{p.id}}" class="{% if p.state_user_deleted_utc %}deleted {% endif %}d-flex flex-row-reverse flex-nowrap justify-content-end"> <div id="post-{{p.id}}" class="{% if p.state_user_deleted_utc %}deleted {% endif %}d-flex flex-row-reverse flex-nowrap justify-content-end">
<div id="post-content" class="{% if p.state_user_deleted_utc %}deleted {% endif %}card-block w-100 my-md-auto"> <div id="post-content" class="{% if p.state_user_deleted_utc %}deleted {% endif %}card-block w-100 my-md-auto">
@ -157,7 +157,7 @@
{% if p.is_bot %} <i class="fas fa-robot text-info" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bot"></i>{% endif %} {% if p.is_bot %} <i class="fas fa-robot text-info" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bot"></i>{% endif %}
{% if p.over_18 %}<span class="badge badge-danger text-small-extra mr-1">+18</span>{% endif %} {% if p.over_18 %}<span class="badge badge-danger text-small-extra mr-1">+18</span>{% endif %}
{% if p.private %}<span class="badge border-warning border-1 text-small-extra">Draft</span>{% endif %} {% if p.private %}<span class="badge border-warning border-1 text-small-extra">Draft</span>{% endif %}
{% if v and p.filter_state == 'reported' and v.can_manage_reports() %} {% if v and p.state_report == StateReport.Reported and v.can_manage_reports() %}
<a class="btn btn-primary" id="submission-report-button" role="button" style="padding:1px 5px; font-size:10px"onclick="document.getElementById('flaggers').classList.toggle('d-none')">{{p.active_flags(v)}} Reports</a> <a class="btn btn-primary" id="submission-report-button" role="button" style="padding:1px 5px; font-size:10px"onclick="document.getElementById('flaggers').classList.toggle('d-none')">{{p.active_flags(v)}} Reports</a>
{% endif %} {% endif %}
{% if p.ghost %} {% if p.ghost %}
@ -183,7 +183,7 @@
{% endif %} {% endif %}
&nbsp;&nbsp;{{p.views}} thread views &nbsp;&nbsp;{{p.views}} thread views
</div> </div>
{% if v and p.filter_state == 'reported' and v.can_manage_reports() %} {% if v and p.state_mod_reported and v.can_manage_reports() %}
<div id="flaggers" class="flaggers d-none"> <div id="flaggers" class="flaggers d-none">
<strong><i class="far fa-fw fa-flag"></i> Reports:</strong> <strong><i class="far fa-fw fa-flag"></i> Reports:</strong>
<a class="btn btn-primary" style="margin:1px 5px" onclick="filter_new_status({{p.id}}, 'normal')">Approve</a> <a class="btn btn-primary" style="margin:1px 5px" onclick="filter_new_status({{p.id}}, 'normal')">Approve</a>
@ -257,8 +257,8 @@
{% endif %} {% endif %}
{{p.realbody(v) | safe}} {{p.realbody(v) | safe}}
{% if p.is_banned and p.ban_reason %} {% if p.state_mod == StateMod.Removed and p.state_mod_set_by %}
<div class="text-removed mb-0">removed by @{{p.ban_reason}}</div> <div class="text-removed mb-0">removed by @{{p.state_mod_set_by}}</div>
{% endif %} {% endif %}
</div> </div>
@ -341,7 +341,7 @@
<div id="voting" class="voting d-none d-md-block mb-auto"> <div id="voting" class="voting d-none d-md-block mb-auto">
<div tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '1')" class="post-{{p.id}}-up arrow-up mx-auto" onclick="location.href='/login?redirect={{request.path | urlencode}}';"> <div tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '1')" class="post-{{p.id}}-up arrow-up mx-auto" onclick="location.href='/login?redirect={{request.path | urlencode}}';">
</div> </div>
<span class="post-{{p.id}}-score-none score text-muted{% if p.controversial %} controversial{% endif %}"{% if not p.is_banned %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span> <span class="post-{{p.id}}-score-none score text-muted{% if p.controversial %} controversial{% endif %}"{% if p.state_mod == StateMod.Visible %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span>
<div {% if not ENABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1')" class="post-{{p.id}}-down arrow-down mx-auto" onclick="location.href='/login?redirect={{request.path | urlencode}}';"></div> <div {% if not ENABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1')" class="post-{{p.id}}-down arrow-down mx-auto" onclick="location.href='/login?redirect={{request.path | urlencode}}';"></div>
</div> </div>
{% endif %} {% endif %}

View file

@ -11,21 +11,16 @@
{% block title %} {% block title %}
<title>{{p.plaintitle(v)}}</title> <title>{{p.plaintitle(v)}}</title>
{% if p.is_banned %}
{% else %}
{% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="mb-2 p-3"> <div class="mb-2 p-3">
<div class="col-12"> <div class="col-12">
<div id="post-{{p.id}}" class="card d-flex flex-row-reverse flex-nowrap justify-content-end border-0 p-0 {% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}"> <div id="post-{{p.id}}" class="card d-flex flex-row-reverse flex-nowrap justify-content-end border-0 p-0 {% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}">
<div class="card-block my-md-auto{% if p.is_banned %} banned{% endif %}"> <div class="card-block my-md-auto{% if p.state_mod == StateMod.Removed %} banned{% endif %}">
<div class="post-meta text-left d-md-none mb-1">{% if p.over_18 %}<span class="badge badge-danger">+18</span> {% endif %}{% if p.is_banned %}removed by @{{p.ban_reason}}{% else %}[Deleted by user]{% endif %}</div> <div class="post-meta text-left d-md-none mb-1">{% if p.over_18 %}<span class="badge badge-danger">+18</span> {% endif %}{% if p.state_mod == StateMod.Removed %}removed by @{{p.state_mod_set_by}}{% else %}[Deleted by user]{% endif %}</div>
<h5 class="card-title post-title text-left mb-0 mb-md-1">{{p.plaintitle(v)}}</h5> <h5 class="card-title post-title text-left mb-0 mb-md-1">{{p.plaintitle(v)}}</h5>
<div class="post-meta text-left d-none d-md-block">{% if p.over_18 %}<span class="badge badge-danger">+18</span> {% endif %}{% if p.is_banned %}removed by @{{p.ban_reason}}{% else %}[Deleted by user]{% endif %}</div> <div class="post-meta text-left d-none d-md-block">{% if p.over_18 %}<span class="badge badge-danger">+18</span> {% endif %}{% if p.state_mod == StateMod.Removed %}removed by @{{p.state_mod_set_by}}{% else %}[Deleted by user]{% endif %}</div>
</div> </div>

View file

@ -63,7 +63,7 @@
{% set voted=-2 %} {% set voted=-2 %}
{% endif %} {% endif %}
{% if v and p.filter_state == 'reported' and v.can_manage_reports() %} {% if v and p.state_mod_reported and v.can_manage_reports() %}
<div id="flaggers-{{p.id}}" class="flaggers d-none"> <div id="flaggers-{{p.id}}" class="flaggers d-none">
<strong><i class="far fa-fw fa-flag"></i> Reports:</strong> <strong><i class="far fa-fw fa-flag"></i> Reports:</strong>
<a class="btn btn-primary" style="margin:1px 5px" onclick="filter_new_status({{p.id}}, 'normal')">Approve</a> <a class="btn btn-primary" style="margin:1px 5px" onclick="filter_new_status({{p.id}}, 'normal')">Approve</a>
@ -78,7 +78,7 @@
</div> </div>
{% endif %} {% endif %}
<div id="post-{{p.id}}" class="card{% if p.is_banned %} banned{% endif %}{% if p.state_user_deleted_utc %} deleted{% endif %}{% if p.stickied %} stickied{% endif %}{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}{% if p.over_18 %} nsfw{% endif %}"> <div id="post-{{p.id}}" class="card{% if p.state_mod == StateMod.Removed %} removed{% endif %}{% if p.state_mod == StateMod.Filtered %} filtered{% endif %}{% if p.state_user_deleted_utc %} deleted{% endif %}{% if p.stickied %} stickied{% endif %}{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}{% if p.over_18 %} nsfw{% endif %}">
<div class="d-flex flex-row flex-nowrap justify-content-end align-items-center"> <div class="d-flex flex-row flex-nowrap justify-content-end align-items-center">
@ -90,7 +90,7 @@
<div class="mx-auto arrow-up post-{{p.id}}-up active"></div> <div class="mx-auto arrow-up post-{{p.id}}-up active"></div>
{% endif %} {% endif %}
<span class="post-score-{{p.id}} score post-score-{{p.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if p.controversial %} controversial{% endif %}"{% if not p.is_banned %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span> <span class="post-score-{{p.id}} score post-score-{{p.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if p.controversial %} controversial{% endif %}"{% if p.state_mod == StateMod.Visible %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span>
{% if voted==-1 %} {% if voted==-1 %}
<div class="text-muted mx-auto arrow-down post-{{p.id}}-down active"></div> <div class="text-muted mx-auto arrow-down post-{{p.id}}-down active"></div>
@ -100,7 +100,7 @@
<div tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '1', '{{v.id}}')" class="post-{{p.id}}-up mx-auto arrow-up upvote-button post-{{p.id}}-up {% if voted==1 %}active{% endif %}"></div> <div tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '1', '{{v.id}}')" class="post-{{p.id}}-up mx-auto arrow-up upvote-button post-{{p.id}}-up {% if voted==1 %}active{% endif %}"></div>
<span class="post-score-{{p.id}} score post-score-{{p.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if p.controversial %} controversial{% endif %}"{% if not p.is_banned %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span> <span class="post-score-{{p.id}} score post-score-{{p.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if p.controversial %} controversial{% endif %}"{% if p.state_mod == StateMod.Visible %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span>
<div {% if not ENABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1', '{{v.id}}')" class="post-{{p.id}}-down text-muted mx-auto arrow-down downvote-button post-{{p.id}}-down {% if voted==-1 %}active{% endif %}"></div> <div {% if not ENABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1', '{{v.id}}')" class="post-{{p.id}}-down text-muted mx-auto arrow-down downvote-button post-{{p.id}}-down {% if voted==-1 %}active{% endif %}"></div>
@ -108,7 +108,7 @@
<div tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '1', '{{v.id}}')" class="post-{{p.id}}-up mx-auto arrow-up" onclick="location.href='/login';"></div> <div tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '1', '{{v.id}}')" class="post-{{p.id}}-up mx-auto arrow-up" onclick="location.href='/login';"></div>
<span class="post-{{p.id}}-score-none score{% if p.controversial %} controversial{% endif %}"{% if not p.is_banned %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span> <span class="post-{{p.id}}-score-none score{% if p.controversial %} controversial{% endif %}"{% if p.state_mod == StateMod.Visible %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span>
<div {% if not ENABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1', '{{v.id}}')" class="post-{{p.id}}-down text-muted mx-auto arrow-down" onclick="location.href='/login';"></div> <div {% if not ENABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1', '{{v.id}}')" class="post-{{p.id}}-down text-muted mx-auto arrow-down" onclick="location.href='/login';"></div>
@ -183,7 +183,7 @@
{% if p.is_blocking %}<i class="fas fa-user-minus text-warning" data-bs-toggle="tooltip" data-bs-placement="bottom" title="You're blocking this user, but you can see this post because you're an admin."></i>{% endif %} {% if p.is_blocking %}<i class="fas fa-user-minus text-warning" data-bs-toggle="tooltip" data-bs-placement="bottom" title="You're blocking this user, but you can see this post because you're an admin."></i>{% endif %}
{% if p.is_blocked %}<i class="fas fa-user-minus text-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="This user is blocking you."></i>{% endif %} {% if p.is_blocked %}<i class="fas fa-user-minus text-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="This user is blocking you."></i>{% endif %}
{% if p.private %}<span class="badge border-warning border-1 text-small-extra">Draft</span>{% endif %} {% if p.private %}<span class="badge border-warning border-1 text-small-extra">Draft</span>{% endif %}
{% if v and p.filter_state == 'reported' and v.can_manage_reports() %} {% if v and p.state_report == StateReport.Reported and v.can_manage_reports() %}
<a class="btn btn-primary" id="report-btn-{{p.id}}" role="button" style="padding:1px 5px; font-size:10px"onclick="document.getElementById('flaggers-{{p.id}}').classList.toggle('d-none')">{{p.active_flags(v)}} Reports</a> <a class="btn btn-primary" id="report-btn-{{p.id}}" role="button" style="padding:1px 5px; font-size:10px"onclick="document.getElementById('flaggers-{{p.id}}').classList.toggle('d-none')">{{p.active_flags(v)}} Reports</a>
{% endif %} {% endif %}

View file

@ -6,6 +6,7 @@ from . import util
from flask import g from flask import g
from files.__main__ import app, db_session from files.__main__ import app, db_session
from files.classes import Submission, Comment, User from files.classes import Submission, Comment, User
from files.classes.visstate import StateMod
from files.helpers.comments import bulk_recompute_descendant_counts from files.helpers.comments import bulk_recompute_descendant_counts
import json import json
import random import random
@ -203,7 +204,7 @@ def test_bulk_update_descendant_count_quick(accounts, submissions, comments):
'title': f'Clever unique post title number {i}', 'title': f'Clever unique post title number {i}',
'title_html': f'Clever unique post title number {i}', 'title_html': f'Clever unique post title number {i}',
'ghost': False, 'ghost': False,
'filter_state': 'normal' 'state_mod': StateMod.Visible,
}) })
db.add(post) db.add(post)
db.commit() db.commit()
@ -222,7 +223,8 @@ def test_bulk_update_descendant_count_quick(accounts, submissions, comments):
'app_id': None, 'app_id': None,
'body_html': f'reply {i} {j}', 'body_html': f'reply {i} {j}',
'body': f'reply {i} {j}', 'body': f'reply {i} {j}',
'ghost': False 'ghost': False,
'state_mod': StateMod.Visible,
}) })
if parent_comment is None: if parent_comment is None:
top_comment = comment top_comment = comment

View file

@ -0,0 +1,233 @@
"""rejigger of the mod/report comment/post state flags
Revision ID: ea282d7c711c
Revises: 4bb8194c8ac0
Create Date: 2023-06-25 07:09:36.333792+00:00
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'ea282d7c711c'
down_revision = '4bb8194c8ac0'
branch_labels = None
depends_on = None
def upgrade():
state_mod_enum = sa.Enum('Visible', 'Filtered', 'Removed', name='statemod', create_type=False)
state_report_enum = sa.Enum('Unreported', 'Resolved', 'Reported', 'Ignored', name='statereport', create_type=False)
state_mod_enum.create(op.get_bind(), checkfirst=True)
state_report_enum.create(op.get_bind(), checkfirst=True)
op.add_column('comments', sa.Column('state_mod', state_mod_enum, nullable=True))
op.add_column('comments', sa.Column('state_mod_set_by', sa.String(), nullable=True))
op.add_column('comments', sa.Column('state_report', state_report_enum, nullable=True))
op.drop_index('fki_comment_approver_fkey', table_name='comments')
op.drop_constraint('comment_approver_fkey', 'comments', type_='foreignkey')
# If `is_banned`, set `state_mod` to the `Removed` enum
# otherwise, if `filter_state` is `filtered`, set `state_mod` to the `Filtered` enum
# otherwise, set `state_mod` to the `Visible` enum
op.execute("""
UPDATE comments
SET state_mod = CASE
WHEN is_banned THEN 'Removed'::statemod
WHEN filter_state = 'filtered' THEN 'Filtered'::statemod
ELSE 'Visible'::statemod
END
""")
# if `state_mod` is `Removed`, set `state_mod_set_by` to `ban_reason`
# otherwise, if `state_mod` is `Visible`, set `state_mod_set_by` to the `username` of the `users` table, indexed by `is_approved == users.id`
op.execute("""
UPDATE comments
SET state_mod_set_by = CASE
WHEN state_mod = 'Removed' THEN ban_reason
WHEN state_mod = 'Visible' THEN (SELECT username FROM users WHERE id = is_approved)
END
""")
# if `filter_state` is `ignored`, set `state_report` to the `Ignored` enum
# otherwise, if `filter_state` is `reported`, set `state_report` to the `Reported` enum
# otherwise, if `state_mod_set_by` is non-NULL, set `state_report` to the `Resolved` enum
# otherwise, set `state_report` to the `Unreported` enum
op.execute("""
UPDATE comments
SET state_report = CASE
WHEN filter_state = 'ignored' THEN 'Ignored'::statereport
WHEN filter_state = 'reported' THEN 'Reported'::statereport
WHEN state_mod_set_by IS NOT NULL THEN 'Resolved'::statereport
ELSE 'Unreported'::statereport
END
""")
op.alter_column('comments', 'state_mod', nullable=False)
op.alter_column('comments', 'state_report', nullable=False)
op.drop_column('comments', 'is_banned')
op.drop_column('comments', 'ban_reason')
op.drop_column('comments', 'is_approved')
op.drop_column('comments', 'filter_state')
op.add_column('submissions', sa.Column('state_mod', state_mod_enum, nullable=True))
op.add_column('submissions', sa.Column('state_mod_set_by', sa.String(), nullable=True))
op.add_column('submissions', sa.Column('state_report', state_report_enum, nullable=True))
op.drop_index('fki_submissions_approver_fkey', table_name='submissions')
op.drop_index('submission_isbanned_idx', table_name='submissions')
op.drop_index('subimssion_binary_group_idx', table_name='submissions')
op.create_index('subimssion_binary_group_idx', 'submissions', ['state_mod', 'state_user_deleted_utc', 'over_18'], unique=False)
op.drop_index('submission_new_sort_idx', table_name='submissions')
op.create_index('submission_new_sort_idx', 'submissions', ['state_mod', 'state_user_deleted_utc', sa.text('created_utc DESC'), 'over_18'], unique=False)
op.create_index('submission_state_mod_idx', 'submissions', ['state_mod'], unique=False)
op.drop_constraint('submissions_approver_fkey', 'submissions', type_='foreignkey')
# If `is_banned`, set `state_mod` to the `Removed` enum
# otherwise, if `filter_state` is `filtered`, set `state_mod` to the `Filtered` enum
# otherwise, set `state_mod` to the `Visible` enum
op.execute("""
UPDATE submissions
SET state_mod = CASE
WHEN is_banned THEN 'Removed'::statemod
WHEN filter_state = 'filtered' THEN 'Filtered'::statemod
ELSE 'Visible'::statemod
END
""")
# if `state_mod` is `Removed`, set `state_mod_set_by` to `ban_reason`
# otherwise, if `state_mod` is `Visible`, set `state_mod_set_by` to the `username` of the `users` table, indexed by `is_approved == users.id`
op.execute("""
UPDATE submissions
SET state_mod_set_by = CASE
WHEN state_mod = 'Removed' THEN ban_reason
WHEN state_mod = 'Visible' THEN (SELECT username FROM users WHERE id = is_approved)
END
""")
# if `filter_state` is `ignored`, set `state_report` to the `Ignored` enum
# otherwise, if `filter_state` is `reported`, set `state_report` to the `Reported` enum
# otherwise, if `state_mod_set_by` is non-NULL, set `state_report` to the `Resolved` enum
# otherwise, set `state_report` to the `Unreported` enum
op.execute("""
UPDATE submissions
SET state_report = CASE
WHEN filter_state = 'ignored' THEN 'Ignored'::statereport
WHEN filter_state = 'reported' THEN 'Reported'::statereport
WHEN state_mod_set_by IS NOT NULL THEN 'Resolved'::statereport
ELSE 'Unreported'::statereport
END
""")
op.alter_column('submissions', 'state_mod', nullable=False)
op.alter_column('submissions', 'state_report', nullable=False)
op.drop_column('submissions', 'is_banned')
op.drop_column('submissions', 'ban_reason')
op.drop_column('submissions', 'is_approved')
op.drop_column('submissions', 'filter_state')
def downgrade():
op.add_column('comments', sa.Column('filter_state', sa.VARCHAR(length=40), autoincrement=False, nullable=True))
op.add_column('comments', sa.Column('is_approved', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('comments', sa.Column('ban_reason', sa.VARCHAR(length=25), autoincrement=False, nullable=True))
op.add_column('comments', sa.Column('is_banned', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=True))
op.create_foreign_key('comment_approver_fkey', 'comments', 'users', ['is_approved'], ['id'])
op.create_index('fki_comment_approver_fkey', 'comments', ['is_approved'], unique=False)
op.execute("""
UPDATE comments
SET is_banned = CASE
WHEN state_mod = 'Removed' THEN TRUE
ELSE FALSE
END
""")
op.execute("""
UPDATE comments
SET is_approved = (SELECT id FROM users WHERE username = state_mod_set_by)
WHERE state_mod = 'Visible' AND (state_report = 'Resolved' OR state_report = 'Ignored')
""")
op.execute("""
UPDATE comments
SET ban_reason = CASE
WHEN state_mod = 'Removed' THEN state_mod_set_by
ELSE NULL
END
""")
op.execute("""
UPDATE comments
SET filter_state = CASE
WHEN state_report = 'Ignored' THEN 'ignored'
WHEN state_report = 'Reported' THEN 'reported'
WHEN state_mod = 'Filtered' THEN 'filtered'
ELSE NULL
END
""")
op.alter_column('comments', sa.Column('filter_state', sa.VARCHAR(length=40), autoincrement=False, nullable=True))
op.alter_column('comments', sa.Column('is_banned', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=True))
op.drop_column('comments', 'state_report')
op.drop_column('comments', 'state_mod_set_by')
op.drop_column('comments', 'state_mod')
op.add_column('submissions', sa.Column('filter_state', sa.VARCHAR(length=40), autoincrement=False, nullable=True))
op.add_column('submissions', sa.Column('is_approved', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('submissions', sa.Column('ban_reason', sa.VARCHAR(length=25), autoincrement=False, nullable=True))
op.add_column('submissions', sa.Column('is_banned', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=True))
op.create_foreign_key('submissions_approver_fkey', 'submissions', 'users', ['is_approved'], ['id'])
op.drop_index('submission_state_mod_idx', table_name='submissions')
op.drop_index('submission_new_sort_idx', table_name='submissions')
op.create_index('submission_new_sort_idx', 'submissions', ['is_banned', 'state_user_deleted_utc', 'created_utc', 'over_18'], unique=False)
op.drop_index('subimssion_binary_group_idx', table_name='submissions')
op.create_index('subimssion_binary_group_idx', 'submissions', ['is_banned', 'state_user_deleted_utc', 'over_18'], unique=False)
op.create_index('submission_isbanned_idx', 'submissions', ['is_banned'], unique=False)
op.create_index('fki_submissions_approver_fkey', 'submissions', ['is_approved'], unique=False)
op.execute("""
UPDATE submissions
SET is_banned = CASE
WHEN state_mod = 'Removed' THEN TRUE
ELSE FALSE
END
""")
op.execute("""
UPDATE submissions
SET is_approved = (SELECT id FROM users WHERE username = state_mod_set_by)
WHERE state_mod = 'Visible' AND (state_report = 'Resolved' OR state_report = 'Ignored')
""")
op.execute("""
UPDATE submissions
SET ban_reason = CASE
WHEN state_mod = 'Removed' THEN state_mod_set_by
ELSE NULL
END
""")
op.execute("""
UPDATE submissions
SET filter_state = CASE
WHEN state_report = 'Ignored' THEN 'ignored'
WHEN state_report = 'Reported' THEN 'reported'
WHEN state_mod = 'Filtered' THEN 'filtered'
ELSE NULL
END
""")
op.alter_column('submissions', sa.Column('filter_state', sa.VARCHAR(length=40), autoincrement=False, nullable=True))
op.alter_column('submissions', sa.Column('is_banned', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=True))
op.drop_column('submissions', 'state_report')
op.drop_column('submissions', 'state_mod_set_by')
op.drop_column('submissions', 'state_mod')