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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
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? }
validates :url, :format => { :with => %r{\Ahttps?://}i,
:allow_blank => true,
:message => :must_be_http }
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
def self.editor_search(term)
words = term.to_s.split(/\s+/).reject(&:blank?)
return none if words.empty?
words.inject(all) do |scope, word|
like = "%#{sanitize_sql_like(word)}%"
tagged = ActsAsTaggableOn::Tagging
.where(:taggable_type => base_class.name)
.joins(:tag).where("tags.name ILIKE ?", like)
.select(:taggable_id)
scope.where(
"events.title ILIKE :t OR events.description ILIKE :t OR " \
"events.location ILIKE :t OR events.url ILIKE :t OR " \
"events.id IN (:tagged)", :t => like, :tagged => tagged
)
end
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
|