Class: TopicView

Inherits:
Object
  • Object
show all
Defined in:
lib/topic_view.rb

Constant Summary collapse

MEGA_TOPIC_POSTS_COUNT =
10_000
MIN_POST_READ_TIME =
4.0
MAX_PARTICIPANTS =
24
MAX_POSTS_COUNT_PARTICIPANTS =

if a topic has more that N posts no longer attempt to get accurate participant count, instead grab cached count from topic

500

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(topic_or_topic_id, user = nil, options = {}) ⇒ TopicView

Returns a new instance of TopicView.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/topic_view.rb', line 103

def initialize(topic_or_topic_id, user = nil, options = {})
  @topic = find_topic(topic_or_topic_id)
  @user = user
  @guardian = Guardian.new(@user)

  check_and_raise_exceptions(options[:skip_staff_action])

  @message_bus_last_id = MessageBus.last_id("/topic/#{@topic.id}")

  options.each { |key, value| self.instance_variable_set("@#{key}".to_sym, value) }

  @post_number = [@post_number.to_i, 1].max

  @include_suggested = options.fetch(:include_suggested) { true }
  @include_related = options.fetch(:include_related) { true }

  @chunk_size =
    case
    when @print
      TopicView.print_chunk_size
    else
      TopicView.chunk_size
    end

  @limit ||= @chunk_size

  @page = @page.to_i > 1 ? @page.to_i : calculate_page

  setup_filtered_posts
  @filtered_posts = apply_default_scope(@filtered_posts)
  filter_posts(options)

  if @posts && !@skip_custom_fields
    if (added_fields = User.allowed_user_custom_fields(@guardian)).present?
      @user_custom_fields = User.custom_fields_for_ids(@posts.map(&:user_id), added_fields)
    end

    if (allowed_fields = TopicView.allowed_post_custom_fields(@user, @topic)).present?
      @post_custom_fields = Post.custom_fields_for_ids(@posts.map(&:id), allowed_fields)
    end
  end

  TopicView.preload(self)

  @draft_key = @topic.draft_key
  @draft_sequence = DraftSequence.current(@user, @draft_key)

  @can_review_topic = @guardian.can_review_topic?(@topic)
  @queued_posts_enabled = NewPostManager.queue_enabled? || category_require_reply_approval?
  @personal_message = @topic.private_message?
end

Instance Attribute Details

#can_review_topicObject (readonly)

Returns the value of attribute can_review_topic.



22
23
24
# File 'lib/topic_view.rb', line 22

def can_review_topic
  @can_review_topic
end

#chunk_sizeObject (readonly)

Returns the value of attribute chunk_size.



22
23
24
# File 'lib/topic_view.rb', line 22

def chunk_size
  @chunk_size
end

#draftObject

Returns the value of attribute draft.



37
38
39
# File 'lib/topic_view.rb', line 37

def draft
  @draft
end

#draft_keyObject

Returns the value of attribute draft_key.



37
38
39
# File 'lib/topic_view.rb', line 37

def draft_key
  @draft_key
end

#draft_sequenceObject

Returns the value of attribute draft_sequence.



37
38
39
# File 'lib/topic_view.rb', line 37

def draft_sequence
  @draft_sequence
end

#filtered_postsObject (readonly)

Returns the value of attribute filtered_posts.



22
23
24
# File 'lib/topic_view.rb', line 22

def filtered_posts
  @filtered_posts
end

#guardianObject (readonly)

Returns the value of attribute guardian.



22
23
24
# File 'lib/topic_view.rb', line 22

def guardian
  @guardian
end

#message_bus_last_idObject (readonly)

Returns the value of attribute message_bus_last_id.



22
23
24
# File 'lib/topic_view.rb', line 22

def message_bus_last_id
  @message_bus_last_id
end

#personal_messageObject (readonly)

Returns the value of attribute personal_message.



22
23
24
# File 'lib/topic_view.rb', line 22

def personal_message
  @personal_message
end

#post_custom_fieldsObject

Returns the value of attribute post_custom_fields.



