From 1550d9bb70849914b560d94e7eb2c7b8b4553da6 Mon Sep 17 00:00:00 2001 From: Hendrik L Date: Mon, 9 Dec 2024 22:52:38 +0100 Subject: [PATCH 01/30] render user note --- app/models.py | 9 ++++++++- app/templates/base.html | 6 ++++++ app/templates/user/show_profile.html | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/models.py b/app/models.py index 0e0a410d..bdb596ba 100644 --- a/app/models.py +++ b/app/models.py @@ -1022,6 +1022,7 @@ class User(UserMixin, db.Model): db.session.query(PostBookmark).filter(PostBookmark.user_id == self.id).delete() db.session.query(PostReplyBookmark).filter(PostReplyBookmark.user_id == self.id).delete() db.session.query(ModLog).filter(ModLog.user_id == self.id).delete() + db.session.query(UserNote).filter(or_(UserNote.user_id == self.id, UserNote.target_id == self.id)).delete() def purge_content(self, soft=True): files = File.query.join(Post).filter(Post.user_id == self.id).all() @@ -1077,7 +1078,13 @@ class User(UserMixin, db.Model): # returns true if the post has been read, false if not def has_read_post(self, post): return self.read_post.filter(read_posts.c.read_post_id == post.id).count() > 0 - + + def get_note(self, by_user): + user_note = UserNote.query.filter(UserNote.target_id == self.id, UserNote.user_id == by_user.id).first() + if user_note: + return user_note.body + else: + return None class ActivityLog(db.Model): diff --git a/app/templates/base.html b/app/templates/base.html index 718d1d83..dff76458 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -27,6 +27,12 @@ {% endif -%} {% endif -%} + {% if current_user.is_authenticated -%} + {% set user_note = user.get_note(current_user) %} + {% if user_note -%} + [{{ user_note | truncate(12, True) }}] + {% endif -%} + {% endif -%} {% endif -%} {% endmacro -%} diff --git a/app/templates/user/show_profile.html b/app/templates/user/show_profile.html index 0b76b8da..5ef64f2f 100644 --- a/app/templates/user/show_profile.html +++ b/app/templates/user/show_profile.html @@ -116,6 +116,7 @@ {% if current_user.is_authenticated and current_user.is_admin() and user.reputation %}{{ _('Reputation') }}: {{ user.reputation | round | int }}
{% endif %} {{ _('Posts') }}: {{ user.post_count }}
{{ _('Comments') }}: {{ user.post_reply_count }}
+ {% if current_user.is_authenticated %}{{ _('User note') }}: {{ user.get_note(current_user) }}
{% endif %}

