From fd066e16a846e6fd2e50abac40fbae1812d6c9f3 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 5 Jan 2023 22:19:38 +0000 Subject: [PATCH] Display of remote hashtags to avoid showing Mastodon UI --- content.py | 40 ++++++++++ daemon.py | 64 ++++++++++++++++ webapp_post.py | 3 + webapp_search.py | 193 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 300 insertions(+) diff --git a/content.py b/content.py index c80dd4229..4194c3a6b 100644 --- a/content.py +++ b/content.py @@ -765,6 +765,46 @@ def _add_hash_tags(word_str: str, http_prefix: str, domain: str, return True +def replace_remote_hashtags(content: str, + nickname: str, domain: str) -> str: + """Replaces remote hashtags with a local version + """ + if not domain: + return content + + if ' href="' not in content: + return content + + sections = content.split(' href="') + ctr = 0 + replacements = {} + for section in sections: + if ctr == 0: + ctr += 1 + continue + if '"' not in section: + ctr += 1 + continue + link = section.split('"')[0] + if '?remotetag=' in link: + ctr += 1 + continue + if '/tags/' not in link: + ctr += 1 + continue + if '/' + domain not in link: + new_link = '/users/' + nickname + \ + '?remotetag=' + link.replace('/', '--') + replacements[link] = new_link + ctr += 1 + if not replacements: + return content + for old_link, new_link in replacements.items(): + content = content.replace('"' + old_link + '"', + '"' + new_link + '"') + return content + + def _add_emoji(base_dir: str, word_str: str, http_prefix: str, domain: str, replace_emoji: {}, post_tags: {}, diff --git a/daemon.py b/daemon.py index c4e13ef0a..7a1a8b790 100644 --- a/daemon.py +++ b/daemon.py @@ -239,6 +239,7 @@ from webapp_column_right import html_edit_news_post from webapp_search import html_skills_search from webapp_search import html_history_search from webapp_search import html_hashtag_search +from webapp_search import html_hashtag_search_remote from webapp_search import rss_hashtag_search from webapp_search import hashtag_search_json from webapp_search import html_search_emoji @@ -18467,6 +18468,69 @@ class PubServer(BaseHTTPRequestHandler): self.server.getreq_busy = False return + if '?remotetag=' in self.path and \ + '/users/' in self.path and authorized: + actor = self.path.split('?remotetag=')[0] + nickname = get_nickname_from_actor(actor) + hashtag_url = self.path.split('?remotetag=')[1] + if ';' in hashtag_url: + hashtag_url = hashtag_url.split(';')[0] + hashtag_url = hashtag_url.replace('--', '/') + + page_number = 1 + if ';page=' in self.path: + page_number_str = self.path.split(';page=')[1] + if ';' in page_number_str: + page_number_str = page_number_str.split(';')[0] + if page_number_str.isdigit(): + page_number = int(page_number_str) + + allow_local_network_access = self.server.allow_local_network_access + show_published_date_only = self.server.show_published_date_only + twitter_replacement_domain = self.server.twitter_replacement_domain + msg = \ + html_hashtag_search_remote(nickname, + self.server.domain, + self.server.port, + self.server.recent_posts_cache, + self.server.max_recent_posts, + self.server.translate, + self.server.base_dir, + hashtag_url, + page_number, MAX_POSTS_IN_FEED, + self.server.session, + self.server.cached_webfingers, + self.server.person_cache, + self.server.http_prefix, + self.server.project_version, + self.server.yt_replace_domain, + twitter_replacement_domain, + show_published_date_only, + self.server.peertube_instances, + allow_local_network_access, + self.server.theme_name, + self.server.system_language, + self.server.max_like_count, + self.server.signing_priv_key_pem, + self.server.cw_lists, + self.server.lists_enabled, + self.server.timezone, + self.server.bold_reading, + self.server.dogwhistles, + self.server.min_images_for_accounts, + self.server.debug) + if msg: + msg = msg.encode('utf-8') + msglen = len(msg) + self._set_headers('text/html', msglen, cookie, calling_domain, + False) + self._write(msg) + self.server.getreq_busy = False + return + self._404() + self.server.getreq_busy = False + return + # hashtag search if self.path.startswith('/tags/') or \ (authorized and '/tags/' in self.path): diff --git a/webapp_post.py b/webapp_post.py index 100475b5e..cba2e791f 100644 --- a/webapp_post.py +++ b/webapp_post.py @@ -67,6 +67,7 @@ from utils import get_domain_from_actor from utils import acct_dir from utils import local_actor_url from utils import is_unlisted_post +from content import replace_remote_hashtags from content import detect_dogwhistles from content import create_edits_html from content import bold_reading_string @@ -2445,6 +2446,8 @@ def individual_post_as_html(signing_priv_key_pem: str, system_language, translate) if not content_str: return '' + content_str = \ + replace_remote_hashtags(content_str, nickname, domain) summary_str = '' if content_str: diff --git a/webapp_search.py b/webapp_search.py index d4163fb5e..2f1e1b0f3 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -46,6 +46,7 @@ from webapp_utils import html_search_result_share from webapp_post import individual_post_as_html from webapp_hashtagswarm import html_hash_tag_swarm from maps import html_hashtag_maps +from session import get_json def html_search_emoji(translate: {}, base_dir: str, search_str: str, @@ -1049,6 +1050,198 @@ def html_hashtag_search(nickname: str, domain: str, port: int, return hashtag_search_form +def html_hashtag_search_remote(nickname: str, domain: str, port: int, + recent_posts_cache: {}, max_recent_posts: int, + translate: {}, + base_dir: str, hashtag_url: str, + page_number: int, posts_per_page: int, + session, cached_webfingers: {}, + person_cache: {}, + http_prefix: str, project_version: str, + yt_replace_domain: str, + twitter_replacement_domain: str, + show_published_date_only: bool, + peertube_instances: [], + allow_local_network_access: bool, + theme_name: str, system_language: str, + max_like_count: int, + signing_priv_key_pem: str, + cw_lists: {}, lists_enabled: str, + timezone: str, bold_reading: bool, + dogwhistles: {}, + min_images_for_accounts: [], + debug: bool) -> str: + """Show a page containing search results for a remote hashtag + """ + hashtag = hashtag_url.split('/')[-1] + + profile_str = 'https://www.w3.org/ns/activitystreams' + as_header = { + 'Accept': 'application/ld+json; profile="' + profile_str + '"' + } + hashtag_json = \ + get_json(signing_priv_key_pem, + session, hashtag_url, as_header, None, debug, + __version__, http_prefix, domain) + lines = [] + if not hashtag_json: + if 'orderedItems' in hashtag_json: + lines = hashtag_json['orderedItems'] + + separator_str = html_post_separator(base_dir, None) + + # check that the directory for the nickname exists + if nickname: + account_dir = acct_dir(base_dir, nickname, domain) + if not os.path.isdir(account_dir): + return None + + # read the css + css_filename = base_dir + '/epicyon-profile.css' + if os.path.isfile(base_dir + '/epicyon.css'): + css_filename = base_dir + '/epicyon.css' + + # ensure that the page number is in bounds + if not page_number: + page_number = 1 + elif page_number < 1: + page_number = 1 + + # get the start end end within the index file + start_index = int((page_number - 1) * posts_per_page) + end_index = start_index + posts_per_page + no_of_lines = len(lines) + if end_index >= no_of_lines and no_of_lines > 0: + end_index = no_of_lines - 1 + + instance_title = \ + get_config_param(base_dir, 'instanceTitle') + hashtag_search_form = \ + html_header_with_external_style(css_filename, instance_title, None) + + # add the page title + hashtag_search_form += '
\n' + \ + '

#' + hashtag + + # RSS link for hashtag feed + hashtag_rss = hashtag_url + if '.html' in hashtag_rss: + hashtag_rss = hashtag_rss.replace('.html', '') + hashtag_search_form += ' ' + hashtag_search_form += \ + '

\n' + + tag_link = '/users/' + nickname + '?remotetag=' + \ + hashtag_url.replace('/', '--') + if start_index > 0: + # previous page link + hashtag_search_form += \ + '
\n' + \ + ' ' + translate['Page up'] + \
+            '\n
\n' + index = start_index + while index <= end_index: + post_id = lines[index] + post_json_object = \ + get_json(signing_priv_key_pem, + session, post_id, as_header, None, debug, + __version__, http_prefix, domain) + if not post_json_object: + index += 1 + continue + if not isinstance(post_json_object, dict): + index += 1 + continue + if not has_object_dict(post_json_object): + if post_json_object.get('id') and \ + 'to' in post_json_object and \ + 'cc' in post_json_object and \ + post_json_object.get('actor'): + new_url = \ + remove_id_ending(post_json_object['id']) + '/activity' + new_post_json_object = { + "type": "Create", + "id": new_url, + "to": post_json_object['to'], + "cc": post_json_object['cc'], + "actor": post_json_object['actor'], + "object": post_json_object + } + post_json_object = new_post_json_object + else: + index += 1 + continue + if not is_public_post(post_json_object): + index += 1 + continue + show_individual_post_icons = False + allow_deletion = False + show_repeats = show_individual_post_icons + show_icons = show_individual_post_icons + manually_approves_followers = False + show_public_only = False + store_to_sache = False + allow_downloads = True + avatar_url = None + show_avatar_options = True + minimize_all_images = False + if nickname in min_images_for_accounts: + minimize_all_images = True + post_str = \ + individual_post_as_html(signing_priv_key_pem, + allow_downloads, recent_posts_cache, + max_recent_posts, + translate, None, + base_dir, session, cached_webfingers, + person_cache, + nickname, domain, port, + post_json_object, + avatar_url, show_avatar_options, + allow_deletion, + http_prefix, project_version, + 'search', + yt_replace_domain, + twitter_replacement_domain, + show_published_date_only, + peertube_instances, + allow_local_network_access, + theme_name, system_language, + max_like_count, + show_repeats, show_icons, + manually_approves_followers, + show_public_only, + store_to_sache, False, cw_lists, + lists_enabled, timezone, False, + bold_reading, dogwhistles, + minimize_all_images, None) + if post_str: + hashtag_search_form += separator_str + post_str + index += 1 + + if end_index < no_of_lines - 1: + # next page link + hashtag_search_form += \ + '
\n' + \ + ' ' + translate['Page down'] + '' + \ + '
' + hashtag_search_form += html_footer() + return hashtag_search_form + + def rss_hashtag_search(nickname: str, domain: str, port: int, recent_posts_cache: {}, max_recent_posts: int, translate: {},