diff --git a/app/activitypub/routes.py b/app/activitypub/routes.py index 6108bd25..82169bbc 100644 --- a/app/activitypub/routes.py +++ b/app/activitypub/routes.py @@ -1447,9 +1447,12 @@ def process_new_content(user, community, store_ap_json, request_json, announced) else: in_reply_to = request_json['object']['object']['inReplyTo'] if 'inReplyTo' in request_json['object']['object'] else None ap_id = request_json['object']['object']['id'] - announce_id = request_json['id'] + announce_id = shorten_string(request_json['id'], 100) activity_json = request_json['object'] + # announce / create IDs that are too long will crash the app. Not referred to again, so it shouldn't matter if they're truncated + activity_json['id'] = shorten_string(activity_json['id'], 100) + if not in_reply_to: # Creating a new post post = Post.query.filter_by(ap_id=ap_id).first() if post: diff --git a/app/activitypub/util.py b/app/activitypub/util.py index c515c273..7234ee82 100644 --- a/app/activitypub/util.py +++ b/app/activitypub/util.py @@ -2329,6 +2329,10 @@ def resolve_remote_post_from_search(uri: str) -> Union[Post, None]: # just gets orderedItems[0] to retrieve the post, and then replies are retrieved in the background topic_post_data = post_data nodebb = False + if ('type' in post_data and post_data['type'] == 'Conversation' and + 'posts' in post_data and isinstance(post_data['posts'], str)): + post_data = remote_object_to_json(post_data['posts']) + topic_post_data = post_data if ('type' in post_data and post_data['type'] == 'OrderedCollection' and 'totalItems' in post_data and post_data['totalItems'] > 0 and 'orderedItems' in post_data and isinstance(post_data['orderedItems'], list)): diff --git a/app/api/alpha/utils/community.py b/app/api/alpha/utils/community.py index ca3a3a7c..1a410446 100644 --- a/app/api/alpha/utils/community.py +++ b/app/api/alpha/utils/community.py @@ -1,21 +1,17 @@ from app import cache from app.api.alpha.views import community_view from app.api.alpha.utils.validators import required, integer_expected, boolean_expected +from app.community.util import search_for_community from app.utils import authorise_api_user from app.models import Community, CommunityMember from app.shared.community import join_community, leave_community, block_community, unblock_community -from app.utils import communities_banned_from, blocked_instances +from app.utils import communities_banned_from, blocked_instances, blocked_communities -from sqlalchemy import desc +from sqlalchemy import desc, or_ @cache.memoize(timeout=3) -def cached_community_list(type, sort, limit, user_id): - if user_id: - banned_from = communities_banned_from(user_id) - else: - banned_from = None - +def cached_community_list(type, sort, limit, user_id, query=''): if type == 'Subscribed': communities = Community.query.filter_by(banned=False).join(CommunityMember).filter(CommunityMember.user_id == user_id) elif type == 'Local': @@ -23,13 +19,19 @@ def cached_community_list(type, sort, limit, user_id): else: communities = Community.query.filter_by(banned=False) - if banned_from: - communities = communities.filter(Community.id.not_in(banned_from)) - if user_id: + banned_from = communities_banned_from(user_id) + if banned_from: + communities = communities.filter(Community.id.not_in(banned_from)) blocked_instance_ids = blocked_instances(user_id) if blocked_instance_ids: communities = communities.filter(Community.instance_id.not_in(blocked_instance_ids)) + blocked_community_ids = blocked_communities(user_id) + if blocked_community_ids: + communities = communities.filter(Community.id.not_in(blocked_community_ids)) + + if query: + communities = communities.filter(or_(Community.title.ilike(f"%{query}%"), Community.ap_id.ilike(f"%{query}%"))) if sort == 'Active': # 'Trending Communities' screen communities = communities.order_by(desc(Community.last_active)).limit(limit) @@ -45,7 +47,14 @@ def get_community_list(auth, data): user_id = authorise_api_user(auth) if auth else None - communities = cached_community_list(type, sort, limit, user_id) + query = data['q'] if data and 'q' in data else '' + if user_id and '@' in query and '.' in query and query.startswith('!'): + search_for_community(query) + query = query[1:] + + user_id = authorise_api_user(auth) if auth else None + + communities = cached_community_list(type, sort, limit, user_id, query) start = (page - 1) * limit end = start + limit diff --git a/app/api/alpha/utils/misc.py b/app/api/alpha/utils/misc.py index 96189b38..1d9f86e5 100644 --- a/app/api/alpha/utils/misc.py +++ b/app/api/alpha/utils/misc.py @@ -1,55 +1,7 @@ -from app.models import Community, Post, User, utcnow -from app.utils import authorise_api_user -from app.api.alpha.views import search_view, community_view, post_view, user_view - -from datetime import timedelta -from sqlalchemy import desc - - -def get_communities_list(sort, page, limit, listing_type, user_id): - # only support 'api/alpha/search?q&type_=Communities&sort=TopAll&listing_type=Local&page=1&limit=15' for now - # (enough for instance view) - communities = Community.query.filter_by(ap_id=None).order_by(desc(Community.subscriptions_count)) - communities = communities.paginate(page=page, per_page=limit, error_out=False) - - community_list = [] - for community in communities: - community_list.append(community_view(community, variant=2, stub=True)) - return community_list - - -def get_posts_list(sort, page, limit, listing_type, user_id): - # only support 'api/alpha/search?q&type_=Posts&sort=TopAll&listing_type=Local&page=1&limit=15' for now - # (enough for instance view) - posts = Post.query.filter_by(instance_id=1, deleted=False) - - if sort == "Hot": - posts = posts.order_by(desc(Post.ranking)).order_by(desc(Post.posted_at)) - elif sort == "TopDay": - posts = posts.filter(Post.posted_at > utcnow() - timedelta(days=1)).order_by(desc(Post.up_votes - Post.down_votes)) - elif sort == "New": - posts = posts.order_by(desc(Post.posted_at)) - elif sort == "Active": - posts = posts.order_by(desc(Post.last_active)) - - posts = posts.paginate(page=page, per_page=limit, error_out=False) - - post_list = [] - for post in posts: - post_list.append(post_view(post, variant=2, stub=True)) - return post_list - - -def get_users_list(sort, page, limit, listing_type, user_id): - # only support 'api/alpha/search?q&type_=Users&sort=TopAll&listing_type=Local&page=1&limit=15' for now - # (enough for instance view) - users = User.query.filter_by(instance_id=1, deleted=False).order_by(User.id) - users = users.paginate(page=page, per_page=limit, error_out=False) - - user_list = [] - for user in users: - user_list.append(user_view(user, variant=2, stub=True)) - return user_list +from app.api.alpha.utils.community import get_community_list +from app.api.alpha.utils.post import get_post_list +from app.api.alpha.utils.user import get_user_list +from app.api.alpha.views import search_view def get_search(auth, data): @@ -57,20 +9,18 @@ def get_search(auth, data): raise Exception('missing_parameters') type = data['type_'] - sort = data['sort'] if 'sort' in data else 'Top' - page = int(data['page']) if 'page' in data else 1 - limit = int(data['limit']) if 'limit' in data else 15 listing_type = data['listing_type'] if 'listing_type' in data else 'Local' - user_id = authorise_api_user(auth) if auth else None + data['type_'] = listing_type - search_json = search_view(type) + search_type = 'Posts' if type == 'Url' else type + search_json = search_view(search_type) if type == 'Communities': - search_json['communities'] = get_communities_list(sort, page, limit, listing_type, user_id) - elif type == 'Posts': - search_json['posts'] = get_posts_list(sort, page, limit, listing_type, user_id) + search_json['communities'] = get_community_list(auth, data)['communities'] + elif type == 'Posts' or type == 'Url': + search_json['posts'] = get_post_list(auth, data, search_type=type)['posts'] elif type == 'Users': - search_json['users'] = get_users_list(sort, page, limit, listing_type, user_id) + search_json['users'] = get_user_list(auth, data)['users'] return search_json diff --git a/app/api/alpha/utils/post.py b/app/api/alpha/utils/post.py index f1902c92..ce168e4c 100644 --- a/app/api/alpha/utils/post.py +++ b/app/api/alpha/utils/post.py @@ -1,7 +1,7 @@ from app import cache from app.api.alpha.views import post_view, post_report_view from app.api.alpha.utils.validators import required, integer_expected, boolean_expected, string_expected -from app.constants import POST_TYPE_ARTICLE, POST_TYPE_LINK, POST_TYPE_IMAGE, POST_TYPE_VIDEO +from app.constants import POST_TYPE_ARTICLE, POST_TYPE_LINK, POST_TYPE_IMAGE, POST_TYPE_VIDEO, POST_TYPE_POLL from app.models import Post, Community, CommunityMember, utcnow from app.shared.post import vote_for_post, bookmark_the_post, remove_the_bookmark_from_post, toggle_post_notification, make_post, edit_post, \ delete_post, restore_post, report_post, lock_post, sticky_post, mod_remove_post, mod_restore_post @@ -12,7 +12,7 @@ from sqlalchemy import desc @cache.memoize(timeout=3) -def cached_post_list(type, sort, user_id, community_id, community_name, person_id): +def cached_post_list(type, sort, user_id, community_id, community_name, person_id, query='', search_type='Posts'): if type == "All": if community_name: name, ap_domain = community_name.split('@') @@ -32,6 +32,9 @@ def cached_post_list(type, sort, user_id, community_id, community_name, person_i else: posts = Post.query.filter_by(deleted=False) + # change when polls are supported + posts = posts.filter(Post.type != POST_TYPE_POLL) + if user_id is not None: blocked_person_ids = blocked_users(user_id) if blocked_person_ids: @@ -44,6 +47,12 @@ def cached_post_list(type, sort, user_id, community_id, community_name, person_i posts = posts.filter(Post.instance_id.not_in(blocked_instance_ids)) # users from blocked instance posts = posts.filter(Post.community_id.not_in(community_ids_from_instances(blocked_instance_ids))) # communities from blocked instance + if query: + if search_type == 'Url': + posts = posts.filter(Post.url.ilike(f"%{query}%")) + else: + posts = posts.filter(Post.title.ilike(f"%{query}%")) + if sort == "Hot": posts = posts.order_by(desc(Post.ranking)).order_by(desc(Post.posted_at)) elif sort == "TopDay": @@ -56,12 +65,14 @@ def cached_post_list(type, sort, user_id, community_id, community_name, person_i return posts.all() -def get_post_list(auth, data, user_id=None): +def get_post_list(auth, data, user_id=None, search_type='Posts'): type = data['type_'] if data and 'type_' in data else "All" sort = data['sort'] if data and 'sort' in data else "Hot" page = int(data['page']) if data and 'page' in data else 1 limit = int(data['limit']) if data and 'limit' in data else 10 + query = data['q'] if data and 'q' in data else '' + if auth: user_id = authorise_api_user(auth) @@ -72,7 +83,7 @@ def get_post_list(auth, data, user_id=None): community_name = data['community_name'] if data and 'community_name' in data else None person_id = int(data['person_id']) if data and 'person_id' in data else None - posts = cached_post_list(type, sort, user_id, community_id, community_name, person_id) + posts = cached_post_list(type, sort, user_id, community_id, community_name, person_id, query, search_type) start = (page - 1) * limit end = start + limit diff --git a/app/api/alpha/utils/user.py b/app/api/alpha/utils/user.py index d3278391..39e668d8 100644 --- a/app/api/alpha/utils/user.py +++ b/app/api/alpha/utils/user.py @@ -3,6 +3,7 @@ from app.utils import authorise_api_user from app.api.alpha.utils.post import get_post_list from app.api.alpha.utils.reply import get_reply_list from app.api.alpha.utils.validators import required, integer_expected, boolean_expected +from app.models import User from app.shared.user import block_another_user, unblock_another_user @@ -35,6 +36,37 @@ def get_user(auth, data): return user_json +def get_user_list(auth, data): + # only support 'api/alpha/search?q&type_=Users&sort=TopAll&listing_type=Local&page=1&limit=15' for now + # (enough for instance view) + + type = data['type_'] if data and 'type_' in data else "All" + sort = data['sort'] if data and 'sort' in data else "Hot" + page = int(data['page']) if data and 'page' in data else 1 + limit = int(data['limit']) if data and 'limit' in data else 10 + + query = data['q'] if data and 'q' in data else '' + + if type == 'Local': + users = User.query.filter_by(instance_id=1, deleted=False).order_by(User.id) + else: + users = User.query.filter_by(deleted=False).order_by(User.id) + + if query: + users = users.filter(User.user_name.ilike(f"%{query}%")) + + users = users.paginate(page=page, per_page=limit, error_out=False) + + user_list = [] + for user in users: + user_list.append(user_view(user, variant=2, stub=True)) + list_json = { + "users": user_list + } + + return list_json + + # would be in app/constants.py SRC_API = 3