diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b89685..40a4377 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ + Allowed operations: change display name, description, restriction status, and exile (guild-local ban) members + Site administrators and guild owners can add moderators - Administrators can claim ownership of abandoned guilds +- Guilds can have restricted posting/commenting now. Unmoderated guilds always have. - Implemented guild subscriptions (not as in $$$, yes as in the follow button) - Minimum karma requirement for creating a guild is now configurable via env variable `FREAK_CREATE_GUILD_THRESHOLD` (previously hardcoded at 15) - Users can now set their display name, biography and color theme in `/settings` diff --git a/freak/ajax.py b/freak/ajax.py index 5fe2d04..00107ed 100644 --- a/freak/ajax.py +++ b/freak/ajax.py @@ -33,9 +33,9 @@ def username_availability(username: str): 'is_available': is_available } -@bp.route('/guild_name_availability/') +@bp.route('/guild_name_availability/') def guild_name_availability(name: str): - is_valid = re.fullmatch('[a-z0-9_-]+', name) is not None + is_valid = username_is_legal(name) if is_valid: gd = db.session.execute(select(Guild).where(Guild.name == name)).scalar() diff --git a/freak/models.py b/freak/models.py index ab0e6b3..6e68213 100644 --- a/freak/models.py +++ b/freak/models.py @@ -42,9 +42,10 @@ post_report_reasons = [ ReportReason(180, 'impersonation', 'Impersonation'), ReportReason(141, 'doxing', 'Diffusion of PII (personally identifiable information)'), ## less urgent - ReportReason(140, 'bullying', 'Harassment, bullying, or suicide incitement'), ReportReason(112, 'lgbt_hate', 'Hate speech against LGBTQ+ or women'), + ReportReason(140, 'bullying', 'Harassment, bullying, or suicide incitement'), ReportReason(150, 'security_exploit', 'Dangerous security exploit or violation'), + ReportReason(160, 'spam', 'Unsolicited advertising'), ReportReason(190, 'false_information', 'False or deceiving information'), ReportReason(123, 'copyviol', 'This is my creation and someone else is using it without my permission/license'), ## minor (unironically) @@ -168,7 +169,7 @@ class User(Base): ## XXX posts and comments relationships are temporarily disabled because they make ## SQLAlchemy fail initialization of models — bricking the app. ## Posts are queried manually anyway - #posts = relationship("Post", back_populates='author', ) + #posts = relationship("Post", primaryjoin=lambda: #back_populates='author', pr) upvoted_posts = relationship("Post", secondary=PostUpvote, back_populates='upvoters') #comments = relationship("Comment", back_populates='author') @@ -364,8 +365,10 @@ class Guild(Base): mem: Member | None = db.session.execute(select(Member).where(Member.user_id == other.id, Member.guild_id == self.id)).scalar() if other else None if mem and mem.is_banned: return False + if other.moderates(self): + return True if self.is_restricted: - return mem and mem.is_approved + return (mem and mem.is_approved) return True @@ -447,7 +450,7 @@ class Post(Base): title = Column(String(256), nullable=False) post_type = Column(SmallInteger, server_default=text('0')) author_id = Column(BigInteger, ForeignKey('freak_user.id', name='post_author_id'), nullable=True) - topic_id = Column(BigInteger, ForeignKey('freak_topic.id', name='post_topic_id'), nullable=True) + topic_id = Column('topic_id', BigInteger, ForeignKey('freak_topic.id', name='post_topic_id'), nullable=True) created_at = Column(DateTime, server_default=func.current_timestamp()) created_ip = Column(String(64), default=get_remote_addr, nullable=False) updated_at = Column(DateTime, nullable=True) @@ -465,7 +468,7 @@ class Post(Base): # utilities author: Relationship[User] = relationship("User", lazy='selectin', foreign_keys=[author_id])#, back_populates="posts") - guild = relationship("Guild", back_populates="posts", lazy='selectin') + guild: Relationship[Guild] = relationship("Guild", back_populates="posts", lazy='selectin') comments = relationship("Comment", back_populates="parent_post") upvoters = relationship("User", secondary=PostUpvote, back_populates='upvoted_posts') diff --git a/freak/static/js/lib.js b/freak/static/js/lib.js index 0e6f824..450d97c 100644 --- a/freak/static/js/lib.js +++ b/freak/static/js/lib.js @@ -27,21 +27,30 @@ usernameInputMessage.className = 'username-input-message error'; return; } + if (value.length >= 100) { + usernameInputMessage.innerHTML = 'Your username must be shorter.'; + usernameInputMessage.className = 'username-input-message error'; + return; + } if(/^[01]/.test(value)) { usernameInputMessage.innerHTML = 'Your username cannot start with 0 or 1.'; usernameInputMessage.className = 'username-input-message error'; return; } usernameInputMessage.innerHTML = 'Checking username...'; - usernameInputMessage.className = 'username-input-message checking'; + usernameInputMessage.className = 'username-input-message checking faint'; requestUsernameAvailability(value, endpoint).then((resp) => { if (['ok', void 0].indexOf(resp.status) < 0){ usernameInputMessage.innerHTML = 'Sorry, there was an unknown error.'; usernameInputMessage.className = 'username-input-message error'; return; } - if (resp.is_available){ - usernameInputMessage.innerHTML = "The username @" + value + " is available!"; + if (!resp.is_legal) { + usernameInputMessage.innerHTML = "You can't use this username."; + usernameInputMessage.className = 'username-input-message error'; + return; + } else if (resp.is_available){ + usernameInputMessage.innerHTML = `The username @${value} is available!`; usernameInputMessage.className = 'username-input-message success'; return; } else { diff --git a/freak/templates/base.html b/freak/templates/base.html index 9009c1f..4833a27 100644 --- a/freak/templates/base.html +++ b/freak/templates/base.html @@ -47,7 +47,7 @@ {% elif current_user.is_authenticated %}
  • - + {{ icon('add') }} New post diff --git a/freak/templates/createguild.html b/freak/templates/createguild.html index b004482..8c03c7c 100644 --- a/freak/templates/createguild.html +++ b/freak/templates/createguild.html @@ -16,7 +16,7 @@
    -

    URL of the guild: +

    +

    URL of the guild: +

    Must be alphanumeric and unique. May not be changed later: choose wisely!

    diff --git a/freak/templates/macros/create.html b/freak/templates/macros/create.html index 6a3a609..360568f 100644 --- a/freak/templates/macros/create.html +++ b/freak/templates/macros/create.html @@ -22,9 +22,16 @@ disabled="" {% endmacro %} -{% macro comment_area(url) %} +{% macro comment_area(p) %} {% if current_user.is_authenticated %} - +{% if current_user.is_disabled %} +
    Your account is suspended
    +{% elif current_guild and not current_guild.allows_posting(current_user) %} +
    This community allows only its members to post and comment
    +{% elif p.is_locked %} +
    Comments are closed
    +{% else %} +
    @@ -35,6 +42,7 @@ disabled=""
    +{% endif %} {% else %}
    Log in to leave a comment
    {% endif %} diff --git a/freak/templates/singlepost.html b/freak/templates/singlepost.html index a4f5955..d6947ef 100644 --- a/freak/templates/singlepost.html +++ b/freak/templates/singlepost.html @@ -66,7 +66,7 @@
    - {{ comment_area(p.url()) }} + {{ comment_area(p) }}
      {% for comment in comments %} diff --git a/freak/website/detail.py b/freak/website/detail.py index 3de15da..e3d11d1 100644 --- a/freak/website/detail.py +++ b/freak/website/detail.py @@ -40,6 +40,20 @@ def user_profile_s(username): def single_post_post_hook(p: Post): + if p.guild is not None: + gu = p.guild + if gu.has_exiled(current_user): + flash(f'You have been banned from {gu.handle()}') + return + + if not gu.allows_posting(current_user): + flash(f'You can\'t post in {gu.handle()}') + return + + if p.is_locked: + flash(f'You can\'t comment on locked posts') + return + if 'reply_to' in request.form: reply_to_id = request.form['reply_to'] text = request.form['text'] @@ -100,7 +114,7 @@ def guild_post_detail(gname, id, slug=''): if request.method == 'POST': single_post_post_hook(post) - return render_template('singlepost.html', p=post, comments=comments_of(post)) + return render_template('singlepost.html', p=post, comments=comments_of(post), current_guild = post.guild) diff --git a/freak/website/frontpage.py b/freak/website/frontpage.py index 0b103fb..acf968c 100644 --- a/freak/website/frontpage.py +++ b/freak/website/frontpage.py @@ -41,7 +41,8 @@ def guild_feed(name): posts = db.paginate(topic_timeline(name)) return render_template( - 'feed.html', feed_type='guild', feed_title=f'{guild.display_name} (+{guild.name})', l=posts, guild=guild) + 'feed.html', feed_type='guild', feed_title=f'{guild.display_name} (+{guild.name})', l=posts, guild=guild, + current_guild=guild) @bp.route('/r//') def guild_feed_r(name):