diff --git a/webapp_theme_designer.py b/webapp_theme_designer.py index e10ebdc09..5f1ba1d19 100644 --- a/webapp_theme_designer.py +++ b/webapp_theme_designer.py @@ -173,32 +173,31 @@ def html_theme_designer(base_dir: str, theme_name: str, access_keys: {}) -> str: """Edit theme settings """ - theme_filename = base_dir + '/theme/' + theme_name + '/theme.json' - theme_json = {} + theme_filename: str = base_dir + '/theme/' + theme_name + '/theme.json' + theme_json: dict = {} if is_a_file(theme_filename): theme_json = load_json(theme_filename) # set custom theme parameters - custom_variables_file = data_dir(base_dir) + '/theme.json' + custom_variables_file: str = data_dir(base_dir) + '/theme.json' if is_a_file(custom_variables_file): - custom_theme_params = load_json(custom_variables_file) + custom_theme_params: str = load_json(custom_variables_file) if custom_theme_params: for variable_name, value in custom_theme_params.items(): theme_json[variable_name] = value theme_form: str = '' - css_filename = base_dir + '/epicyon-profile.css' + css_filename: str = base_dir + '/epicyon-profile.css' if is_a_file(base_dir + '/epicyon.css'): css_filename = base_dir + '/epicyon.css' banner_file, _ = \ get_banner_file(base_dir, nickname, domain, theme_name) - banner_path = '/users/' + nickname + '/' + banner_file + banner_path: str = '/users/' + nickname + '/' + banner_file - instance_title = \ - get_config_param(base_dir, 'instanceTitle') - preload_images = [banner_path] - theme_form = \ + instance_title: str = get_config_param(base_dir, 'instanceTitle') + preload_images: list[str] = [banner_path] + theme_form: str = \ html_header_with_external_style(css_filename, instance_title, None, preload_images) theme_form += \ @@ -216,8 +215,8 @@ def html_theme_designer(base_dir: str, theme_form += '
\n' - reset_key = access_keys['menuLogout'] - submit_key = access_keys['submitButton'] + reset_key: str = access_keys['menuLogout'] + submit_key: str = access_keys['submitButton'] theme_form += \ '
\n' + \ ' ' # filename of the banner shown at the top - banner_file, _ = \ - get_banner_file(base_dir, nickname, domain, theme) - banner_path = users_path + '/' + banner_file + banner_file, _ = get_banner_file(base_dir, nickname, domain, theme) + banner_path: str = users_path + '/' + banner_file # these images are pre-loaded to prevent the web page from # jumping around when rendering - preload_images = [ + preload_images: list[str] = [ banner_path, '/icons/showhide.png', '/icons/repeat_hide.png', @@ -762,9 +758,8 @@ def html_timeline(default_timeline: str, '/icons/categoriesrss.png' ] - instance_title = \ - get_config_param(base_dir, 'instanceTitle') - tl_str = \ + instance_title: str = get_config_param(base_dir, 'instanceTitle') + tl_str: str = \ html_header_with_external_style(css_filename, instance_title, None, preload_images) @@ -781,7 +776,7 @@ def html_timeline(default_timeline: str, header_icons_str = '
' # what screen to go to when a new post is created - new_post_button_str = \ + new_post_button_str: str = \ _html_timeline_new_post(manually_approve_followers, box_name, users_path, translate, access_keys) @@ -1018,7 +1013,7 @@ def html_timeline(default_timeline: str, if nickname in min_images_for_accounts: minimize_all_images = True - no_seen_posts_filename = account_dir + '/.noSeenPosts' + no_seen_posts_filename: str = account_dir + '/.noSeenPosts' no_seen_posts: bool = False if is_a_file(no_seen_posts_filename): no_seen_posts = True @@ -1213,7 +1208,7 @@ def html_individual_share(domain: str, share_id: str, shares_file_type: str) -> str: """Returns an individual shared item as html """ - profile_str = '
\n' + profile_str: str = '
\n' profile_str += \ '\n' if shared_item.get('imageUrl'): @@ -1237,7 +1232,7 @@ def html_individual_share(domain: str, share_id: str, profile_str += \ '' + translate['Location'] + ': ' + \ shared_item['location'] + '
' - contact_title_str = translate['Contact'] + contact_title_str: str = translate['Contact'] if shared_item.get('itemPrice') and shared_item.get('itemCurrency'): if is_float(shared_item['itemPrice']): if float(shared_item['itemPrice']) > 0: @@ -1247,15 +1242,15 @@ def html_individual_share(domain: str, share_id: str, shared_item['itemCurrency'] contact_title_str = translate['Buy'] profile_str += '

