Merge remote-tracking branch 'upstream/frost' into 601_convert_created_utc_to_timestamp_for_commentvotes
This commit is contained in:
commit
9c24f3957a
74 changed files with 1409 additions and 2254 deletions
|
@ -178,7 +178,7 @@ div.deleted.removed {
|
|||
background-color: var(--gray) !important;
|
||||
}
|
||||
.comment-anchor:target, .unread {
|
||||
background: #2280B310 !important;
|
||||
background: #2280B310;
|
||||
}
|
||||
#frontpage .posts .card, #userpage .posts .card, #search .posts .card {
|
||||
border: none;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
--gray-800: #1e293b;
|
||||
--gray-900: #0f172a;
|
||||
--background: #2d3c50;
|
||||
--white: #fff;
|
||||
}
|
||||
|
||||
body, .container.transparent, .card {
|
||||
|
@ -141,3 +142,11 @@ color: var(--gray-700);
|
|||
#frontpage .post-title a:visited, .visited {
|
||||
color: #6e6e6e !important;
|
||||
}
|
||||
|
||||
.custom-switch .custom-control-label::after {
|
||||
background-color: var(--white);
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
background-color: var(--background) !important;
|
||||
}
|
||||
|
|
|
@ -2785,6 +2785,15 @@ ol > li::before {
|
|||
#logo {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
.logo-text {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--black)
|
||||
}
|
||||
.logo-text:hover {
|
||||
text-decoration: none;
|
||||
color: var(--black)
|
||||
}
|
||||
.navbar-brand, .navbar-light .navbar-brand {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
|
@ -4924,7 +4933,7 @@ html {
|
|||
display: block;
|
||||
}
|
||||
.comment-anchor:target, .unread {
|
||||
background: #ffffff22 !important;
|
||||
background: #ffffff22;
|
||||
padding: 12px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
@ -5368,4 +5377,4 @@ div[id^="reply-edit-"] li > p:first-child {
|
|||
|
||||
pre {
|
||||
line-height: .5rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import math
|
||||
from typing import TYPE_CHECKING, Literal, Optional
|
||||
from urllib.parse import parse_qs, urlencode, urlparse
|
||||
|
||||
from flask import g
|
||||
import math
|
||||
from sqlalchemy import *
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from files.classes.base import CreatedBase
|
||||
from files.classes.visstate import StateMod, StateReport
|
||||
from files.classes.visstate import StateMod, StateReport, VisibilityState
|
||||
from files.helpers.config.const import *
|
||||
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 (body_displayed,
|
||||
execute_shadowbanned_fake_votes)
|
||||
from files.helpers.lazy import lazy
|
||||
from files.helpers.math import clamp
|
||||
|
@ -139,7 +139,7 @@ class Comment(CreatedBase):
|
|||
@property
|
||||
@lazy
|
||||
def fullname(self):
|
||||
return f"t3_{self.id}"
|
||||
return f"comment_{self.id}"
|
||||
|
||||
@property
|
||||
@lazy
|
||||
|
@ -151,8 +151,8 @@ class Comment(CreatedBase):
|
|||
@property
|
||||
@lazy
|
||||
def parent_fullname(self):
|
||||
if self.parent_comment_id: return f"t3_{self.parent_comment_id}"
|
||||
elif self.parent_submission: return f"t2_{self.parent_submission}"
|
||||
if self.parent_comment_id: return f"comment_{self.parent_comment_id}"
|
||||
elif self.parent_submission: return f"post_{self.parent_submission}"
|
||||
|
||||
def replies(self, user):
|
||||
if self.replies2 != None: return [x for x in self.replies2 if not x.author.shadowbanned]
|
||||
|
@ -359,7 +359,7 @@ class Comment(CreatedBase):
|
|||
return self.is_message and not self.sentto
|
||||
|
||||
@lazy
|
||||
def header_msg(self, v, is_notification_page:bool, reply_count:int) -> str:
|
||||
def header_msg(self, v, is_notification_page: bool) -> str:
|
||||
'''
|
||||
Returns a message that is in the header for a comment, usually for
|
||||
display on a notification page.
|
||||
|
@ -367,9 +367,9 @@ class Comment(CreatedBase):
|
|||
if self.post:
|
||||
post_html:str = f"<a href=\"{self.post.permalink}\">{self.post.realtitle(v)}</a>"
|
||||
if v:
|
||||
if v.id == self.author_id and reply_count:
|
||||
text = f"Comment {'Replies' if reply_count != 1 else 'Reply'}"
|
||||
elif v.id == self.post.author_id and self.level == 1:
|
||||
if self.level > 1 and v.id == self.parent_comment.author_id:
|
||||
text = "Comment Reply"
|
||||
elif self.level == 1 and v.id == self.post.author_id:
|
||||
text = "Post Reply"
|
||||
elif self.parent_submission in v.subscribed_idlist():
|
||||
text = "Subscribed Thread"
|
||||
|
@ -420,22 +420,23 @@ class Comment(CreatedBase):
|
|||
|
||||
@lazy
|
||||
def show_descendants(self, v:"User | None") -> bool:
|
||||
if self.moderation_state.is_visible_to(v, getattr(self, 'is_blocking', False)):
|
||||
if self.visibility_state.is_visible_to(v, getattr(self, 'is_blocking', False)):
|
||||
return True
|
||||
return bool(self.descendant_count)
|
||||
|
||||
@lazy
|
||||
def visibility_state(self, v:"User | None") -> tuple[bool, str]:
|
||||
def visibility_and_message(self, v:"User | None") -> tuple[bool, str]:
|
||||
'''
|
||||
Returns a tuple of whether this content is visible and a publicly
|
||||
visible message to accompany it. The visibility state machine is
|
||||
a slight mess but... this should at least unify the state checks.
|
||||
'''
|
||||
return self.moderation_state.visibility_state(v, getattr(self, 'is_blocking', False))
|
||||
return self.visibility_state.visibility_and_message(
|
||||
v, getattr(self, 'is_blocking', False))
|
||||
|
||||
@property
|
||||
def moderation_state(self) -> ModerationState:
|
||||
return ModerationState.from_submittable(self)
|
||||
def visibility_state(self) -> VisibilityState:
|
||||
return VisibilityState.from_submittable(self)
|
||||
|
||||
def volunteer_janitor_is_unknown(self):
|
||||
return self.volunteer_janitor_badness > 0.4 and self.volunteer_janitor_badness < 0.6
|
||||
|
|
|
@ -8,9 +8,9 @@ from sqlalchemy.sql.sqltypes import Boolean, Integer, String, Text
|
|||
from files.classes.cron.tasks import (RepeatableTask, ScheduledTaskType,
|
||||
TaskRunContext)
|
||||
from files.classes.submission import Submission
|
||||
from files.classes.visstate import StateMod
|
||||
from files.classes.visstate import StateMod, StateReport, VisibilityState
|
||||
from files.helpers.config.const import SUBMISSION_TITLE_LENGTH_MAXIMUM
|
||||
from files.helpers.content import ModerationState, body_displayed
|
||||
from files.helpers.content import body_displayed
|
||||
from files.helpers.lazy import lazy
|
||||
from files.helpers.sanitize import filter_emojis_only
|
||||
|
||||
|
@ -173,13 +173,12 @@ class ScheduledSubmissionTask(RepeatableTask):
|
|||
return f"/tasks/scheduled_posts/{self.id}/content"
|
||||
|
||||
@property
|
||||
def moderation_state(self) -> ModerationState:
|
||||
return ModerationState(
|
||||
removed=False,
|
||||
removed_by_name=None,
|
||||
def visibility_state(self) -> VisibilityState:
|
||||
return VisibilityState(
|
||||
state_mod=StateMod.VISIBLE,
|
||||
state_mod_set_by=None,
|
||||
state_report=StateReport.UNREPORTED,
|
||||
deleted=False, # we only want to show deleted UI color if disabled
|
||||
reports_ignored=False,
|
||||
filtered=False,
|
||||
op_shadowbanned=False,
|
||||
op_id=self.author_id_submission,
|
||||
op_name_safe=self.author_name
|
||||
|
|
|
@ -6,13 +6,13 @@ from sqlalchemy.orm import Session, declared_attr, deferred, relationship
|
|||
|
||||
from files.classes.base import CreatedBase
|
||||
from files.classes.flags import Flag
|
||||
from files.classes.visstate import StateMod, StateReport
|
||||
from files.classes.visstate import StateMod, StateReport, VisibilityState
|
||||
from files.classes.votes import Vote
|
||||
from files.helpers.assetcache import assetcache_path
|
||||
from files.helpers.config.const import *
|
||||
from files.helpers.config.environment import (SCORE_HIDING_TIME_HOURS, SITE,
|
||||
SITE_FULL, SITE_ID)
|
||||
from files.helpers.content import ModerationState, body_displayed
|
||||
from files.helpers.content import body_displayed
|
||||
from files.helpers.lazy import lazy
|
||||
from files.helpers.time import format_age, format_datetime
|
||||
|
||||
|
@ -164,7 +164,7 @@ class Submission(CreatedBase):
|
|||
@property
|
||||
@lazy
|
||||
def fullname(self):
|
||||
return f"t2_{self.id}"
|
||||
return f"post_{self.id}"
|
||||
|
||||
@property
|
||||
@lazy
|
||||
|
@ -357,5 +357,5 @@ class Submission(CreatedBase):
|
|||
return f"/edit_post/{self.id}"
|
||||
|
||||
@property
|
||||
def moderation_state(self) -> ModerationState:
|
||||
return ModerationState.from_submittable(self)
|
||||
def visibility_state(self) -> VisibilityState:
|
||||
return VisibilityState.from_submittable(self)
|
||||
|
|
|
@ -243,7 +243,7 @@ class User(CreatedBase):
|
|||
@property
|
||||
@lazy
|
||||
def fullname(self):
|
||||
return f"t1_{self.id}"
|
||||
return f"user_{self.id}"
|
||||
|
||||
@property
|
||||
@lazy
|
||||
|
@ -348,25 +348,15 @@ class User(CreatedBase):
|
|||
def post_notifications_count(self):
|
||||
return g.db.query(Notification.user_id).join(Comment).filter(Notification.user_id == self.id, Notification.read == False, Comment.author_id == AUTOJANNY_ID).count()
|
||||
|
||||
@property
|
||||
@lazy
|
||||
def reddit_notifications_count(self):
|
||||
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
|
||||
@lazy
|
||||
def normal_count(self):
|
||||
return self.notifications_count - self.post_notifications_count - self.reddit_notifications_count
|
||||
return self.notifications_count - self.post_notifications_count
|
||||
|
||||
@property
|
||||
@lazy
|
||||
def do_posts(self):
|
||||
return self.post_notifications_count and self.notifications_count-self.reddit_notifications_count == self.post_notifications_count
|
||||
|
||||
@property
|
||||
@lazy
|
||||
def do_reddit(self):
|
||||
return self.notifications_count == self.reddit_notifications_count
|
||||
return self.post_notifications_count and self.notifications_count == self.post_notifications_count
|
||||
|
||||
@property
|
||||
@lazy
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from files.helpers.config.const import PERMS
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from files.classes.user import User
|
||||
from files.helpers.content import Submittable
|
||||
|
||||
|
||||
class StateMod(enum.Enum):
|
||||
VISIBLE = 0
|
||||
|
@ -11,3 +21,107 @@ class StateReport(enum.Enum):
|
|||
RESOLVED = 1
|
||||
REPORTED = 2
|
||||
IGNORED = 3
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True, slots=True)
|
||||
class VisibilityState:
|
||||
'''
|
||||
The full moderation state machine. It holds the moderation state, report
|
||||
state, deleted information, and shadowban information. A decision to show
|
||||
or hide a post or comment should be able to be done with information from
|
||||
this alone.
|
||||
'''
|
||||
state_mod: StateMod
|
||||
state_mod_set_by: str | None
|
||||
state_report: StateReport
|
||||
state_mod_set_by: str | None
|
||||
deleted: bool
|
||||
op_shadowbanned: bool
|
||||
op_id: int
|
||||
op_name_safe: str
|
||||
|
||||
@property
|
||||
def removed(self) -> bool:
|
||||
return self.state_mod == StateMod.REMOVED
|
||||
|
||||
@property
|
||||
def filtered(self) -> bool:
|
||||
return self.state_mod == StateMod.FILTERED
|
||||
|
||||
@property
|
||||
def reports_ignored(self) -> bool:
|
||||
return self.state_report == StateReport.IGNORED
|
||||
|
||||
@classmethod
|
||||
def from_submittable(cls, target: Submittable) -> "VisibilityState":
|
||||
return cls(
|
||||
state_mod=target.state_mod,
|
||||
state_mod_set_by=target.state_mod_set_by, # type: ignore
|
||||
state_report=target.state_report,
|
||||
deleted=bool(target.state_user_deleted_utc != None),
|
||||
op_shadowbanned=bool(target.author.shadowbanned),
|
||||
op_id=target.author_id, # type: ignore
|
||||
op_name_safe=target.author_name
|
||||
)
|
||||
|
||||
def moderated_body(self, v: User | None) -> str | None:
|
||||
if v and (v.admin_level >= PERMS['POST_COMMENT_MODERATION'] \
|
||||
or v.id == self.op_id):
|
||||
return None
|
||||
if self.deleted: return 'Deleted'
|
||||
if self.appear_removed(v): return 'Removed'
|
||||
if self.filtered: return 'Filtered'
|
||||
return None
|
||||
|
||||
def visibility_and_message(self, v: User | None, is_blocking: bool) -> tuple[bool, str]:
|
||||
'''
|
||||
Returns a tuple of whether this content is visible and a publicly
|
||||
visible message to accompany it. The visibility state machine is
|
||||
a slight mess but... this should at least unify the state checks.
|
||||
'''
|
||||
def can(v: User | None, perm_level: int) -> bool:
|
||||
return v and v.admin_level >= perm_level
|
||||
|
||||
can_moderate: bool = can(v, PERMS['POST_COMMENT_MODERATION'])
|
||||
can_shadowban: bool = can(v, PERMS['USER_SHADOWBAN'])
|
||||
|
||||
if v and v.id == self.op_id:
|
||||
return True, "This shouldn't be here, please report it!"
|
||||
if (self.removed and not can_moderate) or \
|
||||
(self.op_shadowbanned and not can_shadowban):
|
||||
msg: str = 'Removed'
|
||||
if self.state_mod_set_by:
|
||||
msg = f'Removed by @{self.state_mod_set_by}'
|
||||
return False, msg
|
||||
if self.filtered and not can_moderate:
|
||||
return False, 'Filtered'
|
||||
if self.deleted and not can_moderate:
|
||||
return False, 'Deleted by author'
|
||||
if is_blocking:
|
||||
return False, f'You are blocking @{self.op_name_safe}'
|
||||
return True, "This shouldn't be here, please report it!"
|
||||
|
||||
def is_visible_to(self, v: User | None, is_blocking: bool) -> bool:
|
||||
return self.visibility_and_message(v, is_blocking)[0]
|
||||
|
||||
def replacement_message(self, v: User | None, is_blocking: bool) -> str:
|
||||
return self.visibility_and_message(v, is_blocking)[1]
|
||||
|
||||
def appear_removed(self, v: User | None) -> bool:
|
||||
if self.removed: return True
|
||||
if not self.op_shadowbanned: return False
|
||||
return (not v) or bool(v.admin_level < PERMS['USER_SHADOWBAN'])
|
||||
|
||||
@property
|
||||
def publicly_visible(self) -> bool:
|
||||
return all(
|
||||
not state for state in
|
||||
[self.deleted, self.removed, self.filtered, self.op_shadowbanned]
|
||||
)
|
||||
|
||||
@property
|
||||
def explicitly_moderated(self) -> bool:
|
||||
'''
|
||||
Whether this was removed or filtered and not as the result of a shadowban
|
||||
'''
|
||||
return self.removed or self.filtered
|
||||
|
|
|
@ -2,14 +2,10 @@ from __future__ import annotations
|
|||
|
||||
import random
|
||||
import urllib.parse
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from files.helpers.config.const import PERMS
|
||||
from files.classes.visstate import StateMod, StateReport
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from files.classes import Comment, Submission, User
|
||||
Submittable = Comment | Submission
|
||||
|
@ -90,100 +86,8 @@ def canonicalize_url2(url:str, *, httpsify:bool=False) -> urllib.parse.ParseResu
|
|||
return url_parsed
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True, slots=True)
|
||||
class ModerationState:
|
||||
'''
|
||||
The moderation state machine. This holds moderation state information,
|
||||
including whether this was removed, deleted, filtered, whether OP was
|
||||
shadowbanned, etc
|
||||
'''
|
||||
removed: bool
|
||||
removed_by_name: str | None
|
||||
deleted: bool
|
||||
reports_ignored: bool
|
||||
filtered: bool
|
||||
op_shadowbanned: bool
|
||||
op_id: int
|
||||
op_name_safe: str
|
||||
|
||||
@classmethod
|
||||
def from_submittable(cls, target: Submittable) -> "ModerationState":
|
||||
return cls(
|
||||
removed=bool(target.state_mod != StateMod.VISIBLE),
|
||||
removed_by_name=target.state_mod_set_by, # type: ignore
|
||||
deleted=bool(target.state_user_deleted_utc != None),
|
||||
reports_ignored=bool(target.state_report == StateReport.IGNORED),
|
||||
filtered=bool(target.state_mod == StateMod.FILTERED),
|
||||
op_shadowbanned=bool(target.author.shadowbanned),
|
||||
op_id=target.author_id, # type: ignore
|
||||
op_name_safe=target.author_name
|
||||
)
|
||||
|
||||
def moderated_body(self, v: User | None) -> str | None:
|
||||
if v and (v.admin_level >= PERMS['POST_COMMENT_MODERATION'] \
|
||||
or v.id == self.op_id):
|
||||
return None
|
||||
if self.deleted: return 'Deleted'
|
||||
if self.appear_removed(v): return 'Removed'
|
||||
if self.filtered: return 'Filtered'
|
||||
return None
|
||||
|
||||
def visibility_state(self, v: User | None, is_blocking: bool) -> tuple[bool, str]:
|
||||
'''
|
||||
Returns a tuple of whether this content is visible and a publicly
|
||||
visible message to accompany it. The visibility state machine is
|
||||
a slight mess but... this should at least unify the state checks.
|
||||
'''
|
||||
def can(v: User | None, perm_level: int) -> bool:
|
||||
return v and v.admin_level >= perm_level
|
||||
|
||||
can_moderate: bool = can(v, PERMS['POST_COMMENT_MODERATION'])
|
||||
can_shadowban: bool = can(v, PERMS['USER_SHADOWBAN'])
|
||||
|
||||
if v and v.id == self.op_id:
|
||||
return True, "This shouldn't be here, please report it!"
|
||||
if (self.removed and not can_moderate) or \
|
||||
(self.op_shadowbanned and not can_shadowban):
|
||||
msg: str = 'Removed'
|
||||
if self.removed_by_name:
|
||||
msg = f'Removed by @{self.removed_by_name}'
|
||||
return False, msg
|
||||
if self.filtered and not can_moderate:
|
||||
return False, 'Filtered'
|
||||
if self.deleted and not can_moderate:
|
||||
return False, 'Deleted by author'
|
||||
if is_blocking:
|
||||
return False, f'You are blocking @{self.op_name_safe}'
|
||||
return True, "This shouldn't be here, please report it!"
|
||||
|
||||
def is_visible_to(self, v: User | None, is_blocking: bool) -> bool:
|
||||
return self.visibility_state(v, is_blocking)[0]
|
||||
|
||||
def replacement_message(self, v: User | None, is_blocking: bool) -> str:
|
||||
return self.visibility_state(v, is_blocking)[1]
|
||||
|
||||
def appear_removed(self, v: User | None) -> bool:
|
||||
if self.removed: return True
|
||||
if not self.op_shadowbanned: return False
|
||||
return (not v) or bool(v.admin_level < PERMS['USER_SHADOWBAN'])
|
||||
|
||||
@property
|
||||
def publicly_visible(self) -> bool:
|
||||
return all(
|
||||
not state for state in
|
||||
[self.deleted, self.removed, self.filtered, self.op_shadowbanned]
|
||||
)
|
||||
|
||||
@property
|
||||
def explicitly_moderated(self) -> bool:
|
||||
'''
|
||||
Whether this was removed or filtered and not as the result of a shadowban
|
||||
'''
|
||||
return self.removed or self.filtered
|
||||
|
||||
|
||||
def body_displayed(target:Submittable, v:Optional[User], is_html:bool) -> str:
|
||||
moderated:Optional[str] = target.moderation_state.moderated_body(v)
|
||||
moderated:Optional[str] = target.visibility_state.moderated_body(v)
|
||||
if moderated: return moderated
|
||||
|
||||
body = target.body_html if is_html else target.body
|
||||
|
|
|
@ -45,7 +45,7 @@ def frontlist(v=None, sort='new', page=1, t="all", ids_only=True, ccmode="false"
|
|||
if (ccmode == "true"):
|
||||
posts = posts.filter(Submission.club == True)
|
||||
|
||||
posts = posts.filter_by(state_mod=StateMod.VISIBLE, private=False, state_user_deleted_utc=None)
|
||||
posts = posts.filter_by(private=False, state_user_deleted_utc=None)
|
||||
|
||||
if ccmode == "false" and not gt and not lt:
|
||||
posts = posts.filter_by(stickied=None)
|
||||
|
|
|
@ -184,9 +184,6 @@ def sanitize_raw(sanitized:Optional[str], allow_newlines:bool, length_limit:Opti
|
|||
|
||||
@with_gevent_timeout(2)
|
||||
def sanitize(sanitized, alert=False, comment=False, edit=False):
|
||||
# double newlines, eg. hello\nworld becomes hello\n\nworld, which later becomes <p>hello</p><p>world</p>
|
||||
sanitized = linefeeds_regex.sub(r'\1\n\n\2', sanitized)
|
||||
|
||||
if MULTIMEDIA_EMBEDDING_ENABLED:
|
||||
# turn eg. https://wikipedia.org/someimage.jpg into 
|
||||
sanitized = image_regex.sub(r'\1\4', sanitized)
|
||||
|
|
|
@ -26,7 +26,7 @@ def pusher_thread2(interests, notifbody, username):
|
|||
'notification': {
|
||||
'title': f'New message from @{username}',
|
||||
'body': notifbody,
|
||||
'deep_link': f'{SITE_FULL}/notifications?messages=true',
|
||||
'deep_link': f'{SITE_FULL}/notifications/messages',
|
||||
'icon': SITE_FULL + assetcache_path(f'images/{SITE_ID}/icon.webp'),
|
||||
}
|
||||
},
|
||||
|
@ -36,7 +36,7 @@ def pusher_thread2(interests, notifbody, username):
|
|||
'body': notifbody,
|
||||
},
|
||||
'data': {
|
||||
'url': '/notifications?messages=true',
|
||||
'url': '/notifications/messages',
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -111,16 +111,15 @@ def api_comment(v):
|
|||
parent_fullname = request.values.get("parent_fullname", "").strip()
|
||||
|
||||
if len(parent_fullname) < 4: abort(400)
|
||||
id = parent_fullname[3:]
|
||||
parent = None
|
||||
parent_post = None
|
||||
parent_comment_id = None
|
||||
|
||||
if parent_fullname.startswith("t2_"):
|
||||
parent = get_post(id, v=v)
|
||||
if parent_fullname.startswith("post_"):
|
||||
parent = get_post(parent_fullname.split("post_")[1], v=v)
|
||||
parent_post = parent
|
||||
elif parent_fullname.startswith("t3_"):
|
||||
parent = get_comment(id, v=v)
|
||||
elif parent_fullname.startswith("comment_"):
|
||||
parent = get_comment(parent_fullname.split("comment_")[1], v=v)
|
||||
parent_post = get_post(parent.parent_submission, v=v) if parent.parent_submission else None
|
||||
parent_comment_id = parent.id
|
||||
else: abort(400)
|
||||
|
|
|
@ -42,124 +42,149 @@ def unread(v):
|
|||
|
||||
@app.get("/notifications")
|
||||
@auth_required
|
||||
def notifications(v):
|
||||
try: page = max(int(request.values.get("page", 1)), 1)
|
||||
except: page = 1
|
||||
def notifications_main(v: User):
|
||||
page: int = max(request.values.get("page", 1, int) or 1, 1)
|
||||
|
||||
messages = request.values.get('messages')
|
||||
modmail = request.values.get('modmail')
|
||||
posts = request.values.get('posts')
|
||||
reddit = request.values.get('reddit')
|
||||
if modmail and v.admin_level >= 2:
|
||||
comments = g.db.query(Comment).filter(Comment.sentto == MODMAIL_ID).order_by(Comment.id.desc()).offset(25*(page-1)).limit(26).all()
|
||||
next_exists = (len(comments) > 25)
|
||||
listing = comments[:25]
|
||||
elif messages:
|
||||
if v and (v.shadowbanned or v.admin_level >= 3):
|
||||
comments = g.db.query(Comment).filter(Comment.sentto != None, or_(Comment.author_id==v.id, Comment.sentto==v.id), Comment.parent_submission == None, Comment.level == 1).order_by(Comment.id.desc()).offset(25*(page-1)).limit(26).all()
|
||||
else:
|
||||
comments = g.db.query(Comment).join(User, User.id == Comment.author_id).filter(User.shadowbanned == None, Comment.sentto != None, or_(Comment.author_id==v.id, Comment.sentto==v.id), Comment.parent_submission == None, Comment.level == 1).order_by(Comment.id.desc()).offset(25*(page-1)).limit(26).all()
|
||||
|
||||
next_exists = (len(comments) > 25)
|
||||
listing = comments[:25]
|
||||
elif posts:
|
||||
notifications = g.db.query(Notification, Comment).join(Comment, Notification.comment_id == Comment.id).filter(Notification.user_id == v.id, Comment.author_id == AUTOJANNY_ID).order_by(Notification.created_utc.desc()).offset(25 * (page - 1)).limit(101).all()
|
||||
|
||||
listing = []
|
||||
|
||||
for index, x in enumerate(notifications[:100]):
|
||||
n, c = x
|
||||
if n.read and index > 24: break
|
||||
elif not n.read:
|
||||
n.read = True
|
||||
c.unread = True
|
||||
g.db.add(n)
|
||||
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
|
||||
listing.append(c)
|
||||
|
||||
g.db.commit()
|
||||
|
||||
next_exists = (len(notifications) > len(listing))
|
||||
elif reddit:
|
||||
notifications = g.db.query(Notification, Comment).join(Comment, Notification.comment_id == Comment.id).filter(Notification.user_id == v.id, Comment.body_html.like('%<p>New site mention: <a href="https://old.reddit.com/r/%'), Comment.parent_submission == None, Comment.author_id == NOTIFICATIONS_ID).order_by(Notification.created_utc.desc()).offset(25 * (page - 1)).limit(101).all()
|
||||
|
||||
listing = []
|
||||
|
||||
for index, x in enumerate(notifications[:100]):
|
||||
n, c = x
|
||||
if n.read and index > 24: break
|
||||
elif not n.read:
|
||||
n.read = True
|
||||
c.unread = True
|
||||
g.db.add(n)
|
||||
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
|
||||
listing.append(c)
|
||||
|
||||
g.db.commit()
|
||||
|
||||
next_exists = (len(notifications) > len(listing))
|
||||
else:
|
||||
comments = g.db.query(Comment, Notification).join(Notification, Notification.comment_id == Comment.id).filter(
|
||||
comments = (g.db.query(Comment, Notification)
|
||||
.join(Notification.comment)
|
||||
.filter(
|
||||
Notification.user_id == v.id,
|
||||
Comment.state_mod == StateMod.VISIBLE,
|
||||
Comment.state_user_deleted_utc == None,
|
||||
Comment.author_id != AUTOJANNY_ID,
|
||||
Comment.body_html.notlike('%<p>New site mention: <a href="https://old.reddit.com/r/%')
|
||||
).order_by(Notification.created_utc.desc())
|
||||
).order_by(Notification.created_utc.desc()))
|
||||
|
||||
if not (v and (v.shadowbanned or v.admin_level >= 3)):
|
||||
comments = comments.join(User, User.id == Comment.author_id).filter(User.shadowbanned == None)
|
||||
if not v.shadowbanned and v.admin_level < 3:
|
||||
comments = comments.join(Comment.author).filter(User.shadowbanned == None)
|
||||
|
||||
comments = comments.offset(25 * (page - 1)).limit(26).all()
|
||||
comments = comments.offset(25 * (page - 1)).limit(26).all()
|
||||
|
||||
next_exists = (len(comments) > 25)
|
||||
comments = comments[:25]
|
||||
next_exists = (len(comments) > 25)
|
||||
comments = comments[:25]
|
||||
|
||||
cids = [x[0].id for x in comments]
|
||||
for c, n in comments:
|
||||
c.notif_utc = n.created_utc
|
||||
c.unread = not n.read
|
||||
n.read = True
|
||||
|
||||
comms = get_comments(cids, v=v)
|
||||
listing: list[Comment] = [c for c, _ in comments]
|
||||
|
||||
listing = []
|
||||
for c, n in comments:
|
||||
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
|
||||
if not n.read:
|
||||
n.read = True
|
||||
c.unread = True
|
||||
g.db.add(n)
|
||||
# TODO: commit after request rendered, then default session expiry is fine
|
||||
g.db.expire_on_commit = False
|
||||
g.db.commit()
|
||||
g.db.expire_on_commit = True
|
||||
|
||||
if c.parent_submission:
|
||||
if c.replies2 == None:
|
||||
c.replies2 = c.child_comments.filter(or_(Comment.author_id == v.id, Comment.id.in_(cids))).all()
|
||||
for x in c.replies2:
|
||||
if x.replies2 == None: x.replies2 = []
|
||||
count = 0
|
||||
while count < 50 and c.parent_comment and (c.parent_comment.author_id == v.id or c.parent_comment.id in cids):
|
||||
count += 1
|
||||
c = c.parent_comment
|
||||
if c.replies2 == None:
|
||||
c.replies2 = c.child_comments.filter(or_(Comment.author_id == v.id, Comment.id.in_(cids))).all()
|
||||
for x in c.replies2:
|
||||
if x.replies2 == None:
|
||||
x.replies2 = x.child_comments.filter(or_(Comment.author_id == v.id, Comment.id.in_(cids))).all()
|
||||
else:
|
||||
while c.parent_comment:
|
||||
c = c.parent_comment
|
||||
c.replies2 = g.db.query(Comment).filter_by(parent_comment_id=c.id).order_by(Comment.id).all()
|
||||
if request.headers.get("Authorization"):
|
||||
return {"data": [x.json for x in listing]}
|
||||
|
||||
if c not in listing: listing.append(c)
|
||||
return render_template("notifications.html",
|
||||
v=v,
|
||||
notifications=listing,
|
||||
next_exists=next_exists,
|
||||
page=page,
|
||||
standalone=True,
|
||||
render_replies=False,
|
||||
is_notification_page=True,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/notifications/posts")
|
||||
@auth_required
|
||||
def notifications_posts(v: User):
|
||||
page: int = max(request.values.get("page", 1, int) or 1, 1)
|
||||
|
||||
notifications = (g.db.query(Notification, Comment)
|
||||
.join(Comment, Notification.comment_id == Comment.id)
|
||||
.filter(Notification.user_id == v.id, Comment.author_id == AUTOJANNY_ID)
|
||||
.order_by(Notification.created_utc.desc()).offset(25 * (page - 1)).limit(101).all())
|
||||
|
||||
listing = []
|
||||
|
||||
for index, x in enumerate(notifications[:100]):
|
||||
n, c = x
|
||||
if n.read and index > 24: break
|
||||
elif not n.read:
|
||||
n.read = True
|
||||
c.unread = True
|
||||
g.db.add(n)
|
||||
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
|
||||
listing.append(c)
|
||||
|
||||
next_exists = (len(notifications) > len(listing))
|
||||
|
||||
g.db.commit()
|
||||
|
||||
if request.headers.get("Authorization"): return {"data":[x.json for x in listing]}
|
||||
if request.headers.get("Authorization"):
|
||||
return {"data": [x.json for x in listing]}
|
||||
|
||||
return render_template("notifications.html",
|
||||
v=v,
|
||||
notifications=listing,
|
||||
next_exists=next_exists,
|
||||
page=page,
|
||||
standalone=True,
|
||||
render_replies=True
|
||||
)
|
||||
v=v,
|
||||
notifications=listing,
|
||||
next_exists=next_exists,
|
||||
page=page,
|
||||
standalone=True,
|
||||
render_replies=True,
|
||||
is_notification_page=True,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/notifications/modmail")
|
||||
@admin_level_required(2)
|
||||
def notifications_modmail(v: User):
|
||||
page: int = max(request.values.get("page", 1, int) or 1, 1)
|
||||
|
||||
comments = (g.db.query(Comment)
|
||||
.filter(Comment.sentto == MODMAIL_ID)
|
||||
.order_by(Comment.id.desc()).offset(25 * (page - 1)).limit(26).all())
|
||||
next_exists = (len(comments) > 25)
|
||||
listing = comments[:25]
|
||||
|
||||
if request.headers.get("Authorization"):
|
||||
return {"data": [x.json for x in listing]}
|
||||
|
||||
return render_template("notifications.html",
|
||||
v=v,
|
||||
notifications=listing,
|
||||
next_exists=next_exists,
|
||||
page=page,
|
||||
standalone=True,
|
||||
render_replies=True,
|
||||
is_notification_page=True,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/notifications/messages")
|
||||
@auth_required
|
||||
def notifications_messages(v: User):
|
||||
page: int = max(request.values.get("page", 1, int) or 1, 1)
|
||||
|
||||
comments = g.db.query(Comment).filter(
|
||||
Comment.sentto != None,
|
||||
or_(Comment.author_id==v.id, Comment.sentto==v.id),
|
||||
Comment.parent_submission == None,
|
||||
Comment.level == 1,
|
||||
)
|
||||
|
||||
if not v.shadowbanned and v.admin_level < 3:
|
||||
comments = comments.join(Comment.author).filter(User.shadowbanned == None)
|
||||
|
||||
comments = comments.order_by(Comment.id.desc()).offset(25 * (page - 1)).limit(26).all()
|
||||
|
||||
next_exists = (len(comments) > 25)
|
||||
listing = comments[:25]
|
||||
|
||||
if request.headers.get("Authorization"):
|
||||
return {"data": [x.json for x in listing]}
|
||||
|
||||
return render_template("notifications.html",
|
||||
v=v,
|
||||
notifications=listing,
|
||||
next_exists=next_exists,
|
||||
page=page,
|
||||
standalone=True,
|
||||
render_replies=True,
|
||||
is_notification_page=True,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
|
|
|
@ -17,7 +17,7 @@ def login_get(v):
|
|||
if redir.startswith(f'{SITE_FULL}/'): return redirect(redir)
|
||||
elif redir.startswith('/'): return redirect(f'{SITE_FULL}{redir}')
|
||||
|
||||
return render_template("login.html", failed=False, redirect=redir)
|
||||
return render_template("login/login.html", failed=False, redirect=redir)
|
||||
|
||||
|
||||
def check_for_alts(current_id):
|
||||
|
@ -95,18 +95,18 @@ def login_post():
|
|||
|
||||
if not account:
|
||||
time.sleep(random.uniform(0, 2))
|
||||
return render_template("login.html", failed=True)
|
||||
return render_template("login/login.html", failed=True)
|
||||
|
||||
if request.values.get("password"):
|
||||
|
||||
if not account.verifyPass(request.values.get("password")):
|
||||
time.sleep(random.uniform(0, 2))
|
||||
return render_template("login.html", failed=True)
|
||||
return render_template("login/login.html", failed=True)
|
||||
|
||||
if account.mfa_secret:
|
||||
now = int(time.time())
|
||||
hash = generate_hash(f"{account.id}+{now}+2fachallenge")
|
||||
return render_template("login_2fa.html",
|
||||
return render_template("login/login_2fa.html",
|
||||
v=account,
|
||||
time=now,
|
||||
hash=hash,
|
||||
|
@ -124,7 +124,7 @@ def login_post():
|
|||
|
||||
if not account.validate_2fa(request.values.get("2fa_token", "").strip()):
|
||||
hash = generate_hash(f"{account.id}+{time}+2fachallenge")
|
||||
return render_template("login_2fa.html",
|
||||
return render_template("login/login_2fa.html",
|
||||
v=account,
|
||||
time=now,
|
||||
hash=hash,
|
||||
|
@ -193,7 +193,7 @@ def sign_up_get(v):
|
|||
ref_user = None
|
||||
|
||||
if ref_user and (ref_user.id in session.get("history", [])):
|
||||
return render_template("sign_up_failed_ref.html")
|
||||
return render_template("login/sign_up_failed_ref.html")
|
||||
|
||||
now = int(time.time())
|
||||
token = token_hex(16)
|
||||
|
@ -209,7 +209,7 @@ def sign_up_get(v):
|
|||
error = request.values.get("error")
|
||||
|
||||
return render_template(
|
||||
"sign_up.html",
|
||||
"login/sign_up.html",
|
||||
formkey=formkey,
|
||||
now=now,
|
||||
ref_user=ref_user,
|
||||
|
@ -358,7 +358,7 @@ def sign_up_post(v):
|
|||
|
||||
@app.get("/forgot")
|
||||
def get_forgot():
|
||||
return render_template("forgot_password.html")
|
||||
return render_template("login/forgot_password.html")
|
||||
|
||||
|
||||
@app.post("/forgot")
|
||||
|
@ -370,7 +370,7 @@ def post_forgot():
|
|||
email = request.values.get("email",'').strip().lower()
|
||||
|
||||
if not email_regex.fullmatch(email):
|
||||
return render_template("forgot_password.html", error="Invalid email.")
|
||||
return render_template("login/forgot_password.html", error="Invalid email.")
|
||||
|
||||
username = username.lstrip('@')
|
||||
|
||||
|
@ -390,7 +390,7 @@ def post_forgot():
|
|||
v=user)
|
||||
)
|
||||
|
||||
return render_template("forgot_password.html",
|
||||
return render_template("login/forgot_password.html",
|
||||
msg="If the username and email matches an account, you will be sent a password reset email. You have ten minutes to complete the password reset process.")
|
||||
|
||||
|
||||
|
@ -420,7 +420,7 @@ def get_reset():
|
|||
|
||||
reset_token = generate_hash(f"{user.id}+{timestamp}+reset+{user.login_nonce}")
|
||||
|
||||
return render_template("reset_password.html",
|
||||
return render_template("login/reset_password.html",
|
||||
v=user,
|
||||
token=reset_token,
|
||||
time=timestamp,
|
||||
|
@ -456,7 +456,7 @@ def post_reset(v):
|
|||
abort(404)
|
||||
|
||||
if password != confirm_password:
|
||||
return render_template("reset_password.html",
|
||||
return render_template("login/reset_password.html",
|
||||
v=user,
|
||||
token=token,
|
||||
time=timestamp,
|
||||
|
@ -475,7 +475,7 @@ def post_reset(v):
|
|||
@auth_desired
|
||||
def lost_2fa(v):
|
||||
return render_template(
|
||||
"lost_2fa.html",
|
||||
"login/lost_2fa.html",
|
||||
v=v
|
||||
)
|
||||
|
||||
|
|
|
@ -78,13 +78,13 @@ def participation_stats(v):
|
|||
"signups last 24h": users.filter(User.created_utc > day).count(),
|
||||
"total posts": submissions.count(),
|
||||
"posting users": g.db.query(Submission.author_id).distinct().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(Submission.state_mod != StateMod.VISIBLE).count(),
|
||||
"listed posts": submissions.filter(Submission.state_mod == StateMod.VISIBLE).filter(Submission.state_user_deleted_utc == None).count(),
|
||||
"removed posts (by admins)": submissions.filter(Submission.state_mod != StateMod.VISIBLE).count(),
|
||||
"deleted posts (by author)": submissions.filter(Submission.state_user_deleted_utc != None).count(),
|
||||
"posts last 24h": submissions.filter(Submission.created_utc > day).count(),
|
||||
"total comments": comments.filter(Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count(),
|
||||
"commenting users": g.db.query(Comment.author_id).distinct().count(),
|
||||
"removed comments (by admins)": comments.filter_by(Comment.state_mod != StateMod.VISIBLE).count(),
|
||||
"removed comments (by admins)": comments.filter(Comment.state_mod != StateMod.VISIBLE).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(),
|
||||
"post votes": g.db.query(Vote.submission_id).count(),
|
||||
|
|
|
@ -540,7 +540,7 @@ def messagereply(v):
|
|||
'notification': {
|
||||
'title': f'New message from @{v.username}',
|
||||
'body': notifbody,
|
||||
'deep_link': f'{SITE_FULL}/notifications?messages=true',
|
||||
'deep_link': f'{SITE_FULL}/notifications/messages',
|
||||
'icon': SITE_FULL + assetcache_path(f'images/{SITE_ID}/icon.webp'),
|
||||
}
|
||||
},
|
||||
|
@ -550,7 +550,7 @@ def messagereply(v):
|
|||
'body': notifbody,
|
||||
},
|
||||
'data': {
|
||||
'url': '/notifications?messages=true',
|
||||
'url': '/notifications/messages',
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -17,8 +17,8 @@ def admin_vote_info_get(v):
|
|||
if not link: return render_template("votes.html", v=v)
|
||||
|
||||
try:
|
||||
if "t2_" in link: thing = get_post(link.split("t2_")[1], v=v)
|
||||
elif "t3_" in link: thing = get_comment(link.split("t3_")[1], v=v)
|
||||
if "post_" in link: thing = get_post(link.split("post_")[1], v=v)
|
||||
elif "comment_" in link: thing = get_comment(link.split("comment_")[1], v=v)
|
||||
else: abort(400)
|
||||
except: abort(400)
|
||||
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
{% include "analytics.html" %}
|
||||
|
||||
<meta name="description" content="{{config('DESCRIPTION')}}">
|
||||
{% include "csp.html" %}
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>{% block pagetitle %}{{SITE_TITLE}}{% endblock %}</title>
|
||||
|
||||
|
||||
{% if v %}
|
||||
<style>:root{--primary:#{{v.themecolor}}}</style>
|
||||
<link rel="stylesheet" href="{{ 'css/main.css' | asset }}">
|
||||
<link rel="stylesheet" href="{{ ('css/'~v.theme~'.css') | asset }}">
|
||||
{% if v.css %}
|
||||
<style>{{v.css | safe}}</style>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<style>:root{--primary:#{{config('DEFAULT_COLOR')}}</style>
|
||||
<link rel="stylesheet" href="{{ 'css/main.css' | asset }}">
|
||||
<link rel="stylesheet" href="{{ ('css/'~config('DEFAULT_THEME')~'.css') | asset }}">
|
||||
{% endif %}
|
||||
|
||||
</head>
|
||||
|
||||
<body id="login">
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-transparent fixed-top border-0">
|
||||
<div class="container-fluid">
|
||||
<button class="navbar-toggler d-none" role="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive"
|
||||
aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid position-absolute h-100 p-0">
|
||||
<div class="row no-gutters h-100">
|
||||
|
||||
<div class="col-12 col-md-6 my-auto p-3">
|
||||
|
||||
<div class="row justify-content-center">
|
||||
|
||||
<div class="col-10 col-md-7">
|
||||
|
||||
<div class="mb-5">
|
||||
<a href="/" class="text-decoration-none"><span class="h3 text-primary"></span></a>
|
||||
</div>
|
||||
|
||||
<h1 class="h2">{% block authtitle %}{% endblock %}</h1>
|
||||
|
||||
<p class="text-muted mb-md-5">{% block authtext %}{% endblock %}</p>
|
||||
|
||||
{% if error %}
|
||||
<div class="alert alert-danger alert-dismissible fade show d-flex my-3" role="alert">
|
||||
<i class="fas fa-exclamation-circle my-auto"></i>
|
||||
<span>
|
||||
{{error}}
|
||||
</span>
|
||||
<button class="close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true"><i class="far fa-times"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if msg %}
|
||||
<div class="alert alert-success alert-dismissible fade show d-flex my-3" role="alert">
|
||||
<i class="fas fa-info-circle my-auto" aria-hidden="true"></i>
|
||||
<span>
|
||||
{{msg}}
|
||||
</span>
|
||||
<button class="close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true"><i class="far fa-times"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 d-none d-md-block">
|
||||
|
||||
<div class="splash-wrapper">
|
||||
|
||||
<div class="splash-overlay"></div>
|
||||
|
||||
<img alt="cover" loading="lazy" class="splash-img" src="{{ ('images/'~SITE_ID~'/cover.webp') | asset }}"></img>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -10,9 +10,8 @@
|
|||
{%- set score = c.score_str(render_ctx) -%}
|
||||
{%- set downs = c.downvotes_str(render_ctx) -%}
|
||||
{% set replies = c.replies(v) %}
|
||||
{%- set is_notification_page = request.path.startswith('/notifications') -%}
|
||||
|
||||
{% if not c.visibility_state(v)[0] %}
|
||||
{% if not c.visibility_and_message(v)[0] %}
|
||||
{% if c.show_descendants(v) %}
|
||||
<div id="comment-{{c.id}}" class="comment">
|
||||
<div class="comment-collapse-icon" onclick="collapse_comment('{{c.id}}', this.parentElement)"></div>
|
||||
|
@ -22,7 +21,7 @@
|
|||
|
||||
<div class="comment-user-info">
|
||||
{% if standalone and c.over_18 %}<span class="badge badge-danger">+18</span>{% endif %}
|
||||
{{c.visibility_state(v)[1]}}
|
||||
{{c.visibility_and_message(v)[1]}}
|
||||
</div>
|
||||
|
||||
<div class="comment-body">
|
||||
|
@ -61,10 +60,10 @@
|
|||
{%- set voted = c.voted_display(v) -%}
|
||||
|
||||
{% if standalone and level==1 %}
|
||||
<div class="post-info post-row-cid-{{c.id}} mb-1 mr-2 {% if is_notification_page %}mt-5{% else %}mt-3{% endif %}">
|
||||
<div class="post-info post-row-cid-{{c.id}} mb-1 mr-2 mt-3">
|
||||
{% if c.post and c.post.over_18 %}<span class="badge badge-danger text-small-extra mr-1">+18</span>{% endif %}
|
||||
<span class="align-top">
|
||||
<span class="font-weight-bold">{{c.header_msg(v, is_notification_page, replies | length) | safe}}</span>
|
||||
<span class="font-weight-bold">{{c.header_msg(v, is_notification_page) | safe}}</span>
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -83,7 +82,7 @@
|
|||
<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.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'-%}
|
||||
{% if c.state_mod == StateMod.REMOVED and c.state_mod_set_by and v and v.admin_level >= 2 %}
|
||||
{% if c.state_mod == StateMod.REMOVED and c.state_mod_set_by and v and v.admin_level >= PERMS['POST_COMMENT_MODERATION'] %}
|
||||
<div id="comment-banned-warning" class="comment-text text-removed mb-0">removed by @{{c.state_mod_set_by}}</div>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<div id="reply-to-{{c.id}}" class="d-none">
|
||||
<div id="comment-form-space-{{c.fullname}}" class="comment-write collapsed child">
|
||||
<form id="reply-to-t3_{{c.id}}" action="/comment" method="post" enctype="multipart/form-data">
|
||||
<form id="reply-to-{{c.fullname}}" action="/comment" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="formkey" value="{{v.formkey}}">
|
||||
<input type="hidden" name="parent_fullname" value="{{c.fullname}}">
|
||||
<input autocomplete="off" id="reply-form-submission-{{c.fullname}}" type="hidden" name="submission" value="{{c.post.id}}">
|
||||
<textarea required autocomplete="off" minlength="1" maxlength="{{COMMENT_BODY_LENGTH_MAXIMUM}}" oninput="markdown('reply-form-body-{{c.fullname}}', 'reply-edit-{{c.id}}');charLimit('reply-form-body-{{c.fullname}}','charcount-{{c.id}}')" id="reply-form-body-{{c.fullname}}" data-fullname="{{c.fullname}}" name="body" form="reply-to-t3_{{c.id}}" class="comment-box form-control rounded" aria-label="With textarea" placeholder="Add your comment..." rows="3"></textarea>
|
||||
<textarea required autocomplete="off" minlength="1" maxlength="{{COMMENT_BODY_LENGTH_MAXIMUM}}" oninput="markdown('reply-form-body-{{c.fullname}}', 'reply-edit-{{c.id}}');charLimit('reply-form-body-{{c.fullname}}','charcount-{{c.id}}')" id="reply-form-body-{{c.fullname}}" data-fullname="{{c.fullname}}" name="body" form="reply-to-{{c.fullname}}" class="comment-box form-control rounded" aria-label="With textarea" placeholder="Add your comment..." rows="3"></textarea>
|
||||
|
||||
<div class="text-small font-weight-bold mt-1" id="charcount-{{c.id}}" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div id="comment-form-space-{{c.id}}" class="comment-write collapsed child">
|
||||
<form id="reply-to-message-{{c.id}}" action="/reply" method="post" class="input-group" enctype="multipart/form-data">
|
||||
<input type="hidden" name="formkey" value="{{v.formkey}}">
|
||||
<textarea required autocomplete="off" minlength="1" maxlength="{{MESSAGE_BODY_LENGTH_MAXIMUM}}" name="body" form="reply-to-t3_{{c.id}}" data-id="{{c.id}}" class="comment-box form-control rounded" id="reply-form-body-{{c.id}}" aria-label="With textarea" rows="3" oninput="markdown('reply-form-body-{{c.id}}', 'message-reply-{{c.id}}')"></textarea>
|
||||
<textarea required autocomplete="off" minlength="1" maxlength="{{MESSAGE_BODY_LENGTH_MAXIMUM}}" name="body" form="reply-to-message-{{c.id}}" data-id="{{c.id}}" class="comment-box form-control rounded" id="reply-form-body-{{c.id}}" aria-label="With textarea" rows="3" oninput="markdown('reply-form-body-{{c.id}}', 'message-reply-{{c.id}}')"></textarea>
|
||||
<div class="comment-format" id="comment-format-bar-{{c.id}}">
|
||||
{% if c.sentto == MODMAIL_ID %}
|
||||
<label class="btn btn-secondary m-0 mt-3" for="file-upload">
|
||||
|
|
|
@ -1,32 +1,15 @@
|
|||
{% extends "email/default.html" %}
|
||||
|
||||
{% block title %}Remove Two-Factor Authentication{% endblock %}</h1>
|
||||
|
||||
{% block preheader %}Remove Two-Factor Authentication.{% endblock %}
|
||||
{% block title %}Remove Two-Factor Authentication{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>We received a request to remove two-factor authentication from your account. In 72 hours, click the link below.</p>
|
||||
<p>If you didn't make this request, change your password and use the Log Out Everywhere feature in your <a href="/settings/security">Security Settings</a> to permanently invalidate the link.</p>
|
||||
<div class="overflow-x-auto"><table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<div class="overflow-x-auto><table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="{{action_url}}" class="f-fallback button" target="_blank">Remove 2FA</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>Please note that {{SITE_TITLE}} will never ask you for your email, password, or two-factor token via email, text, or phone.</p>
|
||||
<div class="overflow-x-auto"><table class="body-sub" role="presentation">
|
||||
<tr>
|
||||
<td>
|
||||
<p class="f-fallback sub">If you’re having trouble with the button above, copy and paste the URL below into your web browser.</p>
|
||||
<p class="f-fallback sub">{{action_url}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="button-container">
|
||||
<a href="{{action_url}}" class="button" target="_blank" rel="noopener">Remove 2FA</a>
|
||||
</div>
|
||||
|
||||
<p class="user-info">Please note that {{SITE_TITLE}} will never ask you for your email, password, or two-factor token via email, text, or phone.</p>
|
||||
<hr />
|
||||
<p class="raw-link">If the button doesn't work, you can copy and paste this link into your browser: <br>{{action_url}}</p>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,410 +1,112 @@
|
|||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta name="description" content="{{config('DESCRIPTION')}}">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; connect-src 'self'; object-src 'none';">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="x-apple-disable-message-reformatting" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title></title>
|
||||
<style type="text/css" rel="stylesheet" media="all">
|
||||
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
a {
|
||||
color: #FF66AC!important;
|
||||
}
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
td {
|
||||
word-break: break-word;
|
||||
}
|
||||
.preheader {
|
||||
display: none !important;
|
||||
visibility: hidden;
|
||||
mso-hide: all;
|
||||
font-size: 1px;
|
||||
line-height: 1px;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body,
|
||||
td,
|
||||
th {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
color: #121213;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
color: #121213;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
color: #121213;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
td,
|
||||
th {
|
||||
font-size: 1rem;
|
||||
}
|
||||
p,
|
||||
ul,
|
||||
ol,
|
||||
blockquote {
|
||||
margin: .4em 0 1.1875em;
|
||||
font-size: 1rem;
|
||||
line-height: 1.625;
|
||||
}
|
||||
p.sub {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: #FF66AC;
|
||||
border-top: 10px solid #FF66AC;
|
||||
border-right: 18px solid #FF66AC;
|
||||
border-bottom: 10px solid #FF66AC;
|
||||
border-left: 18px solid #FF66AC;
|
||||
display: inline-block;
|
||||
color: #FFF!important;
|
||||
text-decoration: none;
|
||||
border-radius: .25rem;
|
||||
-webkit-text-size-adjust: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.button--green {
|
||||
background-color: #23CE6B;
|
||||
border-top: 10px solid #23CE6B;
|
||||
border-right: 18px solid #23CE6B;
|
||||
border-bottom: 10px solid #23CE6B;
|
||||
border-left: 18px solid #23CE6B;
|
||||
}
|
||||
.button--red {
|
||||
background-color: #F05D5E;
|
||||
border-top: 10px solid #F05D5E;
|
||||
border-right: 18px solid #F05D5E;
|
||||
border-bottom: 10px solid #F05D5E;
|
||||
border-left: 18px solid #F05D5E;
|
||||
}
|
||||
@media only screen and (max-width: 500px) {
|
||||
.button {
|
||||
width: 100% !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
}
|
||||
|
||||
.attributes {
|
||||
margin: 0 0 21px;
|
||||
}
|
||||
.attributes_content {
|
||||
background-color: #EDF2F7;
|
||||
padding: 1rem;
|
||||
border-radius: 0.35rem;
|
||||
}
|
||||
.attributes_item {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.related {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 25px 0 0 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
.related_item {
|
||||
padding: 10px 0;
|
||||
color: #CBCCCF;
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.related_item-title {
|
||||
display: block;
|
||||
margin: .5em 0 0;
|
||||
}
|
||||
.related_item-thumb {
|
||||
display: block;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.related_heading {
|
||||
border-top: 1px solid #CBCCCF;
|
||||
text-align: center;
|
||||
padding: 25px 0 10px;
|
||||
}
|
||||
|
||||
.discount {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 24px;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
background-color: #EDF2F7;
|
||||
border: 2px dashed #CBCCCF;
|
||||
}
|
||||
.discount_heading {
|
||||
text-align: center;
|
||||
}
|
||||
.discount_body {
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.social {
|
||||
width: auto;
|
||||
}
|
||||
.social td {
|
||||
padding: 0;
|
||||
width: auto;
|
||||
}
|
||||
.social_icon {
|
||||
height: 20px;
|
||||
margin: 0 8px 10px 8px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.purchase {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 35px 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
.purchase_content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 25px 0 0 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
.purchase_item {
|
||||
padding: 10px 0;
|
||||
color: #121213;
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.purchase_heading {
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #E6E6E6;
|
||||
}
|
||||
.purchase_heading p {
|
||||
margin: 0;
|
||||
color: #85878E;
|
||||
font-size: 12px;
|
||||
}
|
||||
.purchase_footer {
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #E6E6E6;
|
||||
}
|
||||
.purchase_total {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
color: #121213;
|
||||
}
|
||||
.purchase_total--label {
|
||||
padding: 0 15px 0 0;
|
||||
}
|
||||
body {
|
||||
background-color: #EDF2F7;
|
||||
color: #121213;
|
||||
}
|
||||
p {
|
||||
color: #121213;
|
||||
}
|
||||
p.sub {
|
||||
color: #6B6E76;
|
||||
}
|
||||
.email-wrapper {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
background-color: #EDF2F7;
|
||||
}
|
||||
.email-content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
|
||||
.email-masthead {
|
||||
display: none;
|
||||
}
|
||||
.email-masthead_logo {
|
||||
width: 94px;
|
||||
}
|
||||
.email-masthead_name {
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
color: #121213;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.email-body {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
background-color: #cfcfcf;
|
||||
}
|
||||
.email-body_inner {
|
||||
width: 570px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
-premailer-width: 570px;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
background-color: #cfcfcf;
|
||||
}
|
||||
.email-footer {
|
||||
width: 570px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
-premailer-width: 570px;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.email-footer p {
|
||||
color: #6B6E76;
|
||||
}
|
||||
.body-action {
|
||||
width: 100%;
|
||||
margin: 30px auto;
|
||||
padding: 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.body-sub {
|
||||
margin-top: 25px;
|
||||
padding-top: 25px;
|
||||
border-top: 1px solid #E6E6E6;
|
||||
}
|
||||
.content-cell {
|
||||
padding: 35px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.email-body_inner,
|
||||
.email-footer {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body,
|
||||
.email-body,
|
||||
.email-body_inner,
|
||||
.email-content,
|
||||
.email-wrapper,
|
||||
.email-masthead,
|
||||
.email-footer {
|
||||
background-color: #121213 !important;
|
||||
color: #FFF !important;
|
||||
}
|
||||
p,
|
||||
ul,
|
||||
ol,
|
||||
blockquote,
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
color: #FFF !important;
|
||||
}
|
||||
.attributes_content,
|
||||
.discount {
|
||||
background-color: #222 !important;
|
||||
}
|
||||
.email-masthead_name {
|
||||
text-shadow: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<span class="preheader">{% block preheader %}Thanks for joining {{SITE_TITLE}}! Please take a sec to verify the email you used to sign up.{% endblock %}</span>
|
||||
<div class="overflow-x-auto"><table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<div class="overflow-x-auto"><table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="email-masthead">
|
||||
<a href="/" class="f-fallback email-masthead_name">
|
||||
{{SITE_TITLE}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="email-body" width="100%" cellpadding="0" cellspacing="0">
|
||||
<div class="overflow-x-auto"><table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="content-cell">
|
||||
<div class="f-fallback">
|
||||
<h1>{% block title %}Title Goes Here{% endblock %}</h1>
|
||||
|
||||
{% block content %}
|
||||
{% for entry in data %}
|
||||
<h3>{{entry[0]}}</h3>
|
||||
<p>{{entry[1]}}</p>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
<head>
|
||||
<meta name="description" content="{{config('DESCRIPTION')}}">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; connect-src 'self'; object-src 'none';">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title></title>
|
||||
<style type="text/css" rel="stylesheet" media="all">
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333333;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 10px;
|
||||
color: #333333;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
.button-container {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
background-color: #1a6187;
|
||||
color: #ffffff;
|
||||
padding: 10px 20px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.raw-link {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
color: #777777;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.reference-text {
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 1rem;
|
||||
}
|
||||
.user-info {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
}
|
||||
.user-info p {
|
||||
margin-left: 20px;
|
||||
line-height: 1rem;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #222222;
|
||||
color: #ffffff;
|
||||
}
|
||||
h1 {
|
||||
color: #ffffff;
|
||||
}
|
||||
p {
|
||||
color: #cccccc;
|
||||
}
|
||||
.container {
|
||||
background-color: #1d323d;
|
||||
}
|
||||
.button {
|
||||
background-color: #2280B3;
|
||||
}
|
||||
.raw-link {
|
||||
color: #cccccc;
|
||||
}
|
||||
.reference-text {
|
||||
color: #cccccc;
|
||||
}
|
||||
.user-info {
|
||||
color: #cccccc;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 600px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>{% block title %}{% endblock %}</h1>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,54 +1,18 @@
|
|||
{% extends "email/default.html" %}
|
||||
|
||||
{% block title %}Verify Your Email{% endblock %}</h1>
|
||||
|
||||
{% block preheader %}Verify your new {{SITE_TITLE}} email.{% endblock %}
|
||||
{% block title %}Verify your new {{SITE_TITLE}} email.{% endblock %}</h1>
|
||||
|
||||
{% block content %}
|
||||
<p>You told us you wanted to change your {{SITE_TITLE}} account email. To finish this process, please verify your new email address:</p>
|
||||
<div class="overflow-x-auto"><table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<div class="overflow-x-auto><table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="{{action_url}}" class="f-fallback button" target="_blank">Verify email</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>For reference, here's your current information:</p>
|
||||
<div class="overflow-x-auto"><table class="attributes" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="attributes_content">
|
||||
<div class="overflow-x-auto><table width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="attributes_item">
|
||||
<span class="f-fallback">
|
||||
<strong>Email:</strong> {{v.email}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="attributes_item">
|
||||
<span class="f-fallback">
|
||||
<strong>Username:</strong> {{v.username}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>Please note that {{SITE_TITLE}} will never ask you for your email, password, or two-factor token via email, text, or phone.</p>
|
||||
<div class="overflow-x-auto"><table class="body-sub" role="presentation">
|
||||
<tr>
|
||||
<td>
|
||||
<p class="f-fallback sub">If you’re having trouble with the button above, copy and paste the URL below into your web browser.</p>
|
||||
<p class="f-fallback sub">{{action_url}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>You told us you wanted to change your {{SITE_TITLE}} account email. To finish this process, please verify your new email address:</p>
|
||||
<div class="button-container">
|
||||
<a href="{{action_url}}" class="button" target="_blank" rel="noopener">Verify Email</a>
|
||||
</div>
|
||||
<p class="reference-text">For reference, here's your current information:</p>
|
||||
<div class="user-info">
|
||||
<p><strong>Username:</strong> {{v.username}}</p>
|
||||
<p><strong>Email:</strong> {{v.email}}</p>
|
||||
</div>
|
||||
<p class="user-info">Please note that {{SITE_TITLE}} will never ask you for your email, password, or two-factor token via email, text, or phone.</p>
|
||||
<hr />
|
||||
<p class="raw-link">If the button doesn't work, you can copy and paste this link into your browser: <br>{{action_url}}</p>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,52 +1,18 @@
|
|||
{% extends "email/default.html" %}
|
||||
|
||||
{% block title %}Welcome to {{SITE_TITLE}}!{% endblock %}</h1>
|
||||
{% block title %}Welcome to {{SITE_TITLE}}!{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Thanks for joining {{SITE_TITLE}}. We’re happy to have you on board. To get the most out of {{SITE_TITLE}}, please verify your account email:</p>
|
||||
<div class="overflow-x-auto"><table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<div class="overflow-x-auto><table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="{{action_url}}" class="f-fallback button" target="_blank">Verify email</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>For reference, here's your username.</p>
|
||||
<div class="overflow-x-auto"><table class="attributes" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="attributes_content">
|
||||
<div class="overflow-x-auto><table width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="attributes_item">
|
||||
<span class="f-fallback">
|
||||
<strong>Email:</strong> {{v.email}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="attributes_item">
|
||||
<span class="f-fallback">
|
||||
<strong>Username:</strong> {{v.username}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>Please note that {{SITE_TITLE}} will never ask you for your email, password, or two-factor token via email, text, or phone.</p>
|
||||
<div class="overflow-x-auto"><table class="body-sub" role="presentation">
|
||||
<tr>
|
||||
<td>
|
||||
<p class="f-fallback sub">If you’re having trouble with the button above, copy and paste the URL below into your web browser.</p>
|
||||
<p class="f-fallback sub">{{action_url}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>Thanks for joining {{SITE_TITLE}}. We’re happy to have you on board. Please click the button below to verify your email address:</p>
|
||||
<div class="button-container">
|
||||
<a href="{{action_url}}" class="button" target="_blank" rel="noopener">Verify Email</a>
|
||||
</div>
|
||||
<p class="reference-text">For reference, here's your username:</p>
|
||||
<div class="user-info">
|
||||
<p><strong>Username:</strong> {{v.username}}</p>
|
||||
<p><strong>Email:</strong> {{v.email}}</p>
|
||||
</div>
|
||||
<p class="user-info">Please note that {{SITE_TITLE}} will never ask you for your email, password, or two-factor token via email, text, or phone.</p>
|
||||
<hr />
|
||||
<p class="raw-link">If the button doesn't work, you can copy and paste this link into your browser: <br>{{action_url}}</p>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,52 +1,18 @@
|
|||
{% extends "email/default.html" %}
|
||||
|
||||
{% block title %}Reset Your Password{% endblock %}
|
||||
{% block preheader %}Reset your {{SITE_TITLE}} password.{% endblock %}
|
||||
{% block title %}Reset your {{SITE_TITLE}} password.{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>To reset your password, click the button below:</p>
|
||||
<div class="overflow-x-auto"><table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<div class="overflow-x-auto><table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="{{action_url}}" class="f-fallback button" target="_blank">Reset password</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>For reference, here's your login information:</p>
|
||||
<div class="overflow-x-auto"><table class="attributes" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="attributes_content">
|
||||
<div class="overflow-x-auto><table width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="attributes_item">
|
||||
<span class="f-fallback">
|
||||
<strong>Email:</strong> {{v.email}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="attributes_item">
|
||||
<span class="f-fallback">
|
||||
<strong>Username:</strong> {{v.username}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="overflow-x-auto"><table class="body-sub" role="presentation">
|
||||
<tr>
|
||||
<td>
|
||||
<p class="f-fallback sub">If you’re having trouble with the button above, copy and paste the URL below into your web browser.</p>
|
||||
<p class="f-fallback sub">{{action_url}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>To reset your password, click the button below:</p>
|
||||
<div class="button-container">
|
||||
<a href="{{action_url}}" class="button" target="_blank" rel="noopener">Reset password</a>
|
||||
</div>
|
||||
<p class="reference-text">For reference, here's your login information:</p>
|
||||
<div class="user-info">
|
||||
<p><strong>Username:</strong> {{v.username}}</p>
|
||||
<p><strong>Email:</strong> {{v.email}}</p>
|
||||
</div>
|
||||
<p class="user-info">Please note that {{SITE_TITLE}} will never ask you for your email, password, or two-factor token via email, text, or phone.</p>
|
||||
<hr />
|
||||
<p class="raw-link">If the button doesn't work, you can copy and paste this link into your browser: <br>{{action_url}}</p>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
{% extends "authforms.html" %}
|
||||
|
||||
{% block pagetitle %}{{SITE_TITLE}} Password Reset{% endblock %}
|
||||
|
||||
{% block authtitle %}Reset your password.{% endblock %}
|
||||
|
||||
{% block authtext %}If there's an email address associated with your account, you can use it to recover your {{SITE_TITLE}} account and change your password.{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="login-form" class="">
|
||||
|
||||
<form action="/forgot" method="post" class="mt-3">
|
||||
|
||||
<label for="username" class="mt-3">Username</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="username" aria-describedby="usernameHelp"
|
||||
type="text" name="username" required="">
|
||||
|
||||
<label for="email" class="mt-3">Email</label>
|
||||
|
||||
<input type="email" pattern='[^@]+@[^@]+\.[^@]+' autocomplete="off" class="form-control" id="password" aria-describedby="passwordHelp" name="email" required>
|
||||
|
||||
<input autocomplete="off" class="btn btn-primary login w-100 mt-3" type="submit" value="Send recovery link">
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -6,8 +6,8 @@
|
|||
<img alt="header icon" height=33 width=80 src="{{ ('images/'~SITE_ID~'/headericon.webp') | asset }}">
|
||||
</a>
|
||||
|
||||
<a href="/" class="flex-grow-1">
|
||||
<img id="logo" alt="logo" src="{{ ('images/'~SITE_ID~'/logo.webp') | asset }}" width="100" height="20">
|
||||
<a href="/" id="logo" class="flex-grow-1 logo-text">
|
||||
{{SITE_TITLE}}
|
||||
</a>
|
||||
|
||||
<div class="flex-grow-1 d-fl d-none d-md-block {% if not v %}pad{% endif %}">
|
||||
|
@ -27,7 +27,7 @@
|
|||
|
||||
{% if v %}
|
||||
{% if v.notifications_count %}
|
||||
<a class="mobile-nav-icon d-md-none pl-0" href="/notifications{% if v.do_posts %}?posts=true{% elif v.do_reddit %}?reddit=true{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications"><i class="fas fa-bell align-middle text-danger" {% if v.do_posts %}style="color:blue!important"{% elif v.do_reddit %}style="color:#805ad5!important"{% endif %}></i><span class="notif-count ml-1" style="padding-left: 4.5px;{% if v.do_posts %}background:blue{% elif v.do_reddit %}background:#805ad5{% endif %}">{{v.notifications_count}}</span></a>
|
||||
<a class="mobile-nav-icon d-md-none pl-0" href="/notifications{% if v.do_posts %}/posts{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications"><i class="fas fa-bell align-middle text-danger" {% if v.do_posts %}style="color:blue!important"{% endif %}></i><span class="notif-count ml-1" style="padding-left: 4.5px;{% if v.do_posts %}background:blue{% endif %}">{{v.notifications_count}}</span></a>
|
||||
{% else %}
|
||||
<a class="mobile-nav-icon d-md-none" href="/notifications" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications"><i class="fas fa-bell align-middle text-gray-500 black"></i></a>
|
||||
{% endif %}
|
||||
|
@ -57,7 +57,7 @@
|
|||
{% if v.notifications_count %}
|
||||
|
||||
<li class="nav-item d-flex align-items-center text-center justify-content-center mx-1">
|
||||
<a class="nav-link position-relative" href="/notifications{% if v.do_posts %}?posts=true{% elif v.do_reddit %}?reddit=true{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications"><i class="fas fa-bell text-danger" {% if v.do_posts %}style="color:blue!important"{% elif v.do_reddit %}style="color:#805ad5!important"{% endif %}></i><span class="notif-count ml-1" style="padding-left: 4.5px;{% if v.do_posts %}background:blue{% elif v.do_reddit %}background:#805ad5{% endif %}">{{v.notifications_count}}</span></a>
|
||||
<a class="nav-link position-relative" href="/notifications{% if v.do_posts %}/posts{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications"><i class="fas fa-bell text-danger" {% if v.do_posts %}style="color:blue!important"{% endif %}></i><span class="notif-count ml-1" style="padding-left: 4.5px;{% if v.do_posts %}background:blue{% endif %}">{{v.notifications_count}}</span></a>
|
||||
</li>
|
||||
|
||||
{% else %}
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
{%- import "util/forms.html" as forms -%}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
{% include "analytics.html" %}
|
||||
|
||||
<meta name="description" content="{{config('DESCRIPTION')}}">
|
||||
{% include "csp.html" %}
|
||||
|
||||
<script src="{{ 'js/bootstrap.js' | asset }}"></script>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<meta name="author" content="">
|
||||
|
||||
{% block title %}
|
||||
<title>Login - {{SITE_TITLE}}</title>
|
||||
{% endblock %}
|
||||
|
||||
<style>:root{--primary:#{{config('DEFAULT_COLOR')}}</style>
|
||||
<link rel="stylesheet" href="{{ 'css/main.css' | asset }}">
|
||||
<link rel="stylesheet" href="{{ ('css/'~config('DEFAULT_THEME')~'.css') | asset }}">
|
||||
|
||||
</head>
|
||||
|
||||
<body id="login">
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-transparent fixed-top border-0">
|
||||
<div class="container-fluid d-none">
|
||||
<button class="navbar-toggler d-none" role="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive"
|
||||
aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid position-absolute h-100 p-0 overflow-auto">
|
||||
<div class="row no-gutters h-100">
|
||||
|
||||
<div class="col-12 col-md-6 my-auto p-3">
|
||||
|
||||
<div class="row justify-content-center">
|
||||
|
||||
<div class="col-10 col-md-7">
|
||||
|
||||
<div class="mb-5">
|
||||
<a href="/" class="text-decoration-none"><span class="h3 text-primary"></span></a>
|
||||
</div>
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="login-form" class="">
|
||||
|
||||
<h1 class="h2">Welcome back.</h1>
|
||||
|
||||
<p class="text-muted mb-md-5">Glad to have you back!</p>
|
||||
|
||||
{% if failed %}
|
||||
<div class="alert alert-danger alert-dismissible fade show d-flex my-3" role="alert">
|
||||
<i class="fas fa-exclamation-circle my-auto"></i>
|
||||
<div>
|
||||
Incorrect username, email address, or password.
|
||||
<br>
|
||||
<a href="/forgot" class="alert-link">Forgot password?</a>
|
||||
</div>
|
||||
<button class="close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form action="/login" method="post" class="mt-md-3" id="login">
|
||||
|
||||
<label for="username" class="mt-3">Username or Email Address</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="username" aria-describedby="usernameHelp"
|
||||
type="text" name="username" required="">
|
||||
<input type="hidden" name="redirect" value="{{redirect}}">
|
||||
|
||||
<label for="password" class="mt-3">Password</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="password" aria-describedby="passwordHelp"
|
||||
type="password" name="password" required="">
|
||||
|
||||
|
||||
<small><a href="/forgot">Forgot password?</a></small>
|
||||
|
||||
<button class="btn btn-primary login w-100 mt-3" id="login_button">Sign in</button>
|
||||
|
||||
<div class="text-center text-muted text-small mt-5 mb-3">
|
||||
Don't have an account? <a href="/signup{{'?redirect='+redirect if redirect else ''}}" class="font-weight-bold toggle-login">Sign up</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 d-none d-md-block">
|
||||
|
||||
<div class="splash-wrapper">
|
||||
|
||||
<div class="splash-overlay"></div>
|
||||
|
||||
<img alt="cover" loading="lazy" class="splash-img" src="{{ ('images/'~SITE_ID~'/cover.webp') | asset }}"></img>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
63
files/templates/login/authforms.html
Normal file
63
files/templates/login/authforms.html
Normal file
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
{% include "analytics.html" %}
|
||||
{% include "csp.html" %}
|
||||
<meta name="description" content="{{config('DESCRIPTION')}}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>{% block pagetitle %}{{SITE_TITLE}}{% endblock %}</title>
|
||||
{% if v %}
|
||||
<style>:root{--primary:#{{v.themecolor}}}</style>
|
||||
<link rel="stylesheet" href="{{ 'css/main.css' | asset }}">
|
||||
<link rel="stylesheet" href="{{ ('css/'~v.theme~'.css') | asset }}">
|
||||
{% if v.css %}
|
||||
<style>{{v.css | safe}}</style>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<style>:root{--primary:#{{config('DEFAULT_COLOR')}}</style>
|
||||
<link rel="stylesheet" href="{{ 'css/main.css' | asset }}">
|
||||
<link rel="stylesheet" href="{{ ('css/'~config('DEFAULT_THEME')~'.css') | asset }}">
|
||||
{% endif %}
|
||||
</head>
|
||||
|
||||
<body id="login">
|
||||
<div class="container-fluid position-absolute h-100 p-0">
|
||||
<div class="row no-gutters h-100">
|
||||
<div class="col-12 col-md-6 my-auto p-3">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-10 col-md-7">
|
||||
<h2>{% block authtitle %}{% endblock %}</h2>
|
||||
<p class="text-muted mb-md-5">{% block authtext %}{% endblock %}</p>
|
||||
{% if error %}
|
||||
<div class="alert alert-danger alert-dismissible fade show d-flex my-3" role="alert">
|
||||
<i class="fas fa-exclamation-circle my-auto"></i>
|
||||
<span>{{error}}</span>
|
||||
<button class="close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true"><i class="far fa-times"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if msg %}
|
||||
<div class="alert alert-success alert-dismissible fade show d-flex my-3" role="alert">
|
||||
<i class="fas fa-info-circle my-auto" aria-hidden="true"></i>
|
||||
<span>{{msg}}</span>
|
||||
<button class="close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true"><i class="far fa-times"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 d-mob-none h-100">
|
||||
<div class="splash-wrapper">
|
||||
<div class="splash-overlay"></div>
|
||||
<img alt="cover" loading="lazy" class="splash-img" src="{{ ('images/'~SITE_ID~'/cover.webp') | asset }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
15
files/templates/login/forgot_password.html
Normal file
15
files/templates/login/forgot_password.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
{% extends "login/authforms.html" %}
|
||||
{% block pagetitle %}{{SITE_TITLE}} Password Reset{% endblock %}
|
||||
{% block authtitle %}Reset your password.{% endblock %}
|
||||
{% block authtext %}If there's an email address associated with your account, you can use it to recover your {{SITE_TITLE}} account and change your password.{% endblock %}
|
||||
{% block content %}
|
||||
<div id="login-form" class="">
|
||||
<form action="/forgot" method="post" class="mt-3">
|
||||
<label for="username" class="mt-3">Username</label>
|
||||
<input autocomplete="off" class="form-control" id="username" aria-describedby="usernameHelp" type="text" name="username" required="">
|
||||
<label for="email" class="mt-3">Email</label>
|
||||
<input type="email" pattern='[^@]+@[^@]+\.[^@]+' autocomplete="off" class="form-control" id="password" aria-describedby="passwordHelp" name="email" required>
|
||||
<input autocomplete="off" class="btn btn-primary login w-100 mt-3" type="submit" value="Send recovery link">
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
33
files/templates/login/login.html
Normal file
33
files/templates/login/login.html
Normal file
|
@ -0,0 +1,33 @@
|
|||
{%- extends 'login/authforms.html' -%}
|
||||
{% block authtitle %}Welcome back.{% endblock %}
|
||||
{% block authtext %}Glad to have you back!{% endblock %}
|
||||
{% block content %}
|
||||
<div id="login-form" class="">
|
||||
{% if failed %}
|
||||
<div class="alert alert-danger alert-dismissible fade show d-flex my-3" role="alert">
|
||||
<i class="fas fa-exclamation-circle my-auto"></i>
|
||||
<div>
|
||||
Incorrect username, email address, or password.
|
||||
<br>
|
||||
<a href="/forgot" class="alert-link">Forgot password?</a>
|
||||
</div>
|
||||
<button class="close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form action="/login" method="post" class="mt-md-3" id="login">
|
||||
<label for="username" class="mt-3">Username or Email Address</label>
|
||||
<input autocomplete="off" class="form-control" id="username" aria-describedby="usernameHelp" type="text" name="username" required="">
|
||||
<input type="hidden" name="redirect" value="{{redirect}}">
|
||||
<label for="password" class="mt-3">Password</label>
|
||||
<input autocomplete="off" class="form-control" id="password" aria-describedby="passwordHelp" type="password" name="password" required="">
|
||||
<small><a href="/forgot">Forgot password?</a></small>
|
||||
<button class="btn btn-primary login w-100 mt-3" id="login_button">Sign in</button>
|
||||
<div class="text-center text-muted text-small mt-5 mb-3">
|
||||
Don't have an account? <a href="/signup{{'?redirect='+redirect if redirect else ''}}" class="font-weight-bold toggle-login">Sign up</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
29
files/templates/login/login_2fa.html
Normal file
29
files/templates/login/login_2fa.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
{%- extends "login/authforms.html" -%}
|
||||
{% block authtitle %}Two-step login{% endblock %}
|
||||
{% block authtext %}To login, please enter the 6-digit verification code generated in your authenticator app.{% endblock %}
|
||||
{%- block content -%}
|
||||
<div id="login-form" class="">
|
||||
{% if failed %}
|
||||
<div class="alert alert-danger alert-dismissible fade show d-flex my-3" role="alert">
|
||||
<i class="fas fa-exclamation-circle my-auto"></i>
|
||||
<div>
|
||||
Invalid verification code. Please try again.
|
||||
</div>
|
||||
<button class="close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form action="/login" method="post" class="mt-md-3" id="login">
|
||||
<input type="hidden" name="username" value="{{v.username}}">
|
||||
<input type="hidden" name="redirect" value="{{redirect}}">
|
||||
<input type="hidden" name="time" value="{{time}}">
|
||||
<input type="hidden" name="hash" value="{{hash}}">
|
||||
<label for="2fa_token" class="mt-3">Your verification code</label>
|
||||
<input autocomplete="off" class="form-control" id="2fa_token" name="2fa_token" type="text" placeholder="6-digit code">
|
||||
<small><a href="/lost_2fa">Lost your 2FA device?</a></small>
|
||||
<button class="btn btn-primary login w-100 mt-3" id="login_button">Sign in</button>
|
||||
</form>
|
||||
</div>
|
||||
{%- endblock -%}
|
19
files/templates/login/lost_2fa.html
Normal file
19
files/templates/login/lost_2fa.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends "login/authforms.html" %}
|
||||
{% block pagetitle %}{{SITE_TITLE}} Two-Factor Removal{% endblock %}
|
||||
{% block authtitle %}Remove the two-factor authentication from your account.{% endblock %}
|
||||
{% block authtext %}If all information is correct, you will be able to remove 2-factor authentication from your account in 24 hours.{% endblock %}
|
||||
{% block content %}
|
||||
<div id="login-form" class="">
|
||||
<form action="/request_2fa_disable" method="post" class="mt-3">
|
||||
<label for="username" class="mt-3">Username</label>
|
||||
<input autocomplete="off" class="form-control" id="username" aria-describedby="usernameHelp"
|
||||
type="text" name="username" required=""{% if v %} value="{{v.username}}" disabled{% endif %}>
|
||||
<label for="email" class="mt-3">Password</label>
|
||||
<input autocomplete="off" class="form-control" id="password" aria-describedby="passwordHelp"
|
||||
type="password" name="password" required="">
|
||||
<label for="email" class="mt-3">Email</label>
|
||||
<input autocomplete="off" class="form-control" id="password" type="email" pattern='[^@]+@[^@]+\.[^@]+' name="email" required=""{% if v %} value="{{v.email}}" disabled{% endif %}>
|
||||
<input autocomplete="off" class="btn btn-primary login w-100 mt-3" type="submit" value="Send recovery link">
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
20
files/templates/login/reset_password.html
Normal file
20
files/templates/login/reset_password.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
{%- extends 'login/authforms.html' -%}
|
||||
{% block pagetitle %}{{SITE_TITLE}} Password Reset{% endblock %}
|
||||
{% block authtitle %}Change your password.{% endblock %}
|
||||
{% block content %}
|
||||
<div id="login-form" class="">
|
||||
<form action="/reset" method="post" class="mt-3">
|
||||
<input type="hidden" name="time" value="{{time}}">
|
||||
<input type="hidden" name="user_id" value="{{v.id}}">
|
||||
<input type="hidden" name="token" value="{{token}}">
|
||||
|
||||
<label for="passentry" class="mt-3">New Password</label>
|
||||
<input autocomplete="off" class="form-control" id="passentry" aria-describedby="usernameHelp" type="password" name="password" required="">
|
||||
|
||||
<label for="confentry" class="mt-3">Confirm New Password</label>
|
||||
<input autocomplete="off" class="form-control" id="confentry" aria-describedby="passwordHelp" type="password" name="confirm_password" required="">
|
||||
|
||||
<input autocomplete="off" class="btn btn-primary login w-100 mt-3" type="submit" value="Change password">
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
61
files/templates/login/sign_up.html
Normal file
61
files/templates/login/sign_up.html
Normal file
|
@ -0,0 +1,61 @@
|
|||
{%- extends 'login/authforms.html' -%}
|
||||
|
||||
{% set login_namespace = namespace() %}
|
||||
{% if ref_user %}
|
||||
{% set login_namespace.authtitle = '@' ~ ref_user.username ~ ' has invited you!' %}
|
||||
{% set login_namespace.authtext = 'Looks like someone wants you to join ' ~ SITE_NAME ~ '.' %}
|
||||
{% else %}
|
||||
{% set login_namespace.authtitle = "Create your account." %}
|
||||
{% set login_namespace.authtext = "No email address required." %}
|
||||
{% endif %}
|
||||
{% block authtitle %}{{login_namespace.authtitle}}{% endblock %}
|
||||
{% block authtext %}{{login_namespace.authtext}}{% endblock %}
|
||||
|
||||
{%- block content -%}
|
||||
<div id="register-form" class="">
|
||||
<form action="/signup" method="post" class="mt-md-3" id="signup">
|
||||
{% if error %}<span class="text-danger">{{error}}</span><br>{% endif %}
|
||||
|
||||
<input type="hidden" name="formkey" value="{{formkey}}">
|
||||
<input type="hidden" name="now" value="{{now}}">
|
||||
|
||||
{% if redirect %}<input type="hidden" name="redirect" value="{{redirect}}">{% endif %}
|
||||
{% if ref_user %}<input type="hidden" name="referred_by" value="{{ref_user.id}}">{% endif %}
|
||||
<label for="username-register" class="mt-3">Username</label>
|
||||
<input autocomplete="off" class="form-control" id="username-register" aria-describedby="usernameHelpRegister" type="text" name="username" pattern="[a-zA-Z0-9_\-]{3,25}" min="3" max="25" required autofocus tabindex="1">
|
||||
<small id="usernameHelpRegister"></small>
|
||||
|
||||
<label for="email-register" class="mt-3">Email Address</label> <small class="d-inline-block text-muted ml-1">(optional)</small>
|
||||
<input style="background-color: var(--gray-800)" autocomplete="off" class="form-control" id="email-register" aria-describedby="emailHelpRegister" type="email" pattern='[^@]+@[^@]+\.[^@]+' name="email" tabindex="2">
|
||||
|
||||
<label for="password-register" class="mt-3">Password</label>
|
||||
<input autocomplete="off" class="form-control" id="password-register" aria-describedby="passwordHelpReigster" type="password" name="password" required tabindex="4">
|
||||
<small id="passwordHelpRegister" class="form-text font-weight-bold text-muted d-none mt-1">Minimum of 8 characters required.</small>
|
||||
<small id="passwordHelpSuccess" class="form-text font-weight-bold text-success d-none mt-1">Your password meets the requirements.</small>
|
||||
|
||||
<label for="password_confirm" class="mt-3">Confirm Password</label>
|
||||
<input autocomplete="off" class="form-control" id="password_confirm" aria-describedby="passwordConfirmHelp" type="password" name="password_confirm" required tabindex="5">
|
||||
|
||||
<div class="custom-control custom-checkbox mt-4">
|
||||
<input autocomplete="off" type="checkbox" class="custom-control-input" id="termsCheck" required tabindex="6">
|
||||
<label class="custom-control-label terms" for="termsCheck">I accept the <a href="/rules" tabindex="8">rules</a></label>
|
||||
</div>
|
||||
|
||||
{% if hcaptcha %}
|
||||
<div class="h-captcha" data-sitekey="{{hcaptcha}}"></div>
|
||||
{% endif %}
|
||||
|
||||
<button class="btn btn-primary login w-100 mt-3" id="register_button" tabindex="7">Register</button>
|
||||
|
||||
<div class="text-center text-muted text-small mt-2 mb-0">
|
||||
Already have an account? <a href="/login{{'?redirect='+redirect if redirect else ''}}" class="font-weight-bold toggle-login" tabindex="9">Log in</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{%- endblock -%}
|
||||
|
||||
<script src="{{ 'js/signup.js' | asset }}"></script>
|
||||
|
||||
{% if hcaptcha %}
|
||||
<script src="{{ 'js/hcaptcha.js' | asset }}"></script>
|
||||
{% endif %}
|
17
files/templates/login/sign_up_failed_ref.html
Normal file
17
files/templates/login/sign_up_failed_ref.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{%- extends "login/authforms.html" -%}
|
||||
{%- block authtitle -%}Whoops! You can't refer yourself!{%- endblock -%}
|
||||
{%- block authtext -%}Send this link to a friend instead :){%- endblock -%}
|
||||
{%- block content -%}
|
||||
<div id="register-form" class="">
|
||||
<label>Referral code</label>
|
||||
<input autocomplete="off" type="text" class="form-control copy-link" readonly value="{{SITE_FULL}}/signup?ref={{request.values.get('ref')}}" data-clipboard-text="{{SITE_FULL}}/signup?ref={{request.values.get('ref')}}">
|
||||
|
||||
<div class="text-center mt-5 mb-3">
|
||||
Already have an account? <a href="/login" class="font-weight-bold text-small toggle-login">Log in.</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toast clipboard" id="toast-success" role="alert" aria-live="assertive" aria-atomic="true" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
|
||||
<div class="toast-body text-center">
|
||||
<i class="fas fa-check-circle text-success mr-2"></i>Link copied to clipboard
|
||||
</div>
|
||||
{%- endblock -%}
|
|
@ -1,108 +0,0 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
{% include "analytics.html" %}
|
||||
|
||||
<meta name="description" content="{{config('DESCRIPTION')}}">
|
||||
{% include "csp.html" %}
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>2-Step Login - {{SITE_TITLE}}</title>
|
||||
|
||||
<style>:root{--primary:#{{config('DEFAULT_COLOR')}}</style>
|
||||
<link rel="stylesheet" href="{{ 'css/main.css' | asset }}">
|
||||
<link rel="stylesheet" href="{{ ('css/'~config('DEFAULT_THEME')~'.css') | asset }}">
|
||||
|
||||
</head>
|
||||
|
||||
<body id="login">
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-transparent fixed-top border-0">
|
||||
<div class="container-fluid d-none">
|
||||
<button class="navbar-toggler d-none" role="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive"
|
||||
aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid position-absolute h-100 p-0 overflow-auto">
|
||||
<div class="row no-gutters h-100">
|
||||
|
||||
<div class="col-12 col-md-6 my-auto p-3">
|
||||
|
||||
<div class="row justify-content-center">
|
||||
|
||||
<div class="col-10 col-md-7">
|
||||
|
||||
<div class="mb-5">
|
||||
<a href="/" class="text-decoration-none"><span class="h3 text-primary"></span></a>
|
||||
</div>
|
||||
|
||||
<div id="login-form" class="">
|
||||
|
||||
<h1 class="h2">Two-step login</h1>
|
||||
|
||||
<p class="text-muted mb-md-5">To login, please enter the 6-digit verification code generated in your authenticator app.</p>
|
||||
|
||||
{% if failed %}
|
||||
<div class="alert alert-danger alert-dismissible fade show d-flex my-3" role="alert">
|
||||
<i class="fas fa-exclamation-circle my-auto"></i>
|
||||
<div>
|
||||
Invalid verification code. Please try again.
|
||||
</div>
|
||||
<button class="close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form action="/login" method="post" class="mt-md-3" id="login">
|
||||
|
||||
<input type="hidden" name="username" value="{{v.username}}">
|
||||
<input type="hidden" name="redirect" value="{{redirect}}">
|
||||
|
||||
<input type="hidden" name="time" value="{{time}}">
|
||||
<input type="hidden" name="hash" value="{{hash}}">
|
||||
|
||||
<label for="2fa_token" class="mt-3">Your verification code</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="2fa_token" name="2fa_token" type="text" placeholder="6-digit code">
|
||||
<small><a href="/lost_2fa">Lost your 2FA device?</a></small>
|
||||
|
||||
<button class="btn btn-primary login w-100 mt-3" id="login_button">Sign in</button>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 d-none d-md-block">
|
||||
|
||||
<div class="splash-wrapper">
|
||||
|
||||
<div class="splash-overlay"></div>
|
||||
|
||||
<img alt="cover" loading="lazy" class="splash-img" src="{{ ('images/'~SITE_ID~'/cover.webp') | asset }}"></img>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,35 +0,0 @@
|
|||
{% extends "authforms.html" %}
|
||||
|
||||
{% block pagetitle %}{{SITE_TITLE}} Two-Factor Removal{% endblock %}
|
||||
|
||||
{% block authtitle %}Remove the two-factor authentication from your account.{% endblock %}
|
||||
|
||||
{% block authtext %}If all information is correct, you will be able to remove 2-factor authentication from your account in 24 hours.{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="login-form" class="">
|
||||
|
||||
<form action="/request_2fa_disable" method="post" class="mt-3">
|
||||
|
||||
<label for="username" class="mt-3">Username</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="username" aria-describedby="usernameHelp"
|
||||
type="text" name="username" required=""{% if v %} value="{{v.username}}" disabled{% endif %}>
|
||||
|
||||
<label for="email" class="mt-3">Password</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="password" aria-describedby="passwordHelp"
|
||||
type="password" name="password" required="">
|
||||
|
||||
<label for="email" class="mt-3">Email</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="password" type="email" pattern='[^@]+@[^@]+\.[^@]+' name="email" required=""{% if v %} value="{{v.email}}" disabled{% endif %}>
|
||||
|
||||
<input autocomplete="off" class="btn btn-primary login w-100 mt-3" type="submit" value="Send recovery link">
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -19,35 +19,28 @@
|
|||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-3{% if '/notifications?posts=true' in request.full_path %} active{% endif %}" href="/notifications?posts=true">
|
||||
<a class="nav-link py-3{% if '/notifications/posts' in request.full_path %} active{% endif %}" href="/notifications/posts">
|
||||
Posts {% if v.post_notifications_count %}<span class="font-weight-bold" style="color:blue">({{v.post_notifications_count}})</span>{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-3{% if '/notifications?messages=true' in request.full_path %} active{% endif %}" href="/notifications?messages=true">
|
||||
<a class="nav-link py-3{% if '/notifications/messages' in request.full_path %} active{% endif %}" href="/notifications/messages">
|
||||
Messages
|
||||
</a>
|
||||
</li>
|
||||
{% if v.admin_level >= 2 %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-3{% if '/notifications?modmail=true' in request.full_path %} active{% endif %}" href="/notifications?modmail=true">
|
||||
<a class="nav-link py-3{% if '/notifications/modmail' in request.full_path %} active{% endif %}" href="/notifications/modmail">
|
||||
Modmail
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if v.admin_level %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-3{% if '/notifications?reddit=true' in request.full_path %} active{% endif %}" href="/notifications?reddit=true">
|
||||
Reddit {% if v.reddit_notifications_count %}<span class="font-weight-bold" style="color:#805ad5">({{v.reddit_notifications_count}})</span>{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<a class="btn btn-primary mt-3 ml-3" role="button" onclick="post_toast(this,'/clear', '1')">Clear all notifications</a>
|
||||
<a class="btn btn-primary mt-3 ml-3" role="button" onclick="post_toast(this, '/clear', '1')">Mark All Read</a>
|
||||
|
||||
<div class="notifs px-3 p-md-0">
|
||||
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
{% extends "authforms.html" %}
|
||||
|
||||
{% block pagetitle %}{{SITE_TITLE}} Password Reset{% endblock %}
|
||||
|
||||
{% block authtitle %}Change your password.{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="login-form" class="">
|
||||
|
||||
<form action="/reset" method="post" class="mt-3">
|
||||
|
||||
<input type="hidden" name="time" value="{{time}}">
|
||||
<input type="hidden" name="user_id" value="{{v.id}}">
|
||||
<input type="hidden" name="token" value="{{token}}">
|
||||
|
||||
<label for="passentry" class="mt-3">New Password</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="passentry" aria-describedby="usernameHelp"
|
||||
type="password" name="password" required="">
|
||||
|
||||
<label for="confentry" class="mt-3">Confirm New Password</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="confentry" aria-describedby="passwordHelp"
|
||||
type="password" name="confirm_password" required="">
|
||||
|
||||
<input autocomplete="off" class="btn btn-primary login w-100 mt-3" type="submit" value="Change password">
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -51,8 +51,10 @@
|
|||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<span class="text-small-extra text-muted">Themes are not officially supported. If you run into an issue, please report it or submit a pull request.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="h5">Profile Picture</h2>
|
||||
<div class="settings-section rounded">
|
||||
|
|
|
@ -1,161 +0,0 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{% include "analytics.html" %}
|
||||
|
||||
<meta name="description" content="{{config('DESCRIPTION')}}">
|
||||
{% include "csp.html" %}
|
||||
|
||||
<script src="{{ 'js/bootstrap.js' | asset }}"></script>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<meta name="author" content="">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:title" content="{{SITE_TITLE}}">
|
||||
<meta property="og:site_name" content="{{request.host}}">
|
||||
<meta property="og:image" content="{{ ('images/'~SITE_ID~'/site_preview.webp') | asset }}">
|
||||
<meta property="og:url" content="{{request.host}}">
|
||||
<meta property="og:description" name="description" content="{{config('DESCRIPTION')}}">
|
||||
<meta property="og:author" name="author" content="{{SITE_FULL}}">
|
||||
<meta property="og:site_name" content="{{request.host}}">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:site" content="{{SITE_FULL}}">
|
||||
<meta name="twitter:title" content="{{SITE_TITLE}}">
|
||||
<meta name="twitter:creator" content="{{SITE_FULL}}">
|
||||
<meta name="twitter:description" content="{{config('DESCRIPTION')}}">
|
||||
<meta name="twitter:image" content="{{ ('images/'~SITE_ID~'/site_preview.webp') | asset }}">
|
||||
<meta name="twitter:url" content="{{request.host}}">
|
||||
|
||||
<title>{% if ref_user %}{{ref_user.username}} invites you to {{SITE_TITLE}}{% else %}Sign up - {{SITE_TITLE}}{% endif %}</title>
|
||||
|
||||
<style>:root{--primary:#{{config('DEFAULT_COLOR')}}</style>
|
||||
<link rel="stylesheet" href="{{ 'css/main.css' | asset }}">
|
||||
<link rel="stylesheet" href="{{ ('css/'~config('DEFAULT_THEME')~'.css') | asset }}">
|
||||
|
||||
</head>
|
||||
|
||||
<body id="login">
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-transparent fixed-top border-0">
|
||||
<div class="container-fluid d-none">
|
||||
<button class="navbar-toggler d-none" role="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive"
|
||||
aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid position-absolute h-100 p-0 overflow-auto">
|
||||
<div class="row no-gutters h-100">
|
||||
|
||||
<div class="col-12 col-md-6 my-auto p-3">
|
||||
|
||||
<div class="row justify-content-center">
|
||||
|
||||
<div class="col-10 col-md-7">
|
||||
|
||||
<div class="mb-3">
|
||||
<a href="/" class="text-decoration-none"><span class="h3 text-primary"></span></a>
|
||||
</div>
|
||||
|
||||
<div id="register-form" class="">
|
||||
|
||||
{% if ref_user %}
|
||||
<h1 class="h2">@{{ref_user.username}} has invited you!</h1>
|
||||
<p class="text-muted mb-md-2">Looks like someone wants you to join {{SITE_TITLE}}.</p>
|
||||
{% else %}
|
||||
<h1 class="h2">Create your account.</h1>
|
||||
<p class="text-muted mb-md-2">No email address required.</p>
|
||||
{% endif %}
|
||||
|
||||
<form action="/signup" method="post" class="mt-md-3" id="signup">
|
||||
|
||||
{% if error %}<span class="text-danger">{{error}}</span><br>{% endif %}
|
||||
|
||||
<input type="hidden" name="formkey" value="{{formkey}}">
|
||||
<input type="hidden" name="now" value="{{now}}">
|
||||
|
||||
{% if redirect %}<input type="hidden" name="redirect" value="{{redirect}}">{% endif %}
|
||||
{% if ref_user %}
|
||||
<input type="hidden" name="referred_by" value="{{ref_user.id}}">{% endif %}
|
||||
|
||||
<label for="username-register" class="mt-3">Username</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="username-register"
|
||||
aria-describedby="usernameHelpRegister" type="text" name="username" pattern="[a-zA-Z0-9_\-]{3,25}" min="3" max="25" required autofocus tabindex="1">
|
||||
<small id="usernameHelpRegister"></small>
|
||||
|
||||
<label for="email-register" class="mt-3">Email Address</label>
|
||||
|
||||
<small class="d-inline-block text-muted ml-1">(optional)</small>
|
||||
|
||||
<input style="background-color: var(--gray-800)" autocomplete="off" class="form-control" id="email-register"
|
||||
aria-describedby="emailHelpRegister" type="email" pattern='[^@]+@[^@]+\.[^@]+' name="email" tabindex="2">
|
||||
|
||||
<label for="password-register" class="mt-3">Password</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="password-register"
|
||||
aria-describedby="passwordHelpReigster" type="password" name="password" required tabindex="4">
|
||||
<small id="passwordHelpRegister" class="form-text font-weight-bold text-muted d-none mt-1">Minimum of 8
|
||||
characters
|
||||
required.</small>
|
||||
<small id="passwordHelpSuccess" class="form-text font-weight-bold text-success d-none mt-1">Your password meets the requirements.
|
||||
</small>
|
||||
|
||||
<label for="password_confirm" class="mt-3">Confirm Password</label>
|
||||
|
||||
<input autocomplete="off" class="form-control" id="password_confirm"
|
||||
aria-describedby="passwordConfirmHelp" type="password" name="password_confirm"
|
||||
required tabindex="5">
|
||||
<div class="custom-control custom-checkbox mt-4">
|
||||
<input autocomplete="off" type="checkbox" class="custom-control-input" id="termsCheck" required tabindex="6">
|
||||
<label class="custom-control-label terms" for="termsCheck">I accept the <a href="/rules" tabindex="8">rules</a></label>
|
||||
</div>
|
||||
|
||||
{% if hcaptcha %}
|
||||
<div class="h-captcha" data-sitekey="{{hcaptcha}}"></div>
|
||||
{% endif %}
|
||||
|
||||
<button class="btn btn-primary login w-100 mt-3" id="register_button" tabindex="7">Register</button>
|
||||
|
||||
<div class="text-center text-muted text-small mt-2 mb-0">
|
||||
Already have an account? <a href="/login{{'?redirect='+redirect if redirect else ''}}" class="font-weight-bold toggle-login" tabindex="9">Log in</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 d-none d-md-block">
|
||||
|
||||
<div class="splash-wrapper">
|
||||
|
||||
<div class="splash-overlay"></div>
|
||||
|
||||
<img alt="cover" loading="lazy" class="splash-img" src="{{ ('images/'~SITE_ID~'/cover.webp') | asset }}"></img>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{{ 'js/signup.js' | asset }}"></script>
|
||||
|
||||
{% if hcaptcha %}
|
||||
<script src="{{ 'js/hcaptcha.js' | asset }}"></script>
|
||||
{% endif %}
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,106 +0,0 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
{% include "analytics.html" %}
|
||||
|
||||
<meta name="description" content="{{config('DESCRIPTION')}}">
|
||||
{% include "csp.html" %}
|
||||
|
||||
<script src="{{ 'js/bootstrap.js' | asset }}"></script>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<meta name="author" content="">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:title" content="{{SITE_TITLE}}">
|
||||
<meta property="og:site_name" content="{{request.host}}">
|
||||
<meta property="og:image" content="{{ ('images/'~SITE_ID~'/site_preview.webp') | asset }}">
|
||||
<meta property="og:url" content="{{request.host}}">
|
||||
<meta property="og:description" name="description" content="{{config('DESCRIPTION')}}">
|
||||
<meta property="og:author" name="author" content="{{SITE_FULL}}">
|
||||
<meta property="og:site_name" content="{{request.host}}">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:site" content="{{SITE_FULL}}">
|
||||
<meta name="twitter:title" content="{{SITE_TITLE}}">
|
||||
<meta name="twitter:creator" content="{{SITE_FULL}}">
|
||||
<meta name="twitter:description" content="{{config('DESCRIPTION')}}">
|
||||
<meta name="twitter:image" content="{{ ('images/'~SITE_ID~'/site_preview.webp') | asset }}">
|
||||
<meta name="twitter:url" content="{{request.host}}">
|
||||
|
||||
<title>{% if ref_user %}{{ref_user.username}} invites you to {{SITE_TITLE}}{% else %}{{SITE_TITLE}}{% endif %}</title>
|
||||
|
||||
<style>:root{--primary:#{{config('DEFAULT_COLOR')}}</style>
|
||||
<link rel="stylesheet" href="{{ 'css/main.css' | asset }}">
|
||||
<link rel="stylesheet" href="{{ ('css/'~config('DEFAULT_THEME')~'.css') | asset }}">
|
||||
|
||||
</head>
|
||||
|
||||
<body id="login">
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-transparent fixed-top border-0">
|
||||
<div class="container-fluid">
|
||||
<button class="navbar-toggler d-none" role="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive"
|
||||
aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid position-absolute h-100 p-0">
|
||||
<div class="row no-gutters h-100">
|
||||
|
||||
<div class="col-12 col-md-6 my-auto p-3">
|
||||
|
||||
<div class="row justify-content-center">
|
||||
|
||||
<div class="col-10 col-md-7">
|
||||
|
||||
<div class="text-center mb-5">
|
||||
<a href="/" class="text-decoration-none"><span class="h3 text-primary"></span></a>
|
||||
</div>
|
||||
|
||||
<div id="register-form" class="">
|
||||
<h1 class="h4 font-weight-normal text-center">Whoops! You can't refer yourself!</h1>
|
||||
<p class="text-center text-muted mb-md-5">Send this link to a friend instead :)</p>
|
||||
<label>Referral code</label>
|
||||
<input autocomplete="off" type="text" class="form-control copy-link" readonly value="{{SITE_FULL}}/signup?ref={{request.values.get('ref')}}" data-clipboard-text="{{SITE_FULL}}/signup?ref={{request.values.get('ref')}}">
|
||||
|
||||
<div class="text-center mt-5 mb-3">
|
||||
Already have an account? <a href="/login" class="font-weight-bold text-small toggle-login">Log in.</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 d-none d-md-block">
|
||||
|
||||
<div class="splash-wrapper">
|
||||
|
||||
<div class="splash-overlay"></div>
|
||||
|
||||
<img alt="cover" loading="lazy" class="splash-img" src="{{ ('images/'~SITE_ID~'/cover.webp') | asset }}"></img>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="toast clipboard" id="toast-success" role="alert" aria-live="assertive" aria-atomic="true" data-bs-animation="true" data-bs-autohide="true" data-bs-delay="5000">
|
||||
<div class="toast-body text-center">
|
||||
<i class="fas fa-check-circle text-success mr-2"></i>Link copied to clipboard
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -421,7 +421,7 @@
|
|||
<div id="comment-form-space-{{p.fullname}}" class="comment-write mb-3">
|
||||
<form id="reply-to-{{p.fullname}}" action="/comment" method="post">
|
||||
{{forms.formkey(v)}}
|
||||
<input type="hidden" name="parent_fullname" value="t2_{{p.id}}">
|
||||
<input type="hidden" name="parent_fullname" value="{{p.fullname}}">
|
||||
<input autocomplete="off" id="reply-form-submission-{{p.fullname}}" type="hidden" name="submission" value="{{p.id}}">
|
||||
<textarea required autocomplete="off" minlength="1" maxlength="{{COMMENT_BODY_LENGTH_MAXIMUM}}" oninput="markdown('reply-form-body-{{p.fullname}}', 'form-preview-{{p.id}}');charLimit('reply-form-body-{{p.fullname}}','charcount-reply')" id="reply-form-body-{{p.fullname}}" data-fullname="{{p.fullname}}" class="comment-box form-control rounded" id="comment-form" name="body" form="reply-to-{{p.fullname}}" aria-label="With textarea" placeholder="Add your comment..." rows="3"></textarea>
|
||||
|
||||
|
|
|
@ -14,91 +14,73 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="mb-2 p-3">
|
||||
<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 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.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>
|
||||
<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 id="voting" class="d-md-block my-auto mr-3 text-center">
|
||||
<div class="post-{{p.id}}-up arrow-up mx-auto">
|
||||
</div>
|
||||
<span class="post-{{p.id}}-score-up score-up text-muted{% if voted!=1 %} d-none{% endif %}">✖</span>
|
||||
<span class="post-{{p.id}}-score-none score text-muted{% if voted!=0 and voted!=-2 %} d-none{% endif %}">✖</span>
|
||||
<span class="post-{{p.id}}-score-down score-down text-muted{% if voted!=-1 %} d-none{% endif %}">✖</span>
|
||||
<div class="post-{{p.id}}-down arrow-down mx-auto">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% if v and v.admin_level >= 2 and p.body_html %}
|
||||
<div class="post-body mt-4 mb-2">
|
||||
{{p.body_html | safe}}
|
||||
<div class="mb-2 p-3">
|
||||
<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 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.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>
|
||||
<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 id="voting" class="d-md-block my-auto mr-3 text-center">
|
||||
<div class="post-{{p.id}}-up arrow-up mx-auto">
|
||||
</div>
|
||||
<span class="post-{{p.id}}-score-up score-up text-muted{% if voted!=1 %} d-none{% endif %}">✖</span>
|
||||
<span class="post-{{p.id}}-score-none score text-muted{% if voted!=0 and voted!=-2 %} d-none{% endif %}">✖</span>
|
||||
<span class="post-{{p.id}}-score-down score-down text-muted{% if voted!=-1 %} d-none{% endif %}">✖</span>
|
||||
<div class="post-{{p.id}}-down arrow-down mx-auto">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if v and v.admin_level >= 2 and p.body_html %}
|
||||
<div class="post-body mt-4 mb-2">
|
||||
{{p.realbody(v) | safe}}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-2 p-3">
|
||||
<div class="col-12">
|
||||
|
||||
<div class="post-actions d-none d-md-block">
|
||||
<ul class="list-inline text-left mb-2">
|
||||
<li class="list-inline-item"><a href="{{p.permalink}}"><i
|
||||
class="fas fa-comment-dots"></i>{{p.comment_count}} Comment{{'s' if p.comment_count != 1 else ''}}</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% include "volunteer_teaser.html" %}
|
||||
|
||||
<div class="comment-section">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2 p-3">
|
||||
<div class="col-12">
|
||||
<div class="post-actions d-none d-md-block">
|
||||
<ul class="list-inline text-left mb-2">
|
||||
<li class="list-inline-item"><a href="{{p.permalink}}">
|
||||
<i class="fas fa-comment-dots"></i>{{p.comment_count}} Comment{{'s' if p.comment_count != 1 else ''}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include "volunteer_teaser.html" %}
|
||||
<div class="comment-section">
|
||||
{% with comments=p.replies %}
|
||||
{% include "comments.html" %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block mobileactions %}
|
||||
|
||||
<div class="row fixed-top bg-white shadow d-inline-flex d-md-none p-3" id="footer-actions" style="z-index: 3; top: 48px; transition: top cubic-bezier(0, 0, 0.2, 1) 220ms;">
|
||||
<div class="col text-center">
|
||||
<div class="post-actions mx-auto">
|
||||
<ul class="list-inline">
|
||||
<li id="voting-mobile" class="voting list-inline-item{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}">
|
||||
<span class="arrow-mobile-up mr-2 arrow-mobile-up">
|
||||
<i class="fas fa-arrow-alt-up mx-0"></i>
|
||||
</span>
|
||||
|
||||
|
||||
<span class="post-{{p.id}}-score-mobile-up score-up text-muted{% if voted!=1 %} d-none{% endif %}">✖</span>
|
||||
<span class="post-{{p.id}}-score-mobile-none score text-muted{% if voted!=0 and voted!=-2 %} d-none{% endif %}">✖</span>
|
||||
<span class="post-{{p.id}}-score-mobile-down score-down text-muted{% if voted!=-1 %} d-none{% endif %}">✖</span>
|
||||
|
||||
|
||||
<span class="arrow-mobile-down arrow-mobile-down ml-2 my-0">
|
||||
<i class="fas fa-arrow-alt-down mx-0"></i>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row fixed-top bg-white shadow d-inline-flex d-md-none p-3" id="footer-actions" style="z-index: 3; top: 48px; transition: top cubic-bezier(0, 0, 0.2, 1) 220ms;">
|
||||
<div class="col text-center">
|
||||
<div class="post-actions mx-auto">
|
||||
<ul class="list-inline">
|
||||
<li id="voting-mobile" class="voting list-inline-item{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}">
|
||||
<span class="arrow-mobile-up mr-2 arrow-mobile-up">
|
||||
<i class="fas fa-arrow-alt-up mx-0"></i>
|
||||
</span>
|
||||
<span class="post-{{p.id}}-score-mobile-up score-up text-muted{% if voted!=1 %} d-none{% endif %}">✖</span>
|
||||
<span class="post-{{p.id}}-score-mobile-none score text-muted{% if voted!=0 and voted!=-2 %} d-none{% endif %}">✖</span>
|
||||
<span class="post-{{p.id}}-score-mobile-down score-down text-muted{% if voted!=-1 %} d-none{% endif %}">✖</span>
|
||||
<span class="arrow-mobile-down arrow-mobile-down ml-2 my-0">
|
||||
<i class="fas fa-arrow-alt-down mx-0"></i>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -15,7 +15,7 @@ class CommentsFixture:
|
|||
assert submit_get_response.status_code == 200
|
||||
comment_body = data.get('body', util.generate_text())
|
||||
submit_comment_response = client.post("/comment", data={
|
||||
"parent_fullname": f't2_{post_id}',
|
||||
"parent_fullname": f'post_{post_id}',
|
||||
'parent_level': 1,
|
||||
'submission': post_id,
|
||||
"body": comment_body,
|
||||
|
|
|
@ -24,9 +24,9 @@ class SubmissionsFixture:
|
|||
assert post_body in submit_post_response.text
|
||||
post_info = util.ItemData.from_html(submit_post_response.text)
|
||||
post_id_full = post_info.id_full
|
||||
assert post_id_full.startswith('t2_')
|
||||
assert post_id_full.startswith('post_')
|
||||
|
||||
post_id = int(post_id_full[3:])
|
||||
post_id = int(post_id_full.split('_')[1])
|
||||
|
||||
db = db_session()
|
||||
submission = db.query(Submission).filter_by(id=post_id).first()
|
||||
|
|
|
@ -115,19 +115,19 @@ def test_comment_descendant_count(accounts, submissions, comments):
|
|||
|
||||
reply1 = comments.comment_for_client(alice_client, post.id, {
|
||||
'body': 'You\'re wrong, this isn\'t contentious',
|
||||
'parent_fullname': f't3_{root.id}',
|
||||
'parent_fullname': f'comment_{root.id}',
|
||||
'parent_level': root.level,
|
||||
})
|
||||
|
||||
rereply1 = comments.comment_for_client(alice_client, post.id, {
|
||||
'body': 'no u',
|
||||
'parent_fullname': f't3_{reply1.id}',
|
||||
'parent_fullname': f'comment_{reply1.id}',
|
||||
'parent_level': reply1.level,
|
||||
})
|
||||
|
||||
reply2 = comments.comment_for_client(alice_client, post.id, {
|
||||
'body': 'Good poast',
|
||||
'parent_fullname': f't3_{root.id}',
|
||||
'parent_fullname': f'comment_{root.id}',
|
||||
'parent_level': root.level,
|
||||
})
|
||||
|
||||
|
@ -153,7 +153,7 @@ def test_more_button_label_in_deep_threads(accounts, submissions, comments):
|
|||
for i in range(1, 25 + 1):
|
||||
c = comments.comment_for_client(alice_client, post.id, {
|
||||
'body': str(i),
|
||||
'parent_fullname': f't3_{c.id}',
|
||||
'parent_fullname': f'comment_{c.id}',
|
||||
'parent_level': c.level,
|
||||
})
|
||||
if i % 5 == 0:
|
||||
|
|
|
@ -37,9 +37,9 @@ def no_rate_limit(test_function):
|
|||
# this is meant to be a utility class that stores post and comment references so you can use them in other calls
|
||||
# it's pretty barebones and will probably be fleshed out
|
||||
class ItemData:
|
||||
id = None
|
||||
id_full = None
|
||||
url = None
|
||||
id: str | None = None
|
||||
id_full: str | None = None
|
||||
url: str | None = None
|
||||
|
||||
@staticmethod
|
||||
def from_html(text):
|
||||
|
@ -52,7 +52,7 @@ class ItemData:
|
|||
|
||||
result = ItemData()
|
||||
result.id = match.group(1) # this really should get yanked out of the JS, not the URL
|
||||
result.id_full = f"t2_{result.id}"
|
||||
result.id_full = f"post_{result.id}"
|
||||
result.url = url
|
||||
return result
|
||||
|
||||
|
@ -69,6 +69,6 @@ class ItemData:
|
|||
|
||||
result = ItemData()
|
||||
result.id = match.group(1) # this really should get yanked out of the JS, not the HTML
|
||||
result.id_full = f"t3_{result.id}"
|
||||
result.id_full = f"comment_{result.id}"
|
||||
result.url = f"/comment/{result.id}"
|
||||
return result
|
||||
|
|
|
@ -17,8 +17,8 @@ depends_on = ${repr(depends_on)}
|
|||
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade():
|
||||
${downgrades if downgrades else "pass"}
|
||||
${downgrades if downgrades else "pass"}
|
||||
|
|
|
@ -17,22 +17,22 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('submissions', sa.Column('bump_utc', sa.Integer(), server_default=sa.schema.FetchedValue(), nullable=True))
|
||||
op.create_foreign_key('comments_app_id_fkey', 'comments', 'oauth_apps', ['app_id'], ['id'])
|
||||
op.create_foreign_key('commentvotes_app_id_fkey', 'commentvotes', 'oauth_apps', ['app_id'], ['id'])
|
||||
op.create_foreign_key('modactions_user_id_fkey', 'modactions', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key('submissions_app_id_fkey', 'submissions', 'oauth_apps', ['app_id'], ['id'])
|
||||
op.create_foreign_key('votes_app_id_fkey', 'votes', 'oauth_apps', ['app_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('submissions', sa.Column('bump_utc', sa.Integer(), server_default=sa.schema.FetchedValue(), nullable=True))
|
||||
op.create_foreign_key('comments_app_id_fkey', 'comments', 'oauth_apps', ['app_id'], ['id'])
|
||||
op.create_foreign_key('commentvotes_app_id_fkey', 'commentvotes', 'oauth_apps', ['app_id'], ['id'])
|
||||
op.create_foreign_key('modactions_user_id_fkey', 'modactions', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key('submissions_app_id_fkey', 'submissions', 'oauth_apps', ['app_id'], ['id'])
|
||||
op.create_foreign_key('votes_app_id_fkey', 'votes', 'oauth_apps', ['app_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint('votes_app_id_fkey', 'votes', type_='foreignkey')
|
||||
op.drop_constraint('submissions_app_id_fkey', 'submissions', type_='foreignkey')
|
||||
op.drop_constraint('modactions_user_id_fkey', 'modactions', type_='foreignkey')
|
||||
op.drop_constraint('commentvotes_app_id_fkey', 'commentvotes', type_='foreignkey')
|
||||
op.drop_constraint('comments_app_id_fkey', 'comments', type_='foreignkey')
|
||||
op.drop_column('submissions', 'bump_utc')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint('votes_app_id_fkey', 'votes', type_='foreignkey')
|
||||
op.drop_constraint('submissions_app_id_fkey', 'submissions', type_='foreignkey')
|
||||
op.drop_constraint('modactions_user_id_fkey', 'modactions', type_='foreignkey')
|
||||
op.drop_constraint('commentvotes_app_id_fkey', 'commentvotes', type_='foreignkey')
|
||||
op.drop_constraint('comments_app_id_fkey', 'comments', type_='foreignkey')
|
||||
op.drop_column('submissions', 'bump_utc')
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,30 +17,30 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('usernotes', 'note',
|
||||
existing_type=sa.VARCHAR(length=10000),
|
||||
nullable=False)
|
||||
op.alter_column('usernotes', 'tag',
|
||||
existing_type=sa.VARCHAR(length=10),
|
||||
nullable=False)
|
||||
op.create_foreign_key('usernotes_author_id_fkey', 'usernotes', 'users', ['author_id'], ['id'])
|
||||
op.create_foreign_key('usernotes_reference_comment_fkey', 'usernotes', 'comments', ['reference_comment'], ['id'], ondelete='SET NULL')
|
||||
op.create_foreign_key('usernotes_reference_post_fkey', 'usernotes', 'submissions', ['reference_post'], ['id'], ondelete='SET NULL')
|
||||
op.create_foreign_key('usernotes_reference_user_fkey', 'usernotes', 'users', ['reference_user'], ['id'], ondelete='CASCADE')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('usernotes', 'note',
|
||||
existing_type=sa.VARCHAR(length=10000),
|
||||
nullable=False)
|
||||
op.alter_column('usernotes', 'tag',
|
||||
existing_type=sa.VARCHAR(length=10),
|
||||
nullable=False)
|
||||
op.create_foreign_key('usernotes_author_id_fkey', 'usernotes', 'users', ['author_id'], ['id'])
|
||||
op.create_foreign_key('usernotes_reference_comment_fkey', 'usernotes', 'comments', ['reference_comment'], ['id'], ondelete='SET NULL')
|
||||
op.create_foreign_key('usernotes_reference_post_fkey', 'usernotes', 'submissions', ['reference_post'], ['id'], ondelete='SET NULL')
|
||||
op.create_foreign_key('usernotes_reference_user_fkey', 'usernotes', 'users', ['reference_user'], ['id'], ondelete='CASCADE')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint('usernotes_reference_user_fkey', 'usernotes', type_='foreignkey')
|
||||
op.drop_constraint('usernotes_reference_post_fkey', 'usernotes', type_='foreignkey')
|
||||
op.drop_constraint('usernotes_reference_comment_fkey', 'usernotes', type_='foreignkey')
|
||||
op.drop_constraint('usernotes_author_id_fkey', 'usernotes', type_='foreignkey')
|
||||
op.alter_column('usernotes', 'tag',
|
||||
existing_type=sa.VARCHAR(length=10),
|
||||
nullable=True)
|
||||
op.alter_column('usernotes', 'note',
|
||||
existing_type=sa.VARCHAR(length=10000),
|
||||
nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint('usernotes_reference_user_fkey', 'usernotes', type_='foreignkey')
|
||||
op.drop_constraint('usernotes_reference_post_fkey', 'usernotes', type_='foreignkey')
|
||||
op.drop_constraint('usernotes_reference_comment_fkey', 'usernotes', type_='foreignkey')
|
||||
op.drop_constraint('usernotes_author_id_fkey', 'usernotes', type_='foreignkey')
|
||||
op.alter_column('usernotes', 'tag',
|
||||
existing_type=sa.VARCHAR(length=10),
|
||||
nullable=True)
|
||||
op.alter_column('usernotes', 'note',
|
||||
existing_type=sa.VARCHAR(length=10000),
|
||||
nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,12 +17,12 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('comments', 'treasure_amount')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('comments', 'treasure_amount')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('comments', sa.Column('treasure_amount', sa.VARCHAR(length=10), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('comments', sa.Column('treasure_amount', sa.VARCHAR(length=10), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,16 +17,16 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('comments', 'slots_result')
|
||||
op.drop_column('comments', 'blackjack_result')
|
||||
op.drop_column('comments', 'wordle_result')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('comments', 'slots_result')
|
||||
op.drop_column('comments', 'blackjack_result')
|
||||
op.drop_column('comments', 'wordle_result')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('comments', sa.Column('wordle_result', sa.VARCHAR(length=115), autoincrement=False, nullable=True))
|
||||
op.add_column('comments', sa.Column('blackjack_result', sa.VARCHAR(length=860), autoincrement=False, nullable=True))
|
||||
op.add_column('comments', sa.Column('slots_result', sa.VARCHAR(length=32), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('comments', sa.Column('wordle_result', sa.VARCHAR(length=115), autoincrement=False, nullable=True))
|
||||
op.add_column('comments', sa.Column('blackjack_result', sa.VARCHAR(length=860), autoincrement=False, nullable=True))
|
||||
op.add_column('comments', sa.Column('slots_result', sa.VARCHAR(length=32), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,16 +17,16 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'sigs_disabled')
|
||||
op.drop_column('users', 'sig_html')
|
||||
op.drop_column('users', 'sig')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'sigs_disabled')
|
||||
op.drop_column('users', 'sig_html')
|
||||
op.drop_column('users', 'sig')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('sig', sa.VARCHAR(length=200), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('sig_html', sa.VARCHAR(length=1000), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('sigs_disabled', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('sig', sa.VARCHAR(length=200), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('sig_html', sa.VARCHAR(length=1000), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('sigs_disabled', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,34 +17,34 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'alt')
|
||||
op.drop_column('users', 'unblockable')
|
||||
op.drop_column('users', 'deflector')
|
||||
op.drop_column('users', 'bird')
|
||||
op.drop_column('users', 'rehab')
|
||||
op.drop_column('users', 'fish')
|
||||
op.drop_column('users', 'mute')
|
||||
op.drop_column('users', 'eye')
|
||||
op.drop_column('users', 'longpost')
|
||||
op.drop_column('users', 'marseyawarded')
|
||||
op.drop_column('users', 'progressivestack')
|
||||
op.drop_column('users', 'unmutable')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'alt')
|
||||
op.drop_column('users', 'unblockable')
|
||||
op.drop_column('users', 'deflector')
|
||||
op.drop_column('users', 'bird')
|
||||
op.drop_column('users', 'rehab')
|
||||
op.drop_column('users', 'fish')
|
||||
op.drop_column('users', 'mute')
|
||||
op.drop_column('users', 'eye')
|
||||
op.drop_column('users', 'longpost')
|
||||
op.drop_column('users', 'marseyawarded')
|
||||
op.drop_column('users', 'progressivestack')
|
||||
op.drop_column('users', 'unmutable')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('unmutable', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('progressivestack', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('marseyawarded', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('longpost', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('eye', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('mute', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('fish', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('rehab', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('bird', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('deflector', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('unblockable', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('alt', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('unmutable', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('progressivestack', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('marseyawarded', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('longpost', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('eye', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('mute', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('fish', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('rehab', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('bird', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('deflector', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('unblockable', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
op.add_column('users', sa.Column('alt', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,12 +17,12 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('volunteer_last_started_utc', sa.DateTime(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('volunteer_last_started_utc', sa.DateTime(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'volunteer_last_started_utc')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'volunteer_last_started_utc')
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,23 +17,23 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('volunteer_janitor',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('comment_id', sa.Integer(), nullable=False),
|
||||
sa.Column('edited_utc', sa.DateTime(), nullable=False),
|
||||
sa.Column('result', sa.Enum('Pending', 'TopQuality', 'Good', 'Neutral', 'Bad', 'Warning', 'Ban', name='volunteerjanitorresult'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['comment_id'], ['comments.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index('volunteer_comment_index', 'volunteer_janitor', ['user_id', 'comment_id'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('volunteer_janitor',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('comment_id', sa.Integer(), nullable=False),
|
||||
sa.Column('edited_utc', sa.DateTime(), nullable=False),
|
||||
sa.Column('result', sa.Enum('Pending', 'TopQuality', 'Good', 'Neutral', 'Bad', 'Warning', 'Ban', name='volunteerjanitorresult'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['comment_id'], ['comments.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index('volunteer_comment_index', 'volunteer_janitor', ['user_id', 'comment_id'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index('volunteer_comment_index', table_name='volunteer_janitor')
|
||||
op.drop_table('volunteer_janitor')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index('volunteer_comment_index', table_name='volunteer_janitor')
|
||||
op.drop_table('volunteer_janitor')
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,14 +17,14 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('volunteer_janitor', sa.Column('recorded_utc', sa.DateTime(), nullable=False))
|
||||
op.drop_column('volunteer_janitor', 'edited_utc')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('volunteer_janitor', sa.Column('recorded_utc', sa.DateTime(), nullable=False))
|
||||
op.drop_column('volunteer_janitor', 'edited_utc')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('volunteer_janitor', sa.Column('edited_utc', postgresql.TIMESTAMP(), autoincrement=False, nullable=False))
|
||||
op.drop_column('volunteer_janitor', 'recorded_utc')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('volunteer_janitor', sa.Column('edited_utc', postgresql.TIMESTAMP(), autoincrement=False, nullable=False))
|
||||
op.drop_column('volunteer_janitor', 'recorded_utc')
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,16 +17,16 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index('discord_id_idx', table_name='users')
|
||||
op.drop_constraint('one_discord_account', 'users', type_='unique')
|
||||
op.drop_column('users', 'discord_id')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index('discord_id_idx', table_name='users')
|
||||
op.drop_constraint('one_discord_account', 'users', type_='unique')
|
||||
op.drop_column('users', 'discord_id')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('discord_id', sa.VARCHAR(length=64), autoincrement=False, nullable=True))
|
||||
op.create_unique_constraint('one_discord_account', 'users', ['discord_id'])
|
||||
op.create_index('discord_id_idx', 'users', ['discord_id'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('discord_id', sa.VARCHAR(length=64), autoincrement=False, nullable=True))
|
||||
op.create_unique_constraint('one_discord_account', 'users', ['discord_id'])
|
||||
op.create_index('discord_id_idx', 'users', ['discord_id'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,17 +17,17 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('comments', sa.Column(
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('comments', sa.Column(
|
||||
'descendant_count',
|
||||
sa.Integer(),
|
||||
nullable=False,
|
||||
server_default=sa.text('0')
|
||||
))
|
||||
# ### end Alembic commands ###
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('comments', 'descendant_count')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('comments', 'descendant_count')
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -22,9 +22,9 @@ branch_labels = None
|
|||
depends_on = None
|
||||
|
||||
def upgrade():
|
||||
db =Session(bind=op.get_bind())
|
||||
db: Session = Session(bind=op.get_bind())
|
||||
bulk_recompute_descendant_counts(lambda q: q, db)
|
||||
|
||||
def downgrade():
|
||||
db =Session(bind=op.get_bind())
|
||||
db: Session = Session(bind=op.get_bind())
|
||||
db.execute(update(Comment).values(descendant_count=0))
|
||||
|
|
|
@ -17,12 +17,12 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'song')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'song')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('song', sa.VARCHAR(length=50), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('song', sa.VARCHAR(length=50), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,50 +17,50 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('comments', 'body',
|
||||
existing_type=sa.VARCHAR(length=40000),
|
||||
type_=sa.Text(),
|
||||
existing_nullable=True)
|
||||
op.alter_column('comments', 'body_html',
|
||||
existing_type=sa.VARCHAR(length=40000),
|
||||
type_=sa.Text(),
|
||||
nullable=False)
|
||||
op.alter_column('submissions', 'body',
|
||||
existing_type=sa.VARCHAR(length=40000),
|
||||
type_=sa.Text(),
|
||||
existing_nullable=True)
|
||||
op.alter_column('submissions', 'body_html',
|
||||
existing_type=sa.VARCHAR(length=40000),
|
||||
type_=sa.Text(),
|
||||
existing_nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('comments', 'body',
|
||||
existing_type=sa.VARCHAR(length=40000),
|
||||
type_=sa.Text(),
|
||||
existing_nullable=True)
|
||||
op.alter_column('comments', 'body_html',
|
||||
existing_type=sa.VARCHAR(length=40000),
|
||||
type_=sa.Text(),
|
||||
nullable=False)
|
||||
op.alter_column('submissions', 'body',
|
||||
existing_type=sa.VARCHAR(length=40000),
|
||||
type_=sa.Text(),
|
||||
existing_nullable=True)
|
||||
op.alter_column('submissions', 'body_html',
|
||||
existing_type=sa.VARCHAR(length=40000),
|
||||
type_=sa.Text(),
|
||||
existing_nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('submissions', 'body_html',
|
||||
existing_type=sa.Text(),
|
||||
type_=sa.VARCHAR(length=40000),
|
||||
existing_nullable=True)
|
||||
op.alter_column('submissions', 'body',
|
||||
existing_type=sa.Text(),
|
||||
type_=sa.VARCHAR(length=40000),
|
||||
existing_nullable=True)
|
||||
op.alter_column('oauth_apps', 'redirect_uri',
|
||||
existing_type=sa.String(length=50),
|
||||
type_=sa.VARCHAR(length=4096),
|
||||
existing_nullable=False)
|
||||
op.alter_column('oauth_apps', 'client_id',
|
||||
existing_type=sa.String(),
|
||||
type_=sa.CHAR(length=64),
|
||||
existing_nullable=True)
|
||||
op.alter_column('comments', 'body_html',
|
||||
existing_type=sa.Text(),
|
||||
type_=sa.VARCHAR(length=40000),
|
||||
nullable=True)
|
||||
op.alter_column('comments', 'body',
|
||||
existing_type=sa.Text(),
|
||||
type_=sa.VARCHAR(length=40000),
|
||||
existing_nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('submissions', 'body_html',
|
||||
existing_type=sa.Text(),
|
||||
type_=sa.VARCHAR(length=40000),
|
||||
existing_nullable=True)
|
||||
op.alter_column('submissions', 'body',
|
||||
existing_type=sa.Text(),
|
||||
type_=sa.VARCHAR(length=40000),
|
||||
existing_nullable=True)
|
||||
op.alter_column('oauth_apps', 'redirect_uri',
|
||||
existing_type=sa.String(length=50),
|
||||
type_=sa.VARCHAR(length=4096),
|
||||
existing_nullable=False)
|
||||
op.alter_column('oauth_apps', 'client_id',
|
||||
existing_type=sa.String(),
|
||||
type_=sa.CHAR(length=64),
|
||||
existing_nullable=True)
|
||||
op.alter_column('comments', 'body_html',
|
||||
existing_type=sa.Text(),
|
||||
type_=sa.VARCHAR(length=40000),
|
||||
nullable=True)
|
||||
op.alter_column('comments', 'body',
|
||||
existing_type=sa.Text(),
|
||||
type_=sa.VARCHAR(length=40000),
|
||||
existing_nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,65 +17,65 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('tasks_repeatable',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.Column('type_id', sa.SmallInteger(), nullable=False),
|
||||
sa.Column('enabled', sa.Boolean(), nullable=False),
|
||||
sa.Column('run_state', sa.SmallInteger(), nullable=False),
|
||||
sa.Column('run_time_last', sa.DateTime(), nullable=True),
|
||||
sa.Column('frequency_day', sa.SmallInteger(), nullable=False),
|
||||
sa.Column('time_of_day_utc', sa.Time(), nullable=False),
|
||||
sa.Column('created_utc', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('tasks_repeatable_python',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('import_path', sa.String(), nullable=False),
|
||||
sa.Column('callable', sa.String(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['id'], ['tasks_repeatable.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('tasks_repeatable_runs',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('task_id', sa.Integer(), nullable=False),
|
||||
sa.Column('manual', sa.Boolean(), nullable=False),
|
||||
sa.Column('traceback_str', sa.Text(), nullable=True),
|
||||
sa.Column('completed_utc', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_utc', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['task_id'], ['tasks_repeatable.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('tasks_repeatable_scheduled_submissions',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('author_id_submission', sa.Integer(), nullable=False),
|
||||
sa.Column('ghost', sa.Boolean(), nullable=False),
|
||||
sa.Column('private', sa.Boolean(), nullable=False),
|
||||
sa.Column('over_18', sa.Boolean(), nullable=False),
|
||||
sa.Column('is_bot', sa.Boolean(), nullable=False),
|
||||
sa.Column('title', sa.String(length=500), nullable=False),
|
||||
sa.Column('url', sa.String(), nullable=True),
|
||||
sa.Column('body', sa.Text(), nullable=True),
|
||||
sa.Column('body_html', sa.Text(), nullable=True),
|
||||
sa.Column('flair', sa.String(), nullable=True),
|
||||
sa.Column('embed_url', sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['author_id_submission'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['id'], ['tasks_repeatable.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.add_column('submissions', sa.Column('task_id', sa.Integer(), nullable=True))
|
||||
op.create_foreign_key(None, 'submissions', 'tasks_repeatable_scheduled_submissions', ['task_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('tasks_repeatable',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.Column('type_id', sa.SmallInteger(), nullable=False),
|
||||
sa.Column('enabled', sa.Boolean(), nullable=False),
|
||||
sa.Column('run_state', sa.SmallInteger(), nullable=False),
|
||||
sa.Column('run_time_last', sa.DateTime(), nullable=True),
|
||||
sa.Column('frequency_day', sa.SmallInteger(), nullable=False),
|
||||
sa.Column('time_of_day_utc', sa.Time(), nullable=False),
|
||||
sa.Column('created_utc', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('tasks_repeatable_python',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('import_path', sa.String(), nullable=False),
|
||||
sa.Column('callable', sa.String(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['id'], ['tasks_repeatable.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('tasks_repeatable_runs',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('task_id', sa.Integer(), nullable=False),
|
||||
sa.Column('manual', sa.Boolean(), nullable=False),
|
||||
sa.Column('traceback_str', sa.Text(), nullable=True),
|
||||
sa.Column('completed_utc', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_utc', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['task_id'], ['tasks_repeatable.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('tasks_repeatable_scheduled_submissions',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('author_id_submission', sa.Integer(), nullable=False),
|
||||
sa.Column('ghost', sa.Boolean(), nullable=False),
|
||||
sa.Column('private', sa.Boolean(), nullable=False),
|
||||
sa.Column('over_18', sa.Boolean(), nullable=False),
|
||||
sa.Column('is_bot', sa.Boolean(), nullable=False),
|
||||
sa.Column('title', sa.String(length=500), nullable=False),
|
||||
sa.Column('url', sa.String(), nullable=True),
|
||||
sa.Column('body', sa.Text(), nullable=True),
|
||||
sa.Column('body_html', sa.Text(), nullable=True),
|
||||
sa.Column('flair', sa.String(), nullable=True),
|
||||
sa.Column('embed_url', sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['author_id_submission'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['id'], ['tasks_repeatable.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.add_column('submissions', sa.Column('task_id', sa.Integer(), nullable=True))
|
||||
op.create_foreign_key(None, 'submissions', 'tasks_repeatable_scheduled_submissions', ['task_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'submissions', type_='foreignkey')
|
||||
op.drop_column('submissions', 'task_id')
|
||||
op.drop_table('tasks_repeatable_scheduled_submissions')
|
||||
op.drop_table('tasks_repeatable_runs')
|
||||
op.drop_table('tasks_repeatable_python')
|
||||
op.drop_table('tasks_repeatable')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'submissions', type_='foreignkey')
|
||||
op.drop_column('submissions', 'task_id')
|
||||
op.drop_table('tasks_repeatable_scheduled_submissions')
|
||||
op.drop_table('tasks_repeatable_runs')
|
||||
op.drop_table('tasks_repeatable_python')
|
||||
op.drop_table('tasks_repeatable')
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -19,71 +19,71 @@ depends_on = None
|
|||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! (adjusted -@TLSM) ###
|
||||
|
||||
op.drop_index('fki_exile_exiler_fkey', table_name='exiles')
|
||||
op.drop_index('fki_exile_sub_fkey', table_name='exiles')
|
||||
op.drop_table('exiles')
|
||||
op.drop_index('fki_exile_exiler_fkey', table_name='exiles')
|
||||
op.drop_index('fki_exile_sub_fkey', table_name='exiles')
|
||||
op.drop_table('exiles')
|
||||
|
||||
op.drop_index('fki_mod_sub_fkey', table_name='mods')
|
||||
op.drop_table('mods')
|
||||
op.drop_index('fki_mod_sub_fkey', table_name='mods')
|
||||
op.drop_table('mods')
|
||||
|
||||
op.drop_index('fki_sub_blocks_sub_fkey', table_name='sub_blocks')
|
||||
op.drop_table('sub_blocks')
|
||||
op.drop_index('fki_sub_blocks_sub_fkey', table_name='sub_blocks')
|
||||
op.drop_table('sub_blocks')
|
||||
|
||||
op.drop_constraint('sub_fkey', 'submissions', type_='foreignkey')
|
||||
op.drop_column('submissions', 'sub')
|
||||
op.drop_column('users', 'subs_created')
|
||||
op.drop_constraint('sub_fkey', 'submissions', type_='foreignkey')
|
||||
op.drop_column('submissions', 'sub')
|
||||
op.drop_column('users', 'subs_created')
|
||||
|
||||
op.drop_index('subs_idx', table_name='subs')
|
||||
op.drop_table('subs')
|
||||
op.drop_index('subs_idx', table_name='subs')
|
||||
op.drop_table('subs')
|
||||
|
||||
op.execute("DELETE FROM modactions WHERE kind = 'move_hole'")
|
||||
op.execute("DELETE FROM modactions WHERE kind = 'move_hole'")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! (adjusted -@TLSM) ###
|
||||
op.create_table('subs',
|
||||
sa.Column('name', sa.VARCHAR(length=20), autoincrement=False, nullable=False),
|
||||
sa.Column('sidebar', sa.VARCHAR(length=500), autoincrement=False, nullable=True),
|
||||
sa.Column('sidebar_html', sa.VARCHAR(length=1000), autoincrement=False, nullable=True),
|
||||
sa.Column('sidebarurl', sa.VARCHAR(length=60), autoincrement=False, nullable=True),
|
||||
sa.Column('bannerurl', sa.VARCHAR(length=60), autoincrement=False, nullable=True),
|
||||
sa.Column('css', sa.VARCHAR(length=4000), autoincrement=False, nullable=True),
|
||||
sa.PrimaryKeyConstraint('name', name='subs_pkey'),
|
||||
postgresql_ignore_search_path=False
|
||||
)
|
||||
op.create_index('subs_idx', 'subs', ['name'], unique=False)
|
||||
# ### commands auto generated by Alembic - please adjust! (adjusted -@TLSM) ###
|
||||
op.create_table('subs',
|
||||
sa.Column('name', sa.VARCHAR(length=20), autoincrement=False, nullable=False),
|
||||
sa.Column('sidebar', sa.VARCHAR(length=500), autoincrement=False, nullable=True),
|
||||
sa.Column('sidebar_html', sa.VARCHAR(length=1000), autoincrement=False, nullable=True),
|
||||
sa.Column('sidebarurl', sa.VARCHAR(length=60), autoincrement=False, nullable=True),
|
||||
sa.Column('bannerurl', sa.VARCHAR(length=60), autoincrement=False, nullable=True),
|
||||
sa.Column('css', sa.VARCHAR(length=4000), autoincrement=False, nullable=True),
|
||||
sa.PrimaryKeyConstraint('name', name='subs_pkey'),
|
||||
postgresql_ignore_search_path=False
|
||||
)
|
||||
op.create_index('subs_idx', 'subs', ['name'], unique=False)
|
||||
|
||||
op.add_column('users', sa.Column('subs_created', sa.INTEGER(), server_default=sa.text('0'), autoincrement=False, nullable=False))
|
||||
op.add_column('submissions', sa.Column('sub', sa.VARCHAR(length=20), autoincrement=False, nullable=True))
|
||||
op.create_foreign_key('sub_fkey', 'submissions', 'subs', ['sub'], ['name'])
|
||||
op.add_column('users', sa.Column('subs_created', sa.INTEGER(), server_default=sa.text('0'), autoincrement=False, nullable=False))
|
||||
op.add_column('submissions', sa.Column('sub', sa.VARCHAR(length=20), autoincrement=False, nullable=True))
|
||||
op.create_foreign_key('sub_fkey', 'submissions', 'subs', ['sub'], ['name'])
|
||||
|
||||
op.create_table('sub_blocks',
|
||||
sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('sub', sa.VARCHAR(length=20), autoincrement=False, nullable=False),
|
||||
sa.ForeignKeyConstraint(['sub'], ['subs.name'], name='sub_blocks_sub_fkey'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='sub_blocks_user_fkey'),
|
||||
sa.PrimaryKeyConstraint('user_id', 'sub', name='sub_blocks_pkey')
|
||||
)
|
||||
op.create_index('fki_sub_blocks_sub_fkey', 'sub_blocks', ['sub'], unique=False)
|
||||
op.create_table('sub_blocks',
|
||||
sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('sub', sa.VARCHAR(length=20), autoincrement=False, nullable=False),
|
||||
sa.ForeignKeyConstraint(['sub'], ['subs.name'], name='sub_blocks_sub_fkey'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='sub_blocks_user_fkey'),
|
||||
sa.PrimaryKeyConstraint('user_id', 'sub', name='sub_blocks_pkey')
|
||||
)
|
||||
op.create_index('fki_sub_blocks_sub_fkey', 'sub_blocks', ['sub'], unique=False)
|
||||
|
||||
op.create_table('mods',
|
||||
sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('sub', sa.VARCHAR(length=20), autoincrement=False, nullable=False),
|
||||
sa.Column('created_utc', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.ForeignKeyConstraint(['sub'], ['subs.name'], name='mod_sub_fkey'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='user_mod_fkey'),
|
||||
sa.PrimaryKeyConstraint('user_id', 'sub', name='mods_pkey')
|
||||
)
|
||||
op.create_index('fki_mod_sub_fkey', 'mods', ['sub'], unique=False)
|
||||
op.create_table('mods',
|
||||
sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('sub', sa.VARCHAR(length=20), autoincrement=False, nullable=False),
|
||||
sa.Column('created_utc', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.ForeignKeyConstraint(['sub'], ['subs.name'], name='mod_sub_fkey'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='user_mod_fkey'),
|
||||
sa.PrimaryKeyConstraint('user_id', 'sub', name='mods_pkey')
|
||||
)
|
||||
op.create_index('fki_mod_sub_fkey', 'mods', ['sub'], unique=False)
|
||||
|
||||
op.create_table('exiles',
|
||||
sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('sub', sa.VARCHAR(length=20), autoincrement=False, nullable=False),
|
||||
sa.Column('exiler_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.ForeignKeyConstraint(['exiler_id'], ['users.id'], name='exile_exiler_fkey'),
|
||||
sa.ForeignKeyConstraint(['sub'], ['subs.name'], name='exile_sub_fkey'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='exile_user_fkey'),
|
||||
sa.PrimaryKeyConstraint('user_id', 'sub', name='exiles_pkey')
|
||||
)
|
||||
op.create_index('fki_exile_sub_fkey', 'exiles', ['sub'], unique=False)
|
||||
op.create_index('fki_exile_exiler_fkey', 'exiles', ['exiler_id'], unique=False)
|
||||
op.create_table('exiles',
|
||||
sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('sub', sa.VARCHAR(length=20), autoincrement=False, nullable=False),
|
||||
sa.Column('exiler_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.ForeignKeyConstraint(['exiler_id'], ['users.id'], name='exile_exiler_fkey'),
|
||||
sa.ForeignKeyConstraint(['sub'], ['subs.name'], name='exile_sub_fkey'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='exile_user_fkey'),
|
||||
sa.PrimaryKeyConstraint('user_id', 'sub', name='exiles_pkey')
|
||||
)
|
||||
op.create_index('fki_exile_sub_fkey', 'exiles', ['sub'], unique=False)
|
||||
op.create_index('fki_exile_exiler_fkey', 'exiles', ['exiler_id'], unique=False)
|
||||
|
|
|
@ -17,14 +17,14 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('tasks_repeatable', sa.Column('label', sa.String(), nullable=True))
|
||||
op.create_unique_constraint(None, 'tasks_repeatable', ['label'])
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('tasks_repeatable', sa.Column('label', sa.String(), nullable=True))
|
||||
op.create_unique_constraint(None, 'tasks_repeatable', ['label'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'tasks_repeatable', type_='unique')
|
||||
op.drop_column('tasks_repeatable', 'label')
|
||||
# ### end Alembic commands ###
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'tasks_repeatable', type_='unique')
|
||||
op.drop_column('tasks_repeatable', 'label')
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -17,22 +17,22 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# manually adjusted to introduce our intended defaults
|
||||
op.add_column('comments', sa.Column('volunteer_janitor_badness', sa.Float()))
|
||||
op.execute("UPDATE comments SET volunteer_janitor_badness = 0.5")
|
||||
op.alter_column('comments', 'volunteer_janitor_badness', nullable=False)
|
||||
# manually adjusted to introduce our intended defaults
|
||||
op.add_column('comments', sa.Column('volunteer_janitor_badness', sa.Float()))
|
||||
op.execute("UPDATE comments SET volunteer_janitor_badness = 0.5")
|
||||
op.alter_column('comments', 'volunteer_janitor_badness', nullable=False)
|
||||
|
||||
op.add_column('users', sa.Column('volunteer_janitor_correctness', sa.Float()))
|
||||
op.execute("UPDATE users SET volunteer_janitor_correctness = 0")
|
||||
op.alter_column('users', 'volunteer_janitor_correctness', nullable=False)
|
||||
op.add_column('users', sa.Column('volunteer_janitor_correctness', sa.Float()))
|
||||
op.execute("UPDATE users SET volunteer_janitor_correctness = 0")
|
||||
op.alter_column('users', 'volunteer_janitor_correctness', nullable=False)
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||
batch_op.drop_column('volunteer_janitor_correctness')
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||
batch_op.drop_column('volunteer_janitor_correctness')
|
||||
|
||||
with op.batch_alter_table('comments', schema=None) as batch_op:
|
||||
batch_op.drop_column('volunteer_janitor_badness')
|
||||
with op.batch_alter_table('comments', schema=None) as batch_op:
|
||||
batch_op.drop_column('volunteer_janitor_badness')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
@ -19,10 +19,10 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# this is now disabled because this code is no longer compatible with this version of the DB
|
||||
#volunteer_janitor_recalc_all_comments(Session(bind=op.get_bind()))
|
||||
pass
|
||||
# this is now disabled because this code is no longer compatible with this version of the DB
|
||||
#volunteer_janitor_recalc_all_comments(Session(bind=op.get_bind()))
|
||||
pass
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
pass
|
||||
|
|
|
@ -17,76 +17,76 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('comments', sa.Column('state_user_deleted_utc', sa.DateTime(timezone=True), nullable=True))
|
||||
op.add_column('submissions', sa.Column('state_user_deleted_utc', sa.DateTime(timezone=True), nullable=True))
|
||||
op.add_column('comments', sa.Column('state_user_deleted_utc', sa.DateTime(timezone=True), nullable=True))
|
||||
op.add_column('submissions', sa.Column('state_user_deleted_utc', sa.DateTime(timezone=True), nullable=True))
|
||||
|
||||
op.drop_index('subimssion_binary_group_idx', table_name='submissions')
|
||||
op.drop_index('submission_isdeleted_idx', table_name='submissions')
|
||||
op.drop_index('submission_new_sort_idx', table_name='submissions')
|
||||
op.drop_index('subimssion_binary_group_idx', table_name='submissions')
|
||||
op.drop_index('submission_isdeleted_idx', table_name='submissions')
|
||||
op.drop_index('submission_new_sort_idx', table_name='submissions')
|
||||
|
||||
op.execute("""
|
||||
UPDATE comments
|
||||
SET state_user_deleted_utc =
|
||||
CASE
|
||||
WHEN deleted_utc > 0 THEN
|
||||
(timestamp 'epoch' + deleted_utc * interval '1 second') at time zone 'utc'
|
||||
ELSE NULL
|
||||
END
|
||||
""")
|
||||
op.execute("""
|
||||
UPDATE comments
|
||||
SET state_user_deleted_utc =
|
||||
CASE
|
||||
WHEN deleted_utc > 0 THEN
|
||||
(timestamp 'epoch' + deleted_utc * interval '1 second') at time zone 'utc'
|
||||
ELSE NULL
|
||||
END
|
||||
""")
|
||||
|
||||
op.execute("""
|
||||
UPDATE submissions
|
||||
SET state_user_deleted_utc =
|
||||
CASE
|
||||
WHEN deleted_utc > 0 THEN
|
||||
(timestamp 'epoch' + deleted_utc * interval '1 second') at time zone 'utc'
|
||||
ELSE NULL
|
||||
END
|
||||
""")
|
||||
op.execute("""
|
||||
UPDATE submissions
|
||||
SET state_user_deleted_utc =
|
||||
CASE
|
||||
WHEN deleted_utc > 0 THEN
|
||||
(timestamp 'epoch' + deleted_utc * interval '1 second') at time zone 'utc'
|
||||
ELSE NULL
|
||||
END
|
||||
""")
|
||||
|
||||
op.drop_column('comments', 'deleted_utc')
|
||||
op.drop_column('submissions', 'deleted_utc')
|
||||
op.drop_column('comments', 'deleted_utc')
|
||||
op.drop_column('submissions', 'deleted_utc')
|
||||
|
||||
op.create_index('subimssion_binary_group_idx', 'submissions', ['is_banned', 'state_user_deleted_utc', 'over_18'], unique=False)
|
||||
op.create_index('submission_isdeleted_idx', 'submissions', ['state_user_deleted_utc'], unique=False)
|
||||
op.create_index('submission_new_sort_idx', 'submissions', ['is_banned', 'state_user_deleted_utc', sa.text('created_utc DESC'), 'over_18'], unique=False)
|
||||
op.create_index('subimssion_binary_group_idx', 'submissions', ['is_banned', 'state_user_deleted_utc', 'over_18'], unique=False)
|
||||
op.create_index('submission_isdeleted_idx', 'submissions', ['state_user_deleted_utc'], unique=False)
|
||||
op.create_index('submission_new_sort_idx', 'submissions', ['is_banned', 'state_user_deleted_utc', sa.text('created_utc DESC'), 'over_18'], unique=False)
|
||||
|
||||
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.add_column('comments', sa.Column('deleted_utc', sa.INTEGER(), server_default=sa.text('0'), autoincrement=False, nullable=True))
|
||||
op.add_column('submissions', sa.Column('deleted_utc', sa.INTEGER(), server_default=sa.text('0'), autoincrement=False, nullable=True))
|
||||
op.add_column('comments', sa.Column('deleted_utc', sa.INTEGER(), server_default=sa.text('0'), autoincrement=False, nullable=True))
|
||||
op.add_column('submissions', sa.Column('deleted_utc', sa.INTEGER(), server_default=sa.text('0'), autoincrement=False, nullable=True))
|
||||
|
||||
op.drop_index('submission_new_sort_idx', table_name='submissions')
|
||||
op.drop_index('submission_isdeleted_idx', table_name='submissions')
|
||||
op.drop_index('subimssion_binary_group_idx', table_name='submissions')
|
||||
op.drop_index('submission_new_sort_idx', table_name='submissions')
|
||||
op.drop_index('submission_isdeleted_idx', table_name='submissions')
|
||||
op.drop_index('subimssion_binary_group_idx', table_name='submissions')
|
||||
|
||||
op.execute("""
|
||||
UPDATE comments
|
||||
SET deleted_utc =
|
||||
COALESCE(
|
||||
EXTRACT(EPOCH FROM state_user_deleted_utc)::integer,
|
||||
0
|
||||
)
|
||||
""")
|
||||
|
||||
op.execute("""
|
||||
UPDATE submissions
|
||||
SET deleted_utc =
|
||||
COALESCE(
|
||||
EXTRACT(EPOCH FROM state_user_deleted_utc)::integer,
|
||||
0
|
||||
)
|
||||
""")
|
||||
op.execute("""
|
||||
UPDATE comments
|
||||
SET deleted_utc =
|
||||
COALESCE(
|
||||
EXTRACT(EPOCH FROM state_user_deleted_utc)::integer,
|
||||
0
|
||||
)
|
||||
""")
|
||||
|
||||
op.execute("""
|
||||
UPDATE submissions
|
||||
SET deleted_utc =
|
||||
COALESCE(
|
||||
EXTRACT(EPOCH FROM state_user_deleted_utc)::integer,
|
||||
0
|
||||
)
|
||||
""")
|
||||
|
||||
op.alter_column('comments', 'deleted_utc', nullable=False)
|
||||
op.alter_column('submissions', 'deleted_utc', nullable=False)
|
||||
op.alter_column('comments', 'deleted_utc', nullable=False)
|
||||
op.alter_column('submissions', 'deleted_utc', nullable=False)
|
||||
|
||||
op.drop_column('comments', 'state_user_deleted_utc')
|
||||
op.drop_column('submissions', 'state_user_deleted_utc')
|
||||
op.drop_column('comments', 'state_user_deleted_utc')
|
||||
op.drop_column('submissions', 'state_user_deleted_utc')
|
||||
|
||||
op.create_index('submission_new_sort_idx', 'submissions', ['is_banned', 'deleted_utc', 'created_utc', 'over_18'], unique=False)
|
||||
op.create_index('submission_isdeleted_idx', 'submissions', ['deleted_utc'], unique=False)
|
||||
op.create_index('subimssion_binary_group_idx', 'submissions', ['is_banned', 'deleted_utc', 'over_18'], unique=False)
|
||||
op.create_index('submission_new_sort_idx', 'submissions', ['is_banned', 'deleted_utc', 'created_utc', 'over_18'], unique=False)
|
||||
op.create_index('submission_isdeleted_idx', 'submissions', ['deleted_utc'], unique=False)
|
||||
op.create_index('subimssion_binary_group_idx', 'submissions', ['is_banned', 'deleted_utc', 'over_18'], unique=False)
|
||||
|
||||
|
|
|
@ -17,17 +17,17 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
# update for comments
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'remove_comment' WHERE kind = 'ban_comment'"""))
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'unremove_comment' WHERE kind = 'unban_comment'"""))
|
||||
# update for posts
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'remove_post' WHERE kind = 'ban_post'"""))
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'unremove_post' WHERE kind = 'unban_post'"""))
|
||||
# update for comments
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'remove_comment' WHERE kind = 'ban_comment'"""))
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'unremove_comment' WHERE kind = 'unban_comment'"""))
|
||||
# update for posts
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'remove_post' WHERE kind = 'ban_post'"""))
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'unremove_post' WHERE kind = 'unban_post'"""))
|
||||
|
||||
def downgrade():
|
||||
# rollback for comments
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'ban_comment' WHERE kind = 'remove_comment'"""))
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'unban_comment' WHERE kind = 'unremove_comment'"""))
|
||||
# rollback for posts
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'ban_post' WHERE kind = 'remove_post'"""))
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'unban_post' WHERE kind = 'unremove_post'"""))
|
||||
# rollback for comments
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'ban_comment' WHERE kind = 'remove_comment'"""))
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'unban_comment' WHERE kind = 'unremove_comment'"""))
|
||||
# rollback for posts
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'ban_post' WHERE kind = 'remove_post'"""))
|
||||
op.execute(sa.text("""UPDATE modactions SET kind = 'unban_post' WHERE kind = 'unremove_post'"""))
|
||||
|
|
|
@ -17,217 +17,217 @@ 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 = 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)
|
||||
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')
|
||||
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 `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 `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
|
||||
""")
|
||||
# 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.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.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')
|
||||
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 `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 `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
|
||||
""")
|
||||
# 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.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')
|
||||
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.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_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 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 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.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.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.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_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 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 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.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.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')
|
||||
op.drop_column('submissions', 'state_report')
|
||||
op.drop_column('submissions', 'state_mod_set_by')
|
||||
op.drop_column('submissions', 'state_mod')
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue