diff --git a/daemon.py b/daemon.py index 4255e5d02..2465e8d3c 100644 --- a/daemon.py +++ b/daemon.py @@ -3341,7 +3341,7 @@ class PubServer(BaseHTTPRequestHandler): bold_reading = True msg = \ - html_new_post(False, self.server.translate, + html_new_post({}, False, self.server.translate, base_dir, http_prefix, report_path, None, @@ -3370,6 +3370,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertube_instances, self.server.allow_local_network_access, self.server.system_language, + self.server.languages_understood, self.server.max_like_count, self.server.signing_priv_key_pem, self.server.cw_lists, @@ -3488,7 +3489,7 @@ class PubServer(BaseHTTPRequestHandler): bold_reading = True msg = \ - html_new_post(False, self.server.translate, + html_new_post({}, False, self.server.translate, base_dir, http_prefix, report_path, None, [], @@ -3516,6 +3517,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertube_instances, self.server.allow_local_network_access, self.server.system_language, + self.server.languages_understood, self.server.max_like_count, self.server.signing_priv_key_pem, self.server.cw_lists, @@ -15047,7 +15049,8 @@ class PubServer(BaseHTTPRequestHandler): self._write(msg) return True - def _show_new_post(self, calling_domain: str, path: str, + def _show_new_post(self, edit_post_params: {}, + calling_domain: str, path: str, media_instance: bool, translate: {}, base_dir: str, http_prefix: str, in_reply_to_url: str, reply_to_list: [], @@ -15103,7 +15106,7 @@ class PubServer(BaseHTTPRequestHandler): bold_reading = True msg = \ - html_new_post(media_instance, + html_new_post(edit_post_params, media_instance, translate, base_dir, http_prefix, @@ -15134,6 +15137,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertube_instances, self.server.allow_local_network_access, self.server.system_language, + self.server.languages_understood, self.server.max_like_count, self.server.signing_priv_key_pem, self.server.cw_lists, @@ -18210,6 +18214,46 @@ class PubServer(BaseHTTPRequestHandler): self.server.getreq_busy = False return + # Edit a post + edit_post_params = {} + if authorized and \ + '/users/' in self.path and \ + '/editpost?scope=' in self.path and \ + ';postid=' in self.path and \ + ';actor=' in self.path: + post_scope = self.path.split('?scope=')[1] + if ';' in post_scope: + post_scope = post_scope.split(';')[0] + edit_post_params['scope'] = post_scope + message_id = self.path.split(';postid=')[1] + if ';' in message_id: + message_id = message_id.split(';')[0] + edit_post_params['postid'] = message_id + actor = self.path.split(';actor=')[1] + if ';' in actor: + actor = actor.split(';')[0] + edit_post_params['actor'] = actor + nickname = get_nickname_from_actor(self.path.split('?')[0]) + edit_post_params['nickname'] = nickname + if not nickname: + self._404() + self.server.getreq_busy = False + return + if nickname != actor: + self._404() + self.server.getreq_busy = False + return + post_url = \ + local_actor_url(self.server.http_prefix, nickname, + self.server.domain_full) + \ + '/statuses/' + message_id + edit_post_params['post_url'] = post_url + # use the new post functions, but using edit_post_params + new_post_scope = post_scope + if post_scope == 'public': + new_post_scope = 'post' + self.path = '/users/' + nickname + '/new' + new_post_scope + # list of known crawlers accessing nodeinfo or masto API if self._show_known_crawlers(calling_domain, self.path, self.server.base_dir, @@ -18263,7 +18307,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.getreq_busy = False return - if self._show_new_post(calling_domain, self.path, + if self._show_new_post(edit_post_params, + calling_domain, self.path, self.server.media_instance, self.server.translate, self.server.base_dir, @@ -19155,6 +19200,13 @@ class PubServer(BaseHTTPRequestHandler): print('WARN: no nickname found when receiving ' + post_type + ' path ' + path) return -1 + # get the message id of an edited post + edited_postid = None + if '?editid=' in path: + edited_postid = path.split('?editid=')[1] + if '?' in edited_postid: + edited_postid = edited_postid.split('?')[0] + length = int(headers['Content-Length']) if length > self.server.max_post_length: print('POST size too large') @@ -19357,6 +19409,16 @@ class PubServer(BaseHTTPRequestHandler): languages_understood, self.server.translate) if message_json: + if edited_postid: + message_json['id'] = \ + edited_postid + '/activity' + message_json['object']['id'] = \ + edited_postid + message_json['object']['url'] = \ + edited_postid + message_json['type'] = 'Update' + print('DEBUG: sending edited public post ' + + str(message_json)) if fields['schedulePost']: return 1 if pin_to_profile: @@ -19613,6 +19675,17 @@ class PubServer(BaseHTTPRequestHandler): languages_understood, self.server.translate) if message_json: + if edited_postid: + message_json['id'] = \ + edited_postid + '/activity' + message_json['object']['id'] = \ + edited_postid + message_json['object']['url'] = \ + edited_postid + message_json['type'] = 'Update' + print('DEBUG: sending edited unlisted post ' + + str(message_json)) + if fields['schedulePost']: return 1 if self._post_to_outbox(message_json, @@ -19674,6 +19747,17 @@ class PubServer(BaseHTTPRequestHandler): languages_understood, self.server.translate) if message_json: + if edited_postid: + message_json['id'] = \ + edited_postid + '/activity' + message_json['object']['id'] = \ + edited_postid + message_json['object']['url'] = \ + edited_postid + message_json['type'] = 'Update' + print('DEBUG: sending edited followers post ' + + str(message_json)) + if fields['schedulePost']: return 1 if self._post_to_outbox(message_json, @@ -19747,6 +19831,17 @@ class PubServer(BaseHTTPRequestHandler): reply_is_chat, self.server.translate) if message_json: + if edited_postid: + message_json['id'] = \ + edited_postid + '/activity' + message_json['object']['id'] = \ + edited_postid + message_json['object']['url'] = \ + edited_postid + message_json['type'] = 'Update' + print('DEBUG: sending edited dm post ' + + str(message_json)) + if fields['schedulePost']: return 1 print('Sending new DM to ' + diff --git a/utils.py b/utils.py index 9b6551648..480fc62e3 100644 --- a/utils.py +++ b/utils.py @@ -2353,6 +2353,23 @@ def is_public_post(post_json_object: {}) -> bool: return False +def is_followers_post(post_json_object: {}) -> bool: + """Returns true if the given post is to followers + """ + if not post_json_object.get('type'): + return False + if post_json_object['type'] != 'Create': + return False + if not has_object_dict(post_json_object): + return False + if not post_json_object['object'].get('to'): + return False + for recipient in post_json_object['object']['to']: + if recipient.endswith('/followers'): + return True + return False + + def is_unlisted_post(post_json_object: {}) -> bool: """Returns true if the given post is unlisted """ diff --git a/webapp_create_post.py b/webapp_create_post.py index 620ff8efa..ffb38ab61 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -8,6 +8,11 @@ __status__ = "Production" __module_group__ = "Web Interface" import os +from utils import remove_html +from utils import get_content_from_post +from utils import has_object_dict +from utils import load_json +from utils import locate_post from utils import get_new_post_endpoints from utils import is_public_post_from_url from utils import get_nickname_from_actor @@ -31,6 +36,7 @@ from webapp_utils import edit_currency_field from webapp_post import individual_post_as_html from maps import get_map_preferences_url from maps import get_map_preferences_coords +from maps import get_location_from_tags def _html_following_data_list(base_dir: str, nickname: str, @@ -197,7 +203,34 @@ def _html_new_post_drop_down(scope_icon: str, scope_description: str, return drop_down_content -def html_new_post(media_instance: bool, translate: {}, +def _get_date_from_tags(tags: []) -> (str, str): + """Returns the date from the tags list + """ + for tag_item in tags: + if not tag_item.get('type'): + continue + if tag_item['type'] != 'Event': + continue + if not tag_item.get('startTime'): + continue + if not isinstance(tag_item['startTime'], str): + continue + if 'T' not in tag_item['startTime']: + continue + start_time = tag_item['startTime'] + if not tag_item.get('endTime'): + return start_time, '' + if not isinstance(tag_item['endTime'], str): + return start_time, '' + if 'T' not in tag_item['endTime']: + return start_time, '' + end_time = tag_item['endTime'] + return start_time, end_time + return '', '' + + +def html_new_post(edit_post_params: {}, + media_instance: bool, translate: {}, base_dir: str, http_prefix: str, path: str, in_reply_to: str, mentions: [], @@ -221,6 +254,7 @@ def html_new_post(media_instance: bool, translate: {}, peertube_instances: [], allow_local_network_access: bool, system_language: str, + languages_understood: [], max_like_count: int, signing_priv_key_pem: str, cw_lists: {}, lists_enabled: str, box_name: str, @@ -229,6 +263,48 @@ def html_new_post(media_instance: bool, translate: {}, min_images_for_accounts: []) -> str: """New post screen """ + # get the json if this is an edited post + edited_post_json = None + if edit_post_params: + if edit_post_params.get('post_url'): + edited_post_filename = \ + locate_post(base_dir, nickname, + domain, edit_post_params['post_url']) + if edited_post_filename: + edited_post_json = load_json(edited_post_filename) + if not has_object_dict(edited_post_json): + return '' + if not edited_post_json: + return '' + if edited_post_json['object'].get('conversation'): + conversation_id = edited_post_json['object']['conversation'] + + # default subject line or content warning + default_subject = '' + if share_description: + default_subject = share_description + + default_location = '' + default_start_time = '' + default_end_time = '' + if edited_post_json: + # if this is an edited post then get the subject line or + # content warning + summary_str = get_content_from_post(edited_post_json, system_language, + languages_understood, "summary") + if summary_str: + default_subject = remove_html(summary_str) + + if edited_post_json['object'].get('tag'): + # if this is an edited post then get the location + location_str = \ + get_location_from_tags(edited_post_json['object']['tag']) + if location_str: + default_location = location_str + # if this is an edited post then get the start and end time + default_start_time, default_end_time = \ + _get_date_from_tags(edited_post_json['object']['tag']) + reply_str = '' is_new_reminder = False @@ -244,15 +320,25 @@ def html_new_post(media_instance: bool, translate: {}, # select a date and time for this post date_and_time_str += '\n' - date_and_time_str += '\n' + date_default = '' + time_default = '' + if default_start_time: + date_default = ' value="' + default_start_time.split('T')[0] + '"' + time_default = ' value="' + default_start_time.split('T')[1] + '"' + if default_end_time: + end_time_default = ' value="' + default_end_time.split('T')[1] + '"' + date_and_time_str += \ + '\n' date_and_time_str += '\n
\n' + '\n
\n' date_and_time_str += '\n

\n' + '\n

\n' show_public_on_dropdown = True message_box_height = 400 @@ -521,7 +607,8 @@ def html_new_post(media_instance: bool, translate: {}, extra_fields += '\n' extra_fields += '
\n' city_or_loc_str = translate['City or location of the shared item'] - extra_fields += edit_text_field(city_or_loc_str + ':', 'location', '', + extra_fields += edit_text_field(city_or_loc_str + ':', 'location', + default_location, 'https://www.openstreetmap.org/#map=') extra_fields += '
\n' extra_fields += '
\n' @@ -587,7 +674,8 @@ def html_new_post(media_instance: bool, translate: {}, extra_fields += '
\n' extra_fields += '
\n' city_or_loc_str = translate['City or location of the wanted item'] - extra_fields += edit_text_field(city_or_loc_str + ':', 'location', '', + extra_fields += edit_text_field(city_or_loc_str + ':', 'location', + default_location, 'https://www.openstreetmap.org/#map=') extra_fields += '
\n' extra_fields += '
\n' @@ -745,7 +833,8 @@ def html_new_post(media_instance: bool, translate: {}, 'rel="nofollow noopener noreferrer" target="_blank">🗺️ ' + \ translate['Location'] + '' date_and_location += '

\n' + \ - edit_text_field(location_label_with_link, 'location', '', + edit_text_field(location_label_with_link, 'location', + default_location, 'https://www.openstreetmap.org/#map=') + '

\n' date_and_location += end_edit_section() @@ -844,10 +933,17 @@ def html_new_post(media_instance: bool, translate: {}, # reporting a post to moderator mentions_str = 'Re: ' + report_url + '\n\n' + mentions_str - new_post_form += \ - '
\n' + if edited_post_json: + new_post_form += \ + '\n' + else: + new_post_form += \ + '\n' if reply_is_chat: new_post_form += \ ' \n' @@ -908,9 +1004,6 @@ def html_new_post(media_instance: bool, translate: {}, if media_instance and not reply_str: new_post_form += new_post_image_section - if not share_description: - share_description = '' - # for reminders show the date and time at the top if is_new_reminder: new_post_form += '
\n' @@ -918,7 +1011,7 @@ def html_new_post(media_instance: bool, translate: {}, new_post_form += '
\n' new_post_form += \ - edit_text_field(placeholder_subject, 'subject', share_description) + edit_text_field(placeholder_subject, 'subject', default_subject) new_post_form += '' selected_str = ' selected' @@ -952,11 +1045,20 @@ def html_new_post(media_instance: bool, translate: {}, elif endpoint == 'newblog': message_box_height = 800 + # get the default message text + default_message = '' + if edited_post_json: + content_str = \ + get_content_from_post(edited_post_json, system_language, + languages_understood, "content") + if content_str: + default_message = remove_html(content_str) + new_post_form += \ ' \n' + default_message + '\n' new_post_form += \ extra_fields + citations_str + replies_section + date_and_location if not media_instance or reply_str: diff --git a/webapp_post.py b/webapp_post.py index f94d47589..be30a24ab 100644 --- a/webapp_post.py +++ b/webapp_post.py @@ -56,6 +56,7 @@ from utils import is_blog_post from utils import get_display_name from utils import display_name_is_emoji from utils import is_public_post +from utils import is_followers_post from utils import update_recent_posts_cache from utils import remove_id_ending from utils import get_nickname_from_actor @@ -574,6 +575,71 @@ def _get_edit_icon_html(base_dir: str, nickname: str, domain_full: str, '' + edit_event_str + \
                 ' |\n' + elif is_public_post(post_json_object): + # Edit a public post + edit_post_str = 'Edit post' + if translate.get(edit_post_str): + edit_post_str = translate[edit_post_str] + edit_str += \ + ' ' + \ + '' + \ + '' + edit_post_str + \
+                ' |\n' + elif is_dm(post_json_object): + # Edit a DM + edit_post_str = 'Edit post' + if translate.get(edit_post_str): + edit_post_str = translate[edit_post_str] + edit_str += \ + ' ' + \ + '' + \ + '' + edit_post_str + \
+                ' |\n' + elif is_unlisted_post(post_json_object): + # Edit an unlisted post + edit_post_str = 'Edit post' + if translate.get(edit_post_str): + edit_post_str = translate[edit_post_str] + edit_str += \ + ' ' + \ + '' + \ + '' + edit_post_str + \
+                ' |\n' + elif is_followers_post(post_json_object): + # Edit a followers only post + edit_post_str = 'Edit post' + if translate.get(edit_post_str): + edit_post_str = translate[edit_post_str] + edit_str += \ + ' ' + \ + '' + \ + '' + edit_post_str + \
+                ' |\n' + return edit_str