37
38
39
# File 'lib/topic_view.rb', line 37

def post_custom_fields
  @post_custom_fields
end

#postsObject (readonly)

Returns the value of attribute posts.



22
23
24
# File 'lib/topic_view.rb', line 22

def posts
  @posts
end

Returns the value of attribute print.



22
23
24
# File 'lib/topic_view.rb', line 22

def print
  @print
end

#queued_posts_enabledObject (readonly) Also known as: queued_posts_enabled?

Returns the value of attribute queued_posts_enabled.



22
23
24
# File 'lib/topic_view.rb', line 22

def queued_posts_enabled
  @queued_posts_enabled
end

#topicObject (readonly)

Returns the value of attribute topic.



22
23
24
# File 'lib/topic_view.rb', line 22

def topic
  @topic
end

#user_custom_fieldsObject

Returns the value of attribute user_custom_fields.



37
38
39
# File 'lib/topic_view.rb', line 37

def user_custom_fields
  @user_custom_fields
end

Class Method Details

.add_custom_filter(key, &blk) ⇒ Object



75
76
77
78
# File 'lib/topic_view.rb', line 75

def self.add_custom_filter(key, &blk)
  @custom_filters ||= {}
  @custom_filters[key] = blk
end

.add_post_custom_fields_allowlister(&block) ⇒ Object



65
66
67
# File 'lib/topic_view.rb', line 65

def self.add_post_custom_fields_allowlister(&block)
  post_custom_fields_allowlisters << block
end

.allowed_post_custom_fields(user, topic) ⇒ Object



69
70
71
72
73
# File 'lib/topic_view.rb', line 69

def self.allowed_post_custom_fields(user, topic)
  wpcf =
    default_post_custom_fields + post_custom_fields_allowlisters.map { |w| w.call(user, topic) }
  wpcf.flatten.uniq
end

.apply_custom_default_scope(&block) ⇒ Object

Configure a default scope to be applied to @filtered_posts. The registered block is called with @filtered_posts and an instance of ‘TopicView`.

This API should be considered experimental until it is exposed in ‘Plugin::Instance`.



90
91
92
# File 'lib/topic_view.rb', line 90

def self.apply_custom_default_scope(&block)
  custom_default_scopes << block
end

.cancel_preload(&blk) ⇒ Object



11
12
13
14
15
16
# File 'lib/topic_view.rb', line 11

def self.cancel_preload(&blk)
  if @preload
    @preload.delete blk
    @preload = nil if @preload.length == 0
  end
end

.chunk_sizeObject



53
54
55
# File 'lib/topic_view.rb', line 53

def self.chunk_size
  20
end

.custom_default_scopesObject



94
95
96
# File 'lib/topic_view.rb', line 94

def self.custom_default_scopes
  @custom_default_scopes ||= []
end

.custom_filtersObject



80
81
82
# File 'lib/topic_view.rb', line 80

def self.custom_filters
  @custom_filters || {}
end

.default_post_custom_fieldsObject



57
58
59
# File 'lib/topic_view.rb', line 57

def self.default_post_custom_fields
  @default_post_custom_fields ||= [Post::NOTICE, "action_code_who", "action_code_path"]
end

.on_preload(&blk) ⇒ Object



7
8
9
# File 'lib/topic_view.rb', line 7

def self.on_preload(&blk)
  (@preload ||= Set.new) << blk
end

.post_custom_fields_allowlistersObject



61
62
63
# File 'lib/topic_view.rb', line 61

def self.post_custom_fields_allowlisters
  @post_custom_fields_allowlisters ||= Set.new
end

.preload(topic_view) ⇒ Object



18
19
20
# File 'lib/topic_view.rb', line 18

def self.preload(topic_view)
  @preload.each { |preload| preload.call(topic_view) } if @preload
end


49
50
51
# File 'lib/topic_view.rb', line 49

def self.print_chunk_size
  1000
end

.reset_custom_default_scopesObject

For testing



99
100
101
# File 'lib/topic_view.rb', line 99