{{ user.about_html|safe }} From 148df4fcbaf193e7aefdac9b205ee51ad44f70eb Mon Sep 17 00:00:00 2001 From: Hendrik L Date: Tue, 10 Dec 2024 09:56:30 +0100 Subject: [PATCH 02/30] edit form for user note --- app/templates/user/edit_note.html | 69 ++++++++++++++++++++++++++++ app/templates/user/show_profile.html | 1 + app/user/forms.py | 5 ++ app/user/routes.py | 38 ++++++++++++++- 4 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 app/templates/user/edit_note.html diff --git a/app/templates/user/edit_note.html b/app/templates/user/edit_note.html new file mode 100644 index 00000000..6ab886a6 --- /dev/null +++ b/app/templates/user/edit_note.html @@ -0,0 +1,69 @@ +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') -%} + {% extends 'themes/' + theme() + '/base.html' %} +{% else -%} + {% extends "base.html" %} +{% endif -%} +{% from 'bootstrap/form.html' import render_form %} + +{% block app_content %} +
+ +
+ +{% endblock %} diff --git a/app/templates/user/show_profile.html b/app/templates/user/show_profile.html index 5ef64f2f..f50865dd 100644 --- a/app/templates/user/show_profile.html +++ b/app/templates/user/show_profile.html @@ -100,6 +100,7 @@ {% endif -%} {% endif -%}
  • {{ _('Report') }}
  • +
  • {{ _('Edit note') }}
  • {% endif %} diff --git a/app/user/forms.py b/app/user/forms.py index dc702ad2..e17407ce 100644 --- a/app/user/forms.py +++ b/app/user/forms.py @@ -147,3 +147,8 @@ class RemoteFollowForm(FlaskForm): instance_type = SelectField(_l('Instance type'), choices=type_choices, render_kw={'class': 'form-select'}) submit = SubmitField(_l('View profile on remote instance')) + + +class UserNoteForm(FlaskForm): + note = StringField(_l('User note'), validators=[Optional(), Length(max=50)]) + submit = SubmitField(_l('Save note')) diff --git a/app/user/routes.py b/app/user/routes.py index 5bc30c38..12a6e7a9 100644 --- a/app/user/routes.py +++ b/app/user/routes.py @@ -16,10 +16,10 @@ from app.constants import * from app.email import send_verification_email from app.models import Post, Community, CommunityMember, User, PostReply, PostVote, Notification, utcnow, File, Site, \ Instance, Report, UserBlock, CommunityBan, CommunityJoinRequest, CommunityBlock, Filter, Domain, DomainBlock, \ - InstanceBlock, NotificationSubscription, PostBookmark, PostReplyBookmark, read_posts, Topic + InstanceBlock, NotificationSubscription, PostBookmark, PostReplyBookmark, read_posts, Topic, UserNote from app.user import bp from app.user.forms import ProfileForm, SettingsForm, DeleteAccountForm, ReportUserForm, \ - FilterForm, KeywordFilterEditForm, RemoteFollowForm, ImportExportForm + FilterForm, KeywordFilterEditForm, RemoteFollowForm, ImportExportForm, UserNoteForm from app.user.utils import purge_user_then_delete, unsubscribe_from_community from app.utils import get_setting, render_template, markdown_to_html, user_access, markdown_to_text, shorten_string, \ is_image_url, ensure_directory_exists, gibberish, file_get_contents, community_membership, user_filters_home, \ @@ -1330,3 +1330,37 @@ def user_read_posts_delete(): db.session.commit() flash(_('Reading history has been deleted')) return redirect(url_for('user.user_read_posts')) + + +@bp.route('/u//note', methods=['GET', 'POST']) +@login_required +def edit_user_note(actor): + actor = actor.strip() + if '@' in actor: + user: User = User.query.filter_by(ap_id=actor, deleted=False, banned=False).first() + else: + user: User = User.query.filter_by(user_name=actor, deleted=False, ap_id=None).first() + if user is None: + abort(404) + form = UserNoteForm() + if form.validate_on_submit() and not current_user.banned: + text = form.note.data.strip() + usernote = UserNote.query.filter(UserNote.target_id == user.id, UserNote.user_id == current_user.id).first() + if usernote: + usernote.body = text + else: + usernote = UserNote(target_id=user.id, user_id=current_user.id, body=text) + db.session.add(usernote) + db.session.commit() + + flash(_('Your changes have been saved.'), 'success') + referrer = request.headers.get('Referer', None) + if referrer is not None: + return redirect(referrer) + else: + return redirect(url_for('user.edit_user_note', actor=actor)) + elif request.method == 'GET': + form.note.data = user.get_note(current_user) + + return render_template('user/edit_note.html', title=_('Edit note'), form=form, user=user, + menu_topics=menu_topics(), site=g.site) From b4f1706a6b71f50d93326daf024ff01a65141e9e Mon Sep 17 00:00:00 2001 From: Hendrik L Date: Wed, 11 Dec 2024 16:33:42 +0100 Subject: [PATCH 03/30] user_notes relationship, caching, cleanup --- app/models.py | 4 +++- app/templates/base.html | 2 +- app/templates/user/edit_note.html | 21 ++++++++++----------- app/user/routes.py | 11 +++++------ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/models.py b/app/models.py index bdb596ba..a95d026a 100644 --- a/app/models.py +++ b/app/models.py @@ -710,6 +710,7 @@ class User(UserMixin, db.Model): cover = db.relationship('File', lazy='joined', foreign_keys=[cover_id], single_parent=True, cascade="all, delete-orphan") instance = db.relationship('Instance', lazy='joined', foreign_keys=[instance_id]) conversations = db.relationship('Conversation', lazy='dynamic', secondary=conversation_member, backref=db.backref('members', lazy='joined')) + user_notes = db.relationship('UserNote', lazy='dynamic', foreign_keys="UserNote.target_id") ap_id = db.Column(db.String(255), index=True) # e.g. username@server ap_profile_id = db.Column(db.String(255), index=True, unique=True) # e.g. https://server/u/username @@ -1079,8 +1080,9 @@ class User(UserMixin, db.Model): def has_read_post(self, post): return self.read_post.filter(read_posts.c.read_post_id == post.id).count() > 0 + @cache.memoize(timeout=500) def get_note(self, by_user): - user_note = UserNote.query.filter(UserNote.target_id == self.id, UserNote.user_id == by_user.id).first() + user_note = self.user_notes.filter(UserNote.target_id == self.id, UserNote.user_id == by_user.id).first() if user_note: return user_note.body else: diff --git a/app/templates/base.html b/app/templates/base.html index dff76458..320d8bd5 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -30,7 +30,7 @@ {% if current_user.is_authenticated -%} {% set user_note = user.get_note(current_user) %} {% if user_note -%} - [{{ user_note | truncate(12, True) }}] + [{{ user_note | truncate(12, True) }}] {% endif -%} {% endif -%} {% endif -%} diff --git a/app/templates/user/edit_note.html b/app/templates/user/edit_note.html index 6ab886a6..5f003e0e 100644 --- a/app/templates/user/edit_note.html +++ b/app/templates/user/edit_note.html @@ -12,9 +12,8 @@
    {{ _('Edit note for "%(user_name)s"', user_name=user.display_name()) }}
    + {{ _('Emoji quick access') }}
    - Emoji quick access -
    @@ -23,27 +22,27 @@ -
    +
    + - -
    +
    + + + + + - - - - -
    {{ render_form(form) }} -
    This note appears next to the username. It's just for you and not displayed to anyone else.
    +
    {{ _('This note appears next to their username. It\'s meant just for you and not displayed to anyone else.') }}
    diff --git a/app/user/routes.py b/app/user/routes.py index 12a6e7a9..fbde47f4 100644 --- a/app/user/routes.py +++ b/app/user/routes.py @@ -1337,7 +1337,7 @@ def user_read_posts_delete(): def edit_user_note(actor): actor = actor.strip() if '@' in actor: - user: User = User.query.filter_by(ap_id=actor, deleted=False, banned=False).first() + user: User = User.query.filter_by(ap_id=actor, deleted=False).first() else: user: User = User.query.filter_by(user_name=actor, deleted=False, ap_id=None).first() if user is None: @@ -1352,13 +1352,12 @@ def edit_user_note(actor): usernote = UserNote(target_id=user.id, user_id=current_user.id, body=text) db.session.add(usernote) db.session.commit() + cache.delete_memoized(User.get_note, user, current_user) flash(_('Your changes have been saved.'), 'success') - referrer = request.headers.get('Referer', None) - if referrer is not None: - return redirect(referrer) - else: - return redirect(url_for('user.edit_user_note', actor=actor)) + goto = request.args.get('redirect') if 'redirect' in request.args else f'/u/{actor}' + return redirect(goto) + elif request.method == 'GET': form.note.data = user.get_note(current_user) From 410c4735471b4fdac6d37802eb873450bdf449d3 Mon Sep 17 00:00:00 2001 From: Hendrik L Date: Thu, 12 Dec 2024 20:43:20 +0100 Subject: [PATCH 04/30] add modlog to the admin tools --- app/templates/admin/_nav.html | 1 + app/templates/base.html | 3 ++- app/templates/modlog.html | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/templates/admin/_nav.html b/app/templates/admin/_nav.html index e04d0b84..dea6efd2 100644 --- a/app/templates/admin/_nav.html +++ b/app/templates/admin/_nav.html @@ -15,6 +15,7 @@ {{ _('Newsletter') }} | {{ _('Permissions') }} | {{ _('Activities') }} + {{ _('Modlog') }} {% if debug_mode %} | {{ _('Dev Tools') }} {% endif%} diff --git a/app/templates/base.html b/app/templates/base.html index 718d1d83..a7467df1 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -228,8 +228,9 @@
  • {{ _('Federation') }}
  • {{ _('Instances') }}
  • {{ _('Newsletter') }}
  • -
  • {{ _('Activities') }}
  • {{ _('Permissions') }}
  • +
  • {{ _('Activities') }}
  • +
  • {{ _('Modlog') }}
  • {% if debug_mode %}
  • {{ _('Dev Tools') }}
  • {% endif %} diff --git a/app/templates/modlog.html b/app/templates/modlog.html index 7c905546..1068f169 100644 --- a/app/templates/modlog.html +++ b/app/templates/modlog.html @@ -4,7 +4,7 @@ {% extends "base.html" -%} {% endif -%} -%} {% from 'bootstrap5/form.html' import render_form -%} -{% set active_child = 'list_communities' -%} +{% set active_child = 'modlog' -%} {% block app_content -%}

    {{ _('Moderation log') }}

    From 52be7bad5d863eecd14ace3a13c1ba3b45c505b0 Mon Sep 17 00:00:00 2001 From: rimu <3310831+rimu@users.noreply.github.com> Date: Sat, 14 Dec 2024 14:24:21 +1300 Subject: [PATCH 05/30] switch to requests.get in generate_image_from_video_url #373 --- app/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/utils.py b/app/utils.py index 6b3bb600..14bfdc06 100644 --- a/app/utils.py +++ b/app/utils.py @@ -17,6 +17,7 @@ import math from urllib.parse import urlparse, parse_qs, urlencode from functools import wraps import flask +import requests from bs4 import BeautifulSoup, MarkupResemblesLocatorWarning import warnings import jwt @@ -1111,7 +1112,7 @@ def in_sorted_list(arr, target): # Makes a still image from a video url, without downloading the whole video file def generate_image_from_video_url(video_url, output_path, length=2): - response = httpx_client.get(video_url, stream=True, timeout=5, + response = requests.get(video_url, stream=True, timeout=5, headers={'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:127.0) Gecko/20100101 Firefox/127.0'}) # Imgur requires a user agent content_type = response.headers.get('Content-Type') if content_type: From a5881261907c635247a8a20c0f884a6ca0ecc13d Mon Sep 17 00:00:00 2001 From: freamon Date: Sat, 14 Dec 2024 11:52:18 +0000 Subject: [PATCH 06/30] Exclude extra cross-posts from main feed #206 --- app/main/routes.py | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/app/main/routes.py b/app/main/routes.py index aebf502c..85ab6f59 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -116,9 +116,39 @@ def home_page(sort, view_filter): posts = posts.order_by(desc(Post.last_active)) # Pagination - posts = posts.paginate(page=page, per_page=100 if current_user.is_authenticated and not low_bandwidth else 50, error_out=False) - next_url = url_for('main.index', page=posts.next_num, sort=sort, view_filter=view_filter) if posts.has_next else None - prev_url = url_for('main.index', page=posts.prev_num, sort=sort, view_filter=view_filter) if posts.has_prev and page != 1 else None + if view_filter == 'subscribed' and current_user.is_authenticated and sort == 'new': + # use python list instead of DB query + posts = posts.all() + + # exclude extra cross-posts from feed + already_seen = [] + limit = 100 if not low_bandwidth else 50 + #i = -1 # option 1: don't exclude cross-posts + #i = limit - 1 # option 2: exclude cross-posts from the first page only + i = (limit * 10) - 1 # option 3: exclude cross-posts across a 'magic number' of pages + #i = len(posts) - 1 # option 4: exclude all cross-posts ever + while i >= 0: + if not posts[i].cross_posts: + i -= 1 + continue + if posts[i].id in already_seen: + posts.pop(i) + else: + already_seen.extend(posts[i].cross_posts) + i -= 1 + + # paginate manually (can't use paginate()) + start = (page - 1) * limit + end = start + limit + posts = posts[start:end] + next_page = page + 1 if len(posts) == limit else None + previous_page = page - 1 if page != 1 else None + next_url = url_for('main.index', page=next_page, sort=sort, view_filter=view_filter) if next_page else None + prev_url = url_for('main.index', page=previous_page, sort=sort, view_filter=view_filter) if previous_page else None + else: + posts = posts.paginate(page=page, per_page=100 if current_user.is_authenticated and not low_bandwidth else 50, error_out=False) + next_url = url_for('main.index', page=posts.next_num, sort=sort, view_filter=view_filter) if posts.has_next else None + prev_url = url_for('main.index', page=posts.prev_num, sort=sort, view_filter=view_filter) if posts.has_prev and page != 1 else None # Active Communities active_communities = Community.query.filter_by(banned=False) From 46c150cecf385c64fb58946c16fd8dc3a7084170 Mon Sep 17 00:00:00 2001 From: freamon Date: Sat, 14 Dec 2024 13:05:09 +0000 Subject: [PATCH 07/30] Undo changes from previous commit until debugged --- app/main/routes.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/main/routes.py b/app/main/routes.py index 85ab6f59..df7b04ab 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -116,6 +116,7 @@ def home_page(sort, view_filter): posts = posts.order_by(desc(Post.last_active)) # Pagination + """ if view_filter == 'subscribed' and current_user.is_authenticated and sort == 'new': # use python list instead of DB query posts = posts.all() @@ -146,9 +147,10 @@ def home_page(sort, view_filter): next_url = url_for('main.index', page=next_page, sort=sort, view_filter=view_filter) if next_page else None prev_url = url_for('main.index', page=previous_page, sort=sort, view_filter=view_filter) if previous_page else None else: - posts = posts.paginate(page=page, per_page=100 if current_user.is_authenticated and not low_bandwidth else 50, error_out=False) - next_url = url_for('main.index', page=posts.next_num, sort=sort, view_filter=view_filter) if posts.has_next else None - prev_url = url_for('main.index', page=posts.prev_num, sort=sort, view_filter=view_filter) if posts.has_prev and page != 1 else None + """ + posts = posts.paginate(page=page, per_page=100 if current_user.is_authenticated and not low_bandwidth else 50, error_out=False) + next_url = url_for('main.index', page=posts.next_num, sort=sort, view_filter=view_filter) if posts.has_next else None + prev_url = url_for('main.index', page=posts.prev_num, sort=sort, view_filter=view_filter) if posts.has_prev and page != 1 else None # Active Communities active_communities = Community.query.filter_by(banned=False) From d4e9ff14d96b3814de8fe6a994b85b089f9d0825 Mon Sep 17 00:00:00 2001 From: freamon Date: Sat, 14 Dec 2024 14:55:14 +0000 Subject: [PATCH 08/30] bugfix for extra cross-posts exclusion commit --- app/main/routes.py | 12 +++++------- app/templates/index.html | 6 +++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/main/routes.py b/app/main/routes.py index df7b04ab..ba64cf6e 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -116,7 +116,6 @@ def home_page(sort, view_filter): posts = posts.order_by(desc(Post.last_active)) # Pagination - """ if view_filter == 'subscribed' and current_user.is_authenticated and sort == 'new': # use python list instead of DB query posts = posts.all() @@ -125,8 +124,8 @@ def home_page(sort, view_filter): already_seen = [] limit = 100 if not low_bandwidth else 50 #i = -1 # option 1: don't exclude cross-posts - #i = limit - 1 # option 2: exclude cross-posts from the first page only - i = (limit * 10) - 1 # option 3: exclude cross-posts across a 'magic number' of pages + #i = min(limit - 1, len(posts) - 1) # option 2: exclude cross-posts from the first page only + i = min((limit * 10) - 1, len(posts) - 1) # option 3: exclude cross-posts across a 'magic number' of pages #i = len(posts) - 1 # option 4: exclude all cross-posts ever while i >= 0: if not posts[i].cross_posts: @@ -147,10 +146,9 @@ def home_page(sort, view_filter): next_url = url_for('main.index', page=next_page, sort=sort, view_filter=view_filter) if next_page else None prev_url = url_for('main.index', page=previous_page, sort=sort, view_filter=view_filter) if previous_page else None else: - """ - posts = posts.paginate(page=page, per_page=100 if current_user.is_authenticated and not low_bandwidth else 50, error_out=False) - next_url = url_for('main.index', page=posts.next_num, sort=sort, view_filter=view_filter) if posts.has_next else None - prev_url = url_for('main.index', page=posts.prev_num, sort=sort, view_filter=view_filter) if posts.has_prev and page != 1 else None + posts = posts.paginate(page=page, per_page=100 if current_user.is_authenticated and not low_bandwidth else 50, error_out=False) + next_url = url_for('main.index', page=posts.next_num, sort=sort, view_filter=view_filter) if posts.has_next else None + prev_url = url_for('main.index', page=posts.prev_num, sort=sort, view_filter=view_filter) if posts.has_prev and page != 1 else None # Active Communities active_communities = Community.query.filter_by(banned=False) diff --git a/app/templates/index.html b/app/templates/index.html index 9ea7c2ae..8398d712 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -15,12 +15,12 @@ {% include "_home_nav.html" %} {% include "_view_filter_nav.html" %}
    - {% for post in posts.items -%} + {% for post in posts %} {% include 'post/_post_teaser.html' %} - {% else -%} + {% else %}

    {{ _('No posts yet. Join some communities to see more.') }}

    {{ _('More communities') }}

    - {% endfor -%} + {% endfor %}