Class: Libertree::Model::Post

Inherits:
Object
  • Object
show all
Extended by:
HasSearchableText
Includes:
HasDisplayText, IsRemoteOrLocal
Defined in:
lib/libertree/model/post.rb

Constant Summary collapse

VISIBILITY_RANK =
{
  'tree' => 0,
  'forest' => 1,
  'internet' => 2,
}

Class Method Summary collapse

Instance Method Summary collapse

Methods included from HasSearchableText

search

Methods included from HasDisplayText

#glimpse, #text_as_html, #text_to_nodeset

Methods included from IsRemoteOrLocal

#forests, #local?, #public_id, #remote?, #server

Class Method Details

.as_nested_json(id) ⇒ Object



589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
# File 'lib/libertree/model/post.rb', line 589

def self.as_nested_json(id)
  post = self[id]
  JSON[ post.to_json( :include => {
                        :member => {},
                        :likes => {
                          :include => {
                            :member => {}
                          }},
                        :comments => {
                          :include => {
                            :member => {},
                            :likes => {
                              :include => {
                                :member => {}
                              }
                            }
                          }
                        }
                      }) ]
end

.commented_on_by(member, posts = self) ⇒ Object



398
399
400
401
402
403
404
# File 'lib/libertree/model/post.rb', line 398

def self.commented_on_by(member, posts=self)
  posts.
    where(Sequel.qualify(:posts, :id) => Comment.
          select(:post_id).
          distinct(:post_id).
          where(:member_id => member.id))
end

.create(*args) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/libertree/model/post.rb', line 257

def self.create(*args)
  post = super
  Libertree::Model::Job.create(
    task: 'post:add-to-rivers',
    params: { 'post_id' => post.id, }.to_json
  )
  if post.member.
    post.mark_as_read_by post.member.
    post.member..subscribe_to post
  end
  post
end

.filter_by_query(parsed_query, account, posts = self) ⇒ Object



431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
# File 'lib/libertree/model/post.rb', line 431