def self.reset_custom_default_scopes
  @custom_default_scopes = nil
end

Instance Method Details

#absolute_urlObject



218
219
220
# File 'lib/topic_view.rb', line 218

def absolute_url
  "#{Discourse.base_url_no_prefix}#{relative_url}"
end

#actions_summaryObject



581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
# File 'lib/topic_view.rb', line 581

def actions_summary
  return @actions_summary unless @actions_summary.nil?

  @actions_summary = []
  return @actions_summary unless post = posts&.first
  PostActionType.topic_flag_types.each do |sym, id|
    @actions_summary << {
      id: id,
      count: 0,
      hidden: false,
      can_act: @guardian.post_can_act?(post, sym),
    }
  end

  @actions_summary
end

#all_post_actionsObject



531
532
533
# File 'lib/topic_view.rb', line 531

def all_post_actions
  @all_post_actions ||= PostAction.counts_for(@posts, @user)
end

#bookmarksObject



412
413
414
415
416
417
418
419
420
421
422
423
424
425
# File 'lib/topic_view.rb', line 412

def bookmarks
  return [] if @user.blank?
  return [] if @topic.trashed?

  @bookmarks ||=
    Bookmark.for_user_in_topic(@user, @topic.id).select(
      :id,
      :bookmarkable_id,
      :bookmarkable_type,
      :reminder_at,
      :name,
      :auto_delete_preference,
    )
end

#canonical_pathObject



161
162
163
164
165
166
167
168
169
# File 'lib/topic_view.rb', line 161

def canonical_path
  if SiteSetting.embed_set_canonical_url
    topic_embed = topic.topic_embed
    return topic_embed.embed_url if topic_embed
  end
  path = relative_url.dup
  path << ((@page > 1) ? "?page=#{@page}" : "")
  path
end

#category_group_moderator_user_idsObject



511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'lib/topic_view.rb', line 511

def category_group_moderator_user_ids
  @category_group_moderator_user_ids ||=
    begin
      if SiteSetting.enable_category_group_moderation? &&
           @topic.category&.reviewable_by_group.present?
        posts_user_ids = Set.new(@posts.map(&:user_id))
        Set.new(
          @topic
            .category
            .reviewable_by_group
            .group_users
            .where(user_id: posts_user_ids)
            .pluck("distinct user_id"),
        )
      else
        Set.new
      end
    end
end

#contains_gaps?Boolean

Returns:

  • (Boolean)


171
172
173
# File 'lib/topic_view.rb', line 171

def contains_gaps?
  @contains_gaps
end

#current_post_numberObject



694
695
696
697
698
# File 'lib/topic_view.rb', line 694

def current_post_number
  if highest_post_number.present?
    post_number > highest_post_number ? highest_post_number : post_number
  end
end

#desired_postObject



259
260
261
262
263
264
265
266
# File 'lib/topic_view.rb', line 259

def desired_post
  return @desired_post if @desired_post.present?
  return nil if posts.blank?

  @desired_post = posts.detect { |p| p.post_number == @post_number }
  @desired_post ||= posts.first
  @desired_post
end

#filter_best(max, opts = {}) ⇒ Object



381
382
383
384
385
# File 'lib/topic_view.rb', line 381

def filter_best(max, opts = {})
  filter = FilterBestPosts.new(@topic, @filtered_posts, max, opts)
  @posts = filter.posts
  @filtered_posts = filter.filtered_posts
end

#filter_posts(opts = {}) ⇒ Object



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/topic_view.rb', line 305

def filter_posts(opts = {})
  if opts[:post_number].present?
    filter_posts_near(opts[:post_number].to_i)
  elsif opts[:post_ids].present?
    filter_posts_by_ids(opts[:post_ids])
  elsif opts[:filter_post_number].present?
    # Only used for megatopics where we do not load the entire post stream
    filter_posts_by_post_number(opts[:filter_post_number], opts[:asc])
  elsif opts[:best].present?
    # Only used for wordpress
    filter_best(opts[:best], opts)
  else
    filter_posts_paged(@page)
  end
