libertree-model 0.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|