diff --git a/files/classes/mod_logs.py b/files/classes/mod_logs.py index f9e23a68b..19aa5f0fb 100644 --- a/files/classes/mod_logs.py +++ b/files/classes/mod_logs.py @@ -55,7 +55,7 @@ class ModAction(CreatedDateTimeBase): @property @lazy def string(self): - output = self.lookup_action_type()["str"].format(self=self, cc=CC_TITLE) + output = self.lookup_action_type()["str"].format(self=self) if not self.note: return output output += f" ({self.note})" return output @@ -65,7 +65,6 @@ class ModAction(CreatedDateTimeBase): def target_link(self): if self.target_user: return f'{self.target_user.username}' elif self.target_post: - if self.target_post.club: return f'{CC} ONLY' return f'{self.target_post.title_html}' elif self.target_comment_id: return f'comment' @@ -140,16 +139,6 @@ ACTIONTYPES = { "icon": 'fa-badge-check', "color": 'bg-success' }, - 'club_allow': { - "str": 'allowed user {self.target_link} into the {cc}', - "icon": 'fa-golf-club', - "color": 'bg-success' - }, - 'club_ban': { - "str": 'disallowed user {self.target_link} from the {cc}', - "icon": 'fa-golf-club', - "color": 'bg-danger' - }, 'delete_report': { "str": 'deleted report on {self.target_link}', "icon": 'fa-flag', diff --git a/files/classes/submission.py b/files/classes/submission.py index 4346cea2e..d406aba4b 100644 --- a/files/classes/submission.py +++ b/files/classes/submission.py @@ -32,7 +32,6 @@ class Submission(CreatedBase): stickied_utc = Column(Integer) is_pinned = Column(Boolean, default=False, nullable=False) private = Column(Boolean, default=False, nullable=False) - club = Column(Boolean, default=False, nullable=False) comment_count = Column(Integer, default=0, nullable=False) over_18 = Column(Boolean, default=False, nullable=False) is_bot = Column(Boolean, default=False, nullable=False) @@ -170,7 +169,6 @@ class Submission(CreatedBase): @lazy def shortlink(self): link = f"/post/{self.id}" - if self.club: return link + '/-' output = title_regex.sub('', self.title.lower()) output = output.split()[:6] @@ -251,7 +249,6 @@ class Submission(CreatedBase): 'distinguish_level': self.distinguish_level, 'voted': self.voted if hasattr(self, 'voted') else 0, 'flags': flags, - 'club': self.club, } return data diff --git a/files/classes/user.py b/files/classes/user.py index 47f36f169..9bae9fc2e 100644 --- a/files/classes/user.py +++ b/files/classes/user.py @@ -22,9 +22,7 @@ from files.classes.userblock import UserBlock from files.classes.visstate import StateMod from files.helpers.assetcache import assetcache_path from files.helpers.config.const import * -from files.helpers.config.environment import (CARD_VIEW, - CLUB_TRUESCORE_MINIMUM, - DEFAULT_COLOR, +from files.helpers.config.environment import (CARD_VIEW, DEFAULT_COLOR, DEFAULT_TIME_FILTER, SITE_FULL, SITE_ID) from files.helpers.security import * @@ -95,7 +93,6 @@ class User(CreatedBase): is_banned = Column(Integer, default=0, nullable=False) unban_utc = Column(Integer, default=0, nullable=False) ban_reason = deferred(Column(String)) - club_allowed = Column(Boolean) login_nonce = Column(Integer, default=0, nullable=False) reserved = deferred(Column(String)) coins = Column(Integer, default=0, nullable=False) @@ -218,11 +215,6 @@ class User(CreatedBase): def is_blocking(self, target): return g.db.query(UserBlock).filter_by(user_id=self.id, target_id=target.id).one_or_none() - @property - @lazy - def paid_dues(self): - return not self.shadowbanned and not (self.is_banned and not self.unban_utc) and (self.admin_level or self.club_allowed or (self.club_allowed != False and self.truescore > CLUB_TRUESCORE_MINIMUM)) - @lazy def any_block_exists(self, other): return g.db.query(UserBlock).filter( diff --git a/files/commands/seed_db.py b/files/commands/seed_db.py index 23e306047..64c1d6fc8 100644 --- a/files/commands/seed_db.py +++ b/files/commands/seed_db.py @@ -93,7 +93,6 @@ def seed_db_worker(num_users = 900, num_posts = 40, num_toplevel_comments = 1000 user = users[int(len(users) * detrand())] post = Submission( private=False, - club=None, author_id=user.id, over_18=False, app_id=None, diff --git a/files/helpers/config/const.py b/files/helpers/config/const.py index 16f1de03d..fbf3bf75f 100644 --- a/files/helpers/config/const.py +++ b/files/helpers/config/const.py @@ -43,9 +43,6 @@ class Service(IntEnum): def enable_services(self) -> bool: return self not in {self.CRON, self.MIGRATION} -CC = "COUNTRY CLUB" -CC_TITLE = CC.title() - NOTIFICATIONS_ID = 1 AUTOJANNY_ID = 2 MODMAIL_ID = 2 diff --git a/files/helpers/config/environment.py b/files/helpers/config/environment.py index e152664da..b2150b84e 100644 --- a/files/helpers/config/environment.py +++ b/files/helpers/config/environment.py @@ -93,8 +93,6 @@ CARD_VIEW = bool_from_string(environ.get("CARD_VIEW", True)) FINGERPRINT_TOKEN = environ.get("FP", None) # other stuff from const.py that aren't constants -CLUB_TRUESCORE_MINIMUM = int(environ.get("DUES").strip()) - IMGUR_KEY = environ.get("IMGUR_KEY", "").strip() PUSHER_ID = environ.get("PUSHER_ID", "").strip() PUSHER_KEY = environ.get("PUSHER_KEY", "").strip() diff --git a/files/helpers/jinja2.py b/files/helpers/jinja2.py index a8f999ef2..b6d34c57a 100644 --- a/files/helpers/jinja2.py +++ b/files/helpers/jinja2.py @@ -65,8 +65,6 @@ def inject_constants(): "NOTIFICATIONS_ID":NOTIFICATIONS_ID, "MODMAIL_ID":MODMAIL_ID, "PUSHER_ID":PUSHER_ID, - "CC":CC, - "CC_TITLE":CC_TITLE, "listdir":listdir, "config":app.config.get, "ENABLE_DOWNVOTES": ENABLE_DOWNVOTES, diff --git a/files/helpers/listing.py b/files/helpers/listing.py index 70a1f7960..c73c9021b 100644 --- a/files/helpers/listing.py +++ b/files/helpers/listing.py @@ -23,7 +23,7 @@ USERPAGELISTING_TIMEOUT_SECS: Final[int] = 86400 CHANGELOGLIST_TIMEOUT_SECS: Final[int] = 86400 @cache.memoize(timeout=FRONTLIST_TIMEOUT_SECS) -def frontlist(v=None, sort='new', page=1, t="all", ids_only=True, ccmode="false", filter_words='', gt=0, lt=0): +def frontlist(v=None, sort='new', page=1, t="all", ids_only=True, filter_words='', gt=0, lt=0): posts = g.db.query(Submission) if v and v.hidevotedon: @@ -42,14 +42,8 @@ def frontlist(v=None, sort='new', page=1, t="all", ids_only=True, ccmode="false" if not gt and not lt: posts = apply_time_filter(posts, t, Submission) - if (ccmode == "true"): - posts = posts.filter(Submission.club == True) - posts = posts.filter_by(private=False, state_user_deleted_utc=None) - if ccmode == "false" and not gt and not lt: - posts = posts.filter_by(stickied=None) - if v and v.admin_level < 2: posts = posts.filter(Submission.author_id.notin_(v.userblocks)) @@ -77,7 +71,7 @@ def frontlist(v=None, sort='new', page=1, t="all", ids_only=True, ccmode="false" posts = posts[:size] - if page == 1 and ccmode == "false" and not gt and not lt: + if page == 1 and not gt and not lt: pins = g.db.query(Submission).filter(Submission.stickied != None, Submission.state_mod == StateMod.VISIBLE) if v: if v.admin_level < 2: diff --git a/files/routes/admin/admin.py b/files/routes/admin/admin.py index 1dcca6815..ee6636868 100644 --- a/files/routes/admin/admin.py +++ b/files/routes/admin/admin.py @@ -163,62 +163,6 @@ def revert_actions(v, username): return {"message": "Admin actions reverted!"} -@app.post("/@/club_allow") -@limiter.exempt -@admin_level_required(2) -def club_allow(v, username): - u = get_user(username, v=v) - - if not u: abort(404) - - if u.admin_level >= v.admin_level: - abort(403, "Can't target users with admin level higher than you") - - u.club_allowed = True - g.db.add(u) - - for x in u.alts_unique: - x.club_allowed = True - g.db.add(x) - - ma = ModAction( - kind="club_allow", - user_id=v.id, - target_user_id=u.id - ) - g.db.add(ma) - - g.db.commit() - return {"message": f"@{username} has been allowed into the {CC_TITLE}!"} - - -@app.post("/@/club_ban") -@limiter.exempt -@admin_level_required(2) -def club_ban(v, username): - u = get_user(username, v=v) - - if not u: abort(404) - - if u.admin_level >= v.admin_level: - abort(403, "Can't target users with admin level higher than you") - - u.club_allowed = False - - for x in u.alts_unique: - u.club_allowed = False - g.db.add(x) - - ma = ModAction( - kind="club_ban", - user_id=v.id, - target_user_id=u.id - ) - g.db.add(ma) - - g.db.commit() - return {"message": f"@{username} has been kicked from the {CC_TITLE}. Deserved."} - @app.get("/admin/shadowbanned") @limiter.exempt @auth_required diff --git a/files/routes/comments.py b/files/routes/comments.py index 83a57ca3a..53c4c8f86 100644 --- a/files/routes/comments.py +++ b/files/routes/comments.py @@ -23,8 +23,6 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None): g.db.add(notif) g.db.commit() - if comment.post and comment.post.club and not (v and (v.paid_dues or v.id in [comment.author_id, comment.post.author_id])): abort(403) - if comment.post and comment.post.private and not (v and (v.admin_level >= 2 or v.id == comment.post.author.id)): abort(403) if not comment.parent_submission and not (v and (comment.author.id == v.id or comment.sentto == v.id)) and not (v and v.admin_level >= 2) : abort(403) diff --git a/files/routes/front.py b/files/routes/front.py index 2ce1a5b56..415d998c2 100644 --- a/files/routes/front.py +++ b/files/routes/front.py @@ -207,7 +207,6 @@ def front_all(v, subdomain=None): sort=request.values.get("sort", defaultsorting) t=request.values.get('t', defaulttime) - ccmode=request.values.get('ccmode', "false").lower() if sort == 'bump': t='all' @@ -221,7 +220,6 @@ def front_all(v, subdomain=None): page=page, t=t, v=v, - ccmode=ccmode, filter_words=v.filter_words if v else [], gt=gt, lt=lt, @@ -247,7 +245,7 @@ def front_all(v, subdomain=None): g.db.commit() if request.headers.get("Authorization"): return {"data": [x.json for x in posts], "next_exists": next_exists} - return render_template("home.html", v=v, listing=posts, next_exists=next_exists, sort=sort, t=t, page=page, ccmode=ccmode, home=True) + return render_template("home.html", v=v, listing=posts, next_exists=next_exists, sort=sort, t=t, page=page, home=True) @app.get("/changelog") diff --git a/files/routes/posts.py b/files/routes/posts.py index f22389de5..76357f092 100644 --- a/files/routes/posts.py +++ b/files/routes/posts.py @@ -35,21 +35,6 @@ MAX_TITLE_LENGTH = 500 MAX_URL_LENGTH = 2048 -@app.post("/toggle_club/") -@auth_required -def toggle_club(pid, v): - post = get_post(pid) - if post.author_id != v.id and v.admin_level < 2: abort(403) - - post.club = not post.club - g.db.add(post) - - g.db.commit() - - if post.club: return {"message": "Post has been marked as club-only!"} - else: return {"message": "Post has been unmarked as club-only!"} - - @app.post("/publish/") @limiter.limit("1/second;30/minute;200/hour;1000/day") @auth_required @@ -85,8 +70,6 @@ def post_id(pid, anything=None, v=None): else: defaultsortingcomments = "new" sort = request.values.get("sort", defaultsortingcomments) - if post.club and not (v and (v.paid_dues or v.id == post.author_id)): abort(403) - limit = app.config['RESULTS_PER_PAGE_COMMENTS'] offset = 0 @@ -130,8 +113,6 @@ def post_id(pid, anything=None, v=None): @auth_desired def viewmore(v, pid, sort, offset): post = get_post(pid, v=v) - if post.club and not (v and (v.paid_dues or v.id == post.author_id)): abort(403) - offset_prev = int(offset) try: ids = set(int(x) for x in request.values.get("ids").split(',')) except: abort(400) @@ -557,7 +538,6 @@ def submit_post(v): _do_antispam_submission_check(v, validated_post) - club = bool(request.values.get("club","")) is_bot = bool(request.headers.get("Authorization")) # Invariant: these values are guarded and obey the length bound @@ -566,7 +546,6 @@ def submit_post(v): post = Submission( private=bool(request.values.get("private","")), - club=club, author_id=v.id, over_18=bool(request.values.get("over_18","")), app_id=v.client.application.id if v.client else None, diff --git a/files/routes/search.py b/files/routes/search.py index 7830c3865..3732f609c 100644 --- a/files/routes/search.py +++ b/files/routes/search.py @@ -42,8 +42,6 @@ def searchposts(v): posts = g.db.query(Submission.id) - if not (v and v.paid_dues): posts = posts.filter_by(club=False) - if v and v.admin_level < 2: posts = posts.filter(Submission.state_user_deleted_utc == None, Submission.state_mod == StateMod.VISIBLE, Submission.private == False, Submission.author_id.notin_(v.userblocks)) elif not v: @@ -174,15 +172,8 @@ def searchcomments(v): private = [x[0] for x in g.db.query(Submission.id).filter(Submission.private == True).all()] comments = comments.filter(Comment.state_mod == StateMod.VISIBLE, Comment.state_user_deleted_utc == None, Comment.parent_submission.notin_(private)) - - if not (v and v.paid_dues): - club = [x[0] for x in g.db.query(Submission.id).filter(Submission.club == True).all()] - comments = comments.filter(Comment.parent_submission.notin_(club)) - comments = sort_objects(comments, sort, Comment) - total = comments.count() - comments = comments.offset(25 * (page - 1)).limit(26).all() ids = [x[0] for x in comments] diff --git a/files/routes/volunteer_janitor.py b/files/routes/volunteer_janitor.py index 2c8bf171c..eb3777f9e 100644 --- a/files/routes/volunteer_janitor.py +++ b/files/routes/volunteer_janitor.py @@ -4,7 +4,7 @@ from files.__main__ import app from files.classes.comment import Comment from files.classes.flags import CommentFlag from files.classes.user import User -from files.classes.visstate import StateReport +from files.classes.visstate import StateReport, StateMod from files.classes.volunteer_janitor import VolunteerJanitorRecord, VolunteerJanitorResult from files.helpers.volunteer_janitor import update_comment_badness from files.routes.volunteer_common import VolunteerDuty @@ -12,6 +12,7 @@ from flask import g import pprint import random import sqlalchemy +from sqlalchemy.orm import aliased class VolunteerDutyJanitor(VolunteerDuty): @@ -31,7 +32,7 @@ class VolunteerDutyJanitor(VolunteerDuty): def embed_template(self) -> str: return "volunteer_janitor.html" - + def comments(self) -> list[Comment]: return g.db.query(Comment).where(Comment.id.in_(self.choices)) @@ -42,11 +43,15 @@ def get_duty(u: User) -> Optional[VolunteerDutyJanitor]: # these could probably be combined into one query somehow - # find reported not-deleted comments not made by the current user + # find reported visible comments not made by the current user or in reply to the current user + ParentComment = aliased(Comment) reported_comments = g.db.query(Comment) \ .where(Comment.state_report == StateReport.REPORTED) \ + .where(Comment.state_mod == StateMod.VISIBLE) \ .where(Comment.state_user_deleted_utc == None) \ .where(Comment.author_id != u.id) \ + .outerjoin(ParentComment, ParentComment.id == Comment.parent_comment_id) \ + .where(sqlalchemy.or_(ParentComment.author_id != u.id, ParentComment.author_id == None)) \ .with_entities(Comment.id) reported_ids = [reported.id for reported in reported_comments] diff --git a/files/templates/component/post/actions.html b/files/templates/component/post/actions.html index 990e8afe8..1a3d58338 100644 --- a/files/templates/component/post/actions.html +++ b/files/templates/component/post/actions.html @@ -53,11 +53,6 @@ Unpin {% endif %} - {# {% if v.admin_level > 1 or v.id == p.author_id %} - Mark club - Unmark club - {% endif %} #} - {%- if v and v.admin_level >= PERMS['POST_COMMENT_MODERATION'] -%} {%- set show_approve = p.state_mod != StateMod.VISIBLE or "/reported/" in request.path -%} {%- set show_remove = p.state_mod == StateMod.VISIBLE -%} diff --git a/files/templates/component/post/actions_admin_mobile.html b/files/templates/component/post/actions_admin_mobile.html index 2c2376769..9d95d01ed 100644 --- a/files/templates/component/post/actions_admin_mobile.html +++ b/files/templates/component/post/actions_admin_mobile.html @@ -13,10 +13,7 @@ {% endif %} - {# - - - + {# #} diff --git a/files/templates/component/post/actions_mobile.html b/files/templates/component/post/actions_mobile.html index 3e86905d3..2c49d4add 100644 --- a/files/templates/component/post/actions_mobile.html +++ b/files/templates/component/post/actions_mobile.html @@ -31,15 +31,9 @@ {% endif %} - - - - {# - #} - {% else %} diff --git a/files/templates/home.html b/files/templates/home.html index 00ac43b95..12915f406 100644 --- a/files/templates/home.html +++ b/files/templates/home.html @@ -54,16 +54,15 @@ {{t | capitalize}} - {% set ccmode_text = 'ccmode=' ~ ccmode %} - {{sorting_time.sort_dropdown(sort, t, SORTS_POSTS, ccmode_text)}} + {{sorting_time.sort_dropdown(sort, t, SORTS_POSTS)}} {% endblock %} @@ -87,14 +86,14 @@
  • Content
  • Engagement @@ -155,6 +157,15 @@ what I think".

    +

    Keep to a single account.

    +

    + We strongly discourage people from making alt accounts without good reason, and in the absence of a good reason, we consider alt accounts to be bannable on sight. Alt accounts are almost exclusively used for mod evasion purposes and very rarely used for any purpose that helps the community; it makes moderation more difficult and it makes conversation more difficult. +

    + If you do feel you need an alt account (most commonly, if you're a well-established user who wants to post something that can't be linked to their public persona), please ask the mods. +

    + If you don't want the mods to know about it either, be aware that there's a good chance we'll find out about it anyway. +

    +
     
                     
    @@ -167,7 +178,7 @@ invest effort if they want to post things that have traditionally been pain points.

    -

    Avoid low-effort participation

    +

    Avoid low-effort participation.

    Discussing things is hard. Discussing things in a useful way, in an environment with opposing views, is really hard. Doing all of this while responding to three-word shitposts is basically impossible.

    @@ -177,7 +188,7 @@ in encouraging that.

    -

    Do not weakman in order to show how bad a group is

    +

    Do not weakman in order to show how bad a group is.

    There are literally millions of people on either side of every major conflict, and finding that one of them is doing something wrong or thoughtless proves nothing and adds nothing to the conversation. We want to engage with @@ -203,7 +214,7 @@ discussion), a news story which is about tweets from non-prominent people reacting to some event isn't ok.

    -

    Keep culture war in the culture war thread

    +

    Keep culture war in the culture war thread.

    "Culture war" is hard to define, but here's a list of things that currently fall in that category:

      @@ -241,7 +252,7 @@ is inconvenient, but that's intended.

      -

      Leave the rest of the internet at the door

      +

      Leave the rest of the internet at the door.

      In keeping with the rules above regarding "low-effort" and "weak-man" comments, and our goal to produce more light than heat, we ask that you refrain from posting bare links to culture-war-related discussions held outside @@ -254,6 +265,17 @@ may be made for communities specifically designed for compatible content, but these will be examined by the moderators on a case by case basis. If in doubt, please ask first by messaging the moderators.

      + +

      Post on multiple subjects.

      +

      + We occasionally have trouble with people who turn into single-issue posters, posting and commenting only on a single subject. We'd like to discourage this. If you find yourself posting constantly on a single subject, please make an effort to post on other subjects as well. +

      + + This doesn't mean you need to write megaposts! This can be as simple as going to the Friday Fun Thread once in a while and posting a few paragraphs about whatever video game you last played. But this community is fundamentally for people, and if a poster is acting more like a propaganda-bot than a person, we're going to start looking at them suspiciously. +

      + + This rule is going to be applied with delicacy; if I can find not-low-effort comments about three different subjects within your last two weeks or two pages of comments, you're likely fine. +

       
      diff --git a/files/templates/sidebar_TheMotte.html b/files/templates/sidebar_TheMotte.html
      index ffbff109b..e26c346bd 100644
      --- a/files/templates/sidebar_TheMotte.html
      +++ b/files/templates/sidebar_TheMotte.html
      @@ -61,14 +61,16 @@
       					
    • Make your point reasonably clear and plain. Try to assume other people are doing the same.
    • Be no more antagonistic than is absolutely necessary for your argument.
    • Be charitable.
    • +
    • Keep to a single account.
  • Content
  • Engagement diff --git a/files/templates/submission.html b/files/templates/submission.html index 5e4a997bc..0dd1da370 100644 --- a/files/templates/submission.html +++ b/files/templates/submission.html @@ -127,9 +127,6 @@ {% endblock %} {% block content %} - -{% set cc='COUNTRY CLUB' %} -
    @@ -199,13 +196,11 @@ {% endif %} {% if p.realurl(v) %}

    - {% if p.club %}{{CC}}{% endif %} {% if p.flair %}{{p.flair | safe}}{% endif %} {{p.realtitle(v) | safe}}

    {% else %}

    - {% if p.club %}{{CC}}{% endif %} {% if p.flair %}{{p.flair | safe}}{% endif %} {{p.realtitle(v) | safe}}

    diff --git a/files/templates/submission_listing.html b/files/templates/submission_listing.html index e404e5078..6a697083b 100644 --- a/files/templates/submission_listing.html +++ b/files/templates/submission_listing.html @@ -1,5 +1,3 @@ -{% set cc='COUNTRY CLUB' %} - {% if not v or v.highlightcomments %} {% endif %} @@ -148,7 +146,6 @@ diff --git a/files/templates/userpage.html b/files/templates/userpage.html index 3fedb9301..f7889fbbe 100644 --- a/files/templates/userpage.html +++ b/files/templates/userpage.html @@ -246,9 +246,6 @@ -
    
    -						
    -						
     						{% endif %}
     					{% if v and v.admin_level >= PERMS['USER_SET_PROFILE_PRIVACY'] %}
     						Set Private Mode
    @@ -425,15 +422,8 @@
     					
    {% if v and v.admin_level >= 2 %} - - - - -

    -
    -
    {{forms.formkey(v)}} @@ -447,7 +437,6 @@
    -
    
     						{% if u.is_suspended %}
    diff --git a/files/tests/test_child_comment_counts.py b/files/tests/test_child_comment_counts.py
    index f19878842..fd0b57da7 100644
    --- a/files/tests/test_child_comment_counts.py
    +++ b/files/tests/test_child_comment_counts.py
    @@ -192,7 +192,6 @@ def test_bulk_update_descendant_count_quick(accounts, submissions, comments):
     		for i in range(2):
     			post = Submission(**{
     				'private': False,
    -				'club': None,
     				'author_id': alice.id,
     				'over_18': False,
     				'app_id': None,
    diff --git a/migrations/versions/2023_08_08_14_36_38_d08833c2adc7_excise_country_club.py b/migrations/versions/2023_08_08_14_36_38_d08833c2adc7_excise_country_club.py
    new file mode 100644
    index 000000000..7e8f8b188
    --- /dev/null
    +++ b/migrations/versions/2023_08_08_14_36_38_d08833c2adc7_excise_country_club.py
    @@ -0,0 +1,27 @@
    +"""excise country club
    +
    +Revision ID: d08833c2adc7
    +Revises: 85dad82a4a67
    +Create Date: 2023-08-08 14:36:38.797624+00:00
    +
    +"""
    +from alembic import op
    +import sqlalchemy as sa
    +
    +
    +# revision identifiers, used by Alembic.
    +revision = 'd08833c2adc7'
    +down_revision = '85dad82a4a67'
    +branch_labels = None
    +depends_on = None
    +
    +
    +def upgrade():
    +	op.drop_column('submissions', 'club')
    +	op.drop_column('users', 'club_allowed')
    +
    +
    +def downgrade():
    +	op.add_column('users', sa.Column('club_allowed', sa.BOOLEAN(), autoincrement=False, nullable=True))
    +	op.add_column('submissions', sa.Column('club', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))
    +	op.add_column('commentvotes', sa.Column('created_utc', sa.INTEGER(), autoincrement=False, nullable=False))