end

#filter_posts_near(post_number) ⇒ Object

Filter to all posts near a particular post number



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/topic_view.rb', line 338

def filter_posts_near(post_number)
  posts_before = (@limit.to_f / 4).floor
  posts_before = 1 if posts_before.zero?
  sort_order = get_sort_order(post_number)

  before_post_ids =
    @filtered_posts
      .reverse_order
      .where("posts.sort_order < ?", sort_order)
      .limit(posts_before)
      .pluck(:id)

  post_ids =
    before_post_ids +
      @filtered_posts
        .where("posts.sort_order >= ?", sort_order)
        .limit(@limit - before_post_ids.length)
        .pluck(:id)

  if post_ids.length < @limit
    post_ids =
      post_ids +
        @filtered_posts
          .reverse_order
          .where("posts.sort_order < ?", sort_order)
          .offset(before_post_ids.length)
          .limit(@limit - post_ids.length)
          .pluck(:id)
  end

  filter_posts_by_ids(post_ids)
end

#filter_posts_paged(page) ⇒ Object



371
372
373
374
375
376
377
378
379
# File 'lib/topic_view.rb', line 371

def filter_posts_paged(page)
  page = [page, 1].max
  min = @limit * (page - 1)

  # Sometimes we don't care about the OP, for example when embedding comments
  min = 1 if min == 0 && @exclude_first

  filter_posts_by_ids(@filtered_posts.offset(min).limit(@limit).pluck(:id))
end

#filtered_post_id(post_number) ⇒ Object



682
683
684
# File 'lib/topic_view.rb', line 682

def filtered_post_id(post_number)
  @filtered_posts.where(post_number: post_number).pick(:id)
end

#filtered_post_idsObject



660
661
662
663
664
665
666
667
668
669
# File 'lib/topic_view.rb', line 660

def filtered_post_ids
  @filtered_post_ids ||=
    filtered_post_stream.map do |tuple|
      if is_mega_topic?
        tuple
      else
        tuple[0]
      end
    end
end

#filtered_post_streamObject

