From 0311586a1b150ff3ac0d873563732e6bba983304 Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Mon, 7 Jul 2025 13:40:15 +0200 Subject: [PATCH 1/6] implement delete post --- CHANGELOG.md | 1 + alembic/versions/6d418df3c72f_.py | 34 +++++++++++++++++++++++++++++++ freak/models.py | 3 +-- freak/templates/singledelete.html | 27 ++++++++++++++++++++++++ freak/website/__init__.py | 3 +++ freak/website/delete.py | 31 ++++++++++++++++++++++++++++ freak/website/edit.py | 2 +- 7 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 alembic/versions/6d418df3c72f_.py create mode 100644 freak/templates/singledelete.html create mode 100644 freak/website/delete.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e5bc9eb..10f4493 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Added dependency to [SUOU](https://github.com/sakuragasaki46/suou) library - Added user blocks - Added user strikes: a strike logs the content of a removed message for future use +- Posts may now be deleted by author. If it has comments, comments are not spared - Implemented guild subscriptions + Blocking a user prevents them from seeing your comments, posts (standalone or in feed) and profile - Added ✨color themes✨ diff --git a/alembic/versions/6d418df3c72f_.py b/alembic/versions/6d418df3c72f_.py new file mode 100644 index 0000000..68c2f56 --- /dev/null +++ b/alembic/versions/6d418df3c72f_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: 6d418df3c72f +Revises: 90c7d0098efe +Create Date: 2025-07-07 13:37:51.667620 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '6d418df3c72f' +down_revision: Union[str, None] = '90c7d0098efe' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint('comment_parent_post_id_fkey', 'freak_comment', type_='foreignkey') + op.create_foreign_key('comment_parent_post_id', 'freak_comment', 'freak_post', ['parent_post_id'], ['id'], ondelete='cascade') + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint('comment_parent_post_id', 'freak_comment', type_='foreignkey') + op.create_foreign_key('comment_parent_post_id_fkey', 'freak_comment', 'freak_post', ['parent_post_id'], ['id']) + # ### end Alembic commands ### diff --git a/freak/models.py b/freak/models.py index 009bff7..a478595 100644 --- a/freak/models.py +++ b/freak/models.py @@ -245,7 +245,6 @@ class User(Base): target_id = target qq= ~select(UserBlock).where(UserBlock.c.actor_id == actor_id, UserBlock.c.target_id == target_id).exists() - print(qq) return qq def recompute_karma(self): @@ -437,7 +436,7 @@ class Comment(Base): id = snowflake_column() author_id = Column(BigInteger, ForeignKey('freak_user.id', name='comment_author_id'), nullable=True) - parent_post_id = Column(BigInteger, ForeignKey('freak_post.id', name='comment_parent_post_id'), nullable=False) + parent_post_id = Column(BigInteger, ForeignKey('freak_post.id', name='comment_parent_post_id', ondelete='cascade'), nullable=False) parent_comment_id = Column(BigInteger, ForeignKey('freak_comment.id', name='comment_parent_comment_id'), nullable=True) text_content = Column(String(16384), nullable=False) created_at = Column(DateTime, server_default=func.current_timestamp(), index=True) diff --git a/freak/templates/singledelete.html b/freak/templates/singledelete.html new file mode 100644 index 0000000..c60fb68 --- /dev/null +++ b/freak/templates/singledelete.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} +{% from "macros/title.html" import title_tag with context %} +{% from "macros/icon.html" import icon, callout with context %} + +{% block title %}{{ title_tag('Confirm deletion: ' + p.title, False) }}{% endblock %} + +{% block heading %} +

Confirm deletion: {{ p.title }}

+{% endblock %} + +{% block content %} +
+
+ +
+

You are about to delete permanently your post on {{ p.topic_or_user().handle() }}.

+ {% call callout('spoiler', 'error') %}This action cannot be undone.{% endcall %} + {% if (p.comments | count) %} + {% call callout('spoiler') %}Your post has {{ (p.comments | count) }} comments. Your post will be deleted along with ALL the comments.{% endcall %} + {% endif %} +
+
+ +
+
+
+{% endblock %} \ No newline at end of file diff --git a/freak/website/__init__.py b/freak/website/__init__.py index 5d19801..cc1aed7 100644 --- a/freak/website/__init__.py +++ b/freak/website/__init__.py @@ -17,6 +17,9 @@ blueprints.append(bp) from .edit import bp blueprints.append(bp) +from .delete import bp +blueprints.append(bp) + from .about import bp blueprints.append(bp) diff --git a/freak/website/delete.py b/freak/website/delete.py new file mode 100644 index 0000000..afbf5fa --- /dev/null +++ b/freak/website/delete.py @@ -0,0 +1,31 @@ + + +from flask import Blueprint, abort, flash, redirect, render_template, request +from flask_login import current_user, login_required +from sqlalchemy import delete, select + +from ..models import Post, db + + +bp = Blueprint('delete', __name__) + + +@bp.route('/delete/post/', methods=['GET', 'POST']) +@login_required +def delete_post(id: int): + p = db.session.execute(select(Post).where(Post.id == id, Post.author == current_user)).scalar() + + if p is None: + abort(404) + if p.author != current_user: + abort(403) + + pt = p.topic_or_user() + + if request.method == 'POST': + db.session.execute(delete(Post).where(Post.id == id, Post.author == current_user)) + db.session.commit() + flash('Your post has been deleted') + return redirect(pt.url()), 303 + + return render_template('singledelete.html', p=p) \ No newline at end of file diff --git a/freak/website/edit.py b/freak/website/edit.py index cfdf0be..3547b9f 100644 --- a/freak/website/edit.py +++ b/freak/website/edit.py @@ -14,7 +14,7 @@ bp = Blueprint('edit', __name__) @bp.route('/edit/post/', methods=['GET', 'POST']) @login_required def edit_post(id): - p: Post | None = db.session.execute(select(Post).where(Post.id == id)).scalar() + p: Post | None = db.session.execute(select(Post).where(Post.id == id, Post.author == current_user)).scalar() if p is None: abort(404) From 299c29869c7d2c1b5a7ee4e542e13a697bff5904 Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Mon, 7 Jul 2025 13:46:26 +0200 Subject: [PATCH 2/6] fix migration artifacts --- alembic/versions/6d418df3c72f_.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alembic/versions/6d418df3c72f_.py b/alembic/versions/6d418df3c72f_.py index 68c2f56..ccb6063 100644 --- a/alembic/versions/6d418df3c72f_.py +++ b/alembic/versions/6d418df3c72f_.py @@ -21,7 +21,7 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: """Upgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('comment_parent_post_id_fkey', 'freak_comment', type_='foreignkey') + op.drop_constraint('comment_parent_post_id', 'freak_comment', type_='foreignkey') op.create_foreign_key('comment_parent_post_id', 'freak_comment', 'freak_post', ['parent_post_id'], ['id'], ondelete='cascade') # ### end Alembic commands ### @@ -30,5 +30,5 @@ def downgrade() -> None: """Downgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### op.drop_constraint('comment_parent_post_id', 'freak_comment', type_='foreignkey') - op.create_foreign_key('comment_parent_post_id_fkey', 'freak_comment', 'freak_post', ['parent_post_id'], ['id']) + op.create_foreign_key('comment_parent_post_id', 'freak_comment', 'freak_post', ['parent_post_id'], ['id']) # ### end Alembic commands ### From 7d8b518c85100edf18c255b071f2455c0a7dcbe4 Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Mon, 7 Jul 2025 14:02:45 +0200 Subject: [PATCH 3/6] add user list to admin panel --- freak/__init__.py | 2 +- freak/templates/admin/admin_home.html | 3 +++ freak/templates/admin/admin_users.html | 29 ++++++++++++++++++++++++++ freak/website/admin.py | 8 +++++++ 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 freak/templates/admin/admin_users.html diff --git a/freak/__init__.py b/freak/__init__.py index 44416e0..3e6b177 100644 --- a/freak/__init__.py +++ b/freak/__init__.py @@ -22,7 +22,7 @@ from suou.configparse import ConfigOptions, ConfigValue from freak.colors import color_themes, theme_classes -__version__ = '0.4.0-dev24' +__version__ = '0.4.0-dev27' APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__)) diff --git a/freak/templates/admin/admin_home.html b/freak/templates/admin/admin_home.html index bdaa7ae..6634f0b 100644 --- a/freak/templates/admin/admin_home.html +++ b/freak/templates/admin/admin_home.html @@ -8,5 +8,8 @@
  • Strikes

  • +
  • +

    Users

    +
  • {% endblock %} \ No newline at end of file diff --git a/freak/templates/admin/admin_users.html b/freak/templates/admin/admin_users.html new file mode 100644 index 0000000..3ec789d --- /dev/null +++ b/freak/templates/admin/admin_users.html @@ -0,0 +1,29 @@ +{% extends "admin/admin_base.html" %} +{% from "macros/feed.html" import stop_scrolling, no_more_scrolling with context %} + +{% block content %} +
      + {% for u in user_list %} +
    • +

      {{ u.handle() }} (#{{ u.id | to_b32l }}) + {%- if u.is_administrator %} + (Admin) + {% endif -%} + {% if u == current_user %} + (You) + {% endif -%} +

      +
        +
      • Age: {{ u.age() }} years old ({{ u.gdpr_birthday.strftime("%B %d, %Y") }})
      • +
      • Registered at: {{ u.joined_at }}
      • +
      • Registered from IP address: {{ u.joined_ip }}
      • +
      +
    • + {% endfor %} + {% if user_list.has_next %} + {{ stop_scrolling(user_list.page) }} + {% else %} + {{ no_more_scrolling(user_list.page) }} + {% endif %} +
    +{% endblock %} \ No newline at end of file diff --git a/freak/website/admin.py b/freak/website/admin.py index bc9851e..04b1806 100644 --- a/freak/website/admin.py +++ b/freak/website/admin.py @@ -146,3 +146,11 @@ def strikes(): strike_list = db.paginate(select(UserStrike).order_by(UserStrike.id.desc())) return render_template('admin/admin_strikes.html', strike_list=strike_list, report_reasons=REPORT_REASON_STRINGS) + + +@bp.route('/admin/users/') +@admin_required +def users(): + user_list = db.paginate(select(User).order_by(User.joined_at.desc())) + return render_template('admin/admin_users.html', + user_list=user_list) From 6935a6ae71ca65b790f9946cd2c351834a019831 Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Mon, 7 Jul 2025 14:42:52 +0200 Subject: [PATCH 4/6] add account status to user list --- freak/filters.py | 6 ++++++ freak/templates/admin/admin_users.html | 3 ++- freak/website/admin.py | 17 ++++++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/freak/filters.py b/freak/filters.py index df56edd..f085ef9 100644 --- a/freak/filters.py +++ b/freak/filters.py @@ -40,3 +40,9 @@ def append(text, l: list): l.append(text) return None +@app.template_filter() +def faint_paren(text: str): + if not '(' in text: + return text + t1, t2, t3 = text.partition('(') + return Markup('{0} {1}').format(t1, t2 + t3) \ No newline at end of file diff --git a/freak/templates/admin/admin_users.html b/freak/templates/admin/admin_users.html index 3ec789d..58991fc 100644 --- a/freak/templates/admin/admin_users.html +++ b/freak/templates/admin/admin_users.html @@ -15,8 +15,9 @@

    • Age: {{ u.age() }} years old ({{ u.gdpr_birthday.strftime("%B %d, %Y") }})
    • -
    • Registered at: {{ u.joined_at }}
    • +
    • Registered at: {{ u.joined_at.strftime("%B %d, %Y %H:%M %z") }}
    • Registered from IP address: {{ u.joined_ip }}
    • +
    • Status: {{ account_status_string(u) | faint_paren }}
    {% endfor %} diff --git a/freak/website/admin.py b/freak/website/admin.py index 04b1806..2cfe919 100644 --- a/freak/website/admin.py +++ b/freak/website/admin.py @@ -30,6 +30,21 @@ TARGET_TYPES = { Comment: REPORT_TARGET_COMMENT } +def account_status_string(u: User): + if u.is_active: + return 'Active' + elif u.banned_at: + s = 'Suspended' + if u.banned_until: + s += f' until {u.banned_until:%b %d, %Y %H:%M}' + if u.banned_reason in REPORT_REASON_STRINGS: + s += f' ({REPORT_REASON_STRINGS[u.banned_reason]})' + return s + elif u.is_disabled_by_user: + return 'Paused' + else: + return 'Inactive' + def remove_content(target, reason_code: int): if isinstance(target, Post): target.removed_at = datetime.datetime.now() @@ -153,4 +168,4 @@ def strikes(): def users(): user_list = db.paginate(select(User).order_by(User.joined_at.desc())) return render_template('admin/admin_users.html', - user_list=user_list) + user_list=user_list, account_status_string=account_status_string) From 66471558b91aa516fa0c0d2e287d070144ff3cc7 Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Mon, 7 Jul 2025 14:56:24 +0200 Subject: [PATCH 5/6] apply ProxyFix --- freak/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/freak/__init__.py b/freak/__init__.py index 3e6b177..ba98759 100644 --- a/freak/__init__.py +++ b/freak/__init__.py @@ -17,6 +17,7 @@ from sqlalchemy.exc import SQLAlchemyError from suou import Snowflake, ssv_list from werkzeug.routing import BaseConverter from sassutils.wsgi import SassMiddleware +from werkzeug.middleware.proxy_fix import ProxyFix from suou.configparse import ConfigOptions, ConfigValue @@ -36,6 +37,7 @@ class AppConfig(ConfigOptions): domain_name = ConfigValue() private_assets = ConfigValue(cast=ssv_list) jquery_url = ConfigValue(default='https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js') + app_is_behind_proxy = ConfigValue(cast=bool, default=False) app_config = AppConfig() @@ -51,6 +53,12 @@ app.wsgi_app = SassMiddleware(app.wsgi_app, dict( freak=('static/sass', 'static/css', '/static/css', True) )) +# proxy fix +if app_config.app_is_behind_proxy: + app.wsgi_app = ProxyFix( + app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1 + ) + class SlugConverter(BaseConverter): regex = r'[a-z0-9]+(?:-[a-z0-9]+)*' From 793c0b6612d9dfcdf6207e64a3c3c50eb6b226e2 Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Mon, 7 Jul 2025 16:47:52 +0200 Subject: [PATCH 6/6] mobile style improvements, block registration with date unset --- freak/static/sass/layout.sass | 6 ++-- freak/static/sass/mobile.sass | 55 +++++++++++++++++++++++++++++++++-- freak/templates/base.html | 15 ++++++++-- freak/website/accounts.py | 5 ++++ 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/freak/static/sass/layout.sass b/freak/static/sass/layout.sass index 9dbd8c9..c042424 100644 --- a/freak/static/sass/layout.sass +++ b/freak/static/sass/layout.sass @@ -1,7 +1,9 @@ @import "constants.sass" - +body + margin: 0 + .content-container display: flex flex-direction: row-reverse @@ -18,6 +20,7 @@ main min-height: 70vh + margin: 12px auto // __ header styles __ // @@ -28,7 +31,6 @@ header.header overflow: hidden height: 3em padding: .75em 1.5em - margin: -12px line-height: 1 h1 margin: 0 diff --git a/freak/static/sass/mobile.sass b/freak/static/sass/mobile.sass index 2ed1831..4d45ce0 100644 --- a/freak/static/sass/mobile.sass +++ b/freak/static/sass/mobile.sass @@ -10,7 +10,58 @@ grid-template-columns: 1fr 1fr .nomobile - display: none + display: none !important + + body + position: relative + + footer.mobile-nav + position: sticky + bottom: 0 + left: 0 + width: 100% + overflow: hidden + margin: 0 + padding: 0 + background-color: var(--background) + box-shadow: 0 0 6px var(--border) + z-index: 150 + + > ul + display: flex + list-style: none + margin: 0 + padding: 0 + flex-direction: row + align-items: stretch + justify-content: stretch + > li + flex: 1 + padding: .5em + margin: 0 + text-align: center + a + text-decoration: none + .icon + font-size: 2rem + + .content-nav + margin: 1em + width: unset + + header.header h1 + margin-top: 4px + margin-left: 6px + + .content-header + text-align: center + + .big-search-bar form + flex-direction: column + + [type="submit"] + width: unset + margin: 12px auto @media screen and (max-width: 960px) .header-username @@ -33,4 +84,4 @@ @media screen and (min-width: 801px) .mobileonly - display: none \ No newline at end of file + display: none !important \ No newline at end of file diff --git a/freak/templates/base.html b/freak/templates/base.html index 579cee4..7a97685 100644 --- a/freak/templates/base.html +++ b/freak/templates/base.html @@ -46,11 +46,11 @@ {% if g.no_user %} {% elif current_user.is_authenticated %} -
  • +
  • create - New post + New post
  • {{ icon('profile')}}profile @@ -99,6 +99,17 @@
  • GitHub
  • + {% if current_user and current_user.is_authenticated %} + + {% endif %}