def self.filter_by_query(parsed_query, , posts=self)
  flags = parsed_query['flag']
  if flags
    flags[:negations].each do |flag|
      case flag
      when 'tree'
        posts = posts.exclude(:remote_id => nil)
      when 'unread'
        posts = self.read_by(, posts)
      when 'liked'
        posts = self.without_liked_by(.member, posts)
      when 'commented'
        posts = self.without_commented_on_by(.member, posts)
      when 'subscribed'
        posts = self.without_subscribed_to_by(, posts)
      end
    end

    flags[:requirements].each do |flag|
      case flag
      when 'tree'
        posts = posts.where(:remote_id => nil)
      when 'unread'
        posts = self.unread_by(, posts)
      when 'liked'
        posts = self.liked_by(.member, posts)
      when 'commented'
        posts = self.commented_on_by(.member, posts)
      when 'subscribed'
        posts = self.subscribed_to_by(, posts)
      end
    end

    sets = flags[:regular].map do |flag|
      case flag
      when 'tree'
        posts.where(:remote_id => nil)
      when 'unread'
        self.unread_by(, posts)
      when 'liked'
        self.liked_by(.member, posts)
      when 'commented'
        self.commented_on_by(.member, posts)
      when 'subscribed'
        self.subscribed_to_by(, posts)
      end
    end.compact

    unless sets.empty?
      posts = sets.reduce do |res, set|
        res.union(set)
      end
    end
  end

  tags = parsed_query['tag']
  if tags.values.flatten.count > 0
    # Careful!  Don't trust user input!
    # remove any tag that includes array braces
    excluded = tags[:negations].delete_if    {|t| t =~ /[{}]/ }
    required = tags[:requirements].delete_if {|t| t =~ /[{}]/ }
    regular  = tags[:regular].delete_if      {|t| t =~ /[{}]/ }

    posts = posts.exclude(Sequel.pg_array(:hashtags).cast('text[]').pg_array.overlaps(excluded))  unless excluded.empty?
    posts = posts.where  (Sequel.pg_array(:hashtags).cast('text[]').pg_array.contains(required))  unless required.empty?
    posts = posts.where  (Sequel.pg_array(:hashtags).cast('text[]').pg_array.overlaps(regular))   unless regular.empty?
  end

  phrases = parsed_query['phrase']
  if phrases.values.flatten.count > 0
    req_patterns = phrases[:requirements].map {|phrase| /(^|\b|\s)#{Regexp.escape(phrase)}(\b|\s|$)/ }
    neg_patterns = phrases[:negations].map    {|phrase| /(^|\b|\s)#{Regexp.escape(phrase)}(\b|\s|$)/ }
    reg_patterns = phrases[:regular].map      {|phrase| /(^|\b|\s)#{Regexp.escape(phrase)}(\b|\s|$)/ }

    unless req_patterns.empty?
      posts = posts.grep(:text, req_patterns, { all_patterns: true, case_insensitive: true })
    end

    unless neg_patterns.empty?
      # NOTE: using Regexp.union results in a postgresql error when the phrase includes '#', or '?'
      pattern = "(#{neg_patterns.map(&:source).join('|')})"
      posts = posts.exclude(Sequel.ilike(:text, pattern))
    end

    unless reg_patterns.empty?
      posts = posts.grep(:text, reg_patterns, { all_patterns: false, case_insensitive: true })
    end
  end

  words = parsed_query['word']
  if words.values.flatten.count > 0
    # strip query characters
    words.each_pair {|k,v| words[k].each {|word| word.gsub!(/[\(\)&|!]/, '')}}

    # filter by simple terms first to avoid having to check so many posts
    # TODO: prevent empty arguments to to_tsquery
    posts = posts.where(
      Sequel.lit(
        %{
          to_tsvector('simple', text)
          @@ (to_tsquery('simple', ?)
          && to_tsquery('simple', ?)
          && to_tsquery('simple', ?))
        },
        words[:negations].map{|w| "!#{w}" }.join(' & '),
        words[:requirements].join(' & '),
        words[:regular].join(' | ')
      )
    )
  end

  { 'visibility' => :visibility,
    'from'       => :member_id,
    'via'        => :via,
  }.each_pair do |key, column|
    set = parsed_query[key]
    if set.values.flatten.count > 0
      excluded = set[:negations]
      required = set[:requirements]
      regular  = set[:regular]

      posts = posts.exclude(column => excluded)  unless excluded.empty?
      posts = posts.where(column => required)    unless required.empty?

      unless regular.empty?
        posts = regular.
          map {|value| posts.where(column => value)}.
          reduce {|res, set| res.union(set)}
      end
    end
  end

  posts
end

.get_full(id, viewing_account = nil) ⇒ Object

Expand and embed all associated records.



611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
# File 'lib/libertree/model/post.rb', line 611

def self.get_full(id,  = nil)
  post = self[id]
  return  unless post

  # cache member records
  members = Hash.new
  members.default_proc = proc do |hash, key|
    member = Member[ key ]
    name = member.name_display
    member.define_singleton_method(:name_display) { name }
    hash[key] = member
  end

  post_likes = post.likes

  like_proc = proc do |like|
    like.define_singleton_method(:member) { members[like.member_id] }
    like
  end

  get_comments = lambda do
    comments = Comment.on_post(post, viewing_account: )
    comment_likes = CommentLike.where(
      Sequel.lit('comment_id IN ?', comments.map(&:id))
    ).reduce({}) { |hash, like|
      if hash[like.comment_id]
        hash[like.comment_id] << like
      else
        hash[like.comment_id] = [like]
      end
      hash
    }

    comments.map do |comment|
      likes = if comment_likes[comment.id]
                comment_likes[comment.id].map{|l| like_proc.call(l)}
              else
                []
              end

      comment.define_singleton_method(:member) { members[comment.member_id] }
      comment.define_singleton_method(:likes)  { likes }
      comment.define_singleton_method(:post)   { post }

      comment
    end
  end

  comments = get_comments.call

  # enhance post object with expanded associations
  post.define_singleton_method(:member) { members[post.member_id] }
  post.define_singleton_method(:likes)  { post_likes.map{|l| like_proc.call(l)} }
  post.define_singleton_method(:comments) {|opts={}|
    if opts[:refresh_cache]
      comments = get_comments.call
    end

    res = comments
    if opts
      res = res.find_all {|c| c.id >= opts[:from_id].to_i}  if opts[:from_id]
      res = res.find_all {|c| c.id < opts[:to_id].to_i}     if opts[:to_id]
      res = res.last(opts[:limit].to_i)                     if opts[:limit]
    end
    res
  }

  post
end

.liked_by(member, posts = self) ⇒ Object



382
383
384
385
386
387
388
# File 'lib/libertree/model/post.rb', line 382

def self.liked_by(member, posts=self)
  posts.
    qualify.
    join(:post_likes,
         Sequel.qualify(:post_likes, :post_id) => Sequel.qualify(:posts, :id),
         Sequel.qualify(:post_likes, :member_id) => member.id)
end

.mark_all_as_read_by(account) ⇒ Object



116
117
118
# File 'lib/libertree/model/post.rb', line 116

def self.mark_all_as_read_by()
  DB.dbh[ %{SELECT mark_all_posts_as_read_by(?)}, .id ].get
end

.not_hidden_by(account, posts = self) ⇒ Object



356
357
358
359
360
361
362
363
# File 'lib/libertree/model/post.rb', line 356

def self.not_hidden_by(, posts=self)
  posts.
    qualify.
    left_outer_join(:posts_hidden,
                    Sequel.qualify(:posts_hidden, :post_id) => Sequel.qualify(:posts, :id),
                    Sequel.qualify(:posts_hidden, :account_id) => .id).
    where(Sequel.qualify(:posts_hidden, :post_id) => nil)
end

.read_by(account, posts = self) ⇒ Object



365
366
367
368
369
370
371
# File 'lib/libertree/model/post.rb', line 365

def self.read_by(, posts=self)
  posts.
    qualify.
    join(:posts_read,
         Sequel.qualify(:posts_read, :post_id) => Sequel.qualify(:posts, :id),
         Sequel.qualify(:posts_read, :account_id) => .id)
end

.subscribed_to_by(account, posts = self) ⇒ Object



414
415
416
417
418
419
420
# File 'lib/libertree/model/post.rb', line 414

def self.subscribed_to_by(, posts=self)
  posts.
    qualify.
    join(:post_subscriptions,
         Sequel.qualify(:post_subscriptions, :post_id) => Sequel.qualify(:posts, :id),
         Sequel.qualify(:post_subscriptions, :account_id) => .id)
end

.unread_by(account, posts = self) ⇒ Object



373
374
375
376
377
378
379
380
# File 'lib/libertree/model/post.rb', line 373

def self.unread_by(, posts=self)
  posts.
    qualify.
    left_outer_join(:posts_read,
                    Sequel.qualify(:posts_read, :post_id) => Sequel.qualify(:posts, :id),
                    Sequel.qualify(:posts_read, :account_id) => .id).
    where(Sequel.qualify(:posts_read, :post_id) => nil)
end

.urls_already_posted?(prospective_post_text) ⇒ Post

This method only searches recent posts, not necessarily every post in the DB.

Returns:

  • (Post)

    the earliest Post which contains a URL that the prospective post text contains



684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
# File 'lib/libertree/model/post.rb', line 684

def self.urls_already_posted?(prospective_post_text)
  posts_found = []

  prospective_post_text.scan(
    %r{(?<=\]\()(https?://\S+?)(?=\))|(?:^|\b)(https?://\S+)(?=\s|$)}
  ) do |match1, match2|
    url = match1 || match2
    posts = self.s(
      %{
        SELECT *
        FROM (
          SELECT *
          FROM posts
          ORDER BY id DESC
          LIMIT 256
        ) AS subquery
        WHERE text ~ ( '(^|\\A|\\s)' || ? || '(\\)|\\Z|\\s|$)' )
        OR text ~ ( '\\]\\(' || ? || '\\)' )
        ORDER by time_created
      },
      Regexp.escape(url),
      Regexp.escape(url)
    )
    if posts.count > 0 && posts.count < 4
      posts_found << posts[0]
    end
  end

  posts_found.sort_by(&:time_created)[0]
end

.with_tag(opts = {}) ⇒ Object



311
312
313
314
315
316
317
318
319
320
321
# File 'lib/libertree/model/post.rb', line 311

def self.with_tag( opts = {} )
  return []  if opts[:tag].nil? || opts[:tag].empty?
  tags = Sequel.pg_array(Array(opts[:tag]).map(&:downcase))
  time = Time.at( opts.fetch(:time, Time.now.to_f) ).strftime("%Y-%m-%d %H:%M:%S.%6N%z")
  Post.s(%{SELECT * FROM tagged_posts(?, ?, ?, ?, ?)},
         tags,
         time,
         opts[:newer],
         opts[:order_by] == :comment,
         opts.fetch(:limit, 30))
end

.without_commented_on_by(member, posts = self) ⇒ Object



406
407
408
409
410
411
412
# File 'lib/libertree/model/post.rb', line 406

def self.without_commented_on_by(member, posts=self)
  posts.
    exclude(Sequel.qualify(:posts, :id) => Comment.
            select(:post_id).
            distinct(:post_id).
            where(:member_id => member.id))
end

.without_liked_by(member, posts = self) ⇒ Object



390
391
392
393
394
395
396
# File 'lib/libertree/model/post.rb', line 390

def self.without_liked_by(member, posts=self)
  posts.
    qualify.
    join(:post_likes,
         Sequel.qualify(:post_likes, :post_id) => Sequel.qualify(:posts, :id),
         Sequel.qualify(:post_likes, :member_id) => member.id)
end

.without_subscribed_to_by(account, posts = self) ⇒ Object



422
423
424
425
426
427
428
429
# File 'lib/libertree/model/post.rb', line 422

def self.without_subscribed_to_by(, posts=self)
  posts.
    qualify.
    left_outer_join(:post_subscriptions,
                    Sequel.qualify(:post_subscriptions, :post_id) => Sequel.qualify(:posts, :id),
                    Sequel.qualify(:post_subscriptions, :account_id) => .id).
    where(Sequel.qualify(:post_subscriptions, :post_id) => nil)
end

Instance Method Details

#after_createObject



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/libertree/model/post.rb', line 28

def after_create
  super
  if self.local? && self.distribute?
    Libertree::Model::Job.create_for_forests(
      {
        task: 'request:POST',
        params: { 'post_id' => self.id, }
      },
      *self.forests
    )
  end
  Libertree::Embedder.autoembed(self.text)
  self.notify_mentioned
  self.notify_group_members
end

#after_updateObject



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/libertree/model/post.rb', line 49

def after_update
  super
  has_distributable_difference = (
    self.previous_changes.include?(:text) ||
    self.previous_changes.include?(:visibility)
  )

  # TODO: deny change of visibility to 'tree' visibility?
  #       or trigger deletion on remotes?
  if self.local? && self.distribute? && has_distributable_difference
    Libertree::Model::Job.create_for_forests(
      {
        task: 'request:POST',
        params: { 'post_id' => self.id, }
      },
      *self.forests
    )
  end
  Libertree::Embedder.autoembed(self.text)
end

#before_createObject



23
24
25
26
# File 'lib/libertree/model/post.rb', line 23

def before_create
  self.hashtags = self.extract_hashtags
  super
end

#before_destroyObject



231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/libertree/model/post.rb', line 231

def before_destroy
  if self.local? && self.distribute?
    Libertree::Model::Job.create_for_forests(
      {
        task: 'request:POST-DELETE',
        params: { 'post_id' => self.id, }
      },
      *self.forests
    )
  end
  super
end

#before_updateObject



44
45
46
47
# File 'lib/libertree/model/post.rb', line 44

def before_update
  self.hashtags = self.extract_hashtags
  super
end

#collected_by?(account) ⇒ Boolean

Returns:

  • (Boolean)


566
567
568
# File 'lib/libertree/model/post.rb', line 566

def collected_by?()
  DB.dbh[ "SELECT account_collected_post(?, ?)", .id, self.id ].single_value
end

#commented_on_by?(member) ⇒ Boolean

Returns:

  • (Boolean)


126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/libertree/model/post.rb', line 126

def commented_on_by?(member)
  DB.dbh[
    %{
      SELECT EXISTS(
        SELECT 1
        FROM comments
        WHERE
          post_id = ?
          AND member_id = ?
      )
    },
    self.id,
    member.id
  ].single_value
end

#comments(opt = nil) ⇒ Object

Parameters:

  • opt (Hash) (defaults to: nil)

    options for restricting the comment set returned. See Comment.on_post .



121
122
123
124
# File 'lib/libertree/model/post.rb', line 121

def comments(opt = nil)
  opt ||= {}  # We put this here instead of in the method signature because sometimes nil is literally sent
  Comment.on_post(self, opt)
end

#deleteObject

TODO: the correct method to call is “destroy”



245
246
247
248
# File 'lib/libertree/model/post.rb', line 245

def delete
  self.before_destroy
  super
end

#delete_cascade(force = false) ⇒ Object

NOTE: deletion is NOT distributed when force=true



251
252
253
254
255
# File 'lib/libertree/model/post.rb', line 251

def delete_cascade(force=false)
  self.before_destroy  unless force

  DB.dbh[ "SELECT delete_cascade_post(?)", self.id ].get
end

#distribute?Boolean

Returns:

  • (Boolean)


585
586
587
# File 'lib/libertree/model/post.rb', line 585

def distribute?
  self.visibility != 'tree'
end

#extract_hashtagsObject



304
305
306
307
308
309
# File 'lib/libertree/model/post.rb', line 304

def extract_hashtags
  self.text_as_html.xpath('.//span[@rel="hashtag"]').map do |n|
    n.content[1..-1] =~ /([\p{Word}_-]+)/i
    $1.downcase  if $1
  end
end

#groupObject



749
750
751
# File 'lib/libertree/model/post.rb', line 749

def group
  Libertree::Model::Group[self.group_id]
end

#guidObject



743
744
745
746
747
# File 'lib/libertree/model/post.rb', line 743

def guid
  server = self.member.server
  origin = server ? server.domain : Server.own_domain
  "xmpp:#{origin}?;node=/posts;item=#{self.public_id}"
end

#hidden_by?(account) ⇒ Boolean

Returns:

  • (Boolean)


352
353
354
# File 'lib/libertree/model/post.rb', line 352

def hidden_by?()
  DB.dbh[ "SELECT post_hidden_by_account(?, ?)", self.id, .id ].single_value
end

#like_by(member) ⇒ Object

This is a search, not a create



271
272
273
# File 'lib/libertree/model/post.rb', line 271

def like_by(member)
  PostLike[ member_id: member.id, post_id: self.id ]
end

#liked_by?(member) ⇒ Boolean

Returns:

  • (Boolean)


274
275
276
# File 'lib/libertree/model/post.rb', line 274

def liked_by?(member)
  !! like_by(member)
end

#likesObject



142
143
144
145
# File 'lib/libertree/model/post.rb', line 142

def likes
  return @likes   if @likes
  @likes = PostLike.where(post_id: self.id).reverse_order(:id)
end

#mark_as_read_by(account) ⇒ Object



83
84
85
# File 'lib/libertree/model/post.rb', line 83

def mark_as_read_by()
  DB.dbh[ "SELECT mark_post_as_read_by( ?, ? )", self.id, .id ].get
end

#mark_as_unread_by(account) ⇒ Object



87
88
89
90
91
92
93
94
# File 'lib/libertree/model/post.rb', line 87

def mark_as_unread_by()
  DB.dbh[  "DELETE FROM posts_read WHERE post_id = ? AND account_id = ?", self.id, .id ].get
  .rivers.each do |river|
    if river.should_contain? self
      DB.dbh[ "INSERT INTO river_posts ( river_id, post_id ) VALUES ( ?, ? )", river.id, self.id ].get
    end
  end
end

#mark_as_unread_by_all(options = {}) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/libertree/model/post.rb', line 96

def mark_as_unread_by_all( options = {} )
  except_accounts = options.fetch(:except, [])
  if except_accounts.any?
    DB.dbh[:posts_read].where(
      Sequel.lit(
        'post_id = ? AND NOT account_id IN ?',
        self.id,
        except_accounts.map(&:id)
      )
    ).delete
  else
    DB.dbh[ "DELETE FROM posts_read WHERE post_id = ?", self.id ].get
  end

  Libertree::Model::Job.create(
    task: 'post:add-to-rivers',
    params: { 'post_id' => self.id, }.to_json
  )
end

#memberObject

TODO: DB: replace with association



71
72
73
# File 'lib/libertree/model/post.rb', line 71

def member
  @member ||= Member[self.member_id]
end

#mentioned_accountsObject



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/libertree/model/post.rb', line 212

def mentioned_accounts
  accounts = Set.new

  # find all JIDs first
  # TODO: username matching (left here for compatibility reasons) is deprecated
  self.text_as_html.xpath('.//span[@rel="username"]').each do |n|
    handle = n.content[1..-1].downcase
    if  = Account[ username: handle ]
      accounts << 
    elsif member = Member.with_handle(handle)
      accounts << member.  if member.
    end
  end

  # remove post author from result set
  accounts.delete(self.member.)
  accounts.to_a
end

#notify_about_comment(comment) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/libertree/model/post.rb', line 147

def notify_about_comment(comment)
  notification_attributes = {
    'type'       => 'comment',
    'comment_id' => comment.id,
  }
  accounts = comment.post.subscribers
  if comment.member.
    accounts = accounts.select {|a| a.id != comment.member..id }
  end
  accounts.each do ||
    if ! comment.post.hidden_by?() && ! .ignoring?(comment.member)
      .notify_about notification_attributes
    end
  end
end

#notify_about_like(like) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/libertree/model/post.rb', line 163

def notify_about_like(like)
  notification_attributes = {
    'type'         => 'post-like',
    'post_like_id' => like.id,
  }
   = like.post.member.
  like_author = like.member.

  if(
     &&
    (!like_author || .id != like_author.id) &&
    ! .ignoring?(like.member)
  )
    .notify_about notification_attributes
  end
end

#notify_group_membersObject



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/libertree/model/post.rb', line 193

def notify_group_members
  return  if self.group.nil?

  notification_attributes = {
    'type'    => 'group-post',
    'post_id' => self.id,
  }

  self.group.members.each do |member|
    if(
      member. &&
      member != self.member &&
      ! member..ignoring?(self.member)
    )
      member..notify_about notification_attributes
    end
  end
end

#notify_mentionedObject



180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/libertree/model/post.rb', line 180

def notify_mentioned
  notification_attributes = {
    'type'    => 'mention',
    'post_id' => self.id,
  }

  mentioned_accounts.each do |a|
    if ! a.ignoring?(self.member)
      a.notify_about notification_attributes
    end
  end
end

#pools_by_member(member_id) ⇒ Object



715
716
717
718
719
720
721
722
723
724
# File 'lib/libertree/model/post.rb', line 715

def pools_by_member(member_id)
  Libertree::Model::Pool.qualify
    .join(
      :pools_posts,
      Sequel.qualify(:pools_posts, :pool_id) => Sequel.qualify(:pools, :id)
    ).where(
      Sequel.qualify(:pools_posts, :post_id) => self.id,
      Sequel.qualify(:pools, :member_id) => member_id
    )
end

#read_by?(account) ⇒ Boolean

Returns:

  • (Boolean)


79
80
81
# File 'lib/libertree/model/post.rb', line 79

def read_by?()
  DB.dbh[ "SELECT EXISTS( SELECT 1 FROM posts_read WHERE post_id = ? AND account_id = ? LIMIT 1 )", self.id, .id ].single_value
end

#revise(text_new, visibility = self.visibility) ⇒ Object



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/libertree/model/post.rb', line 330

def revise(text_new, visibility = self.visibility)
  if VISIBILITY_RANK[visibility] > VISIBILITY_RANK[self.visibility] && ! visibility_expandable?
    raise VisibilityExpansionError
  end

  PostRevision.create(
    post_id: self.id,
    text:    self.text
  )
  self.update(
    text:         text_new,
    visibility:   visibility,
    time_updated: Time.now
  )

  mark_as_unread_by_all
end

#rivers_belonged_to(account = nil) ⇒ Object

TODO: Optionally restrict by account, so as not to reveal too much to browser/client i.e. rivers not belonging to current account



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/libertree/model/post.rb', line 280

def rivers_belonged_to( = nil)
  query_params = [self.id]

  if 
     = "AND r.account_id = ?"
    query_params << .id
  end

  River.s(
    %{
      SELECT
        r.*
      FROM
          rivers r
        , river_posts rp
      WHERE
        rp.river_id = r.id
        AND rp.post_id = ?
        #{  }
    },
    *query_params
  )
end

#subscribersObject



323
324
325
326
327
328
# File 'lib/libertree/model/post.rb', line 323

def subscribers
  Account.s(%{
    SELECT a.*
    FROM accounts a, post_subscriptions ps
    WHERE ps.post_id = ? AND a.id = ps.account_id}, self.id)
end

#time_updated_overallObject



75
76
77
# File 'lib/libertree/model/post.rb', line 75

def time_updated_overall
  [time_commented, time_updated].compact.max
end

#to_hashObject



570
571
572
573
574
575
576
577
# File 'lib/libertree/model/post.rb', line 570

def to_hash
  {
    'id'           => self.id,
    'time_created' => self.time_created,
    'time_updated' => self.time_updated,
    'text'         => self.text,
  }
end

#update_collection_status_for_member(member_id, pool_ids) ⇒ Object



726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
# File 'lib/libertree/model/post.rb', line 726

def update_collection_status_for_member(member_id, pool_ids)
  # - ignore ids in pool_ids that are also in ids
  # - remove: ids that are not in pool_ids but are in ids
  # - add: ids that are in pool_ids but not in ids
  ids = pools_by_member(member_id).map(&:id)

  add_ids = pool_ids - ids
  add_pools = Libertree::Model::Pool.where(member_id: member_id, id: add_ids)
  add_pools.each {|pool| pool << self }

  remove_ids = ids - pool_ids
  remove_pools = Libertree::Model::Pool.where(member_id: member_id, id: remove_ids)
  remove_pools.each {|pool| pool.remove_post self }

  add_pools
end

#v_forest?Boolean

Returns:

  • (Boolean)


582
583
584
# File 'lib/libertree/model/post.rb', line 582

def v_forest?
  self.visibility == 'forest' || self.visibility == 'internet'
end

#v_internet?Boolean

Returns:

  • (Boolean)


579
580
581
# File 'lib/libertree/model/post.rb', line 579

def v_internet?
  self.visibility == 'internet'
end