collectiveidea / audited

Audited (formerly acts_as_audited) is an ORM extension that logs all changes to your Rails models.
MIT License
3.38k stars 659 forks source link

Action_text auditing is not supported #547

Open weiserma opened 4 years ago

weiserma commented 4 years ago

Wondering is anyone has thought about this?

Get this error when adding 'audited' to the class: ActiveRecord::InverseOfAssociationNotFoundError (Could not find the inverse association for rich_text_text (:record in ActionText::RichText))

jasper502 commented 2 years ago

Anyone manage a work around for this? It's a bit of a deal breaker for me at this point. Would be nice to see the updates to the action_text fields tracked.

codyrobbins commented 2 years ago

For anyone else stymied by this limitation, adding the following monkey patch in an initializer will at least enable recording of changes to the underlying Action Text records:

module ActionTextRichTextAuditing
  extend ActiveSupport::Concern

  prepended do
    audited
  end

  def audited_attributes
    _stringify_body_attribute super
  end

  private

  def audited_changes
    _stringify_body_attribute super
  end

  def _stringify_body_attribute(attributes)
    attributes.tap do |attributes|
      if attributes.include? 'body'
        attributes['body'] =
          if attributes['body'].is_a? Array
            attributes['body'].collect &:to_s
          else
            attributes['body'].to_s
          end
      end
    end
  end
end

ActiveSupport.on_load(:action_text_rich_text) do
  ActionText::RichText.prepend ActionTextRichTextAuditing
end

This enables auditing on the underlying Action Text wrapper class and stringifies the ActionText::Content instances that are used to represent the actual content of the attribute.

maland commented 2 years ago

thx @codyrobbins

if one now also adds in the private part of your code this method:

def write_audit(attrs)
  attrs[:associated_type] = record_type
  attrs[:associated_id] = record_id
  super
end

and in the model with the has_rich_text add has_associated_audits than one can access e.g. own_and_associated_audits and receive a full list of audits

webakimbo commented 1 year ago

Implementing this solution worked well for me for tracking changes to has_many_attached files. I tried extending it to handle has_rich_text and to my delight it appears to have worked.

# config/initializers/audited.rb
Rails.configuration.to_prepare do
  ActiveStorage::Attachment.audited associated_with: :record
  ActionText::RichText.audited associated_with: :record
end
Halloran commented 6 months ago

In my experience with this, the monkey patch from @codyrobbins, with the addition from @maland work in the case where the ActionText field starts as empty, but in the case where you start with content in the ActionText field and then update it, the audited_changes method gets called with unexpected arguments.

Have tried playing around with this for a while, but no solution as yet.

Halloran commented 5 months ago

If anyone is still looking for a fix on this, it seems that the proposed monkey patch above needs only to be modified to change the lne: def audited_changes to def audited_changes(for_touch: false, exclude_readonly_attrs: false) in order to resolve the issue I described above

codyrobbins commented 5 months ago

If you’re using Ruby 2.7 or above, you can use (...), the new argument forwarding shorthand, to avoid having to track the exact method signatures for audited_attributes and audited_changes.

Here’s an updated monkey patch:

concern :ActionTextRichTextAuditing do
  prepended do
    audited
  end

  def audited_attributes(...)
    _stringify_body_attribute super
  end

  private

  def audited_changes(...)
    _stringify_body_attribute super
  end

  def _stringify_body_attribute(attributes)
    attributes.tap do |attributes|
      if attributes.include? 'body'
        attributes['body'] =
          if attributes['body'].is_a? Array
            attributes['body'].collect &:to_s
          else
            attributes['body'].to_s
          end
      end
    end
  end
end

ActiveSupport.on_load(:action_text_rich_text) do
  ActionText::RichText.prepend ActionTextRichTextAuditing
end
rishijain commented 3 weeks ago

When I am using the solution mentioned here https://github.com/collectiveidea/audited/issues/547#issuecomment-1660826964

I get this error:

Tried to dump unspecified class: ActionText::Content (Psych::DisallowedClass)

To work around that, this is what I did:

module ActionText
  class Content
    def init_with(map)
      content = map["html"]
      initialize(content)
    end

    def encode_with(coder)
      coder.add "html", to_html
    end
  end
end

This works for me. Do you see any potential issues with this?

One of the potential problems I see is that if the user starts saving big html data in the rich text field, then it can potentially lead to "length too big" for the column in the database.