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 %}
+
+{% 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 @@
+
+
+
{% 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 %}
+
+{% 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 %}