diff --git a/app/activitypub/util.py b/app/activitypub/util.py index 46b48c68..17ad579d 100644 --- a/app/activitypub/util.py +++ b/app/activitypub/util.py @@ -13,7 +13,7 @@ from sqlalchemy import text, func from app import db, cache, constants, celery from app.models import User, Post, Community, BannedInstances, File, PostReply, AllowedInstances, Instance, utcnow, \ PostVote, PostReplyVote, ActivityPubLog, Notification, Site, CommunityMember, InstanceRole, Report, Conversation, \ - Language + Language, Tag from app.activitypub.signature import signed_get_request import time import base64 @@ -374,6 +374,23 @@ def find_language_or_create(code: str, name: str) -> Language: return new_language +def find_hashtag_or_create(hashtag: str) -> Tag: + if hashtag is None or hashtag == '': + return None + + hashtag = hashtag.strip() + if hashtag[0] == '#': + hashtag = hashtag[1:] + + existing_tag = Tag.query.filter(Tag.name == hashtag.lower()).first() + if existing_tag: + return existing_tag + else: + new_tag = Tag(name=hashtag.lower(), display_as=hashtag) + db.session.add(new_tag) + return new_tag + + def extract_domain_and_actor(url_string: str): # Parse the URL parsed_url = urlparse(url_string) @@ -765,6 +782,13 @@ def post_json_to_model(activity_log, post_json, user, community) -> Post: if language: post.language_id = language.id + if 'tag' in post_json: + for json_tag in post_json['tag']: + if json_tag['type'] == 'Hashtag': + hashtag = find_hashtag_or_create(json_tag['name']) + if hashtag: + post.tags.append(hashtag) + if post is not None: if 'image' in post_json and post.image is None: image = File(source_url=post_json['image']['url']) @@ -1496,6 +1520,12 @@ def create_post(activity_log: ActivityPubLog, community: Community, request_json language = find_language_or_create(request_json['object']['language']['identifier'], request_json['object']['language']['name']) post.language_id = language.id + if 'tag' in request_json['object'] and isinstance(request_json['object']['tag'], list): + for json_tag in request_json['object']['tag']: + if json_tag['type'] == 'Hashtag': + hashtag = find_hashtag_or_create(json_tag['name']) + if hashtag: + post.tags.append(hashtag) if 'image' in request_json['object'] and post.image is None: image = File(source_url=request_json['object']['image']['url']) db.session.add(image) @@ -1721,6 +1751,13 @@ def update_post_from_activity(post: Post, request_json: dict): post.nsfl = True elif 'nsfl' in request_json['object']: post.nsfl = request_json['object']['nsfl'] + if 'tag' in request_json['object'] and isinstance(request_json['object']['tag'], list): + db.session.execute(text('DELETE FROM "post_tag" WHERE post_id = :post_id'), {'post_id': post.id}) + for json_tag in request_json['object']['tag']: + if json_tag['type'] == 'Hashtag': + hashtag = find_hashtag_or_create(json_tag['name']) + if hashtag: + post.tags.append(hashtag) post.comments_enabled = request_json['object']['commentsEnabled'] if 'commentsEnabled' in request_json['object'] else True post.edited_at = utcnow() db.session.commit() diff --git a/app/models.py b/app/models.py index a6867805..aeb5edee 100644 --- a/app/models.py +++ b/app/models.py @@ -172,7 +172,8 @@ class ChatMessage(db.Model): class Tag(db.Model): id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(256)) + name = db.Column(db.String(256), index=True) # lowercase version of tag, e.g. solarstorm + display_as = db.Column(db.String(256)) # Version of tag with uppercase letters, e.g. SolarStorm class Language(db.Model): diff --git a/migrations/versions/20ee24728510_tag_formatting.py b/migrations/versions/20ee24728510_tag_formatting.py new file mode 100644 index 00000000..46adf6fa --- /dev/null +++ b/migrations/versions/20ee24728510_tag_formatting.py @@ -0,0 +1,34 @@ +"""tag formatting + +Revision ID: 20ee24728510 +Revises: 9ad372b72d7c +Create Date: 2024-05-11 13:42:31.772863 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '20ee24728510' +down_revision = '9ad372b72d7c' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('tag', schema=None) as batch_op: + batch_op.add_column(sa.Column('display_as', sa.String(length=256), nullable=True)) + batch_op.create_index(batch_op.f('ix_tag_name'), ['name'], unique=False) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('tag', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_tag_name')) + batch_op.drop_column('display_as') + + # ### end Alembic commands ###