\n' - sharedesc = shared_item['displayName'] + sharedesc: str = shared_item['displayName'] if '<' not in sharedesc and ';' not in sharedesc: if show_contact: - button_style_str = 'button' + button_style_str: str = 'button' if shared_item['category'] == 'accommodation': contact_title_str = translate['Request to stay'] button_style_str = 'contactbutton' - contact_actor = get_actor_from_post(shared_item) + contact_actor: str = get_actor_from_post(shared_item) profile_str += \ '

' + \ '\n' + \ '

\n' - separator_str = html_post_separator(base_dir, None) + separator_str: str = html_post_separator(base_dir, None) ctr: int = 0 is_admin_account: bool = False @@ -1409,10 +1404,10 @@ def html_shares(default_timeline: str, block_nostr: {}) -> str: """Show the shares timeline as html """ - manually_approve_followers = \ + manually_approve_followers: bool = \ follower_approval_active(base_dir, nickname, domain) - artist = is_artist(base_dir, nickname) - show_announces = True + artist: bool = is_artist(base_dir, nickname) + show_announces: bool = True return html_timeline(default_timeline, recent_posts_cache, max_recent_posts, @@ -1490,10 +1485,10 @@ def html_wanted(default_timeline: str, block_nostr: {}) -> str: """Show the wanted timeline as html """ - manually_approve_followers = \ + manually_approve_followers: bool = \ follower_approval_active(base_dir, nickname, domain) - artist = is_artist(base_dir, nickname) - show_announces = True + artist: bool = is_artist(base_dir, nickname) + show_announces: bool = True return html_timeline(default_timeline, recent_posts_cache, max_recent_posts, @@ -1574,9 +1569,9 @@ def html_inbox(default_timeline: str, block_nostr: {}) -> str: """Show the inbox as html """ - manually_approve_followers = \ + manually_approve_followers: bool = \ follower_approval_active(base_dir, nickname, domain) - artist = is_artist(base_dir, nickname) + artist: bool = is_artist(base_dir, nickname) return html_timeline(default_timeline, recent_posts_cache, max_recent_posts, @@ -1655,10 +1650,10 @@ def html_bookmarks(default_timeline: str, block_nostr: {}) -> str: """Show the bookmarks as html """ - manually_approve_followers = \ + manually_approve_followers: bool = \ follower_approval_active(base_dir, nickname, domain) - artist = is_artist(base_dir, nickname) - show_announces = True + artist: bool = is_artist(base_dir, nickname) + show_announces: bool = True return html_timeline(default_timeline, recent_posts_cache, max_recent_posts, @@ -2192,9 +2187,9 @@ def html_outbox(default_timeline: str, block_nostr: {}) -> str: """Show the Outbox as html """ - manually_approve_followers = \ + manually_approve_followers: bool = \ follower_approval_active(base_dir, nickname, domain) - artist = is_artist(base_dir, nickname) + artist: bool = is_artist(base_dir, nickname) return html_timeline(default_timeline, recent_posts_cache, max_recent_posts, translate, page_number, diff --git a/webapp_utils.py b/webapp_utils.py index afe2374d9..1eb12c5bf 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -77,11 +77,11 @@ def minimizing_attached_images(base_dir: str, nickname: str, domain: str, if following_nickname == nickname and following_domain == domain: # reminder post return False - minimize_filename = \ + minimize_filename: str = \ acct_dir(base_dir, nickname, domain) + '/followingMinimizeImages.txt' - handle = following_nickname + '@' + following_domain + handle: str = following_nickname + '@' + following_domain if not is_a_file(minimize_filename): - following_filename = \ + following_filename: str = \ acct_dir(base_dir, nickname, domain) + '/following.txt' if not is_a_file(following_filename): return False @@ -103,21 +103,22 @@ def get_broken_link_substitute() -> str: def html_following_list(base_dir: str, following_filename: str) -> str: """Returns a list of handles being followed """ - msg = load_string(following_filename, - 'EX: html_following_list unable to read ' + - following_filename) + msg: str = \ + load_string(following_filename, + 'EX: html_following_list unable to read ' + + following_filename) if msg: - following_list = msg.split('\n') + following_list: list[str] = msg.split('\n') following_list.sort() if following_list: - css_filename = base_dir + '/epicyon-profile.css' + css_filename: str = base_dir + '/epicyon-profile.css' if is_a_file(base_dir + '/epicyon.css'): css_filename = base_dir + '/epicyon.css' - instance_title = \ + instance_title: str = \ get_config_param(base_dir, 'instanceTitle') preload_images: list[str] = [] - following_list_html = \ + following_list_html: str = \ html_header_with_external_style(css_filename, instance_title, None, preload_images) @@ -135,11 +136,12 @@ def csv_following_list(following_filename: str, base_dir: str, nickname: str, domain: str) -> str: """Returns a csv of handles being followed """ - msg = load_string(following_filename, - 'EX: csv_following_list unable to read ' + - following_filename) + msg: str = \ + load_string(following_filename, + 'EX: csv_following_list unable to read ' + + following_filename) if msg: - following_list = msg.split('\n') + following_list: list[str] = msg.split('\n') following_list.sort() if following_list: following_list_csv: str = '' @@ -147,23 +149,23 @@ def csv_following_list(following_filename: str, if not following_address: continue - following_nickname = \ + following_nickname: str = \ get_nickname_from_actor(following_address) following_domain, _ = \ get_domain_from_actor(following_address) - announce_is_allowed = \ + announce_is_allowed: bool = \ allowed_announce(base_dir, nickname, domain, following_nickname, following_domain) - notify_on_new = 'false' + notify_on_new: str = 'false' languages: str = '' - person_notes = \ + person_notes: str = \ get_person_notes(base_dir, nickname, domain, following_address) if person_notes: # make notes suitable for csv file - replacements = { + replacements: dict = { ',': ' ', '"': "'", '\n': '
', @@ -189,14 +191,14 @@ def html_hashtag_blocked(base_dir: str, translate: {}) -> str: """Show the screen for a blocked hashtag """ blocked_hashtag_form: str = '' - css_filename = base_dir + '/epicyon-suspended.css' + css_filename: str = base_dir + '/epicyon-suspended.css' if is_a_file(base_dir + '/suspended.css'): css_filename = base_dir + '/suspended.css' - instance_title = \ + instance_title: str = \ get_config_param(base_dir, 'instanceTitle') preload_images: list[str] = [] - blocked_hashtag_form = \ + blocked_hashtag_form: str = \ html_header_with_external_style(css_filename, instance_title, None, preload_images) blocked_hashtag_form += '
\n' @@ -275,7 +277,7 @@ def get_show_map_button(post_id: str, translate: {}, map_content: str) -> str: """Returns the markup for a "show map" button """ - show_map_str = 'Show Map' + show_map_str: str = 'Show Map' if translate.get('Show Map'): show_map_str = translate['Show Map'] html_str = '
' + \ @@ -289,7 +291,7 @@ def open_content_warning(text: str, translate: {}) -> str: """Opens content warning when replying to a post with a cw so that you can see what you are replying to """ - text = replace_embedded_map_with_link(text, translate) + text: str = replace_embedded_map_with_link(text, translate) text = text.replace('
', '').replace('
', '') text = text.replace(translate['Show Map'], '', 1) text = text.replace(translate['SHOW MORE'], '', 1) @@ -303,7 +305,7 @@ def _set_actor_property_url(actor_json: {}, if not actor_json.get('attachment'): actor_json['attachment']: list[dict] = [] - property_name_lower = property_name.lower() + property_name_lower: str = property_name.lower() # remove any existing value property_found = None @@ -397,17 +399,17 @@ def update_avatar_image_cache(signing_priv_key_pem: str, """ if not avatar_url: return None - actor_str = actor.replace('/', '-') - avatar_image_path = base_dir + '/cache/avatars/' + actor_str + actor_str: str = actor.replace('/', '-') + avatar_image_path: str = base_dir + '/cache/avatars/' + actor_str # try different image types image_formats = image_mime_types_dict() - avatar_image_filename = None + avatar_image_filename: str = None session_headers = None for im_format, mime_type in image_formats.items(): if avatar_url.endswith('.' + im_format) or \ '.' + im_format + '?' in avatar_url: - session_headers = { + session_headers: dict = { 'Accept': 'image/' + mime_type } avatar_image_filename = avatar_image_path + '.' + im_format @@ -454,7 +456,7 @@ def update_avatar_image_cache(signing_priv_key_pem: str, except BaseException as ex: print('EX: Failed to download avatar image: ' + str(avatar_url) + ' ' + str(ex)) - prof = 'https://www.w3.org/ns/activitystreams' + prof: str = 'https://www.w3.org/ns/activitystreams' if '/channel/' not in actor or '/accounts/' not in actor: session_headers = { 'Accept': 'application/activity+json; profile="' + prof + '"' @@ -463,13 +465,15 @@ def update_avatar_image_cache(signing_priv_key_pem: str, session_headers = { 'Accept': 'application/ld+json; profile="' + prof + '"' } - person_json = \ + person_json: dict = \ get_json(signing_priv_key_pem, session, actor, session_headers, None, debug, mitm_servers, __version__, http_prefix, None) if get_json_valid(person_json): if not person_json.get('id'): return None + if not isinstance(person_json['id'], str): + return None pub_key, _ = get_actor_public_key_from_id(person_json, None) if not pub_key: return None @@ -495,7 +499,7 @@ def update_avatar_image_cache(signing_priv_key_pem: str, def scheduled_posts_exist(base_dir: str, nickname: str, domain: str) -> bool: """Returns true if there are posts scheduled to be delivered """ - schedule_index_filename = \ + schedule_index_filename: str = \ acct_dir(base_dir, nickname, domain) + '/schedule.index' if not is_a_file(schedule_index_filename): return False @@ -513,27 +517,29 @@ def shares_timeline_json(actor: str, page_number: int, items_per_page: int, max_shares_per_account helps to avoid one person dominating the timeline by sharing a large number of things """ - all_shares_json = {} - dir_str = data_dir(base_dir) + all_shares_json: dict = {} + dir_str: str = data_dir(base_dir) for _, dirs, files in os.walk(dir_str): for handle in dirs: if not is_account_dir(handle): continue - account_dir = acct_handle_dir(base_dir, handle) - shares_filename = account_dir + '/' + shares_file_type + '.json' + account_dir: str = acct_handle_dir(base_dir, handle) + shares_filename: str = \ + account_dir + '/' + shares_file_type + '.json' if not is_a_file(shares_filename): continue shares_json = load_json(shares_filename) if not shares_json: continue - account_nickname = handle.split('@')[0] + account_nickname: str = handle.split('@')[0] # Don't include shared items from blocked accounts if account_nickname != nickname: if is_blocked(base_dir, nickname, domain, account_nickname, domain, None, None): continue # actor who owns this share - owner = actor.split('/users/')[0] + '/users/' + account_nickname + owner: str = \ + actor.split('/users/')[0] + '/users/' + account_nickname ctr: int = 0 for item_id, item in shares_json.items(): # assign owner to the item @@ -546,9 +552,9 @@ def shares_timeline_json(actor: str, page_number: int, items_per_page: int, break if shared_items_federated_domains: if shares_file_type == 'shares': - catalogs_dir = base_dir + '/cache/catalogs' + catalogs_dir: str = base_dir + '/cache/catalogs' else: - catalogs_dir = base_dir + '/cache/wantedItems' + catalogs_dir: str = base_dir + '/cache/wantedItems' if is_a_dir(catalogs_dir): for _, dirs, files in os.walk(catalogs_dir): for fname in files: @@ -556,11 +562,11 @@ def shares_timeline_json(actor: str, page_number: int, items_per_page: int, continue if not fname.endswith('.' + shares_file_type + '.json'): continue - federated_domain = fname.split('.')[0] + federated_domain: str = fname.split('.')[0] if federated_domain not in shared_items_federated_domains: continue - shares_filename = catalogs_dir + '/' + fname - shares_json = load_json(shares_filename) + shares_filename: str = catalogs_dir + '/' + fname + shares_json: dict = load_json(shares_filename) if not shares_json: continue ctr: int = 0 @@ -568,14 +574,15 @@ def shares_timeline_json(actor: str, page_number: int, items_per_page: int, # assign owner to the item if '--shareditems--' not in item_id: continue - share_actor = item_id.split('--shareditems--')[0] - replacements = { + share_actor: str = item_id.split('--shareditems--')[0] + replacements: dict = { '___': '://', '--': '/' } - share_actor = \ + share_actor: str = \ replace_strings(share_actor, replacements) - share_nickname = get_nickname_from_actor(share_actor) + share_nickname: str = \ + get_nickname_from_actor(share_actor) if not share_nickname: continue if is_blocked(base_dir, nickname, domain, @@ -590,9 +597,10 @@ def shares_timeline_json(actor: str, page_number: int, items_per_page: int, break break # sort the shared items in descending order of publication date - shares_json = OrderedDict(sorted(all_shares_json.items(), reverse=True)) + shares_json: dict = \ + OrderedDict(sorted(all_shares_json.items(), reverse=True)) last_page: bool = False - start_index = items_per_page * page_number + start_index: int = items_per_page * page_number max_index = len(shares_json.items()) if max_index < items_per_page: last_page = True @@ -629,11 +637,11 @@ def get_shares_collection(actor: str, page_number: int, items_per_page: int, shared_items_federated_domains, shares_file_type) if shares_file_type == 'shares': - share_type = 'offer' - collection_name = nickname + "'s Shared Items" + share_type: str = 'offer' + collection_name: str = nickname + "'s Shared Items" else: - share_type = 'request' - collection_name = nickname + "'s Wanted Items" + share_type: str = 'request' + collection_name: str = nickname + "'s Wanted Items" for share_id, shared_item in shares_json.items(): shared_item['shareId'] = share_id @@ -642,7 +650,7 @@ def get_shares_collection(actor: str, page_number: int, items_per_page: int, if offer_item: shares_collection.append(offer_item) - result_json = { + result_json: dict = { "@context": [ 'https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1' @@ -665,6 +673,8 @@ def post_contains_public(post_json_object: {}) -> bool: return contains_public for to_address in post_json_object['object']['to']: + if not isinstance(to_address, str): + continue if to_address.endswith('#Public') or \ to_address == 'as:Public' or \ to_address == 'Public': @@ -673,6 +683,8 @@ def post_contains_public(post_json_object: {}) -> bool: if not contains_public: if post_json_object['object'].get('cc'): for to_address2 in post_json_object['object']['cc']: + if not isinstance(to_address2, str): + continue if to_address2.endswith('#Public') or \ to_address2 == 'as:Public' or \ to_address2 == 'Public': @@ -685,7 +697,7 @@ def get_banner_file(base_dir: str, nickname: str, domain: str, theme: str) -> (str, str): """Gets the image for the timeline banner """ - account_dir = acct_dir(base_dir, nickname, domain) + account_dir: str = acct_dir(base_dir, nickname, domain) banner_file, banner_filename = \ get_image_file(base_dir, 'banner', account_dir, theme) return banner_file, banner_filename @@ -696,7 +708,7 @@ def get_profile_background_file(base_dir: str, theme: str) -> (str, str): """Gets the image for the profile background """ - account_dir = acct_dir(base_dir, nickname, domain) + account_dir: str = acct_dir(base_dir, nickname, domain) banner_file, banner_filename = \ get_image_file(base_dir, 'image', account_dir, theme) return banner_file, banner_filename @@ -707,7 +719,7 @@ def get_search_banner_file(base_dir: str, theme: str) -> (str, str): """Gets the image for the search banner """ - account_dir = acct_dir(base_dir, nickname, domain) + account_dir: str = acct_dir(base_dir, nickname, domain) banner_file, banner_filename = \ get_image_file(base_dir, 'search_banner', account_dir, theme) return banner_file, banner_filename @@ -717,7 +729,7 @@ def get_left_image_file(base_dir: str, nickname: str, domain: str, theme: str) -> (str, str): """Gets the image for the left column """ - account_dir = acct_dir(base_dir, nickname, domain) + account_dir: str = acct_dir(base_dir, nickname, domain) banner_file, banner_filename = \ get_image_file(base_dir, 'left_col_image', account_dir, theme) return banner_file, banner_filename @@ -727,7 +739,7 @@ def get_right_image_file(base_dir: str, nickname: str, domain: str, theme: str) -> (str, str): """Gets the image for the right column """ - account_dir = acct_dir(base_dir, nickname, domain) + account_dir: str = acct_dir(base_dir, nickname, domain) banner_file, banner_filename = \ get_image_file(base_dir, 'right_col_image', account_dir, theme) return banner_file, banner_filename @@ -737,8 +749,8 @@ def html_header_with_external_style(css_filename: str, instance_title: str, metadata: str, preload_images: [], lang: str = 'en') -> str: if metadata is None: - metadata: str = '' - css_file = '/' + css_filename.split('/')[-1] + metadata = '' + css_file: str = '/' + css_filename.split('/')[-1] pwa_theme_color, pwa_theme_background_color = \ get_pwa_theme_colors(css_filename) preload_images_str: str = '' @@ -747,7 +759,7 @@ def html_header_with_external_style(css_filename: str, instance_title: str, preload_images_str += \ ' \n' - html_str = \ + html_str: str = \ '\n' + \ '