Returns an array of [id, days_ago] tuples. ‘days_ago` is there for the timeline calculations.



646
647
648
649
650
651
652
653
654
655
656
657
658
# File 'lib/topic_view.rb', line 646

def filtered_post_stream
  @filtered_post_stream ||=
    begin
      posts = @filtered_posts
      columns = [:id]

      if !is_mega_topic?
        columns << "(EXTRACT(EPOCH FROM CURRENT_TIMESTAMP - posts.created_at) / 86400)::INT AS days_ago"
      end

      posts.pluck(*columns)
    end
end

#gapsObject



175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/topic_view.rb', line 175

def gaps
  return unless @contains_gaps

  @gaps ||=
    begin
      if is_mega_topic?
        nil
      else
        Gaps.new(filtered_post_ids, apply_default_scope(unfiltered_posts).pluck(:id))
      end
    end
end

#group_allowed_user_idsObject



504
505
506
507
508
509
# File 'lib/topic_view.rb', line 504

def group_allowed_user_ids
  return @group_allowed_user_ids unless @group_allowed_user_ids.nil?

  @group_allowed_user_ids =
    GroupUser.where(group_id: topic_allowed_group_ids).pluck("distinct user_id")
end

#has_bookmarks?Boolean

Returns:

  • (Boolean)


408
409
410
# File 'lib/topic_view.rb', line 408

def has_bookmarks?
  bookmarks.any?
end

#has_deleted?Boolean

Returns:

  • (Boolean)


392
393
394
395
396
397
398
# File 'lib/topic_view.rb', line 392

def has_deleted?
  @predelete_filtered_posts
    .with_deleted
    .where("posts.deleted_at IS NOT NULL")
    .where("posts.post_number > 1")
    .exists?
end

#highest_post_numberObject

This is pending a larger refactor, that allows custom orders for now we need to look for the highest_post_number in the stream the cache on topics is not correct if there are deleted posts at the end of the stream (for mods), nor is it correct for filtered streams



636
637
638
# File 'lib/topic_view.rb', line 636

def highest_post_number
  @highest_post_number ||= @filtered_posts.maximum(:post_number)
end

#image_urlObject



300
301
302
303
# File 'lib/topic_view.rb', line 300

def image_url
  return @topic.image_url if @post_number == 1
  desired_post&.image_url
end

#is_mega_topic?Boolean

Returns:

  • (Boolean)


686
687
688
# File 'lib/topic_view.rb', line 686

def is_mega_topic?
  @is_mega_topic ||= (@topic.posts_count >= MEGA_TOPIC_POSTS_COUNT)
end

#last_postObject



188
189
190
191
# File 'lib/topic_view.rb', line 188

def last_post
  return nil if @posts.blank?
  @last_post ||= @posts.last
end

#last_post_idObject



690
691
692
# File 'lib/topic_view.rb', line 690

def last_post_id
  @filtered_posts.reverse_order.pick(:id)
end

#like_countObject



286
287
288
289
# File 'lib/topic_view.rb', line 286

def like_count
  return nil if @post_number > 1 # only show for topic URLs
  @topic.like_count
end


598
599
600
# File 'lib/topic_view.rb', line 598

def link_counts
  @link_counts ||= TopicLink.counts_for(@guardian, @topic, posts)
end


535
536
537
# File 'lib/topic_view.rb', line 535

def links
  @links ||= TopicLink.topic_map(@guardian, @topic.id)
end

#mentioned_usersObject



708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
# File 'lib/topic_view.rb', line 708

def mentioned_users
  @mentioned_users ||=
    begin
      mentions = @posts.to_h { |p| [p.id, p.mentions] }.reject { |_, v| v.empty? }
      usernames = mentions.values
      usernames.flatten!
      usernames.uniq!

      users = User.where(username: usernames).includes(:user_status).index_by(&:username)

      mentions.reduce({}) do |hash, (post_id, post_mentioned_usernames)|
        hash[post_id] = post_mentioned_usernames.map { |username| users[username] }.compact
        hash
      end
    end
end

#next_pageObject



197
198
199
200
201
202
203
204
# File 'lib/topic_view.rb', line 197

def next_page
  @next_page ||=
    begin
      if last_post && highest_post_number && (highest_post_number > last_post.post_number)
        @page + 1
      end
    end
end

#next_page_pathObject



214
215
216
# File 'lib/topic_view.rb', line 214

def next_page_path
  "#{relative_url}?page=#{next_page}"
end

#page_titleObject



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/topic_view.rb', line 226

def page_title
  title = @topic.title
  if @post_number > 1
    title += " - "
    post = @topic.posts.find_by(post_number: @post_number)
    author = post&.user
    if author && @guardian.can_see_post?(post)
      title +=
        I18n.t(
          "inline_oneboxer.topic_page_title_post_number_by_user",
          post_number: @post_number,
          username: author.username,
        )
    else
      title += I18n.t("inline_oneboxer.topic_page_title_post_number", post_number: @post_number)
    end
  end
  if SiteSetting.topic_page_title_includes_category
    if @topic.category_id != SiteSetting.uncategorized_category_id && @topic.category_id &&
         @topic.category
      title += " - #{@topic.category.name}"
    elsif SiteSetting.tagging_enabled && visible_tags.exists?
      title +=
        " - #{visible_tags.order("tags.#{Tag.topic_count_column(@guardian)} DESC").first.name}"
    end
  end
  title
end

#participant_countObject



464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
# File 'lib/topic_view.rb', line 464

def participant_count
  @participant_count ||=
    begin
      if participants.size == MAX_PARTICIPANTS
        if @topic.posts_count > MAX_POSTS_COUNT_PARTICIPANTS
          @topic.participant_count
        else
          sql = <<~SQL
            SELECT COUNT(DISTINCT user_id)
            FROM posts
            WHERE id IN (:post_ids)
            AND user_id IS NOT NULL
          SQL
          DB.query_single(sql, post_ids: unfiltered_post_ids).first.to_i
        end
      else
        participants.size
      end
    end
end

#participantsObject



485
486
487
488
489
490
491
492
493
494
495
# File 'lib/topic_view.rb', line 485

def participants
  @participants ||=
    begin
      participants = {}
      User
        .where(id: post_counts_by_user.keys)
        .includes(:primary_group, :flair_group)
        .each { |u| participants[u.id] = u }
      participants
    end
end

#pending_postsObject



576
577
578
579
# File 'lib/topic_view.rb', line 576

def pending_posts
  @pending_posts ||=
    ReviewableQueuedPost.pending.where(target_created_by: @user, topic: @topic).order(:created_at)
end

#pm_paramsObject



602
603
604
# File 'lib/topic_view.rb', line 602

def pm_params
  @pm_params ||= TopicQuery.new(@user).get_pm_params(topic)
end

#post_counts_by_userObject



429
430
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
# File 'lib/topic_view.rb', line 429

def post_counts_by_user
  @post_counts_by_user ||=
    begin
      if is_mega_topic?
        {}
      else
        sql = <<~SQL
          SELECT user_id, count(*) AS count_all
            FROM posts
           WHERE topic_id = :topic_id
             AND post_type IN (:post_types)
             AND user_id IS NOT NULL
             AND posts.deleted_at IS NULL
             AND action_code IS NULL
        GROUP BY user_id
        ORDER BY count_all DESC
           LIMIT #{MAX_PARTICIPANTS}
      SQL

        Hash[
          *DB.query_single(
            sql,
            topic_id: @topic.id,
            post_types: Topic.visible_post_types(@guardian&.user),
          )
        ]
      end
    end
end

#prev_pageObject



193
194
195
# File 'lib/topic_view.rb', line 193

def prev_page
  @page > 1 && posts.size > 0 ? @page - 1 : nil
end

#prev_page_pathObject



206
207
208
209
210
211
212
# File 'lib/topic_view.rb', line 206

def prev_page_path
  if prev_page > 1
    "#{relative_url}?page=#{prev_page}"
  else
    relative_url
  end
end

#primary_group_namesObject



321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/topic_view.rb', line 321

def primary_group_names
  return @group_names if @group_names

  primary_group_ids = Set.new
  @posts.each do |p|
    primary_group_ids << p.user.primary_group_id if p.user.try(:primary_group_id)
  end

  result = {}
  unless primary_group_ids.empty?
    Group.where(id: primary_group_ids.to_a).pluck(:id, :name).each { |g| result[g[0]] = g[1] }
  end

  @group_names = result
end

#published_pageObject



704
705
706
# File 'lib/topic_view.rb', line 704

def published_page
  @topic.published_page
end

#published_timeObject



291
292
293
294
295
296
297
298
# File 'lib/topic_view.rb', line 291

def published_time
  return nil if desired_post.blank?
  if desired_post.wiki && desired_post.post_number == 1 && desired_post.revisions.size > 0
    desired_post.revisions.last.updated_at.strftime("%FT%T%:z")
  else
    desired_post.created_at.strftime("%FT%T%:z")
  end
end

#queued_posts_countObject



700
701
702
# File 'lib/topic_view.rb', line 700

def queued_posts_count
  ReviewableQueuedPost.viewable_by(@user).where(topic_id: @topic.id).pending.count
end

#read?(post_number) ⇒ Boolean

Returns:

  • (Boolean)


387
388
389
390
# File 'lib/topic_view.rb', line 387

def read?(post_number)
  return true unless @user
  read_posts_set.include?(post_number)
end

#read_timeObject



275
276
277
278
279
280
281
282
283
284
# File 'lib/topic_view.rb', line 275

def read_time
  return nil if @post_number > 1 # only show for topic URLs

  if @topic.word_count && SiteSetting.read_time_word_count > 0
    [
      @topic.word_count / SiteSetting.read_time_word_count,
      @topic.posts_count * MIN_POST_READ_TIME / 60,
    ].max.ceil
  end
end

#recent_postsObject



640
641
642
# File 'lib/topic_view.rb', line 640

def recent_posts
  @filtered_posts.unscope(:order).by_newest.with_user.first(25)
end


623
624
625
626
627
628
629
# File 'lib/topic_view.rb', line 623

def related_messages
  if @include_related
    @related_messages ||= TopicQuery.new(@user).list_related_for(topic, pm_params: pm_params)
  else
    nil
  end
end

#relative_urlObject



222
223
224
# File 'lib/topic_view.rb', line 222

def relative_url
  "#{@topic.relative_url}#{@print ? "/print" : ""}"
end

#reviewable_countsObject



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
565
566
567
568
569
570
571
572
573
574
# File 'lib/topic_view.rb', line 539

def reviewable_counts
  @reviewable_counts ||=
    begin
      sql = <<~SQL
      SELECT
        target_id,
        MAX(r.id) reviewable_id,
        COUNT(*) total,
        SUM(CASE WHEN s.status = :pending THEN 1 ELSE 0 END) pending
      FROM
        reviewables r
      JOIN
        reviewable_scores s ON reviewable_id = r.id
      WHERE
        r.target_id IN (:post_ids) AND
        r.target_type = 'Post' AND
        COALESCE(s.reason, '') != 'category'
      GROUP BY
        target_id
    SQL

      counts = {}

      DB
        .query(sql, pending: ReviewableScore.statuses[:pending], post_ids: @posts.map(&:id))
        .each do |row|
          counts[row.target_id] = {
            total: row.total,
            pending: row.pending,
            reviewable_id: row.reviewable_id,
          }
        end

      counts
    end
end

#show_read_indicator?Boolean

Returns:

  • (Boolean)


155
156
157
158
159
# File 'lib/topic_view.rb', line 155

def show_read_indicator?
  return false if !@user || !topic.private_message?

  topic.allowed_groups.any? { |group| group.publish_read_state? && group.users.include?(@user) }
end

#suggested_topicsObject



606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
# File 'lib/topic_view.rb', line 606

def suggested_topics
  if @include_suggested
    @suggested_topics ||=
      begin
        kwargs =
          DiscoursePluginRegistry.apply_modifier(
            :topic_view_suggested_topics_options,
            { include_random: true, pm_params: pm_params },
            self,
          )
        TopicQuery.new(@user).list_suggested_for(topic, **kwargs)
      end
  else
    nil
  end
end

#summarizable?Boolean

Returns:

  • (Boolean)


725
726
727
# File 'lib/topic_view.rb', line 725

def summarizable?
  Summarization::Base.can_see_summary?(@topic, @user)
end

#summary(opts = {}) ⇒ Object



268
269
270
271
272
273
# File 'lib/topic_view.rb', line 268

def summary(opts = {})
  return nil if desired_post.blank?
  # TODO, this is actually quite slow, should be cached in the post table
  excerpt = desired_post.excerpt(500, opts.merge(strip_links: true, text_entities: true))
  (excerpt || "").gsub(/\n/, " ").strip
end

#titleObject



255
256
257
# File 'lib/topic_view.rb', line 255

def title
  @topic.title
end

#topic_allowed_group_idsObject



497
498
499
500
501
502
# File 'lib/topic_view.rb', line 497

def topic_allowed_group_ids
  @topic_allowed_group_ids ||=
    begin
      @topic.allowed_groups.map(&:id)
    end
end

#topic_userObject



400
401
402
403
404
405
406
# File 'lib/topic_view.rb', line 400

def topic_user
  @topic_user ||=
    begin
      return nil if @user.blank?
      @topic.topic_users.find_by(user_id: @user.id)
    end
end

#unfiltered_post_idsObject



671
672
673
674
675
676
677
678
679
680
# File 'lib/topic_view.rb', line 671

def unfiltered_post_ids
  @unfiltered_post_ids ||=
    begin
      if @contains_gaps
        unfiltered_posts.pluck(:id)
      else
        filtered_post_ids
      end
    end
end