about summary refs log tree commit diff
path: root/app/services/activitypub/process_status_update_service.rb
diff options
context:
space:
mode:
authorClaire <claire.github-309c@sitedethib.com>2022-04-06 21:01:02 +0200
committerGitHub <noreply@github.com>2022-04-06 21:01:02 +0200
commit8f91e304a5adb98b657a5c096359d0423a5d7e84 (patch)
tree86332766fc6501b0a80f0ae451603f71f0686e26 /app/services/activitypub/process_status_update_service.rb
parent454ef42aab48e73613c4588faaacfb5941bd3e6a (diff)
Fix spurious edits and require incoming edits to be explicitly marked as such (#17918)
* Change post text edit to not be considered significant if it's identical after reformatting

* We don't need to clear previous change information anymore

* Require status edits to be explicit, except for poll tallies

* Fix tests

* Add some tests

* Add poll-related tests

* Add HTML-formatting related tests
Diffstat (limited to 'app/services/activitypub/process_status_update_service.rb')
-rw-r--r--app/services/activitypub/process_status_update_service.rb50
1 files changed, 40 insertions, 10 deletions
diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb
index 6dc14d8c2..3d9d9cb84 100644
--- a/app/services/activitypub/process_status_update_service.rb
+++ b/app/services/activitypub/process_status_update_service.rb
@@ -17,10 +17,19 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
     # Only native types can be updated at the moment
     return @status if !expected_type? || already_updated_more_recently?
 
-    last_edit_date = status.edited_at.presence || status.created_at
+    if @status_parser.edited_at.present? && (@status.edited_at.nil? || @status_parser.edited_at > @status.edited_at)
+      handle_explicit_update!
+    else
+      handle_implicit_update!
+    end
+
+    @status
+  end
+
+  private
 
-    # Since we rely on tracking of previous changes, ensure clean slate
-    status.clear_changes_information
+  def handle_explicit_update!
+    last_edit_date = @status.edited_at.presence || @status.created_at
 
     # Only allow processing one create/update per status at a time
     RedisLock.acquire(lock_options) do |lock|
@@ -45,12 +54,20 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
       end
     end
 
-    forward_activity! if significant_changes? && @status_parser.edited_at.present? && @status_parser.edited_at > last_edit_date
-
-    @status
+    forward_activity! if significant_changes? && @status_parser.edited_at > last_edit_date
   end
 
-  private
+  def handle_implicit_update!
+    RedisLock.acquire(lock_options) do |lock|
+      if lock.acquired?
+        update_poll!(allow_significant_changes: false)
+      else
+        raise Mastodon::RaceConditionError
+      end
+    end
+
+    queue_poll_notifications!
+  end
 
   def update_media_attachments!
     previous_media_attachments     = @status.media_attachments.to_a
@@ -98,7 +115,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
     @media_attachments_changed = true if @status.ordered_media_attachment_ids != previous_media_attachments_ids
   end
 
-  def update_poll!
+  def update_poll!(allow_significant_changes: true)
     previous_poll        = @status.preloadable_poll
     @previous_expires_at = previous_poll&.expires_at
     poll_parser          = ActivityPub::Parser::PollParser.new(@json)
@@ -109,6 +126,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
       # If for some reasons the options were changed, it invalidates all previous
       # votes, so we need to remove them
       @poll_changed = true if poll_parser.significantly_changes?(poll)
+      return if @poll_changed && !allow_significant_changes
 
       poll.last_fetched_at = Time.now.utc
       poll.options         = poll_parser.options
@@ -121,6 +139,8 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
 
       @status.poll_id = poll.id
     elsif previous_poll.present?
+      return unless allow_significant_changes
+
       previous_poll.destroy!
       @poll_changed = true
       @status.poll_id = nil
@@ -132,7 +152,10 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
     @status.spoiler_text = @status_parser.spoiler_text || ''
     @status.sensitive    = @account.sensitized? || @status_parser.sensitive || false
     @status.language     = @status_parser.language
-    @status.edited_at    = @status_parser.edited_at || Time.now.utc if significant_changes?
+
+    @significant_changes = text_significantly_changed? || @status.spoiler_text_changed? || @media_attachments_changed || @poll_changed
+
+    @status.edited_at = @status_parser.edited_at if significant_changes?
 
     @status.save!
   end
@@ -243,7 +266,14 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
   end
 
   def significant_changes?
-    @status.text_changed? || @status.text_previously_changed? || @status.spoiler_text_changed? || @status.spoiler_text_previously_changed? || @media_attachments_changed || @poll_changed
+    @significant_changes
+  end
+
+  def text_significantly_changed?
+    return false unless @status.text_changed?
+
+    old, new = @status.text_change
+    HtmlAwareFormatter.new(old, false).to_s != HtmlAwareFormatter.new(new, false).to_s
   end
 
   def already_updated_more_recently?