diff --git a/daemon.py b/daemon.py index 36f7f44f9..8994e1d35 100644 --- a/daemon.py +++ b/daemon.py @@ -178,6 +178,7 @@ from webapp_podcast import html_podcast_episode from webapp_theme_designer import html_theme_designer from webapp_minimalbutton import set_minimal from webapp_minimalbutton import is_minimal +from webapp_utils import load_buy_sites from webapp_utils import get_default_path from webapp_utils import get_avatar_image_url from webapp_utils import html_hashtag_blocked @@ -483,7 +484,8 @@ class PubServer(BaseHTTPRequestHandler): theme_name: str, max_like_count: int, cw_lists: {}, dogwhistles: {}, min_images_for_accounts: [], - max_hashtags: int) -> None: + max_hashtags: int, + buy_sites: {}) -> None: """When an edited post is created this assigns a published and updated date to it, and uses the previous id @@ -531,7 +533,7 @@ class PubServer(BaseHTTPRequestHandler): theme_name, max_like_count, cw_lists, dogwhistles, min_images_for_accounts, - max_hashtags) + max_hashtags, buy_sites) # update the index id_str = edited_postid.split('/')[-1] @@ -665,6 +667,7 @@ class PubServer(BaseHTTPRequestHandler): event_end_time = None location = None conversation_id = None + buy_url = '' city = get_spoofed_city(self.server.city, self.server.base_dir, nickname, self.server.domain) @@ -698,7 +701,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.low_bandwidth, self.server.content_license_url, languages_understood, False, - self.server.translate) + self.server.translate, buy_url) if message_json: # NOTE: content and contentMap are not required, but we will keep # them in there so that the post does not get filtered out by @@ -1876,7 +1879,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, self.server.content_license_url, self.server.dogwhistles, - self.server.min_images_for_accounts) + self.server.min_images_for_accounts, + self.server.buy_sites) def _get_outbox_thread_index(self, nickname: str, max_outbox_threads_per_account: int) -> int: @@ -3240,7 +3244,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.i2p_domain, bold_reading, self.server.dogwhistles, - min_images_for_accounts) + min_images_for_accounts, + self.server.buy_sites) if profile_str: msg = profile_str.encode('utf-8') msglen = len(msg) @@ -3667,6 +3672,7 @@ class PubServer(BaseHTTPRequestHandler): if self.server.default_post_language.get(nickname): default_post_language = \ self.server.default_post_language[nickname] + default_buy_site = '' msg = \ html_new_post({}, False, self.server.translate, base_dir, @@ -3707,7 +3713,9 @@ class PubServer(BaseHTTPRequestHandler): bold_reading, self.server.dogwhistles, self.server.min_images_for_accounts, - None, None, default_post_language) + None, None, default_post_language, + self.server.buy_sites, + default_buy_site) if msg: msg = msg.encode('utf-8') msglen = len(msg) @@ -3827,6 +3835,7 @@ class PubServer(BaseHTTPRequestHandler): if self.server.default_post_language.get(nickname): default_post_language = \ self.server.default_post_language[nickname] + default_buy_site = '' msg = \ html_new_post({}, False, self.server.translate, base_dir, @@ -3866,7 +3875,9 @@ class PubServer(BaseHTTPRequestHandler): bold_reading, self.server.dogwhistles, self.server.min_images_for_accounts, - None, None, default_post_language) + None, None, default_post_language, + self.server.buy_sites, + default_buy_site) if msg: msg = msg.encode('utf-8') msglen = len(msg) @@ -4444,7 +4455,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.map_format, self.server.access_keys, 'search', - self.server.min_images_for_accounts) + self.server.min_images_for_accounts, + self.server.buy_sites) if hashtag_str: msg = hashtag_str.encode('utf-8') msglen = len(msg) @@ -4555,7 +4567,8 @@ class PubServer(BaseHTTPRequestHandler): timezone, bold_reading, self.server.dogwhistles, self.server.access_keys, - self.server.min_images_for_accounts) + self.server.min_images_for_accounts, + self.server.buy_sites) if history_str: msg = history_str.encode('utf-8') msglen = len(msg) @@ -4638,7 +4651,8 @@ class PubServer(BaseHTTPRequestHandler): timezone, bold_reading, self.server.dogwhistles, self.server.access_keys, - self.server.min_images_for_accounts) + self.server.min_images_for_accounts, + self.server.buy_sites) if bookmarks_str: msg = bookmarks_str.encode('utf-8') msglen = len(msg) @@ -4817,7 +4831,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.i2p_domain, bold_reading, self.server.dogwhistles, - min_images_for_accounts) + min_images_for_accounts, + self.server.buy_sites) if profile_str: msg = profile_str.encode('utf-8') msglen = len(msg) @@ -7867,6 +7882,44 @@ class PubServer(BaseHTTPRequestHandler): set_config_param(base_dir, 'crawlersAllowed', crawlers_allowed_str) + # save allowed buy domains + buy_sites = {} + if fields.get('buySitesStr'): + buy_sites_str = \ + fields['buySitesStr'] + buy_sites_list = \ + buy_sites_str.split('\n') + for site_url in buy_sites_list: + if ' ' in site_url: + site_url = site_url.split(' ')[-1] + buy_icon_text = \ + site_url.replace(site_url, '').strip() + if not buy_icon_text: + buy_icon_text = site_url + else: + buy_icon_text = site_url + if buy_sites.get(buy_icon_text): + continue + if '<' in site_url: + continue + if not site_url.strip(): + continue + buy_sites[buy_icon_text] = site_url.strip() + if str(self.server.buy_sites) != \ + str(buy_sites): + self.server.buy_sites = buy_sites + buy_sites_filename = \ + base_dir + '/accounts/buy_sites.json' + if buy_sites: + save_json(buy_sites, buy_sites_filename) + else: + if os.path.isfile(buy_sites_filename): + try: + os.remove(buy_sites_filename) + except OSError: + print('EX: unable to delete ' + + buy_sites_filename) + # save peertube instances list peertube_instances_file = \ base_dir + '/accounts/peertube.txt' @@ -9201,7 +9254,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.map_format, self.server.access_keys, 'search', - self.server.min_images_for_accounts) + self.server.min_images_for_accounts, + self.server.buy_sites) if hashtag_str: msg = hashtag_str.encode('utf-8') msglen = len(msg) @@ -9522,7 +9576,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, mitm, bold_reading, self.server.dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + self.server.buy_sites) actor_absolute = self._get_instance_url(calling_domain) + actor @@ -10088,7 +10143,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, mitm, bold_reading, self.server.dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + self.server.buy_sites) else: print('WARN: Liked post not found: ' + liked_post_filename) # clear the icon from the cache so that it gets updated @@ -10292,7 +10348,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, mitm, bold_reading, self.server.dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + self.server.buy_sites) else: print('WARN: Unliked post not found: ' + liked_post_filename) # clear the icon from the cache so that it gets updated @@ -10525,7 +10582,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, mitm, bold_reading, self.server.dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + self.server.buy_sites) else: print('WARN: Emoji reaction post not found: ' + reaction_post_filename) @@ -10748,7 +10806,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, mitm, bold_reading, self.server.dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + self.server.buy_sites) else: print('WARN: Unreaction post not found: ' + reaction_post_filename) @@ -10857,7 +10916,8 @@ class PubServer(BaseHTTPRequestHandler): timeline_str, page_number, timezone, bold_reading, self.server.dogwhistles, - self.server.min_images_for_accounts) + self.server.min_images_for_accounts, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -11016,7 +11076,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, mitm, bold_reading, self.server.dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + self.server.buy_sites) else: print('WARN: Bookmarked post not found: ' + bookmark_filename) # self._post_to_outbox(bookmark_json, @@ -11184,7 +11245,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, mitm, bold_reading, self.server.dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + self.server.buy_sites) else: print('WARN: Unbookmarked post not found: ' + bookmark_filename) @@ -11298,7 +11360,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.lists_enabled, self.server.dogwhistles, - self.server.min_images_for_accounts) + self.server.min_images_for_accounts, + self.server.buy_sites) if delete_str: delete_str_len = len(delete_str) self._set_headers('text/html', delete_str_len, @@ -11439,7 +11502,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, mitm, bold_reading, self.server.dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + self.server.buy_sites) else: print('WARN: Muted post not found: ' + mute_filename) @@ -11583,7 +11647,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, mitm, bold_reading, self.server.dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + self.server.buy_sites) else: print('WARN: Unmuted post not found: ' + mute_filename) if calling_domain.endswith('.onion') and onion_domain: @@ -11714,7 +11779,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, bold_reading, self.server.dogwhistles, - self.server.min_images_for_accounts) + self.server.min_images_for_accounts, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -11828,7 +11894,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, bold_reading, self.server.dogwhistles, - self.server.min_images_for_accounts) + self.server.min_images_for_accounts, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -11948,7 +12015,8 @@ class PubServer(BaseHTTPRequestHandler): None, None, self.server.cw_lists, self.server.lists_enabled, self.server.content_license_url, - timezone, bold_reading) + timezone, bold_reading, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -12074,7 +12142,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.lists_enabled, content_license_url, - timezone, bold_reading) + timezone, bold_reading, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -12173,7 +12242,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.dogwhistles, self.server.access_keys, self.server.min_images_for_accounts, - self.server.debug) + self.server.debug, + self.server.buy_sites) if conv_str: msg = conv_str.encode('utf-8') msglen = len(msg) @@ -12336,7 +12406,8 @@ class PubServer(BaseHTTPRequestHandler): 'inbox', self.server.default_timeline, bold_reading, self.server.dogwhistles, - self.server.min_images_for_accounts) + self.server.min_images_for_accounts, + self.server.buy_sites) if not msg: self._404() return True @@ -12402,6 +12473,7 @@ class PubServer(BaseHTTPRequestHandler): 'inbox', self.server.default_timeline, bold_reading, self.server.dogwhistles, self.server.min_images_for_accounts, + self.server.buy_sites, 'shares') if not msg: self._404() @@ -12491,7 +12563,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.lists_enabled, timezone, mitm, bold_reading, self.server.dogwhistles, - self.server.min_images_for_accounts) + self.server.min_images_for_accounts, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -12817,7 +12890,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.dogwhistles, ua_str, self.server.min_images_for_accounts, - reverse_sequence, last_post_id) + reverse_sequence, last_post_id, + self.server.buy_sites) if getreq_start_time: fitness_performance(getreq_start_time, self.server.fitness, @@ -13000,7 +13074,8 @@ class PubServer(BaseHTTPRequestHandler): timezone, bold_reading, self.server.dogwhistles, ua_str, self.server.min_images_for_accounts, - reverse_sequence, last_post_id) + reverse_sequence, last_post_id, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -13174,7 +13249,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.dogwhistles, ua_str, self.server.min_images_for_accounts, - reverse_sequence, last_post_id) + reverse_sequence, last_post_id, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -13344,7 +13420,8 @@ class PubServer(BaseHTTPRequestHandler): timezone, bold_reading, self.server.dogwhistles, ua_str, self.server.min_images_for_accounts, - reverse_sequence, last_post_id) + reverse_sequence, last_post_id, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -13514,7 +13591,8 @@ class PubServer(BaseHTTPRequestHandler): timezone, bold_reading, self.server.dogwhistles, ua_str, self.server.min_images_for_accounts, - reverse_sequence, last_post_id) + reverse_sequence, last_post_id, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -13687,7 +13765,8 @@ class PubServer(BaseHTTPRequestHandler): timezone, bold_reading, self.server.dogwhistles, ua_str, self.server.min_images_for_accounts, - reverse_sequence) + reverse_sequence, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -13864,7 +13943,8 @@ class PubServer(BaseHTTPRequestHandler): timezone, bold_reading, self.server.dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence) + reverse_sequence, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -13992,7 +14072,8 @@ class PubServer(BaseHTTPRequestHandler): bold_reading, self.server.dogwhistles, ua_str, self.server.min_images_for_accounts, - reverse_sequence) + reverse_sequence, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -14093,7 +14174,8 @@ class PubServer(BaseHTTPRequestHandler): timezone, bold_reading, self.server.dogwhistles, ua_str, self.server.min_images_for_accounts, - reverse_sequence) + reverse_sequence, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -14235,7 +14317,8 @@ class PubServer(BaseHTTPRequestHandler): timezone, bold_reading, self.server.dogwhistles, ua_str, self.server.min_images_for_accounts, - reverse_sequence) + reverse_sequence, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -14395,7 +14478,8 @@ class PubServer(BaseHTTPRequestHandler): timezone, bold_reading, self.server.dogwhistles, ua_str, self.server.min_images_for_accounts, - reverse_sequence) + reverse_sequence, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -14552,7 +14636,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence) + reverse_sequence, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -14696,7 +14781,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.lists_enabled, self.server.content_license_url, - timezone, bold_reading) + timezone, bold_reading, + self.server.buy_sites) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -14838,7 +14924,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.lists_enabled, content_license_url, - timezone, bold_reading).encode('utf-8') + timezone, bold_reading, + self.server.buy_sites).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, calling_domain, False) @@ -14974,7 +15061,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.lists_enabled, content_license_url, - timezone, bold_reading).encode('utf-8') + timezone, bold_reading, + self.server.buy_sites).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, calling_domain, False) @@ -15114,7 +15202,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.lists_enabled, content_license_url, - timezone, bold_reading).encode('utf-8') + timezone, bold_reading, + self.server.buy_sites).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, calling_domain, False) @@ -15255,7 +15344,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.lists_enabled, content_license_url, - timezone, bold_reading).encode('utf-8') + timezone, bold_reading, + self.server.buy_sites).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, calling_domain, False) @@ -15422,7 +15512,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.lists_enabled, self.server.content_license_url, - timezone, bold_reading).encode('utf-8') + timezone, bold_reading, + self.server.buy_sites).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, calling_domain, False) @@ -16220,7 +16311,7 @@ class PubServer(BaseHTTPRequestHandler): nickname, self.server.domain_full, self.server.person_cache) - + default_buy_site = '' msg = \ html_new_post(edit_post_params, media_instance, translate, @@ -16264,7 +16355,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.dogwhistles, self.server.min_images_for_accounts, new_post_month, new_post_year, - default_post_language) + default_post_language, + self.server.buy_sites, + default_buy_site) if not msg: print('Error replying to ' + in_reply_to_url) self._404() @@ -16343,7 +16436,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.system_language, self.server.min_images_for_accounts, self.server.max_recent_posts, - self.server.reverse_sequence) + self.server.reverse_sequence, + self.server.buy_sites) if msg: msg = msg.encode('utf-8') msglen = len(msg) @@ -18634,7 +18728,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.bold_reading, self.server.dogwhistles, self.server.min_images_for_accounts, - self.server.debug) + self.server.debug, + self.server.buy_sites) if msg: msg = msg.encode('utf-8') msglen = len(msg) @@ -20625,6 +20720,10 @@ class PubServer(BaseHTTPRequestHandler): else: comments_enabled = True + buy_url = '' + if fields.get('buyUrl'): + buy_url = fields['buyUrl'] + if post_type == 'newpost': if not fields.get('pinToProfile'): pin_to_profile = False @@ -20675,7 +20774,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.low_bandwidth, self.server.content_license_url, languages_understood, - self.server.translate) + self.server.translate, buy_url) if message_json: if edited_postid: recent_posts_cache = self.server.recent_posts_cache @@ -20722,7 +20821,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.dogwhistles, min_images_for_accounts, - self.server.max_hashtags) + self.server.max_hashtags, + self.server.buy_sites) print('DEBUG: sending edited public post ' + str(message_json)) if fields['schedulePost']: @@ -20814,7 +20914,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.low_bandwidth, self.server.content_license_url, languages_understood, - self.server.translate) + self.server.translate, buy_url) if message_json: if fields['schedulePost']: return 1 @@ -20979,7 +21079,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.low_bandwidth, self.server.content_license_url, languages_understood, - self.server.translate) + self.server.translate, buy_url) if message_json: if edited_postid: recent_posts_cache = self.server.recent_posts_cache @@ -21026,7 +21126,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.dogwhistles, min_images_for_accounts, - self.server.max_hashtags) + self.server.max_hashtags, + self.server.buy_sites) print('DEBUG: sending edited unlisted post ' + str(message_json)) @@ -21089,7 +21190,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.low_bandwidth, self.server.content_license_url, languages_understood, - self.server.translate) + self.server.translate, + buy_url) if message_json: if edited_postid: recent_posts_cache = self.server.recent_posts_cache @@ -21136,7 +21238,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.dogwhistles, min_images_for_accounts, - self.server.max_hashtags) + self.server.max_hashtags, + self.server.buy_sites) print('DEBUG: sending edited followers post ' + str(message_json)) @@ -21211,7 +21314,8 @@ class PubServer(BaseHTTPRequestHandler): content_license_url, languages_understood, reply_is_chat, - self.server.translate) + self.server.translate, + buy_url) if message_json: print('DEBUG: posting DM edited_postid ' + str(edited_postid)) @@ -21260,7 +21364,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.dogwhistles, min_images_for_accounts, - self.server.max_hashtags) + self.server.max_hashtags, + self.server.buy_sites) print('DEBUG: sending edited dm post ' + str(message_json)) @@ -21326,7 +21431,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.low_bandwidth, self.server.content_license_url, languages_understood, - False, self.server.translate) + False, self.server.translate, + buy_url) if message_json: if fields['schedulePost']: return 1 @@ -21378,7 +21484,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.cw_lists, self.server.dogwhistles, min_images_for_accounts, - self.server.max_hashtags) + self.server.max_hashtags, + self.server.buy_sites) print('DEBUG: sending edited reminder post ' + str(message_json)) if self._post_to_outbox(message_json, @@ -22928,6 +23035,9 @@ def run_daemon(max_hashtags: int, # scan the theme directory for any svg files containing scripts assert not scan_themes_for_scripts(base_dir) + # permitted sites from which the buy button may be displayed + httpd.buy_sites = load_buy_sites(base_dir) + # which accounts should minimize all attached images by default httpd.min_images_for_accounts = load_min_images_for_accounts(base_dir) diff --git a/desktop_client.py b/desktop_client.py index e7d4f6de6..5c3c7e5b2 100644 --- a/desktop_client.py +++ b/desktop_client.py @@ -528,6 +528,7 @@ def _desktop_reply_to_post(session, post_id: str, event_time = None event_end_time = None location = None + buy_url = '' _say_command(say_str, say_str, screenreader, system_language, espeak) if send_post_via_server(signing_priv_key_pem, __version__, base_dir, session, nickname, password, @@ -540,7 +541,7 @@ def _desktop_reply_to_post(session, post_id: str, system_language, languages_understood, low_bandwidth, content_license_url, event_date, event_time, event_end_time, location, - translate, debug, post_id, post_id, + translate, buy_url, debug, post_id, post_id, conversation_id, subject) == 0: say_str = 'Reply sent' else: @@ -602,6 +603,7 @@ def _desktop_new_post(session, event_time = None event_end_time = None location = None + buy_url = '' _say_command(say_str, say_str, screenreader, system_language, espeak) if send_post_via_server(signing_priv_key_pem, __version__, base_dir, session, nickname, password, @@ -614,7 +616,7 @@ def _desktop_new_post(session, system_language, languages_understood, low_bandwidth, content_license_url, event_date, event_time, event_end_time, location, - translate, debug, None, None, + translate, buy_url, debug, None, None, conversation_id, subject) == 0: say_str = 'Post sent' else: @@ -1345,6 +1347,7 @@ def _desktop_new_dm_base(session, to_handle: str, event_time = None event_end_time = None location = None + buy_url = '' say_str = 'Sending' _say_command(say_str, say_str, screenreader, system_language, espeak) @@ -1359,7 +1362,7 @@ def _desktop_new_dm_base(session, to_handle: str, system_language, languages_understood, low_bandwidth, content_license_url, event_date, event_time, event_end_time, location, - translate, debug, None, None, + translate, buy_url, debug, None, None, conversation_id, subject) == 0: say_str = 'Direct message sent' else: diff --git a/epicyon.py b/epicyon.py index 9bb1c7126..0bbacdceb 100644 --- a/epicyon.py +++ b/epicyon.py @@ -149,6 +149,9 @@ def _command_options() -> None: parser.add_argument('--eventLocation', type=str, default=None, help='Location for an event when sending a c2s post') + parser.add_argument('--buyUrl', type=str, + default=None, + help='Link for buying something') parser.add_argument('--content_license_url', type=str, default='https://creativecommons.org/' + 'licenses/by-nc/4.0', @@ -1702,7 +1705,7 @@ def _command_options() -> None: argb.low_bandwidth, argb.content_license_url, argb.eventDate, argb.eventTime, argb.eventEndTime, - argb.eventLocation, translate, + argb.eventLocation, translate, argb.buyUrl, argb.debug, reply_to, reply_to, argb.conversationId, subject) for _ in range(10): @@ -3375,6 +3378,7 @@ def _command_options() -> None: low_bandwidth = False languages_understood = [argb.language] translate = {} + buy_url = '' create_public_post(base_dir, nickname, domain, port, http_prefix, "like this is totally just a #test man", @@ -3389,7 +3393,7 @@ def _command_options() -> None: test_event_end_time, test_location, test_is_article, argb.language, conversation_id, low_bandwidth, argb.content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) create_public_post(base_dir, nickname, domain, port, http_prefix, "Zoiks!!!", test_save_to_file, @@ -3403,7 +3407,7 @@ def _command_options() -> None: test_event_end_time, test_location, test_is_article, argb.language, conversation_id, low_bandwidth, argb.content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) create_public_post(base_dir, nickname, domain, port, http_prefix, "Hey scoob we need like a hundred more #milkshakes", test_save_to_file, @@ -3417,7 +3421,7 @@ def _command_options() -> None: test_event_end_time, test_location, test_is_article, argb.language, conversation_id, low_bandwidth, argb.content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) create_public_post(base_dir, nickname, domain, port, http_prefix, "Getting kinda spooky around here", test_save_to_file, @@ -3431,7 +3435,7 @@ def _command_options() -> None: test_event_end_time, test_location, test_is_article, argb.language, conversation_id, low_bandwidth, argb.content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) create_public_post(base_dir, nickname, domain, port, http_prefix, "And they would have gotten away with it too" + "if it wasn't for those pesky hackers", @@ -3446,7 +3450,7 @@ def _command_options() -> None: test_event_end_time, test_location, test_is_article, argb.language, conversation_id, low_bandwidth, argb.content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) create_public_post(base_dir, nickname, domain, port, http_prefix, "man these centralized sites are like the worst!", test_save_to_file, @@ -3460,7 +3464,7 @@ def _command_options() -> None: test_event_end_time, test_location, test_is_article, argb.language, conversation_id, low_bandwidth, argb.content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) create_public_post(base_dir, nickname, domain, port, http_prefix, "another mystery solved #test", test_save_to_file, @@ -3474,7 +3478,7 @@ def _command_options() -> None: test_event_end_time, test_location, test_is_article, argb.language, conversation_id, low_bandwidth, argb.content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) create_public_post(base_dir, nickname, domain, port, http_prefix, "let's go bowling", test_save_to_file, @@ -3488,7 +3492,7 @@ def _command_options() -> None: test_event_end_time, test_location, test_is_article, argb.language, conversation_id, low_bandwidth, argb.content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) domain_full = domain + ':' + str(port) clear_follows(base_dir, nickname, domain) follow_person(base_dir, nickname, domain, 'maxboardroom', domain_full, diff --git a/inbox.py b/inbox.py index a6295f20a..66c1f36f0 100644 --- a/inbox.py +++ b/inbox.py @@ -452,7 +452,8 @@ def _inbox_store_post_to_html_cache(recent_posts_cache: {}, mitm: bool, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> None: + min_images_for_accounts: [], + buy_sites: {}) -> None: """Converts the json post into html and stores it in a cache This enables the post to be quickly displayed later """ @@ -482,7 +483,7 @@ def _inbox_store_post_to_html_cache(recent_posts_cache: {}, not_dm, True, True, False, True, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, minimize_all_images, - None) + None, buy_sites) def valid_inbox(base_dir: str, nickname: str, domain: str) -> bool: @@ -1257,7 +1258,8 @@ def receive_edit_to_post(recent_posts_cache: {}, message_json: {}, theme_name: str, max_like_count: int, cw_lists: {}, dogwhistles: {}, min_images_for_accounts: [], - max_hashtags: int) -> bool: + max_hashtags: int, + buy_sites: {}) -> bool: """A post was edited """ if not has_object_dict(message_json): @@ -1392,7 +1394,8 @@ def receive_edit_to_post(recent_posts_cache: {}, message_json: {}, False, True, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) return True @@ -1414,7 +1417,8 @@ def _receive_update_activity(recent_posts_cache: {}, session, base_dir: str, theme_name: str, max_like_count: int, cw_lists: {}, dogwhistles: {}, min_images_for_accounts: [], - max_hashtags: int) -> bool: + max_hashtags: int, + buy_sites: {}) -> bool: """Receives an Update activity within the POST section of HTTPServer """ @@ -1456,7 +1460,7 @@ def _receive_update_activity(recent_posts_cache: {}, session, base_dir: str, theme_name, max_like_count, cw_lists, dogwhistles, min_images_for_accounts, - max_hashtags): + max_hashtags, buy_sites): print('EDITPOST: received ' + message_json['object']['id']) return True else: @@ -1507,7 +1511,8 @@ def _receive_like(recent_posts_cache: {}, max_like_count: int, cw_lists: {}, lists_enabled: str, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> bool: + min_images_for_accounts: [], + buy_sites: {}) -> bool: """Receives a Like activity within the POST section of HTTPServer """ if message_json['type'] != 'Like': @@ -1621,7 +1626,7 @@ def _receive_like(recent_posts_cache: {}, False, True, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, buy_sites) return True @@ -1642,7 +1647,8 @@ def _receive_undo_like(recent_posts_cache: {}, max_like_count: int, cw_lists: {}, lists_enabled: str, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> bool: + min_images_for_accounts: [], + buy_sites: {}) -> bool: """Receives an undo like activity within the POST section of HTTPServer """ if message_json['type'] != 'Undo': @@ -1746,7 +1752,8 @@ def _receive_undo_like(recent_posts_cache: {}, False, True, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) return True @@ -1768,7 +1775,8 @@ def _receive_reaction(recent_posts_cache: {}, max_like_count: int, cw_lists: {}, lists_enabled: str, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> bool: + min_images_for_accounts: [], + buy_sites: {}) -> bool: """Receives an emoji reaction within the POST section of HTTPServer """ if message_json['type'] != 'EmojiReact': @@ -1903,7 +1911,7 @@ def _receive_reaction(recent_posts_cache: {}, False, True, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, buy_sites) return True @@ -1925,7 +1933,8 @@ def _receive_zot_reaction(recent_posts_cache: {}, max_like_count: int, cw_lists: {}, lists_enabled: str, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> bool: + min_images_for_accounts: [], + buy_sites: {}) -> bool: """Receives an zot-style emoji reaction within the POST section of HTTPServer A zot style emoji reaction is an ordinary reply Note whose content is exactly one emoji @@ -2084,7 +2093,8 @@ def _receive_zot_reaction(recent_posts_cache: {}, False, True, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) return True @@ -2107,7 +2117,8 @@ def _receive_undo_reaction(recent_posts_cache: {}, max_like_count: int, cw_lists: {}, lists_enabled: str, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> bool: + min_images_for_accounts: [], + buy_sites: {}) -> bool: """Receives an undo emoji reaction within the POST section of HTTPServer """ if message_json['type'] != 'Undo': @@ -2229,7 +2240,8 @@ def _receive_undo_reaction(recent_posts_cache: {}, False, True, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) return True @@ -2249,7 +2261,8 @@ def _receive_bookmark(recent_posts_cache: {}, max_like_count: int, cw_lists: {}, lists_enabled: {}, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> bool: + min_images_for_accounts: [], + buy_sites: {}) -> bool: """Receives a bookmark activity within the POST section of HTTPServer """ if not message_json.get('type'): @@ -2350,7 +2363,8 @@ def _receive_bookmark(recent_posts_cache: {}, False, True, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) return True @@ -2372,7 +2386,8 @@ def _receive_undo_bookmark(recent_posts_cache: {}, max_like_count: int, cw_lists: {}, lists_enabled: str, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> bool: + min_images_for_accounts: [], + buy_sites: {}) -> bool: """Receives an undo bookmark activity within the POST section of HTTPServer """ if not message_json.get('type'): @@ -2473,7 +2488,8 @@ def _receive_undo_bookmark(recent_posts_cache: {}, manually_approve_followers, False, True, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, - dogwhistles, minimize_all_images, None) + dogwhistles, minimize_all_images, None, + buy_sites) return True @@ -2572,7 +2588,8 @@ def _receive_announce(recent_posts_cache: {}, max_like_count: int, cw_lists: {}, lists_enabled: str, bold_reading: bool, dogwhistles: {}, mitm: bool, - min_images_for_accounts: []) -> bool: + min_images_for_accounts: [], + buy_sites: {}) -> bool: """Receives an announce activity within the POST section of HTTPServer """ if message_json['type'] != 'Announce': @@ -2714,7 +2731,8 @@ def _receive_announce(recent_posts_cache: {}, False, True, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) if not announce_html: print('WARN: Unable to generate html for announce ' + str(message_json)) @@ -3727,6 +3745,7 @@ def _bounce_dm(sender_post_id: str, session, http_prefix: str, location = None conversation_id = None low_bandwidth = False + buy_url = '' post_json_object = \ create_direct_message_post(base_dir, nickname, domain, port, http_prefix, content, @@ -3741,7 +3760,7 @@ def _bounce_dm(sender_post_id: str, session, http_prefix: str, low_bandwidth, content_license_url, languages_understood, bounce_is_chat, - translate) + translate, buy_url) if not post_json_object: print('WARN: unable to create bounce message to ' + sending_handle) return False @@ -3903,7 +3922,8 @@ def _receive_question_vote(server, base_dir: str, nickname: str, domain: str, max_like_count: int, cw_lists: {}, lists_enabled: bool, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> None: + min_images_for_accounts: [], + buy_sites: {}) -> None: """Updates the votes on a Question/poll """ # if this is a reply to a question then update the votes @@ -3961,7 +3981,8 @@ def _receive_question_vote(server, base_dir: str, nickname: str, domain: str, False, True, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) # add id to inbox index inbox_update_index('inbox', base_dir, handle, @@ -4136,7 +4157,7 @@ def _inbox_after_initial(server, inbox_start_time, languages_understood: [], mitm: bool, bold_reading: bool, dogwhistles: {}, - max_hashtags: int) -> bool: + max_hashtags: int, buy_sites: {}) -> bool: """ Anything which needs to be done after initial checks have passed """ # if this is a clearnet instance then replace any onion/i2p @@ -4185,7 +4206,8 @@ def _inbox_after_initial(server, inbox_start_time, theme_name, system_language, max_like_count, cw_lists, lists_enabled, bold_reading, dogwhistles, - server.min_images_for_accounts): + server.min_images_for_accounts, + buy_sites): if debug: print('DEBUG: Like accepted from ' + actor) fitness_performance(inbox_start_time, server.fitness, @@ -4211,7 +4233,8 @@ def _inbox_after_initial(server, inbox_start_time, theme_name, system_language, max_like_count, cw_lists, lists_enabled, bold_reading, dogwhistles, - server.min_images_for_accounts): + server.min_images_for_accounts, + buy_sites): if debug: print('DEBUG: Undo like accepted from ' + actor) fitness_performance(inbox_start_time, server.fitness, @@ -4238,7 +4261,8 @@ def _inbox_after_initial(server, inbox_start_time, theme_name, system_language, max_like_count, cw_lists, lists_enabled, bold_reading, dogwhistles, - server.min_images_for_accounts): + server.min_images_for_accounts, + buy_sites): if debug: print('DEBUG: Reaction accepted from ' + actor) fitness_performance(inbox_start_time, server.fitness, @@ -4265,7 +4289,8 @@ def _inbox_after_initial(server, inbox_start_time, theme_name, system_language, max_like_count, cw_lists, lists_enabled, bold_reading, dogwhistles, - server.min_images_for_accounts): + server.min_images_for_accounts, + buy_sites): if debug: print('DEBUG: Zot reaction accepted from ' + actor) fitness_performance(inbox_start_time, server.fitness, @@ -4293,7 +4318,8 @@ def _inbox_after_initial(server, inbox_start_time, theme_name, system_language, max_like_count, cw_lists, lists_enabled, bold_reading, dogwhistles, - server.min_images_for_accounts): + server.min_images_for_accounts, + buy_sites): if debug: print('DEBUG: Undo reaction accepted from ' + actor) fitness_performance(inbox_start_time, server.fitness, @@ -4321,7 +4347,8 @@ def _inbox_after_initial(server, inbox_start_time, theme_name, system_language, max_like_count, cw_lists, lists_enabled, bold_reading, dogwhistles, - server.min_images_for_accounts): + server.min_images_for_accounts, + server.buy_sites): if debug: print('DEBUG: Bookmark accepted from ' + actor) fitness_performance(inbox_start_time, server.fitness, @@ -4349,7 +4376,8 @@ def _inbox_after_initial(server, inbox_start_time, theme_name, system_language, max_like_count, cw_lists, lists_enabled, bold_reading, dogwhistles, - server.min_images_for_accounts): + server.min_images_for_accounts, + server.buy_sites): if debug: print('DEBUG: Undo bookmark accepted from ' + actor) fitness_performance(inbox_start_time, server.fitness, @@ -4385,7 +4413,8 @@ def _inbox_after_initial(server, inbox_start_time, peertube_instances, max_like_count, cw_lists, lists_enabled, bold_reading, dogwhistles, mitm, - server.min_images_for_accounts): + server.min_images_for_accounts, + server.buy_sites): if debug: print('DEBUG: Announce accepted from ' + actor) fitness_performance(inbox_start_time, server.fitness, @@ -4462,7 +4491,8 @@ def _inbox_after_initial(server, inbox_start_time, max_like_count, cw_lists, lists_enabled, bold_reading, dogwhistles, - server.min_images_for_accounts) + server.min_images_for_accounts, + server.buy_sites) fitness_performance(inbox_start_time, server.fitness, 'INBOX', '_receive_question_vote', debug) @@ -4779,7 +4809,8 @@ def _inbox_after_initial(server, inbox_start_time, timezone, mitm, bold_reading, dogwhistles, - min_img_for_accounts) + min_img_for_accounts, + buy_sites) fitness_performance(inbox_start_time, server.fitness, 'INBOX', @@ -5840,7 +5871,7 @@ def run_inbox_queue(server, theme_name, max_like_count, cw_lists, dogwhistles, server.min_images_for_accounts, - max_hashtags): + max_hashtags, server.buy_sites): if debug: print('Queue: Update accepted from ' + key_id) if os.path.isfile(queue_filename): @@ -5969,7 +6000,7 @@ def run_inbox_queue(server, content_license_url, languages_understood, mitm, bold_reading, dogwhistles, - max_hashtags) + max_hashtags, server.buy_sites) fitness_performance(inbox_start_time, server.fitness, 'INBOX', 'handle_after_initial', debug) diff --git a/manual/manual.epub b/manual/manual.epub index 88558e9fc..3af5450a1 100644 Binary files a/manual/manual.epub and b/manual/manual.epub differ diff --git a/manual/manual.html b/manual/manual.html index 327a5885b..3d7faef65 100644 --- a/manual/manual.html +++ b/manual/manual.html @@ -17,6 +17,7 @@ defaults
  • Media timeline
  • Moderation
  • Themes
  • +
  • Buying and selling
  • Sharing economy
  • Search
  • Browsing in a command @@ -65,7 +66,11 @@ hardware. Think many small communicating nodes rather than a small number of large servers. Also, in spite of the prevailing great obsession with scale, not everything needs to. You can federate with a small number of servers for a particular purpose - such as running a -club or hackspace - and that’s ok.

    +club or hackspace - and that’s ok. It supports both the server-to-server +(S2S) and client-to-server (C2S) versions of the ActivityPub protocol, +with
    basic +auth for C2S authentication.

    Anti-virality is a common design approach in the fediverse, and Epicyon also follows @@ -429,7 +434,7 @@ preferable, so that it matches your typical pattern of daily posting activity without giving away your real location.

    Verifying your website or blog

    -

    It is possible to indicate that a website of blog belongs to you by +

    It is possible to indicate that a website or blog belongs to you by linking it to your profile screen. Within the head html section of your website or blog index page include a line similar to:

     

    Selecting the location header will open the last known geolocation, so if your current location is near this makes it quicker to find.

    +

    Scientific references

    +

    It is possible to have references to scientific papers linked +automatically, such that they are readable with one click/press. +Supported references are arXiv and Digital +object identifier (DOI). For example:

    +
    This is a reference to a paper: arxiv:2203.15752

    The Timeline

    Layout

    @@ -902,6 +914,18 @@ you to change colors and values for user interface components.

    Theme designer screen
    +

    Buying and selling

    +

    When creating a new post you have the option of specifying a buy +link This is a link to a web page where you can buy some particular +item. When someone receives the post if they have a compatible instance +then a small shopping cart icon will appear at the bottom of the post +along with the other icons. Clicking or pressing the shopping cart will +then take you to the buying site. It’s a predictable and machine +parsable way indicating that something is for sale, separate from the +post content.

    +

    To avoid spam, it is possible for the shopping icon to only appear if +it links to one of an allowed list of seller domains. In this way you +can be confident that you are only navigating to approved sites.

    Sharing economy

    This is intended to add Freecycle diff --git a/manual/manual.md b/manual/manual.md index 2a37d6a89..ac6b594ec 100644 --- a/manual/manual.md +++ b/manual/manual.md @@ -15,10 +15,11 @@ 14. [Media timeline](#media-timeline) 15. [Moderation](#moderation) 16. [Themes](#themes) -17. [Sharing economy](#sharing-economy) -18. [Search](#search) -19. [Browsing in a command shell](#browsing-in-a-command-shell) -20. [Building fediverse communities](#building-fediverse-communities) +17. [Buying and selling](#buying-and-selling) +18. [Sharing economy](#sharing-economy) +19. [Search](#search) +20. [Browsing in a command shell](#browsing-in-a-command-shell) +21. [Building fediverse communities](#building-fediverse-communities) # Introduction *"Every new beginning comes from some other beginning’s end."* @@ -655,6 +656,11 @@ If you have the *artist* role then from the top of the left column of the main t ![Theme designer screen](manual-theme-designer.png) +# Buying and selling +When creating a new post you have the option of specifying a *buy link* This is a link to a web page where you can buy some particular item. When someone receives the post if they have a compatible instance then a small shopping cart icon will appear at the bottom of the post along with the other icons. Clicking or pressing the shopping cart will then take you to the buying site. It's a predictable and machine parsable way indicating that something is for sale, separate from the post content. + +To avoid spam, it is possible for the shopping icon to only appear if it links to one of an allowed list of seller domains. In this way you can be confident that you are only navigating to approved sites. + # Sharing economy This is intended to add [Freecycle](https://en.wikipedia.org/wiki/The_Freecycle_Network) type functionality within a social network context, leveraging your social connections on the instance, or between participating instances, to facilitate sharing and reduce wasteful consumerism. diff --git a/newsdaemon.py b/newsdaemon.py index dedcddfba..f8c7b633d 100644 --- a/newsdaemon.py +++ b/newsdaemon.py @@ -636,6 +636,7 @@ def _convert_rss_to_activitypub(base_dir: str, http_prefix: str, city = 'London, England' conversation_id = None languages_understood = [system_language] + buy_url = '' blog = create_news_post(base_dir, domain, port, http_prefix, rss_description, @@ -645,7 +646,8 @@ def _convert_rss_to_activitypub(base_dir: str, http_prefix: str, rss_title, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, + buy_url) if not blog: continue diff --git a/outbox.py b/outbox.py index 8870096ea..a5b56ed36 100644 --- a/outbox.py +++ b/outbox.py @@ -238,7 +238,8 @@ def post_message_to_outbox(session, translate: {}, lists_enabled: str, content_license_url: str, dogwhistles: {}, - min_images_for_accounts: []) -> bool: + min_images_for_accounts: [], + buy_sites: {}) -> bool: """post is received by the outbox Client to server message post https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery @@ -593,7 +594,8 @@ def post_message_to_outbox(session, translate: {}, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) if is_edited_post: message_json['type'] = 'Update' diff --git a/posts.py b/posts.py index 21a8c0574..aa4052406 100644 --- a/posts.py +++ b/posts.py @@ -1092,6 +1092,29 @@ def _attach_post_license(post_json_object: {}, }) +def _attach_buy_link(post_json_object: {}, + buy_url: str, translate: {}) -> None: + """Attaches a link for buying something + """ + if not buy_url: + return + if '://' not in buy_url: + return + if ' ' in buy_url or '<' in buy_url: + return + buy_str = 'Buy' + if translate.get(buy_str): + buy_str = translate[buy_str] + if 'attachment' not in post_json_object: + post_json_object['attachment'] = [] + post_json_object['attachment'].append({ + "type": "Link", + "name": buy_str, + "href": buy_url, + "mediaType": "text/html" + }) + + def _create_post_s2s(base_dir: str, nickname: str, domain: str, port: int, http_prefix: str, content: str, status_number: str, published: str, new_post_id: str, post_context: {}, @@ -1102,7 +1125,8 @@ def _create_post_s2s(base_dir: str, nickname: str, domain: str, port: int, post_object_type: str, summary: str, in_reply_to_atom_uri: str, system_language: str, conversation_id: str, low_bandwidth: bool, - content_license_url: str) -> {}: + content_license_url: str, buy_url: str, + translate: {}) -> {}: """Creates a new server-to-server post """ actor_url = local_actor_url(http_prefix, nickname, domain) @@ -1166,6 +1190,7 @@ def _create_post_s2s(base_dir: str, nickname: str, domain: str, port: int, media_type, image_description, city, low_bandwidth, content_license_url) _attach_post_license(new_post['object'], content_license_url) + _attach_buy_link(new_post['object'], buy_url, translate) return new_post @@ -1179,7 +1204,8 @@ def _create_post_c2s(base_dir: str, nickname: str, domain: str, port: int, post_object_type: str, summary: str, in_reply_to_atom_uri: str, system_language: str, conversation_id: str, low_bandwidth: str, - content_license_url: str) -> {}: + content_license_url: str, buy_url: str, + translate: {}) -> {}: """Creates a new client-to-server post """ domain_full = get_full_domain(domain, port) @@ -1233,6 +1259,7 @@ def _create_post_c2s(base_dir: str, nickname: str, domain: str, port: int, media_type, image_description, city, low_bandwidth, content_license_url) _attach_post_license(new_post, content_license_url) + _attach_buy_link(new_post, buy_url, translate) return new_post @@ -1433,7 +1460,8 @@ def _create_post_base(base_dir: str, system_language: str, conversation_id: str, low_bandwidth: bool, content_license_url: str, - languages_understood: [], translate: {}) -> {}: + languages_understood: [], translate: {}, + buy_url: str) -> {}: """Creates a message """ content = remove_invalid_chars(content) @@ -1591,7 +1619,7 @@ def _create_post_base(base_dir: str, post_object_type, summary, in_reply_to_atom_uri, system_language, conversation_id, low_bandwidth, - content_license_url) + content_license_url, buy_url, translate) else: new_post = \ _create_post_c2s(base_dir, nickname, domain, port, @@ -1604,7 +1632,7 @@ def _create_post_base(base_dir: str, post_object_type, summary, in_reply_to_atom_uri, system_language, conversation_id, low_bandwidth, - content_license_url) + content_license_url, buy_url, translate) _create_post_mentions(cc_url, new_post, to_recipients, tags) @@ -1845,7 +1873,8 @@ def create_public_post(base_dir: str, location: str, is_article: bool, system_language: str, conversation_id: str, low_bandwidth: bool, content_license_url: str, - languages_understood: [], translate: {}) -> {}: + languages_understood: [], translate: {}, + buy_url: str) -> {}: """Public post """ domain_full = get_full_domain(domain, port) @@ -1879,7 +1908,7 @@ def create_public_post(base_dir: str, event_status, ticket_url, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) def _append_citations_to_blog_post(base_dir: str, @@ -1924,7 +1953,8 @@ def create_blog_post(base_dir: str, location: str, system_language: str, conversation_id: str, low_bandwidth: bool, content_license_url: str, - languages_understood: [], translate: {}) -> {}: + languages_understood: [], translate: {}, + buy_url: str) -> {}: blog_json = \ create_public_post(base_dir, nickname, domain, port, http_prefix, @@ -1937,7 +1967,7 @@ def create_blog_post(base_dir: str, event_date, event_time, event_end_time, location, True, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) blog_json['object']['url'] = \ blog_json['object']['url'].replace('/@', '/users/') _append_citations_to_blog_post(base_dir, nickname, domain, blog_json) @@ -1953,7 +1983,8 @@ def create_news_post(base_dir: str, subject: str, system_language: str, conversation_id: str, low_bandwidth: bool, content_license_url: str, - languages_understood: [], translate: {}) -> {}: + languages_understood: [], translate: {}, + buy_url: str) -> {}: client_to_server = False in_reply_to = None in_reply_to_atom_uri = None @@ -1974,7 +2005,7 @@ def create_news_post(base_dir: str, event_date, event_time, event_end_time, location, True, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) blog['object']['type'] = 'Article' return blog @@ -1995,6 +2026,7 @@ def create_question_post(base_dir: str, """ domain_full = get_full_domain(domain, port) local_actor = local_actor_url(http_prefix, nickname, domain_full) + buy_url = '' message_json = \ _create_post_base(base_dir, nickname, domain, port, 'https://www.w3.org/ns/activitystreams#Public', @@ -2008,7 +2040,7 @@ def create_question_post(base_dir: str, None, None, None, None, None, None, None, None, system_language, None, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) message_json['object']['type'] = 'Question' message_json['object']['oneOf'] = [] message_json['object']['votersCount'] = 0 @@ -2043,7 +2075,8 @@ def create_unlisted_post(base_dir: str, location: str, system_language: str, conversation_id: str, low_bandwidth: bool, content_license_url: str, - languages_understood: [], translate: {}) -> {}: + languages_understood: [], translate: {}, + buy_url: str) -> {}: """Unlisted post. This has the #Public and followers links inverted. """ domain_full = get_full_domain(domain, port) @@ -2063,7 +2096,7 @@ def create_unlisted_post(base_dir: str, None, None, None, None, None, system_language, conversation_id, low_bandwidth, content_license_url, languages_understood, - translate) + translate, buy_url) def create_followers_only_post(base_dir: str, @@ -2082,7 +2115,7 @@ def create_followers_only_post(base_dir: str, conversation_id: str, low_bandwidth: bool, content_license_url: str, languages_understood: [], - translate: {}) -> {}: + translate: {}, buy_url: str) -> {}: """Followers only post """ domain_full = get_full_domain(domain, port) @@ -2100,7 +2133,7 @@ def create_followers_only_post(base_dir: str, None, None, None, None, None, system_language, conversation_id, low_bandwidth, content_license_url, languages_understood, - translate) + translate, buy_url) def get_mentioned_people(base_dir: str, http_prefix: str, @@ -2157,7 +2190,8 @@ def create_direct_message_post(base_dir: str, conversation_id: str, low_bandwidth: bool, content_license_url: str, languages_understood: [], - dm_is_chat: bool, translate: {}) -> {}: + dm_is_chat: bool, translate: {}, + buy_url: str) -> {}: """Direct Message post """ content = resolve_petnames(base_dir, nickname, domain, content) @@ -2183,7 +2217,7 @@ def create_direct_message_post(base_dir: str, None, None, None, None, None, system_language, conversation_id, low_bandwidth, content_license_url, languages_understood, - translate) + translate, buy_url) # mentioned recipients go into To rather than Cc message_json['to'] = message_json['object']['cc'] message_json['object']['to'] = message_json['to'] @@ -2269,11 +2303,11 @@ def create_report_post(base_dir: str, post_to = moderators_list post_cc = None post_json_object = None + buy_url = '' for to_url in post_to: # who is this report going to? to_nickname = to_url.split('/users/')[1] handle = to_nickname + '@' + domain - post_json_object = \ _create_post_base(base_dir, nickname, domain, port, to_url, post_cc, @@ -2286,7 +2320,7 @@ def create_report_post(base_dir: str, None, None, None, None, None, None, None, None, system_language, None, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) if not post_json_object: continue @@ -2411,7 +2445,7 @@ def send_post(signing_priv_key_pem: str, project_version: str, shared_items_federated_domains: [], shared_item_federation_tokens: {}, low_bandwidth: bool, content_license_url: str, - translate: {}, + translate: {}, buy_url: str, debug: bool = False, in_reply_to: str = None, in_reply_to_atom_uri: str = None, subject: str = None) -> int: """Post to another inbox. Used by unit tests. @@ -2479,7 +2513,7 @@ def send_post(signing_priv_key_pem: str, project_version: str, None, None, None, None, None, system_language, conversation_id, low_bandwidth, content_license_url, languages_understood, - translate) + translate, buy_url) # get the senders private key private_key_pem = get_person_key(nickname, domain, base_dir, 'private') @@ -2576,7 +2610,7 @@ def send_post_via_server(signing_priv_key_pem: str, project_version: str, low_bandwidth: bool, content_license_url: str, event_date: str, event_time: str, event_end_time: str, - location: str, translate: {}, + location: str, translate: {}, buy_url: str, debug: bool = False, in_reply_to: str = None, in_reply_to_atom_uri: str = None, @@ -2668,7 +2702,7 @@ def send_post_via_server(signing_priv_key_pem: str, project_version: str, None, None, None, None, None, system_language, conversation_id, low_bandwidth, content_license_url, languages_understood, - translate) + translate, buy_url) auth_header = create_basic_auth_header(from_nickname, password) diff --git a/schedule.py b/schedule.py index 04960e032..5ac876958 100644 --- a/schedule.py +++ b/schedule.py @@ -147,7 +147,8 @@ def _update_post_schedule(base_dir: str, handle: str, httpd, httpd.lists_enabled, httpd.content_license_url, httpd.dogwhistles, - httpd.min_images_for_accounts): + httpd.min_images_for_accounts, + httpd.buy_sites): index_lines.remove(line) try: os.remove(post_filename) diff --git a/tests.py b/tests.py index 994cfa108..40b2ef2c8 100644 --- a/tests.py +++ b/tests.py @@ -773,6 +773,7 @@ def create_server_alice(path: str, domain: str, port: int, conversation_id = None translate = {} content_license_url = 'https://creativecommons.org/licenses/by-nc/4.0' + buy_url = '' create_public_post(path, nickname, domain, port, http_prefix, "No wise fish would go anywhere without a porpoise", test_save_to_file, @@ -787,7 +788,7 @@ def create_server_alice(path: str, domain: str, port: int, test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) create_public_post(path, nickname, domain, port, http_prefix, "Curiouser and curiouser!", test_save_to_file, @@ -802,7 +803,7 @@ def create_server_alice(path: str, domain: str, port: int, test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) create_public_post(path, nickname, domain, port, http_prefix, "In the gardens of memory, in the palace " + "of dreams, that is where you and I shall meet", @@ -818,7 +819,7 @@ def create_server_alice(path: str, domain: str, port: int, test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) regenerate_index_for_box(path, nickname, domain, 'outbox') global TEST_SERVER_ALICE_RUNNING TEST_SERVER_ALICE_RUNNING = True @@ -937,6 +938,7 @@ def create_server_bob(path: str, domain: str, port: int, conversation_id = None content_license_url = 'https://creativecommons.org/licenses/by-nc/4.0' translate = {} + buy_url = '' create_public_post(path, nickname, domain, port, http_prefix, "It's your life, live it your way.", test_save_to_file, @@ -951,7 +953,7 @@ def create_server_bob(path: str, domain: str, port: int, test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) create_public_post(path, nickname, domain, port, http_prefix, "One of the things I've realised is that " + "I am very simple", @@ -967,7 +969,7 @@ def create_server_bob(path: str, domain: str, port: int, test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) create_public_post(path, nickname, domain, port, http_prefix, "Quantum physics is a bit of a passion of mine", test_save_to_file, @@ -982,7 +984,7 @@ def create_server_bob(path: str, domain: str, port: int, test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) regenerate_index_for_box(path, nickname, domain, 'outbox') global TEST_SERVER_BOB_RUNNING TEST_SERVER_BOB_RUNNING = True @@ -1321,6 +1323,7 @@ def test_post_message_between_servers(base_dir: str) -> None: low_bandwidth = False signing_priv_key_pem = None translate = {} + buy_url = '' send_result = \ send_post(signing_priv_key_pem, __version__, session_alice, alice_dir, 'alice', alice_domain, alice_port, @@ -1335,7 +1338,7 @@ def test_post_message_between_servers(base_dir: str) -> None: languages_understood, alice_shared_items_federated_domains, alice_shared_item_federation_tokens, low_bandwidth, - content_license_url, translate, + content_license_url, translate, buy_url, in_reply_to, in_reply_to_atom_uri, subject) print('send_result: ' + str(send_result)) @@ -1402,6 +1405,8 @@ def test_post_message_between_servers(base_dir: str) -> None: assert 'यह एक परीक्षण है' in received_json['object']['content'] print('Check that message received from Alice contains an attachment') assert received_json['object']['attachment'] + if len(received_json['object']['attachment']) != 2: + pprint(received_json['object']['attachment']) assert len(received_json['object']['attachment']) == 2 attached = received_json['object']['attachment'][0] pprint(attached) @@ -1693,6 +1698,7 @@ def test_follow_between_servers(base_dir: str) -> None: low_bandwidth = False signing_priv_key_pem = None translate = {} + buy_url = '' send_result = \ send_post(signing_priv_key_pem, __version__, session_alice, alice_dir, 'alice', alice_domain, alice_port, @@ -1705,7 +1711,7 @@ def test_follow_between_servers(base_dir: str) -> None: languages_understood, alice_shared_items_federated_domains, alice_shared_item_federation_tokens, low_bandwidth, - content_license_url, translate, + content_license_url, translate, buy_url, in_reply_to, in_reply_to_atom_uri, subject) print('send_result: ' + str(send_result)) @@ -2060,6 +2066,7 @@ def test_shared_items_federation(base_dir: str) -> None: low_bandwidth = False signing_priv_key_pem = None translate = {} + buy_url = '' send_result = \ send_post(signing_priv_key_pem, __version__, session_alice, alice_dir, 'alice', alice_domain, alice_port, @@ -2072,7 +2079,7 @@ def test_shared_items_federation(base_dir: str) -> None: languages_understood, alice_shared_items_federated_domains, alice_shared_item_federation_tokens, low_bandwidth, - content_license_url, translate, True, + content_license_url, translate, buy_url, True, in_reply_to, in_reply_to_atom_uri, subject) print('send_result: ' + str(send_result)) @@ -2490,6 +2497,7 @@ def test_group_follow(base_dir: str) -> None: if os.path.isfile(os.path.join(outbox_path, name))]) translate = {} + buy_url = '' send_result = \ send_post(signing_priv_key_pem, __version__, session_alice, alice_dir, 'alice', alice_domain, alice_port, @@ -2502,7 +2510,7 @@ def test_group_follow(base_dir: str) -> None: languages_understood, alice_shared_items_federated_domains, alice_shared_item_federation_tokens, low_bandwidth, - content_license_url, translate, + content_license_url, translate, buy_url, in_reply_to, in_reply_to_atom_uri, subject) print('send_result: ' + str(send_result)) @@ -2877,6 +2885,7 @@ def _test_create_person_account(base_dir: str): "(yawn)\n\n...then it's not really independent.\n\n" + \ "Politicians will threaten to withdraw funding if you do " + \ "anything which challenges middle class sensibilities or incomes." + buy_url = '' test_post_json = \ create_public_post(base_dir, nickname, domain, port, http_prefix, content, save_to_file, @@ -2889,7 +2898,7 @@ def _test_create_person_account(base_dir: str): test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) assert test_post_json assert test_post_json.get('object') assert test_post_json['object']['content'] @@ -2915,7 +2924,7 @@ def _test_create_person_account(base_dir: str): test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) assert test_post_json assert test_post_json.get('object') assert test_post_json['object']['content'] @@ -3116,6 +3125,7 @@ def test_client_to_server(base_dir: str): event_end_time = '12:30' location = "Kinshasa" translate = {} + buy_url = '' send_result = \ send_post_via_server(signing_priv_key_pem, __version__, alice_dir, session_alice, 'alice', password, @@ -3128,7 +3138,7 @@ def test_client_to_server(base_dir: str): system_language, languages_understood, low_bandwidth, content_license_url, event_date, event_time, event_end_time, location, - translate, True, None, None, + translate, buy_url, True, None, None, conversation_id, None) print('send_result: ' + str(send_result)) @@ -4728,6 +4738,7 @@ def _test_reply_to_public_post(base_dir: str) -> None: low_bandwidth = True content_license_url = 'https://creativecommons.org/licenses/by-nc/4.0' translate = {} + buy_url = '' reply = \ create_public_post(base_dir, nickname, domain, port, http_prefix, content, save_to_file, @@ -4740,7 +4751,7 @@ def _test_reply_to_public_post(base_dir: str) -> None: test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) # print(str(reply)) expected_str = \ '

    ' + \ @@ -5678,6 +5689,7 @@ def _test_links_within_post(base_dir: str) -> None: low_bandwidth = True content_license_url = 'https://creativecommons.org/licenses/by-nc/4.0' translate = {} + buy_url = '' post_json_object = \ create_public_post(base_dir, nickname, domain, port, http_prefix, @@ -5691,7 +5703,7 @@ def _test_links_within_post(base_dir: str) -> None: test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) expected_str = \ '

    This is a test post with links.

    ' + \ @@ -5735,7 +5747,7 @@ def _test_links_within_post(base_dir: str) -> None: test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) assert post_json_object['object']['content'] == content assert post_json_object['object']['contentMap'][system_language] == content @@ -6787,6 +6799,7 @@ def _test_can_replyto(base_dir: str) -> None: low_bandwidth = True content_license_url = 'https://creativecommons.org/licenses/by-nc/4.0' translate = {} + buy_url = '' post_json_object = \ create_public_post(base_dir, nickname, domain, port, http_prefix, @@ -6800,7 +6813,7 @@ def _test_can_replyto(base_dir: str) -> None: test_event_end_time, test_location, test_is_article, system_language, conversation_id, low_bandwidth, content_license_url, - languages_understood, translate) + languages_understood, translate, buy_url) # set the date on the post curr_date_str = "2021-09-08T20:45:00Z" post_json_object['published'] = curr_date_str diff --git a/theme/blue/icons/buy.png b/theme/blue/icons/buy.png new file mode 100644 index 000000000..ab86f115d Binary files /dev/null and b/theme/blue/icons/buy.png differ diff --git a/theme/debian/icons/buy.png b/theme/debian/icons/buy.png new file mode 100644 index 000000000..ab86f115d Binary files /dev/null and b/theme/debian/icons/buy.png differ diff --git a/theme/default/icons/buy.png b/theme/default/icons/buy.png new file mode 100644 index 000000000..ab86f115d Binary files /dev/null and b/theme/default/icons/buy.png differ diff --git a/theme/hacker/icons/buy.png b/theme/hacker/icons/buy.png new file mode 100644 index 000000000..58db5e48e Binary files /dev/null and b/theme/hacker/icons/buy.png differ diff --git a/theme/henge/icons/buy.png b/theme/henge/icons/buy.png new file mode 100644 index 000000000..61e4d09e4 Binary files /dev/null and b/theme/henge/icons/buy.png differ diff --git a/theme/indymediaclassic/icons/buy.png b/theme/indymediaclassic/icons/buy.png new file mode 100644 index 000000000..e80cb6645 Binary files /dev/null and b/theme/indymediaclassic/icons/buy.png differ diff --git a/theme/indymediamodern/icons/buy.png b/theme/indymediamodern/icons/buy.png new file mode 100644 index 000000000..f7afcf64e Binary files /dev/null and b/theme/indymediamodern/icons/buy.png differ diff --git a/theme/lcd/icons/buy.png b/theme/lcd/icons/buy.png new file mode 100644 index 000000000..4968fa5e1 Binary files /dev/null and b/theme/lcd/icons/buy.png differ diff --git a/theme/light/icons/buy.png b/theme/light/icons/buy.png new file mode 100644 index 000000000..8f37cc095 Binary files /dev/null and b/theme/light/icons/buy.png differ diff --git a/theme/night/icons/buy.png b/theme/night/icons/buy.png new file mode 100644 index 000000000..ab86f115d Binary files /dev/null and b/theme/night/icons/buy.png differ diff --git a/theme/pixel/icons/buy.png b/theme/pixel/icons/buy.png new file mode 100644 index 000000000..93708bfce Binary files /dev/null and b/theme/pixel/icons/buy.png differ diff --git a/theme/purple/icons/buy.png b/theme/purple/icons/buy.png new file mode 100644 index 000000000..8e41a7b08 Binary files /dev/null and b/theme/purple/icons/buy.png differ diff --git a/theme/rc3/icons/buy.png b/theme/rc3/icons/buy.png new file mode 100644 index 000000000..cc52b0c79 Binary files /dev/null and b/theme/rc3/icons/buy.png differ diff --git a/theme/solidaric/icons/buy.png b/theme/solidaric/icons/buy.png new file mode 100644 index 000000000..311637eb1 Binary files /dev/null and b/theme/solidaric/icons/buy.png differ diff --git a/theme/starlight/icons/buy.png b/theme/starlight/icons/buy.png new file mode 100644 index 000000000..791c08ea4 Binary files /dev/null and b/theme/starlight/icons/buy.png differ diff --git a/theme/zen/icons/buy.png b/theme/zen/icons/buy.png new file mode 100644 index 000000000..63dd2417e Binary files /dev/null and b/theme/zen/icons/buy.png differ diff --git a/translations/ar.json b/translations/ar.json index e6f0b1bf4..d0e871237 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -608,5 +608,9 @@ "Inactive": "غير نشط", "Registrations open": "التسجيلات مفتوحة", "Registrations remaining": "التسجيلات المتبقية", - "Edit reminder": "تحرير التذكير" + "Edit reminder": "تحرير التذكير", + "Purchase": "شراء", + "Subscribe": "الإشتراك", + "Buy link": "رابط شراء", + "Buy links are allowed from the following domains": "روابط الشراء مسموح بها من المجالات التالية" } diff --git a/translations/bn.json b/translations/bn.json index 79ed7eb6b..75629536b 100644 --- a/translations/bn.json +++ b/translations/bn.json @@ -608,5 +608,9 @@ "Inactive": "নিষ্ক্রিয়", "Registrations open": "রেজিস্ট্রেশন খোলা", "Registrations remaining": "রেজিস্ট্রেশন বাকি", - "Edit reminder": "অনুস্মারক সম্পাদনা করুন" + "Edit reminder": "অনুস্মারক সম্পাদনা করুন", + "Purchase": "ক্রয়", + "Subscribe": "সাবস্ক্রাইব", + "Buy link": "সংযোগ কেনা", + "Buy links are allowed from the following domains": "নিম্নলিখিত ডোমেনগুলি থেকে লিঙ্কগুলি কেনার অনুমতি দেওয়া হয়েছে" } diff --git a/translations/ca.json b/translations/ca.json index f50f95ef1..85b5bb3f1 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -608,5 +608,9 @@ "Inactive": "Inactiu", "Registrations open": "Inscripcions obertes", "Registrations remaining": "Inscripcions restants", - "Edit reminder": "Edita el recordatori" + "Edit reminder": "Edita el recordatori", + "Purchase": "Compra", + "Subscribe": "Subscriu-te", + "Buy link": "Enllaç de compra", + "Buy links are allowed from the following domains": "Els enllaços de compra es permeten des dels dominis següents" } diff --git a/translations/cy.json b/translations/cy.json index 50814b105..56dcee7d0 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -608,5 +608,9 @@ "Inactive": "Anactif", "Registrations open": "Cofrestriadau yn agor", "Registrations remaining": "Cofrestriadau ar ôl", - "Edit reminder": "Golygu nodyn atgoffa" + "Edit reminder": "Golygu nodyn atgoffa", + "Purchase": "Prynu", + "Subscribe": "Tanysgrifio", + "Buy link": "Prynu dolen", + "Buy links are allowed from the following domains": "Caniateir dolenni prynu o'r parthau canlynol" } diff --git a/translations/de.json b/translations/de.json index 5631c951f..8126e5514 100644 --- a/translations/de.json +++ b/translations/de.json @@ -608,5 +608,9 @@ "Inactive": "Inaktiv", "Registrations open": "Anmeldungen geöffnet", "Registrations remaining": "Anmeldungen verbleiben", - "Edit reminder": "Erinnerung bearbeiten" + "Edit reminder": "Erinnerung bearbeiten", + "Purchase": "Kaufen", + "Subscribe": "Abonnieren", + "Buy link": "Link kaufen", + "Buy links are allowed from the following domains": "Kauflinks sind von den folgenden Domains erlaubt" } diff --git a/translations/el.json b/translations/el.json index fb4822846..700da97fa 100644 --- a/translations/el.json +++ b/translations/el.json @@ -608,5 +608,9 @@ "Inactive": "Αδρανής", "Registrations open": "Οι εγγραφές ανοίγουν", "Registrations remaining": "Απομένουν οι εγγραφές", - "Edit reminder": "Επεξεργασία υπενθύμισης" + "Edit reminder": "Επεξεργασία υπενθύμισης", + "Purchase": "Αγορά", + "Subscribe": "Εγγραφείτε", + "Buy link": "Σύνδεσμος αγοράς", + "Buy links are allowed from the following domains": "Οι σύνδεσμοι αγοράς επιτρέπονται από τους παρακάτω τομείς" } diff --git a/translations/en.json b/translations/en.json index 337d25c2d..4cfd16b63 100644 --- a/translations/en.json +++ b/translations/en.json @@ -608,5 +608,9 @@ "Inactive": "Inactive", "Registrations open": "Registrations open", "Registrations remaining": "Registrations remaining", - "Edit reminder": "Edit reminder" + "Edit reminder": "Edit reminder", + "Purchase": "Purchase", + "Subscribe": "Subscribe", + "Buy link": "Buy link", + "Buy links are allowed from the following domains": "Buy links are allowed from the following domains" } diff --git a/translations/es.json b/translations/es.json index 6b439ff94..484fc52a8 100644 --- a/translations/es.json +++ b/translations/es.json @@ -608,5 +608,9 @@ "Inactive": "Αδρανής", "Registrations open": "Inscripciones abiertas", "Registrations remaining": "Registros restantes", - "Edit reminder": "Editar recordatorio" + "Edit reminder": "Editar recordatorio", + "Purchase": "Compra", + "Subscribe": "Suscribir", + "Buy link": "Enlace de compra", + "Buy links are allowed from the following domains": "Se permiten enlaces de compra de los siguientes dominios" } diff --git a/translations/fa.json b/translations/fa.json index 194c8622a..61755dfbe 100644 --- a/translations/fa.json +++ b/translations/fa.json @@ -608,5 +608,9 @@ "Inactive": "غیر فعال", "Registrations open": "ثبت نام ها باز شد", "Registrations remaining": "ثبت نام باقی مانده است", - "Edit reminder": "ویرایش یادآوری" + "Edit reminder": "ویرایش یادآوری", + "Purchase": "خرید", + "Subscribe": "اشتراک در", + "Buy link": "لینک خرید", + "Buy links are allowed from the following domains": "لینک خرید از دامنه های زیر مجاز است" } diff --git a/translations/fr.json b/translations/fr.json index fbac9eea5..cb8020e82 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -608,5 +608,9 @@ "Inactive": "Inactive", "Registrations open": "Inscriptions ouvertes", "Registrations remaining": "Inscriptions restantes", - "Edit reminder": "Modifier le rappel" + "Edit reminder": "Modifier le rappel", + "Purchase": "Acheter", + "Subscribe": "S'abonner", + "Buy link": "Acheter un lien", + "Buy links are allowed from the following domains": "Les liens d'achat sont autorisés à partir des domaines suivants" } diff --git a/translations/ga.json b/translations/ga.json index c6f6d7cc9..9416cc13b 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -608,5 +608,9 @@ "Inactive": "Neamhghníomhach", "Registrations open": "Clárúcháin oscailte", "Registrations remaining": "Clárúcháin fágtha", - "Edit reminder": "Cuir meabhrúchán in eagar" + "Edit reminder": "Cuir meabhrúchán in eagar", + "Purchase": "Ceannach", + "Subscribe": "Liostáil", + "Buy link": "Ceannaigh nasc", + "Buy links are allowed from the following domains": "Ceadaítear naisc cheannaigh ó na fearainn seo a leanas" } diff --git a/translations/hi.json b/translations/hi.json index 2a26bcaf3..3f1c5fd6e 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -608,5 +608,9 @@ "Inactive": "निष्क्रिय", "Registrations open": "पंजीकरण खुले हैं", "Registrations remaining": "रजिस्ट्रेशन बाकी हैं", - "Edit reminder": "रिमाइंडर संपादित करें" + "Edit reminder": "रिमाइंडर संपादित करें", + "Purchase": "खरीदना", + "Subscribe": "सदस्यता लेने के", + "Buy link": "लिंक खरीदें", + "Buy links are allowed from the following domains": "निम्नलिखित डोमेन से खरीदें लिंक की अनुमति है" } diff --git a/translations/it.json b/translations/it.json index a54ef111d..f9bd2df67 100644 --- a/translations/it.json +++ b/translations/it.json @@ -608,5 +608,9 @@ "Inactive": "Non attivo", "Registrations open": "Aperte le iscrizioni", "Registrations remaining": "Iscrizioni rimanenti", - "Edit reminder": "Modifica promemoria" + "Edit reminder": "Modifica promemoria", + "Purchase": "Acquistare", + "Subscribe": "Sottoscrivi", + "Buy link": "Link per l'acquisto", + "Buy links are allowed from the following domains": "I link di acquisto sono consentiti dai seguenti domini" } diff --git a/translations/ja.json b/translations/ja.json index 4405a2120..f274f6bcf 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -608,5 +608,9 @@ "Inactive": "非活性", "Registrations open": "登録開始", "Registrations remaining": "残りの登録数", - "Edit reminder": "リマインダーを編集" + "Edit reminder": "リマインダーを編集", + "Purchase": "購入", + "Subscribe": "申し込む", + "Buy link": "購入リンク", + "Buy links are allowed from the following domains": "次のドメインからの購入リンクが許可されています" } diff --git a/translations/ko.json b/translations/ko.json index 85e69ecba..71e437519 100644 --- a/translations/ko.json +++ b/translations/ko.json @@ -608,5 +608,9 @@ "Inactive": "비활성", "Registrations open": "등록 시작", "Registrations remaining": "남은 등록", - "Edit reminder": "알림 수정" + "Edit reminder": "알림 수정", + "Purchase": "구입", + "Subscribe": "구독하다", + "Buy link": "구매 링크", + "Buy links are allowed from the following domains": "다음 도메인에서 구매 링크가 허용됩니다." } diff --git a/translations/ku.json b/translations/ku.json index a147f1d91..29a310e21 100644 --- a/translations/ku.json +++ b/translations/ku.json @@ -608,5 +608,9 @@ "Inactive": "Bêkar", "Registrations open": "Qeydkirin vedibin", "Registrations remaining": "Registrations maye", - "Edit reminder": "Bîranîna biguherîne" + "Edit reminder": "Bîranîna biguherîne", + "Purchase": "Kirrîn", + "Subscribe": "Subscribe", + "Buy link": "Girêdanê bikirin", + "Buy links are allowed from the following domains": "Zencîreyên kirînê ji domên jêrîn têne destûr kirin" } diff --git a/translations/nl.json b/translations/nl.json index 95d6beb0e..d8eb2d268 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -608,5 +608,9 @@ "Inactive": "Inactief", "Registrations open": "Inschrijvingen geopend", "Registrations remaining": "Resterende inschrijvingen", - "Edit reminder": "Herinnering bewerken" + "Edit reminder": "Herinnering bewerken", + "Purchase": "Aankoop", + "Subscribe": "Abonneren", + "Buy link": "koop link", + "Buy links are allowed from the following domains": "Kooplinks zijn toegestaan vanaf de volgende domeinen" } diff --git a/translations/oc.json b/translations/oc.json index 3c5b2af14..bad9e02dd 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -604,5 +604,9 @@ "Inactive": "Inactive", "Registrations open": "Registrations open", "Registrations remaining": "Registrations remaining", - "Edit reminder": "Edit reminder" + "Edit reminder": "Edit reminder", + "Purchase": "Purchase", + "Subscribe": "Subscribe", + "Buy link": "Buy link", + "Buy links are allowed from the following domains": "Buy links are allowed from the following domains" } diff --git a/translations/pl.json b/translations/pl.json index 149362ada..b85fe67e9 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -608,5 +608,9 @@ "Inactive": "Nieaktywny", "Registrations open": "Rejestracje otwarte", "Registrations remaining": "Pozostały zapisy", - "Edit reminder": "Edytuj przypomnienie" + "Edit reminder": "Edytuj przypomnienie", + "Purchase": "Zakup", + "Subscribe": "Subskrybuj", + "Buy link": "Kup Link", + "Buy links are allowed from the following domains": "Kupuj linki są dozwolone z następujących domen" } diff --git a/translations/pt.json b/translations/pt.json index c36dfdffb..9df0cdff5 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -608,5 +608,9 @@ "Inactive": "Inativa", "Registrations open": "inscrições abertas", "Registrations remaining": "Inscrições restantes", - "Edit reminder": "Editar lembrete" + "Edit reminder": "Editar lembrete", + "Purchase": "Comprar", + "Subscribe": "Se inscrever", + "Buy link": "Link de compra", + "Buy links are allowed from the following domains": "Links de compra são permitidos nos seguintes domínios" } diff --git a/translations/ru.json b/translations/ru.json index 88acfa275..bd3f0ce13 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -608,5 +608,9 @@ "Inactive": "Неактивный", "Registrations open": "Регистрация открыта", "Registrations remaining": "Осталось регистраций", - "Edit reminder": "Изменить напоминание" + "Edit reminder": "Изменить напоминание", + "Purchase": "Покупка", + "Subscribe": "Подписаться", + "Buy link": "Купить ссылку", + "Buy links are allowed from the following domains": "Ссылки на покупку разрешены со следующих доменов" } diff --git a/translations/sw.json b/translations/sw.json index fe23bbd7b..7bbf34f89 100644 --- a/translations/sw.json +++ b/translations/sw.json @@ -608,5 +608,9 @@ "Inactive": "Isiyotumika", "Registrations open": "Usajili umefunguliwa", "Registrations remaining": "Usajili uliosalia", - "Edit reminder": "Badilisha kikumbusho" + "Edit reminder": "Badilisha kikumbusho", + "Purchase": "Nunua", + "Subscribe": "Jisajili", + "Buy link": "Nunua kiungo", + "Buy links are allowed from the following domains": "Viungo vya kununua vinaruhusiwa kutoka kwa vikoa vifuatavyo" } diff --git a/translations/tr.json b/translations/tr.json index 15b82c164..1606176b8 100644 --- a/translations/tr.json +++ b/translations/tr.json @@ -608,5 +608,9 @@ "Inactive": "etkin değil", "Registrations open": "kayıtlar açık", "Registrations remaining": "kalan kayıtlar", - "Edit reminder": "Hatırlatıcıyı düzenle" + "Edit reminder": "Hatırlatıcıyı düzenle", + "Purchase": "Satın alma", + "Subscribe": "Abone", + "Buy link": "Bağlantı satın al", + "Buy links are allowed from the following domains": "Aşağıdaki alanlardan satın alma bağlantılarına izin verilir" } diff --git a/translations/uk.json b/translations/uk.json index 260ff5905..590a799a1 100644 --- a/translations/uk.json +++ b/translations/uk.json @@ -608,5 +608,9 @@ "Inactive": "Неактивний", "Registrations open": "Реєстрація відкрита", "Registrations remaining": "Залишилось реєстрацій", - "Edit reminder": "Редагувати нагадування" + "Edit reminder": "Редагувати нагадування", + "Purchase": "Купівля", + "Subscribe": "Підпишіться", + "Buy link": "Купити посилання", + "Buy links are allowed from the following domains": "Посилання на купівлю дозволено з таких доменів" } diff --git a/translations/yi.json b/translations/yi.json index 89f674704..c3d62bf14 100644 --- a/translations/yi.json +++ b/translations/yi.json @@ -608,5 +608,9 @@ "Inactive": "ינאַקטיוו", "Registrations open": "רעדזשיסטריישאַנז עפענען", "Registrations remaining": "רעדזשיסטריישאַנז רוען", - "Edit reminder": "רעדאַגירן דערמאָנונג" + "Edit reminder": "רעדאַגירן דערמאָנונג", + "Purchase": "קויפן", + "Subscribe": "אַבאָנירן", + "Buy link": "קויפן לינק", + "Buy links are allowed from the following domains": "קויפן פֿאַרבינדונגען זענען ערלויבט פֿון די פאלגענדע דאָומיינז" } diff --git a/translations/zh.json b/translations/zh.json index 90b76161d..d7c5fb673 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -608,5 +608,9 @@ "Inactive": "无效", "Registrations open": "注册开放", "Registrations remaining": "剩余名额", - "Edit reminder": "编辑提醒" + "Edit reminder": "编辑提醒", + "Purchase": "购买", + "Subscribe": "订阅", + "Buy link": "购买链接", + "Buy links are allowed from the following domains": "允许来自以下域的购买链接" } diff --git a/utils.py b/utils.py index 80fb52d7b..2c6096015 100644 --- a/utils.py +++ b/utils.py @@ -4166,7 +4166,7 @@ def save_reverse_timeline(base_dir: str, reverse_sequence: []) -> []: def is_quote_toot(post_json_object: str, content: str) -> bool: - """Returns true if the given post is a quote toot + """Returns true if the given post is a quote toot / quote tweet """ # Pleroma/Misskey implementations if post_json_object['object'].get('quoteUri') or \ diff --git a/webapp_confirm.py b/webapp_confirm.py index 7905272f6..b56f552ef 100644 --- a/webapp_confirm.py +++ b/webapp_confirm.py @@ -40,7 +40,8 @@ def html_confirm_delete(server, max_like_count: int, signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, dogwhistles: {}, - min_images_for_accounts: []) -> str: + min_images_for_accounts: [], + buy_sites: {}) -> str: """Shows a screen asking to confirm the deletion of a post """ if '/statuses/' not in message_id: @@ -96,7 +97,7 @@ def html_confirm_delete(server, False, False, False, False, False, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, buy_sites) delete_post_str += '

    ' delete_post_str += \ '

    ' + \ diff --git a/webapp_conversation.py b/webapp_conversation.py index 9753e59f3..c15f26c64 100644 --- a/webapp_conversation.py +++ b/webapp_conversation.py @@ -42,7 +42,7 @@ def html_conversation_view(post_id: str, timezone: str, bold_reading: bool, dogwhistles: {}, access_keys: {}, min_images_for_accounts: [], - debug: bool) -> str: + debug: bool, buy_sites: {}) -> str: """Show a page containing a conversation thread """ conv_posts = \ @@ -96,7 +96,8 @@ def html_conversation_view(post_id: str, cw_lists, lists_enabled, timezone, False, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) if post_str: # check for "HTTP/1.1 303 See Other Server" if 'X-AP-Instance-ID' not in post_str: diff --git a/webapp_create_post.py b/webapp_create_post.py index 8155b6c96..bcb559003 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -24,6 +24,7 @@ from utils import get_currencies from utils import get_category_types from utils import get_account_timezone from utils import get_supported_languages +from webapp_utils import get_buy_links from webapp_utils import html_following_data_list from webapp_utils import html_common_emoji from webapp_utils import begin_edit_section @@ -239,7 +240,9 @@ def html_new_post(edit_post_params: {}, dogwhistles: {}, min_images_for_accounts: [], default_month: int, default_year: int, - default_post_language: str) -> str: + default_post_language: str, + buy_sites: {}, + default_buy_site: str) -> str: """New post screen """ # get the json if this is an edited post @@ -256,6 +259,12 @@ def html_new_post(edit_post_params: {}, return '' if not edited_post_json: return '' + buy_links = \ + get_buy_links(edited_post_json, translate, buy_sites) + if buy_links: + for _, buy_url in buy_links.items(): + default_buy_site = buy_url + break if edited_post_json['object'].get('conversation'): conversation_id = edited_post_json['object']['conversation'] elif edited_post_json['object'].get('context'): @@ -390,7 +399,8 @@ def html_new_post(edit_post_params: {}, cw_lists, lists_enabled, timezone, False, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) reply_str = '\n' @@ -479,7 +489,6 @@ def html_new_post(edit_post_params: {}, ' \n' - new_post_image_section += end_edit_section() new_post_emoji_section = '' @@ -766,6 +775,9 @@ def html_new_post(edit_post_params: {}, ' \n' replies_section += languages_dropdown + buy_link_str = '🛒 ' + translate['Buy link'] + replies_section += edit_text_field(buy_link_str, 'buyUrl', + default_buy_site, 'https://...') replies_section += '

    \n' date_and_location = \ diff --git a/webapp_frontscreen.py b/webapp_frontscreen.py index 6c829bcdf..4c3158130 100644 --- a/webapp_frontscreen.py +++ b/webapp_frontscreen.py @@ -40,7 +40,8 @@ def _html_front_screen_posts(recent_posts_cache: {}, max_recent_posts: int, lists_enabled: str, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> str: + min_images_for_accounts: [], + buy_sites: {}) -> str: """Shows posts on the front screen of a news instance These should only be public blog posts from the features timeline which is the blog timeline of the news actor @@ -95,7 +96,8 @@ def _html_front_screen_posts(recent_posts_cache: {}, max_recent_posts: int, cw_lists, lists_enabled, timezone, False, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) if post_str: profile_str += post_str + separator_str ctr += 1 @@ -128,7 +130,8 @@ def html_front_screen(signing_priv_key_pem: str, max_items_per_page: int, cw_lists: {}, lists_enabled: str, dogwhistles: {}, - min_images_for_accounts: []) -> str: + min_images_for_accounts: [], + buy_sites: {}) -> str: """Show the news instance front screen """ bold_reading = False @@ -204,7 +207,8 @@ def html_front_screen(signing_priv_key_pem: str, signing_priv_key_pem, cw_lists, lists_enabled, bold_reading, dogwhistles, - min_images_for_accounts) + license_str + min_images_for_accounts, + buy_sites) + license_str # Footer which is only used for system accounts profile_footer_str = ' \n' diff --git a/webapp_likers.py b/webapp_likers.py index d5221b8da..57d755f81 100644 --- a/webapp_likers.py +++ b/webapp_likers.py @@ -43,6 +43,7 @@ def html_likers_of_post(base_dir: str, nickname: str, box_name: str, default_timeline: str, bold_reading: bool, dogwhistles: {}, min_images_for_accounts: [], + buy_sites: {}, dict_name: str = 'likes') -> str: """Returns html for a screen showing who liked a post """ @@ -113,7 +114,8 @@ def html_likers_of_post(base_dir: str, nickname: str, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) # show likers beneath the post obj = post_json_object diff --git a/webapp_moderation.py b/webapp_moderation.py index 5ebb799a5..3ff6b93f6 100644 --- a/webapp_moderation.py +++ b/webapp_moderation.py @@ -61,7 +61,8 @@ def html_moderation(default_timeline: str, timezone: str, bold_reading: bool, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], - reverse_sequence: bool) -> str: + reverse_sequence: bool, + buy_sites: {}) -> str: """Show the moderation feed as html This is what you see when selecting the "mod" timeline """ @@ -88,7 +89,8 @@ def html_moderation(default_timeline: str, max_like_count, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, - min_images_for_accounts, reverse_sequence, None) + min_images_for_accounts, reverse_sequence, None, + buy_sites) def html_account_info(translate: {}, diff --git a/webapp_post.py b/webapp_post.py index 31c8ac4da..998aa05ac 100644 --- a/webapp_post.py +++ b/webapp_post.py @@ -81,6 +81,7 @@ from content import get_mentions_from_html from content import switch_words from person import is_person_snoozed from person import get_person_avatar_url +from webapp_utils import get_buy_links from webapp_utils import language_right_to_left from webapp_utils import get_banner_file from webapp_utils import get_avatar_image_url @@ -1648,6 +1649,7 @@ def _get_footer_with_icons(show_icons: bool, like_str: str, reaction_str: str, bookmark_str: str, delete_str: str, mute_str: str, edit_str: str, + buy_str: str, post_json_object: {}, published_link: str, time_class: str, published_str: str, nickname: str, content_license_url: str, @@ -1661,7 +1663,7 @@ def _get_footer_with_icons(show_icons: bool, footer_str += '
    \n' footer_str += \ reply_str + announce_str + like_str + bookmark_str + reaction_str - footer_str += delete_str + mute_str + edit_str + footer_str += delete_str + mute_str + edit_str + buy_str if not is_news_post(post_json_object): footer_str += ' ' if content_license_url: @@ -1836,6 +1838,26 @@ def _get_copyright_footer(content_license_url: str, return copyright_str +def _get_buy_footer(buy_links: {}, translate: {}) -> str: + """Returns the footer buy link + """ + if not buy_links: + return '' + icon_filename = 'buy.png' + description = translate['Buy'] + buy_str = '' + for buy_title, buy_url in buy_links.items(): + buy_str = \ + ' ' + \ + '' + \ + '' + description + \
+            ' |\n' + break + return buy_str + + def individual_post_as_html(signing_priv_key_pem: str, allow_downloads: bool, recent_posts_cache: {}, max_recent_posts: int, @@ -1867,7 +1889,8 @@ def individual_post_as_html(signing_priv_key_pem: str, mitm: bool, bold_reading: bool, dogwhistles: {}, minimize_all_images: bool, - first_post_id: str) -> str: + first_post_id: str, + buy_sites: {}) -> str: """ Shows a single post as html """ if not post_json_object: @@ -2475,12 +2498,24 @@ def individual_post_as_html(signing_priv_key_pem: str, if disallow_reply(content_all_str): reply_str = '' + is_patch = is_git_patch(base_dir, nickname, domain, + post_json_object['object']['type'], + summary_str, content_str) + + # html for the buy icon + buy_str = '' + if 'attachment' not in post_json_object['object']: + post_json_object['object']['attachment'] = [] + if not is_patch: + buy_links = get_buy_links(post_json_object, translate, buy_sites) + buy_str = _get_buy_footer(buy_links, translate) + new_footer_str = \ _get_footer_with_icons(show_icons, container_class_icons, reply_str, announce_str, like_str, reaction_str, bookmark_str, - delete_str, mute_str, edit_str, + delete_str, mute_str, edit_str, buy_str, post_json_object, published_link, time_class, published_str, nickname, content_license_url, translate) @@ -2495,9 +2530,6 @@ def individual_post_as_html(signing_priv_key_pem: str, if not summary_str: summary_str = get_summary_from_post(post_json_object, system_language, languages_understood) - is_patch = is_git_patch(base_dir, nickname, domain, - post_json_object['object']['type'], - summary_str, content_str) _log_post_timing(enable_timing_log, post_start_time, '16') @@ -2582,13 +2614,14 @@ def individual_post_as_html(signing_priv_key_pem: str, _log_post_timing(enable_timing_log, post_start_time, '17') map_str = '' + buy_links = {} if post_json_object['object'].get('tag'): if not is_patch: content_str = \ replace_emoji_from_tags(session, base_dir, content_str, post_json_object['object']['tag'], 'content', False, True) - + buy_links = get_buy_links(post_json_object, translate, buy_sites) # show embedded map if the location contains a map url location_str = \ get_location_from_tags(post_json_object['object']['tag']) @@ -2707,7 +2740,8 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int, cw_lists: {}, lists_enabled: str, timezone: str, mitm: bool, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> str: + min_images_for_accounts: [], + buy_sites: {}) -> str: """Show an individual post as html """ original_post_json = post_json_object @@ -2791,7 +2825,7 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int, False, authorized, False, False, False, False, cw_lists, lists_enabled, timezone, mitm, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, buy_sites) message_id = remove_id_ending(post_json_object['id']) # show the previous posts @@ -2835,7 +2869,7 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int, bold_reading, dogwhistles, minimize_all_images, - None) + post_str + None, buy_sites) + post_str # show the following posts post_filename = locate_post(base_dir, nickname, domain, message_id) @@ -2875,7 +2909,8 @@ def html_individual_post(recent_posts_cache: {}, max_recent_posts: int, cw_lists, lists_enabled, timezone, False, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) css_filename = base_dir + '/epicyon-profile.css' if os.path.isfile(base_dir + '/epicyon.css'): css_filename = base_dir + '/epicyon.css' @@ -2906,7 +2941,8 @@ def html_post_replies(recent_posts_cache: {}, max_recent_posts: int, lists_enabled: str, timezone: str, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> str: + min_images_for_accounts: [], + buy_sites: {}) -> str: """Show the replies to an individual post as html """ replies_str = '' @@ -2937,7 +2973,8 @@ def html_post_replies(recent_posts_cache: {}, max_recent_posts: int, cw_lists, lists_enabled, timezone, False, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) css_filename = base_dir + '/epicyon-profile.css' if os.path.isfile(base_dir + '/epicyon.css'): @@ -2968,7 +3005,8 @@ def html_emoji_reaction_picker(recent_posts_cache: {}, max_recent_posts: int, box_name: str, page_number: int, timezone: str, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> str: + min_images_for_accounts: [], + buy_sites: {}) -> str: """Returns the emoji picker screen """ minimize_all_images = False @@ -2996,7 +3034,7 @@ def html_emoji_reaction_picker(recent_posts_cache: {}, max_recent_posts: int, False, False, False, False, False, False, cw_lists, lists_enabled, timezone, False, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, buy_sites) reactions_filename = base_dir + '/emoji/reactions.json' if not os.path.isfile(reactions_filename): diff --git a/webapp_profile.py b/webapp_profile.py index e310f4198..3d201f4e0 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -164,7 +164,8 @@ def html_profile_after_search(recent_posts_cache: {}, max_recent_posts: int, timezone: str, onion_domain: str, i2p_domain: str, bold_reading: bool, dogwhistles: {}, - min_images_for_accounts: []) -> str: + min_images_for_accounts: [], + buy_sites: {}) -> str: """Show a profile page after a search for a fediverse address """ http = False @@ -416,7 +417,8 @@ def html_profile_after_search(recent_posts_cache: {}, max_recent_posts: int, cw_lists, lists_enabled, timezone, False, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) i += 1 if i >= 8: break @@ -646,7 +648,8 @@ def html_profile(signing_priv_key_pem: str, max_items_per_page: int, cw_lists: {}, lists_enabled: str, content_license_url: str, - timezone: str, bold_reading: bool) -> str: + timezone: str, bold_reading: bool, + buy_sites: {}) -> str: """Show the profile page as html """ show_moved_accounts = False @@ -678,7 +681,7 @@ def html_profile(signing_priv_key_pem: str, shared_items_federated_domains, None, page_number, max_items_per_page, cw_lists, lists_enabled, {}, - min_images_for_accounts) + min_images_for_accounts, buy_sites) domain, port = get_domain_from_actor(profile_json['id']) if not domain: @@ -1169,7 +1172,8 @@ def html_profile(signing_priv_key_pem: str, cw_lists, lists_enabled, timezone, bold_reading, {}, min_images_for_accounts, - max_profile_posts) + license_str + max_profile_posts, + buy_sites) + license_str if not is_group: if selected == 'following': profile_str += \ @@ -1269,7 +1273,8 @@ def _html_profile_posts(recent_posts_cache: {}, max_recent_posts: int, timezone: str, bold_reading: bool, dogwhistles: {}, min_images_for_accounts: [], - max_profile_posts: int) -> str: + max_profile_posts: int, + buy_sites: {}) -> str: """Shows posts on the profile screen These should only be public posts """ @@ -1327,7 +1332,8 @@ def _html_profile_posts(recent_posts_cache: {}, max_recent_posts: int, cw_lists, lists_enabled, timezone, False, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) if post_str and item_id not in shown_items: profile_str += post_str + separator_str shown_items.append(item_id) @@ -1878,7 +1884,8 @@ def _html_edit_profile_filtering(base_dir: str, nickname: str, domain: str, user_agents_blocked: str, crawlers_allowed: str, translate: {}, reply_interval_hours: int, - cw_lists: {}, lists_enabled: str) -> str: + cw_lists: {}, lists_enabled: str, + buy_sites: {}) -> str: """Filtering and blocking section of edit profile screen """ filter_str = '' @@ -2065,6 +2072,20 @@ def _html_edit_profile_filtering(base_dir: str, nickname: str, domain: str, 'crawlersAllowedStr', crawlers_allowed_str, 200, '', False) + buy_domains_list_str = '' + for buy_icon_text, buy_url in buy_sites.items(): + if buy_icon_text != buy_url: + buy_domains_list_str += \ + buy_icon_text + ' ' + buy_url.strip() + '\n' + else: + buy_domains_list_str += buy_url.strip() + '\n' + buy_domains_str = \ + "Buy links are allowed from the following domains" + edit_profile_form += \ + edit_text_area(translate[buy_domains_str], None, + 'buySitesStr', buy_domains_list_str, + 200, '', False) + cw_lists_str = '' for name, _ in cw_lists.items(): variablename = get_cw_list_variable(name) @@ -2507,7 +2528,8 @@ def html_edit_profile(server, translate: {}, system_language: str, min_images_for_accounts: [], max_recent_posts: int, - reverse_sequence: []) -> str: + reverse_sequence: [], + buy_sites: {}) -> str: """Shows the edit profile screen """ path = path.replace('/inbox', '').replace('/outbox', '') @@ -2748,7 +2770,7 @@ def html_edit_profile(server, translate: {}, _html_edit_profile_filtering(base_dir, nickname, domain, user_agents_blocked, crawlers_allowed, translate, reply_interval_hours, - cw_lists, lists_enabled) + cw_lists, lists_enabled, buy_sites) # git projects section edit_profile_form += \ diff --git a/webapp_search.py b/webapp_search.py index 70cb8e0a0..b9204371d 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -702,7 +702,8 @@ def html_history_search(translate: {}, base_dir: str, lists_enabled: str, timezone: str, bold_reading: bool, dogwhistles: {}, access_keys: {}, - min_images_for_accounts: []) -> str: + min_images_for_accounts: [], + buy_sites: {}) -> str: """Show a page containing search results for your post history """ if historysearch.startswith("'"): @@ -812,7 +813,8 @@ def html_history_search(translate: {}, base_dir: str, cw_lists, lists_enabled, timezone, False, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) if post_str: history_search_form += separator_str + post_str index += 1 @@ -840,7 +842,8 @@ def html_hashtag_search(nickname: str, domain: str, port: int, timezone: str, bold_reading: bool, dogwhistles: {}, map_format: str, access_keys: {}, box_name: str, - min_images_for_accounts: []) -> str: + min_images_for_accounts: [], + buy_sites: {}) -> str: """Show a page containing search results for a hashtag or after selecting a hashtag from the swarm """ @@ -1032,7 +1035,8 @@ def html_hashtag_search(nickname: str, domain: str, port: int, store_to_sache, False, cw_lists, lists_enabled, timezone, False, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) if post_str: hashtag_search_form += \ text_mode_separator + separator_str + post_str @@ -1075,7 +1079,7 @@ def html_hashtag_search_remote(nickname: str, domain: str, port: int, timezone: str, bold_reading: bool, dogwhistles: {}, min_images_for_accounts: [], - debug: bool) -> str: + debug: bool, buy_sites: {}) -> str: """Show a page containing search results for a remote hashtag """ hashtag = hashtag_url.split('/')[-1] @@ -1235,7 +1239,8 @@ def html_hashtag_search_remote(nickname: str, domain: str, port: int, store_to_sache, False, cw_lists, lists_enabled, timezone, False, bold_reading, dogwhistles, - minimize_all_images, None) + minimize_all_images, None, + buy_sites) if post_str: hashtag_search_form += \ text_mode_separator + separator_str + post_str diff --git a/webapp_timeline.py b/webapp_timeline.py index e1ce92329..ee9d21483 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -501,7 +501,8 @@ def html_timeline(default_timeline: str, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], reverse_sequence: bool, - last_post_id: str) -> str: + last_post_id: str, + buy_sites: {}) -> str: """Show the timeline as html """ enable_timing_log = False @@ -1037,7 +1038,7 @@ def html_timeline(default_timeline: str, timezone, mitm, bold_reading, dogwhistles, minimize_all_images, - first_post_id) + first_post_id, buy_sites) _log_timeline_timing(enable_timing_log, timeline_start_time, box_name, '12') @@ -1306,7 +1307,8 @@ def html_shares(default_timeline: str, timezone: str, bold_reading: bool, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], - reverse_sequence: bool) -> str: + reverse_sequence: bool, + buy_sites: {}) -> str: """Show the shares timeline as html """ manually_approve_followers = \ @@ -1339,7 +1341,7 @@ def html_shares(default_timeline: str, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence, None) + reverse_sequence, None, buy_sites) def html_wanted(default_timeline: str, @@ -1371,7 +1373,8 @@ def html_wanted(default_timeline: str, timezone: str, bold_reading: bool, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], - reverse_sequence: bool) -> str: + reverse_sequence: bool, + buy_sites: {}) -> str: """Show the wanted timeline as html """ manually_approve_followers = \ @@ -1404,7 +1407,7 @@ def html_wanted(default_timeline: str, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence, None) + reverse_sequence, None, buy_sites) def html_inbox(default_timeline: str, @@ -1438,7 +1441,8 @@ def html_inbox(default_timeline: str, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], reverse_sequence: bool, - last_post_id: str) -> str: + last_post_id: str, + buy_sites: {}) -> str: """Show the inbox as html """ manually_approve_followers = \ @@ -1471,7 +1475,8 @@ def html_inbox(default_timeline: str, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence, last_post_id) + reverse_sequence, last_post_id, + buy_sites) def html_bookmarks(default_timeline: str, @@ -1504,7 +1509,8 @@ def html_bookmarks(default_timeline: str, timezone: str, bold_reading: bool, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], - reverse_sequence: bool) -> str: + reverse_sequence: bool, + buy_sites: {}) -> str: """Show the bookmarks as html """ manually_approve_followers = \ @@ -1536,7 +1542,7 @@ def html_bookmarks(default_timeline: str, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence, None) + reverse_sequence, None, buy_sites) def html_inbox_dms(default_timeline: str, @@ -1570,7 +1576,8 @@ def html_inbox_dms(default_timeline: str, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], reverse_sequence: bool, - last_post_id: str) -> str: + last_post_id: str, + buy_sites: {}) -> str: """Show the DM timeline as html """ artist = is_artist(base_dir, nickname) @@ -1598,7 +1605,8 @@ def html_inbox_dms(default_timeline: str, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence, last_post_id) + reverse_sequence, last_post_id, + buy_sites) def html_inbox_replies(default_timeline: str, @@ -1632,7 +1640,8 @@ def html_inbox_replies(default_timeline: str, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], reverse_sequence: bool, - last_post_id: str) -> str: + last_post_id: str, + buy_sites: {}) -> str: """Show the replies timeline as html """ artist = is_artist(base_dir, nickname) @@ -1658,7 +1667,7 @@ def html_inbox_replies(default_timeline: str, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence, last_post_id) + reverse_sequence, last_post_id, buy_sites) def html_inbox_media(default_timeline: str, @@ -1692,7 +1701,8 @@ def html_inbox_media(default_timeline: str, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], reverse_sequence: bool, - last_post_id: str) -> str: + last_post_id: str, + buy_sites: {}) -> str: """Show the media timeline as html """ artist = is_artist(base_dir, nickname) @@ -1718,7 +1728,7 @@ def html_inbox_media(default_timeline: str, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence, last_post_id) + reverse_sequence, last_post_id, buy_sites) def html_inbox_blogs(default_timeline: str, @@ -1752,7 +1762,8 @@ def html_inbox_blogs(default_timeline: str, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], reverse_sequence: bool, - last_post_id: str) -> str: + last_post_id: str, + buy_sites: {}) -> str: """Show the blogs timeline as html """ artist = is_artist(base_dir, nickname) @@ -1778,7 +1789,7 @@ def html_inbox_blogs(default_timeline: str, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence, last_post_id) + reverse_sequence, last_post_id, buy_sites) def html_inbox_features(default_timeline: str, @@ -1812,7 +1823,8 @@ def html_inbox_features(default_timeline: str, timezone: str, bold_reading: bool, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], - reverse_sequence: bool) -> str: + reverse_sequence: bool, + buy_sites: {}) -> str: """Show the features timeline as html """ return html_timeline(default_timeline, @@ -1837,7 +1849,7 @@ def html_inbox_features(default_timeline: str, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence, None) + reverse_sequence, None, buy_sites) def html_inbox_news(default_timeline: str, @@ -1870,7 +1882,8 @@ def html_inbox_news(default_timeline: str, timezone: str, bold_reading: bool, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], - reverse_sequence: bool) -> str: + reverse_sequence: bool, + buy_sites: {}) -> str: """Show the news timeline as html """ return html_timeline(default_timeline, @@ -1895,7 +1908,7 @@ def html_inbox_news(default_timeline: str, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence, None) + reverse_sequence, None, buy_sites) def html_outbox(default_timeline: str, @@ -1928,7 +1941,8 @@ def html_outbox(default_timeline: str, timezone: str, bold_reading: bool, dogwhistles: {}, ua_str: str, min_images_for_accounts: [], - reverse_sequence: bool) -> str: + reverse_sequence: bool, + buy_sites: {}) -> str: """Show the Outbox as html """ manually_approve_followers = \ @@ -1956,4 +1970,4 @@ def html_outbox(default_timeline: str, shared_items_federated_domains, signing_priv_key_pem, cw_lists, lists_enabled, timezone, bold_reading, dogwhistles, ua_str, min_images_for_accounts, - reverse_sequence, None) + reverse_sequence, None, buy_sites) diff --git a/webapp_utils.py b/webapp_utils.py index 984e8c78a..53d635d9b 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -2080,3 +2080,77 @@ def html_following_dropdown(base_dir: str, nickname: str, following_address + '\n' list_str += '\n' return list_str + + +def get_buy_links(post_json_object: str, translate: {}, buy_sites: {}) -> {}: + """Returns any links to buy something from an external site + """ + if not post_json_object['object'].get('attachment'): + return {} + if not isinstance(post_json_object['object']['attachment'], list): + return {} + links = {} + buy_strings = [] + for buy_str in ('Buy', 'Purchase', 'Subscribe'): + if translate.get(buy_str): + buy_str = translate[buy_str] + buy_strings += buy_str.lower() + for item in post_json_object['object']['attachment']: + if not isinstance(item, dict): + continue + if not item.get('name'): + continue + if not isinstance(item['name'], str): + continue + if not item.get('type'): + continue + if not item.get('href'): + continue + if not isinstance(item['type'], str): + continue + if not isinstance(item['href'], str): + continue + if item['type'] != 'Link': + continue + if not item.get('mediaType'): + continue + if not isinstance(item['mediaType'], str): + continue + if 'html' not in item['mediaType']: + continue + item_name = item['name'] + # The name should not be excessively long + if len(item_name) > 32: + continue + # there should be no html in the name + if remove_html(item_name) != item_name: + continue + # there should be no html in the link + if '<' in item['href'] or \ + '://' not in item['href'] or \ + ' ' in item['href']: + continue + if buy_sites: + # limited to an allowlist of buying sites + for site, buy_domain in buy_sites.items(): + if buy_domain in item['href']: + links[site.title()] = item['href'] + continue + else: + # The name only needs to indicate that this is a buy link + for buy_str in buy_strings: + if buy_str in item_name.lower(): + links[item_name] = item['href'] + continue + return links + + +def load_buy_sites(base_dir: str) -> {}: + """Loads domains from which buying is permitted + """ + buy_sites_filename = base_dir + '/accounts/buy_sites.json' + if os.path.isfile(buy_sites_filename): + buy_sites_json = load_json(buy_sites_filename) + if buy_sites_json: + return buy_sites_json + return {}