summaryrefslogtreecommitdiff
path: root/app/models/event.rb
blob: 7726f9bf68afdc616f6ac423f5e7c9fc0ddaf328 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
class Event < ApplicationRecord
  include RruleHumanizer

  belongs_to :node, optional: true
  has_many   :occurrences, dependent: :destroy
  acts_as_taggable_on :tags

  validates :title, presence: true, unless: -> { node_id.present? }

  after_save :generate_occurrences

  def occurrences_in_range start_time, end_time
    self.occurrences.where(
      "start_time > ? AND end_time < ?",
      start_time, end_time
    )
  end

  def display_title
    title.presence || node&.head&.title || "Untitled event"
  end

  def save_witnessed(actor:)
    saved = false
    transaction do
      saved = save
      raise ActiveRecord::Rollback unless saved
      witness_event!("event_create", actor)
    end
    saved
  end

  def update_witnessed(attributes, actor:)
    updated = false
    transaction do
      updated = update(attributes)
      raise ActiveRecord::Rollback unless updated
      changes = event_changes
      witness_event!("event_update", actor, changes) if changes.any?
    end
    updated
  end

  def destroy_witnessed(actor:)
    destroyed = false
    transaction do
      witness_event!("event_destroy", actor)
      destroyed = destroy
      raise ActiveRecord::Rollback unless destroyed
    end
    destroyed
  end

  private
    def generate_occurrences
      Occurrence.generate self
    end

    def event_snapshot
      {
        :event_title => title.presence || node&.unique_name || "##{id}",
        :start_time  => start_time&.iso8601,
        :end_time    => end_time&.iso8601,
        :allday      => allday,
        :rrule       => rrule.presence,
        :location    => location.presence,
        :url         => url.presence,
        :event_tags  => tag_list.to_a.sort,
        :path        => node&.unique_name
      }.compact
    end

    def event_changes
      pairs = {}

      saved_changes.except("updated_at", "created_at", "description")
                   .each do |attribute, (before, after)|
        key, pair =
          case attribute
          when "node_id"
            ["node_path", { "from" => Node.find_by(:id => before)&.unique_name,
                            "to"   => Node.find_by(:id => after)&.unique_name }]
          when "start_time", "end_time"
            [attribute, { "from" => before&.iso8601, "to" => after&.iso8601 }]
          when "tag_list"
            ["tags", { "from" => Array(before).sort, "to" => Array(after).sort }]
          else
            [attribute, { "from" => before, "to" => after }]
          end
        pairs[key] = pair
      end

      extra = {}
      extra[:changes] = pairs if pairs.any?
      extra[:description_changed] = true if saved_changes.key?("description")
      extra
    end

    def witness_event!(verb, actor, extra = {})
      NodeAction.record!(:node => node, :participants => [node, self].compact,
                          :action => verb, :user => actor,
                          **event_snapshot, **extra)
    end
end