libertree-model 0.9.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/libertree/compat.rb +29 -0
- data/lib/libertree/console.rb +17 -0
- data/lib/libertree/db.rb +37 -0
- data/lib/libertree/embedder.rb +69 -0
- data/lib/libertree/embedding/custom-providers.rb +148 -0
- data/lib/libertree/model/account-settings.rb +14 -0
- data/lib/libertree/model/account.rb +487 -0
- data/lib/libertree/model/chat-message.rb +75 -0
- data/lib/libertree/model/comment-like.rb +68 -0
- data/lib/libertree/model/comment.rb +164 -0
- data/lib/libertree/model/contact-list.rb +81 -0
- data/lib/libertree/model/embed-cache.rb +6 -0
- data/lib/libertree/model/file.rb +6 -0
- data/lib/libertree/model/forest.rb +82 -0
- data/lib/libertree/model/group-member.rb +13 -0
- data/lib/libertree/model/group.rb +47 -0
- data/lib/libertree/model/has-display-text.rb +34 -0
- data/lib/libertree/model/has-searchable-text.rb +19 -0
- data/lib/libertree/model/ignored-member.rb +13 -0
- data/lib/libertree/model/invitation.rb +6 -0
- data/lib/libertree/model/is-remote-or-local.rb +30 -0
- data/lib/libertree/model/job.rb +93 -0
- data/lib/libertree/model/member.rb +222 -0
- data/lib/libertree/model/message.rb +154 -0
- data/lib/libertree/model/node.rb +22 -0
- data/lib/libertree/model/node_affiliation.rb +14 -0
- data/lib/libertree/model/node_subscription.rb +23 -0
- data/lib/libertree/model/notification.rb +71 -0
- data/lib/libertree/model/pool-post.rb +17 -0
- data/lib/libertree/model/pool.rb +172 -0
- data/lib/libertree/model/post-hidden.rb +21 -0
- data/lib/libertree/model/post-like.rb +72 -0
- data/lib/libertree/model/post-revision.rb +9 -0
- data/lib/libertree/model/post.rb +735 -0
- data/lib/libertree/model/profile.rb +25 -0
- data/lib/libertree/model/remote-storage-connection.rb +6 -0
- data/lib/libertree/model/river.rb +249 -0
- data/lib/libertree/model/server.rb +35 -0
- data/lib/libertree/model/session-account.rb +10 -0
- data/lib/libertree/model/url-expansion.rb +6 -0
- data/lib/libertree/model.rb +48 -0
- data/lib/libertree/query.rb +167 -0
- data/lib/libertree/render.rb +28 -0
- metadata +198 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
module Libertree
|
2
|
+
module Model
|
3
|
+
class Member < Sequel::Model(:members)
|
4
|
+
|
5
|
+
def after_create
|
6
|
+
super
|
7
|
+
self.distribute
|
8
|
+
end
|
9
|
+
|
10
|
+
def after_update
|
11
|
+
super
|
12
|
+
self.distribute
|
13
|
+
end
|
14
|
+
|
15
|
+
def before_destroy
|
16
|
+
# TODO: expand later for more granularity:
|
17
|
+
# - only abandon (not delete) posts and comments
|
18
|
+
# - only empty posts if they contain a discussion
|
19
|
+
# - only empty and anonymise comments
|
20
|
+
# - etc.
|
21
|
+
if self.local?
|
22
|
+
Libertree::Model::Job.create_for_forests(
|
23
|
+
{
|
24
|
+
task: 'request:MEMBER-DELETE',
|
25
|
+
params: { 'username' => self.account.username, }
|
26
|
+
}
|
27
|
+
)
|
28
|
+
end
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def distribute
|
33
|
+
return if ! self.local?
|
34
|
+
Libertree::Model::Job.create_for_forests(
|
35
|
+
{ task: 'request:MEMBER',
|
36
|
+
params: { 'username' => self.account.username }
|
37
|
+
}
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def local?
|
42
|
+
! self.account.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
def account
|
46
|
+
@account ||= Account[self.account_id]
|
47
|
+
end
|
48
|
+
|
49
|
+
# TODO: DB: association
|
50
|
+
#many_to_one :server
|
51
|
+
def server
|
52
|
+
@server ||= Server[self.server_id]
|
53
|
+
end
|
54
|
+
alias :tree :server
|
55
|
+
|
56
|
+
def name_display
|
57
|
+
@name_display ||= ( profile && profile.name_display || self.username )
|
58
|
+
end
|
59
|
+
|
60
|
+
def handle
|
61
|
+
if self.username && self.server
|
62
|
+
self.username + "@#{self.server.name_display}"
|
63
|
+
elsif account
|
64
|
+
account.username + "@#{Server.own_domain}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.with_handle(h)
|
69
|
+
if h =~ /^(.+?)@(.+?)$/
|
70
|
+
username = $1
|
71
|
+
host = $2
|
72
|
+
if host == Server.own_domain
|
73
|
+
local = true
|
74
|
+
end
|
75
|
+
else
|
76
|
+
username = h
|
77
|
+
local = true
|
78
|
+
end
|
79
|
+
|
80
|
+
if local
|
81
|
+
self.qualify.
|
82
|
+
join(:accounts, :id=>:account_id).
|
83
|
+
where(:accounts__username => username).
|
84
|
+
limit(1).
|
85
|
+
first
|
86
|
+
else
|
87
|
+
# TODO: servers.name_given is no longer used. Remove it after
|
88
|
+
# migrating user rivers/contact lists etc.
|
89
|
+
self.qualify.
|
90
|
+
join(:servers, :id=>:server_id).
|
91
|
+
where(:members__username => username).
|
92
|
+
where(Sequel.or(:servers__domain => host, :servers__name_given => host)).
|
93
|
+
limit(1).
|
94
|
+
first
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# TODO: this is a temporary helper because with_handle no longer includes display name matches.
|
99
|
+
# This will eventually be removed when river :from queries no longer accept display names.
|
100
|
+
def self.with_display_name(name)
|
101
|
+
self.qualify.
|
102
|
+
join(:profiles, :member_id=>:id).
|
103
|
+
where(:profiles__name_display => name).
|
104
|
+
limit(1).
|
105
|
+
first
|
106
|
+
end
|
107
|
+
|
108
|
+
def username
|
109
|
+
if val = super
|
110
|
+
val
|
111
|
+
elsif a = self.account
|
112
|
+
a.username
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# TODO: DB: association
|
117
|
+
def profile
|
118
|
+
@profile ||= Profile[ member_id: self.id ]
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.create(*args)
|
122
|
+
member = super
|
123
|
+
Profile.create( member_id: member.id )
|
124
|
+
member
|
125
|
+
end
|
126
|
+
|
127
|
+
def posts( opts = {} )
|
128
|
+
limit = opts.fetch(:limit, 30)
|
129
|
+
time = Time.at( opts.fetch(:time, Time.now.to_f) ).strftime("%Y-%m-%d %H:%M:%S.%6N%z")
|
130
|
+
time_clause = if opts[:newer]
|
131
|
+
proc { time_created > time }
|
132
|
+
else
|
133
|
+
proc { time_created < time }
|
134
|
+
end
|
135
|
+
|
136
|
+
res = Post.where(member_id: self.id).
|
137
|
+
where(&time_clause).
|
138
|
+
reverse_order(:time_created).
|
139
|
+
limit(limit)
|
140
|
+
|
141
|
+
# optionally restrict to Internet visible posts
|
142
|
+
res = res.where(visibility: 'internet') if opts[:public]
|
143
|
+
res
|
144
|
+
end
|
145
|
+
|
146
|
+
def comments(n = 10)
|
147
|
+
Comment.where(member_id: self.id).reverse_order(:id).limit(n)
|
148
|
+
end
|
149
|
+
|
150
|
+
def pools
|
151
|
+
@pools ||= Pool.where( member_id: self.id ).all
|
152
|
+
end
|
153
|
+
def springs
|
154
|
+
@springs ||= Pool.where( member_id: self.id, sprung: true ).all
|
155
|
+
end
|
156
|
+
|
157
|
+
def online?
|
158
|
+
self.account && self.account.online?
|
159
|
+
end
|
160
|
+
|
161
|
+
def delete_cascade
|
162
|
+
DB.dbh[ "SELECT delete_cascade_member(?)", self.id ].get
|
163
|
+
end
|
164
|
+
|
165
|
+
def dirty
|
166
|
+
@account = nil
|
167
|
+
@name_display = nil
|
168
|
+
@profile = nil
|
169
|
+
@pools = nil
|
170
|
+
@springs = nil
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.search(name:, include_old: false)
|
174
|
+
if include_old
|
175
|
+
newness_clause = "TRUE"
|
176
|
+
else
|
177
|
+
newness_clause = %{
|
178
|
+
EXISTS (
|
179
|
+
SELECT 1
|
180
|
+
FROM posts po
|
181
|
+
WHERE
|
182
|
+
po.member_id = m.id
|
183
|
+
AND po.time_created > NOW() - '30 days'::INTERVAL
|
184
|
+
) OR EXISTS (
|
185
|
+
SELECT 1
|
186
|
+
FROM comments c
|
187
|
+
WHERE
|
188
|
+
c.member_id = m.id
|
189
|
+
AND c.time_created > NOW() - '30 days'::INTERVAL
|
190
|
+
)
|
191
|
+
}
|
192
|
+
end
|
193
|
+
|
194
|
+
self.s(
|
195
|
+
%{
|
196
|
+
SELECT
|
197
|
+
m.*
|
198
|
+
FROM
|
199
|
+
members m
|
200
|
+
LEFT OUTER JOIN accounts a ON (a.id = m.account_id)
|
201
|
+
LEFT OUTER JOIN profiles p ON (m.id = p.member_id)
|
202
|
+
WHERE
|
203
|
+
(
|
204
|
+
m.username ILIKE '%' || ? || '%'
|
205
|
+
OR a.username ILIKE '%' || ? || '%'
|
206
|
+
OR p.name_display ILIKE '%' || ? || '%'
|
207
|
+
) AND (
|
208
|
+
#{newness_clause}
|
209
|
+
)
|
210
|
+
},
|
211
|
+
name,
|
212
|
+
name,
|
213
|
+
name
|
214
|
+
)
|
215
|
+
end
|
216
|
+
|
217
|
+
def groups
|
218
|
+
Libertree::Model::GroupMember.where(member_id: self.id).map { |gm| gm.group }
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Libertree
|
4
|
+
module Model
|
5
|
+
class Message < Sequel::Model(:messages)
|
6
|
+
include HasDisplayText
|
7
|
+
|
8
|
+
def sender
|
9
|
+
@sender ||= Member[self.sender_member_id]
|
10
|
+
end
|
11
|
+
alias :member :sender
|
12
|
+
|
13
|
+
def distribute
|
14
|
+
trees = self.recipients.reduce(Set.new) { |_trees, recipient|
|
15
|
+
if recipient.tree
|
16
|
+
_trees << recipient.tree
|
17
|
+
end
|
18
|
+
_trees
|
19
|
+
}
|
20
|
+
recipient_ids = self.recipients.map(&:id)
|
21
|
+
|
22
|
+
trees.each do |tree|
|
23
|
+
Libertree::Model::Job.create(
|
24
|
+
{
|
25
|
+
task: 'request:MESSAGE',
|
26
|
+
params: {
|
27
|
+
'message_id' => self.id,
|
28
|
+
'server_id' => tree.id,
|
29
|
+
'recipient_member_ids' => recipient_ids,
|
30
|
+
}.to_json,
|
31
|
+
}
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# forward direct message to the given email address of the provided account
|
37
|
+
def forward_via_email(account)
|
38
|
+
return unless account
|
39
|
+
return unless self.visible_to?(account)
|
40
|
+
|
41
|
+
Libertree::Model::Job.create(
|
42
|
+
task: 'forward-via-email',
|
43
|
+
params: {
|
44
|
+
'username' => account.username,
|
45
|
+
'message_id' => self.id
|
46
|
+
}.to_json
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def recipients
|
51
|
+
return @recipients if @recipients
|
52
|
+
@recipients = Member.s(
|
53
|
+
%{
|
54
|
+
SELECT
|
55
|
+
m.*
|
56
|
+
FROM
|
57
|
+
members m
|
58
|
+
, message_recipients mr
|
59
|
+
WHERE
|
60
|
+
mr.message_id = ?
|
61
|
+
AND m.id = mr.member_id
|
62
|
+
},
|
63
|
+
self.id
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
# the subset of participants who have not deleted the message
|
68
|
+
def active_local_participants
|
69
|
+
recipients = Member.s(%{SELECT DISTINCT m.* FROM members m
|
70
|
+
JOIN accounts a ON (a.id = m.account_id)
|
71
|
+
JOIN message_recipients mr ON (m.id = mr.member_id)
|
72
|
+
WHERE mr.message_id = ?
|
73
|
+
AND NOT mr.deleted
|
74
|
+
}, self.id)
|
75
|
+
if ! self.deleted && self.sender.local?
|
76
|
+
( recipients + [self.sender] ).uniq
|
77
|
+
else
|
78
|
+
recipients
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def delete_for_participant(local_member)
|
83
|
+
# Delete the message for the local participant only, i.e. by
|
84
|
+
# removing the assignment. If this is the last local
|
85
|
+
# participant, delete the whole message. Note that other
|
86
|
+
# recipients / the sender will not see a change in the number
|
87
|
+
# of recipients when one of the recipients "deletes" their
|
88
|
+
# "copy" of the Message
|
89
|
+
participants = active_local_participants
|
90
|
+
|
91
|
+
# not authorised to delete
|
92
|
+
return if participants.empty?
|
93
|
+
|
94
|
+
if participants == [local_member]
|
95
|
+
# This is the only member interested in this message; delete it completely.
|
96
|
+
self.delete_cascade
|
97
|
+
else
|
98
|
+
# there are other local members with a pointer to the
|
99
|
+
# message. Only mark as deleted for this recipient.
|
100
|
+
DB.dbh[ "UPDATE message_recipients SET deleted=true WHERE message_id = ? AND member_id = ?",
|
101
|
+
self.id, local_member.id ].get
|
102
|
+
if local_member == self.sender
|
103
|
+
self.deleted = true
|
104
|
+
self.save
|
105
|
+
end
|
106
|
+
end
|
107
|
+
return true
|
108
|
+
end
|
109
|
+
|
110
|
+
def visible_to?(account)
|
111
|
+
self.sender == account.member || recipients.include?(account.member)
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.create_with_recipients(args)
|
115
|
+
message = self.create(
|
116
|
+
sender_member_id: args[:sender_member_id],
|
117
|
+
remote_id: args[:remote_id],
|
118
|
+
text: args[:text]
|
119
|
+
)
|
120
|
+
sender_member = Model::Member[ args[:sender_member_id].to_i ]
|
121
|
+
|
122
|
+
recipient_member_ids = Array(args[:recipient_member_ids])
|
123
|
+
recipient_member_ids.each do |member_id|
|
124
|
+
DB.dbh[ "INSERT INTO message_recipients ( message_id, member_id ) VALUES ( ?, ? )", message.id, member_id.to_i ].get
|
125
|
+
m = Member[member_id]
|
126
|
+
if m.account
|
127
|
+
a = m.account
|
128
|
+
if ! a.ignoring?(sender_member)
|
129
|
+
a.notify_about 'type' => 'message', 'message_id' => message.id
|
130
|
+
if a.email && a.settings.forward_dms_via_email
|
131
|
+
message.forward_via_email(a)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
message.distribute if sender_member.local?
|
137
|
+
message
|
138
|
+
end
|
139
|
+
|
140
|
+
def delete_cascade
|
141
|
+
DB.dbh[ "SELECT delete_cascade_message(?)", self.id ].get
|
142
|
+
end
|
143
|
+
|
144
|
+
def to_hash
|
145
|
+
{
|
146
|
+
'id' => self.id,
|
147
|
+
'time_created' => self.time_created,
|
148
|
+
'to' => self.recipients.map(&:name_display),
|
149
|
+
'text' => self.text,
|
150
|
+
}
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Libertree
|
2
|
+
module Model
|
3
|
+
class Node < Sequel::Model(:nodes)
|
4
|
+
ACCESS_MODELS = [ :open,
|
5
|
+
:presence,
|
6
|
+
:roster,
|
7
|
+
:authorize,
|
8
|
+
:whitelist ]
|
9
|
+
def affiliations
|
10
|
+
NodeAffiliation.where(:node_id => self.id)
|
11
|
+
end
|
12
|
+
|
13
|
+
def subs(jid=nil)
|
14
|
+
NodeSubscription.for(jid).where(:node_id => self.id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def local_subscribers
|
18
|
+
self.subs.join(:accounts, :id => :account_id)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Libertree
|
2
|
+
module Model
|
3
|
+
class NodeSubscription < Sequel::Model(:node_subscriptions)
|
4
|
+
STATES = [ :none,
|
5
|
+
:pending,
|
6
|
+
:unconfigured,
|
7
|
+
:subscribed ]
|
8
|
+
|
9
|
+
many_to_one :node
|
10
|
+
|
11
|
+
def self.for(jid_or_host)
|
12
|
+
return self unless jid_or_host
|
13
|
+
jid_or_host = jid_or_host.to_s
|
14
|
+
if jid_or_host.include?('@')
|
15
|
+
self.where(jid: jid_or_host)
|
16
|
+
else
|
17
|
+
host_pattern = self.where.escape_like(jid_or_host.to_s)
|
18
|
+
self.where(Sequel.like(:jid, "%@#{host_pattern}"))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Libertree
|
4
|
+
module Model
|
5
|
+
class Notification < Sequel::Model(:notifications)
|
6
|
+
def account
|
7
|
+
@account ||= Account[self.account_id]
|
8
|
+
end
|
9
|
+
|
10
|
+
def data
|
11
|
+
if val = super
|
12
|
+
JSON.parse val
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def subject
|
17
|
+
@subject ||= case self.data['type']
|
18
|
+
when 'comment'
|
19
|
+
Libertree::Model::Comment[ self.data['comment_id'] ]
|
20
|
+
when 'comment-like'
|
21
|
+
Libertree::Model::CommentLike[ self.data['comment_like_id'] ]
|
22
|
+
when 'message'
|
23
|
+
Libertree::Model::Message[ self.data['message_id'] ]
|
24
|
+
when 'post-like'
|
25
|
+
Libertree::Model::PostLike[ self.data['post_like_id'] ]
|
26
|
+
when 'springing'
|
27
|
+
Libertree::Model::PoolPost[ self.data['pool_post_id'] ]
|
28
|
+
when 'mention', 'group-post'
|
29
|
+
Libertree::Model::Post[ self.data['post_id'] ]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.mark_seen_for_account_and_comment_id(account, comment_ids)
|
34
|
+
data = comment_ids.map {|id| %|{"type":"comment","comment_id":#{id.to_i}}| }
|
35
|
+
data += CommentLike.where(comment_id: comment_ids).
|
36
|
+
map {|like| %|{"type":"comment-like","comment_like_id":#{like.id}}| }
|
37
|
+
|
38
|
+
self.where(account_id: account.id, data: data).update(seen: true)
|
39
|
+
account.dirty
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.mark_seen_for_account_and_post(account, post)
|
43
|
+
data = post.likes.map {|like| %|{"type":"post-like","post_like_id":#{like.id.to_i}}| }
|
44
|
+
self.where(account_id: account.id, data: data).update(seen: true)
|
45
|
+
account.dirty
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.mark_seen_for_account_and_message(account, message)
|
49
|
+
self.where("account_id = ? AND data = ?", account.id, %|{"type":"message","message_id":#{message.id}}|).
|
50
|
+
update(seen: true)
|
51
|
+
account.dirty
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.mark_seen_for_account(account, notification_ids)
|
55
|
+
if notification_ids[0] == 'all'
|
56
|
+
self.where(account_id: account.id).update(seen: true)
|
57
|
+
else
|
58
|
+
self.where(account_id: account.id, id: notification_ids).
|
59
|
+
update(seen: true)
|
60
|
+
end
|
61
|
+
account.dirty
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.mark_unseen_for_account(account, notification_ids)
|
65
|
+
self.where(account_id: account.id, id: notification_ids).
|
66
|
+
update(seen: false)
|
67
|
+
account.dirty
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module Libertree
|
2
|
+
module Model
|
3
|
+
class Pool < Sequel::Model(:pools)
|
4
|
+
include IsRemoteOrLocal
|
5
|
+
|
6
|
+
def create_pool_post_job(post)
|
7
|
+
Libertree::Model::Job.create_for_forests(
|
8
|
+
{
|
9
|
+
task: 'request:POOL-POST',
|
10
|
+
params: {
|
11
|
+
'pool_id' => self.id,
|
12
|
+
'post_id' => post.id,
|
13
|
+
}
|
14
|
+
},
|
15
|
+
*self.forests
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_pool_delete_job
|
20
|
+
Libertree::Model::Job.create_for_forests(
|
21
|
+
{
|
22
|
+
task: 'request:POOL-DELETE',
|
23
|
+
params: { 'pool_id' => self.id, }
|
24
|
+
},
|
25
|
+
*self.forests
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def after_create
|
30
|
+
super
|
31
|
+
if self.local? && self.sprung?
|
32
|
+
Libertree::Model::Job.create_for_forests(
|
33
|
+
{
|
34
|
+
task: 'request:POOL',
|
35
|
+
params: { 'pool_id' => self.id, }
|
36
|
+
},
|
37
|
+
*self.forests
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def after_update
|
43
|
+
super
|
44
|
+
if self.local?
|
45
|
+
if ! self.sprung?
|
46
|
+
self.create_pool_delete_job
|
47
|
+
else
|
48
|
+
if self.previous_changes.include?(:sprung)
|
49
|
+
Libertree::Model::Job.create_for_forests(
|
50
|
+
{
|
51
|
+
task: 'request:POOL',
|
52
|
+
params: { 'pool_id' => self.id, }
|
53
|
+
},
|
54
|
+
*self.forests
|
55
|
+
)
|
56
|
+
self.posts.last(16).each do |post|
|
57
|
+
self.create_pool_post_job(post)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def before_destroy
|
65
|
+
if self.local? && self.sprung?
|
66
|
+
self.create_pool_delete_job
|
67
|
+
end
|
68
|
+
super
|
69
|
+
end
|
70
|
+
|
71
|
+
def member
|
72
|
+
@member ||= Member[self.member_id]
|
73
|
+
end
|
74
|
+
|
75
|
+
# TODO: DRY up with member.posts?
|
76
|
+
def posts( opts = {} )
|
77
|
+
limit = opts.fetch(:limit, 30)
|
78
|
+
time = Time.at( opts.fetch(:time, Time.now.to_f) ).strftime("%Y-%m-%d %H:%M:%S.%6N%z")
|
79
|
+
time_clause = if opts[:newer]
|
80
|
+
proc { time_created > time }
|
81
|
+
else
|
82
|
+
proc { time_created < time }
|
83
|
+
end
|
84
|
+
|
85
|
+
res = Post.qualify.
|
86
|
+
join(:pools_posts, :post_id=>:id).
|
87
|
+
where(&time_clause).
|
88
|
+
where(:pool_id => self.id).
|
89
|
+
reverse_order(:posts__id).
|
90
|
+
limit(limit)
|
91
|
+
|
92
|
+
# optionally restrict to Internet visible posts
|
93
|
+
res = res.where(visibility: 'internet') if opts[:public]
|
94
|
+
res
|
95
|
+
end
|
96
|
+
|
97
|
+
def includes?(post)
|
98
|
+
DB.dbh[ "SELECT EXISTS( SELECT 1 FROM pools_posts WHERE post_id = ? AND pool_id = ? LIMIT 1 )", post.id, self.id ].single_value
|
99
|
+
end
|
100
|
+
|
101
|
+
# NOTE: deletion is NOT distributed
|
102
|
+
def delete_cascade
|
103
|
+
DB.dbh[ "SELECT delete_cascade_pool(?)", self.id ].get
|
104
|
+
end
|
105
|
+
|
106
|
+
def dirty
|
107
|
+
@posts = nil
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
def notify_about_springing(pool_post)
|
112
|
+
pool = pool_post.pool
|
113
|
+
return if ! pool.sprung
|
114
|
+
|
115
|
+
post = pool_post.post
|
116
|
+
local_post_author = post.member.account
|
117
|
+
pool_owner = pool.member.account
|
118
|
+
|
119
|
+
if(
|
120
|
+
local_post_author &&
|
121
|
+
local_post_author != pool_owner &&
|
122
|
+
! local_post_author.ignoring?(pool.member)
|
123
|
+
)
|
124
|
+
local_post_author.notify_about( {
|
125
|
+
'type' => 'springing',
|
126
|
+
'pool_post_id' => pool_post.id,
|
127
|
+
} )
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def <<(post)
|
132
|
+
pool_post = PoolPost[ pool_id: self.id, post_id: post.id ]
|
133
|
+
if pool_post.nil?
|
134
|
+
pool_post_created = true
|
135
|
+
pool_post = PoolPost.create(
|
136
|
+
'pool_id' => self.id,
|
137
|
+
'post_id' => post.id
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
self.dirty
|
142
|
+
if self.sprung? && pool_post_created
|
143
|
+
self.notify_about_springing pool_post
|
144
|
+
if self.local?
|
145
|
+
create_pool_post_job(post)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def remove_post(post)
|
151
|
+
DB.dbh[ "DELETE FROM pools_posts WHERE pool_id = ? AND post_id = ?", self.id, post.id ].get
|
152
|
+
self.dirty
|
153
|
+
if self.local? && self.sprung?
|
154
|
+
Libertree::Model::Job.create_for_forests(
|
155
|
+
{
|
156
|
+
task: 'request:POOL-POST-DELETE',
|
157
|
+
params: {
|
158
|
+
'pool_id' => self.id,
|
159
|
+
'post_id' => post.id,
|
160
|
+
}
|
161
|
+
},
|
162
|
+
*self.forests
|
163
|
+
)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def sprung?
|
168
|
+
self.sprung
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|