2013-02-26 00:42:20 +08:00
#
2013-02-06 03:16:51 +08:00
# Helps us find topics. Returns a TopicList object containing the topics
# found.
#
require_dependency 'topic_list'
2013-07-13 02:38:20 +08:00
require_dependency 'suggested_topics_builder'
2013-02-06 03:16:51 +08:00
class TopicQuery
2013-07-17 03:20:18 +08:00
# Could be rewritten to %i if Ruby 1.9 is no longer supported
2013-10-24 18:05:06 +08:00
VALID_OPTIONS = %w( except_topic_id exclude_category limit page per_page topic_ids visible category ) . map ( & :to_sym )
2013-02-06 03:16:51 +08:00
2013-03-07 04:17:07 +08:00
class << self
# use the constants in conjuction with COALESCE to determine the order with regard to pinned
# topics that have been cleared by the user. There
# might be a cleaner way to do this.
def lowest_date
" 2010-01-01 "
end
def highest_date
" 3000-01-01 "
end
# If you've clearned the pin, use bumped_at, otherwise put it at the top
def order_with_pinned_sql
" CASE
WHEN ( COALESCE ( topics . pinned_at , '#{lowest_date}' ) > COALESCE ( tu . cleared_pinned_at , '#{lowest_date}' ) )
THEN '#{highest_date}'
ELSE topics . bumped_at
END DESC "
end
2013-04-02 01:49:35 +08:00
def order_hotness
2013-07-17 03:20:18 +08:00
if @user
# When logged in take into accounts what pins you've closed
" CASE
WHEN ( COALESCE ( topics . pinned_at , '#{lowest_date}' ) > COALESCE ( tu . cleared_pinned_at , '#{lowest_date}' ) )
THEN 100
ELSE hot_topics . score + ( COALESCE ( categories . hotness , 5 . 0 ) / 11 . 0 )
END DESC "
else
# When anonymous, don't use topic_user
" CASE
WHEN topics . pinned_at IS NOT NULL THEN 100
ELSE hot_topics . score + ( COALESCE ( categories . hotness , 5 . 0 ) / 11 . 0 )
END DESC "
2013-04-02 02:54:53 +08:00
end
2013-04-02 01:49:35 +08:00
end
2013-03-07 04:17:07 +08:00
# If you've clearned the pin, use bumped_at, otherwise put it at the top
def order_nocategory_with_pinned_sql
" CASE
2013-10-24 07:05:51 +08:00
WHEN topics . category_id = #{SiteSetting.uncategorized_category_id.to_i} and (COALESCE(topics.pinned_at, '#{lowest_date}') > COALESCE(tu.cleared_pinned_at, '#{lowest_date}'))
2013-03-07 04:17:07 +08:00
THEN '#{highest_date}'
ELSE topics . bumped_at
END DESC "
end
# For anonymous users
def order_nocategory_basic_bumped
2013-10-24 07:05:51 +08:00
" CASE WHEN topics.category_id = #{ SiteSetting . uncategorized_category_id . to_i } and (topics.pinned_at IS NOT NULL) THEN 0 ELSE 1 END, topics.bumped_at DESC "
2013-03-07 04:17:07 +08:00
end
def order_basic_bumped
" CASE WHEN (topics.pinned_at IS NOT NULL) THEN 0 ELSE 1 END, topics.bumped_at DESC "
end
2013-10-02 11:05:03 +08:00
def top_viewed ( max = 10 )
Topic . listable_topics . visible . secured . order ( 'views desc' ) . limit ( max )
2013-06-13 08:27:17 +08:00
end
2013-10-02 11:05:03 +08:00
def recent ( max = 10 )
Topic . listable_topics . visible . secured . order ( 'created_at desc' ) . limit ( max )
2013-06-13 08:27:17 +08:00
end
2013-03-07 04:17:07 +08:00
end
2013-07-17 03:20:18 +08:00
def initialize ( user = nil , options = { } )
options . assert_valid_keys ( VALID_OPTIONS )
2013-02-06 03:16:51 +08:00
2013-07-17 03:20:18 +08:00
@options = options
@user = user
2013-02-06 03:16:51 +08:00
end
# Return a list of suggested topics for a topic
2013-02-26 00:42:20 +08:00
def list_suggested_for ( topic )
2013-07-13 02:38:20 +08:00
builder = SuggestedTopicsBuilder . new ( topic )
2013-02-06 03:16:51 +08:00
2013-07-13 02:38:20 +08:00
# When logged in we start with different results
2013-07-17 03:20:18 +08:00
if @user
2013-08-28 08:51:49 +08:00
builder . add_results ( unread_results ( topic : topic , per_page : builder . results_left ) , :high )
builder . add_results ( new_results ( topic : topic , per_page : builder . category_results_left ) , :high ) unless builder . category_full?
2013-02-06 03:16:51 +08:00
end
2013-08-28 08:51:49 +08:00
builder . add_results ( random_suggested ( topic , builder . results_left ) , :low ) unless builder . full?
2013-02-06 03:16:51 +08:00
2013-07-17 03:20:18 +08:00
create_list ( :suggested , { } , builder . results )
2013-02-06 03:16:51 +08:00
end
2013-03-28 04:17:49 +08:00
# The latest view of topics
def list_latest
2013-04-03 04:52:51 +08:00
create_list ( :latest )
2013-02-06 03:16:51 +08:00
end
# The favorited topics
def list_favorited
2013-04-03 04:52:51 +08:00
create_list ( :favorited ) { | topics | topics . where ( 'tu.starred' ) }
2013-02-06 03:16:51 +08:00
end
def list_read
2013-04-03 04:52:51 +08:00
create_list ( :read , unordered : true ) do | topics |
topics . order ( 'COALESCE(tu.last_visited_at, topics.bumped_at) DESC' )
2013-02-06 03:16:51 +08:00
end
end
2013-03-28 04:17:49 +08:00
def list_hot
2013-04-03 04:52:51 +08:00
create_list ( :hot , unordered : true ) do | topics |
topics . joins ( :hot_topic ) . order ( TopicQuery . order_hotness )
2013-03-28 04:17:49 +08:00
end
end
2013-02-06 03:16:51 +08:00
def list_new
2013-07-17 03:20:18 +08:00
create_list ( :new , { } , new_results )
2013-02-06 03:16:51 +08:00
end
def list_unread
2013-07-17 03:20:18 +08:00
create_list ( :unread , { } , unread_results )
2013-02-06 03:16:51 +08:00
end
def list_posted
2013-04-03 04:52:51 +08:00
create_list ( :posted ) { | l | l . where ( 'tu.user_id IS NOT NULL' ) }
2013-02-06 03:16:51 +08:00
end
2013-07-25 05:15:21 +08:00
def list_topics_by ( user )
create_list ( :user_topics ) do | topics |
topics . where ( user_id : user . id )
end
end
2013-08-25 04:58:16 +08:00
def list_private_messages ( user )
list = private_messages_for ( user )
TopicList . new ( :private_messages , user , list )
end
def list_private_messages_sent ( user )
list = private_messages_for ( user )
list = list . where ( user_id : user . id )
TopicList . new ( :private_messages , user , list )
end
2013-08-31 00:32:05 +08:00
def list_private_messages_unread ( user )
list = private_messages_for ( user )
list = TopicQuery . unread_filter ( list )
TopicList . new ( :private_messages , user , list )
end
2013-07-25 05:15:21 +08:00
2013-02-06 03:16:51 +08:00
def list_category ( category )
2013-04-03 04:52:51 +08:00
create_list ( :category , unordered : true ) do | list |
2013-03-07 04:17:07 +08:00
list = list . where ( category_id : category . id )
2013-07-17 03:20:18 +08:00
if @user
2013-03-07 04:17:07 +08:00
list . order ( TopicQuery . order_with_pinned_sql )
else
list . order ( TopicQuery . order_basic_bumped )
end
end
2013-02-06 03:16:51 +08:00
end
2013-02-28 11:36:12 +08:00
def list_new_in_category ( category )
2013-09-21 05:36:19 +08:00
create_list ( :new_in_category , unordered : true ) { | l | l . where ( category_id : category . id ) . by_newest . first ( 25 ) }
2013-02-28 11:36:12 +08:00
end
2013-07-17 03:20:18 +08:00
def self . new_filter ( list , treat_as_new_topic_start_date )
2013-05-23 13:21:07 +08:00
list . where ( " topics.created_at >= :created_at " , created_at : treat_as_new_topic_start_date )
. where ( " tu.last_read_post_number IS NULL " )
. where ( " COALESCE(tu.notification_level, :tracking) >= :tracking " , tracking : TopicUser . notification_levels [ :tracking ] )
end
def self . unread_filter ( list )
list . where ( " tu.last_read_post_number < topics.highest_post_number " )
. where ( " COALESCE(tu.notification_level, :regular) >= :tracking " , regular : TopicUser . notification_levels [ :regular ] , tracking : TopicUser . notification_levels [ :tracking ] )
2013-05-21 14:39:51 +08:00
end
2013-07-17 03:20:18 +08:00
def unread_count
unread_results ( limit : false ) . count
end
def new_count
new_results ( limit : false ) . count
2013-05-21 14:39:51 +08:00
end
2013-02-06 03:16:51 +08:00
protected
2013-07-17 03:20:18 +08:00
def create_list ( filter , options = { } , topics = nil )
topics || = default_results ( options )
2013-04-03 04:52:51 +08:00
topics = yield ( topics ) if block_given?
TopicList . new ( filter , @user , topics )
2013-02-06 03:16:51 +08:00
end
2013-08-25 04:58:16 +08:00
def private_messages_for ( user )
options = @options
options . reverse_merge! ( per_page : SiteSetting . topics_per_page )
# Start with a list of all topics
result = Topic . where ( id : TopicAllowedUser . where ( user_id : user . id ) . pluck ( :topic_id ) )
result = result . joins ( " LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{ user . id . to_i } ) " )
result = result . order ( TopicQuery . order_nocategory_basic_bumped )
result = result . private_messages
result = result . limit ( options [ :per_page ] ) unless options [ :limit ] == false
result = result . visible if options [ :visible ] || @user . nil? || @user . regular?
result = result . offset ( options [ :page ] . to_i * options [ :per_page ] ) if options [ :page ]
result
end
2013-07-17 03:20:18 +08:00
# Create results based on a bunch of default options
def default_results ( options = { } )
options . reverse_merge! ( @options )
options . reverse_merge! ( per_page : SiteSetting . topics_per_page )
2013-02-06 03:16:51 +08:00
2013-03-07 04:17:07 +08:00
# Start with a list of all topics
2013-02-06 03:16:51 +08:00
result = Topic
2013-03-07 04:17:07 +08:00
2013-07-17 03:20:18 +08:00
if @user
result = result . joins ( " LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{ @user . id . to_i } ) " )
2013-03-07 04:17:07 +08:00
end
2013-11-09 04:05:14 +08:00
category_id = nil
if options [ :category ] . present?
category_id = options [ :category ] . to_i
if category_id == 0
result = result . where ( 'categories.slug = ?' , options [ :category ] )
else
result = result . where ( 'categories.id = ?' , category_id )
end
result = result . references ( :categories )
end
2013-07-17 03:20:18 +08:00
unless options [ :unordered ]
2013-03-07 04:17:07 +08:00
# If we're logged in, we have to pay attention to our pinned settings
2013-04-29 14:33:24 +08:00
if @user
2013-11-09 04:05:14 +08:00
result = category_id . nil? ? result . order ( TopicQuery . order_nocategory_with_pinned_sql ) :
result . order ( TopicQuery . order_with_pinned_sql )
2013-03-07 04:17:07 +08:00
else
2013-03-12 09:29:13 +08:00
result = result . order ( TopicQuery . order_nocategory_basic_bumped )
2013-03-07 04:17:07 +08:00
end
end
2013-03-23 23:02:59 +08:00
result = result . listable_topics . includes ( category : :topic_only_relative_url )
2013-08-16 20:53:40 +08:00
result = result . where ( 'categories.name is null or categories.name <> ?' , options [ :exclude_category ] ) . references ( :categories ) if options [ :exclude_category ]
2013-11-01 04:10:54 +08:00
2013-11-09 04:05:14 +08:00
2013-11-01 04:10:54 +08:00
2013-07-17 03:20:18 +08:00
result = result . limit ( options [ :per_page ] ) unless options [ :limit ] == false
result = result . visible if options [ :visible ] || @user . nil? || @user . regular?
2013-08-16 20:53:40 +08:00
result = result . where ( 'topics.id <> ?' , options [ :except_topic_id ] ) . references ( :topics ) if options [ :except_topic_id ]
2013-07-17 03:20:18 +08:00
result = result . offset ( options [ :page ] . to_i * options [ :per_page ] ) if options [ :page ]
if options [ :topic_ids ]
2013-08-16 20:53:40 +08:00
result = result . where ( 'topics.id in (?)' , options [ :topic_ids ] ) . references ( :topics )
2013-05-28 15:52:52 +08:00
end
2013-09-10 14:02:54 +08:00
guardian = Guardian . new ( @user )
unless guardian . is_staff?
allowed_ids = guardian . allowed_category_ids
if allowed_ids . length > 0
result = result . where ( 'topics.category_id IS NULL or topics.category_id IN (?)' , allowed_ids )
2013-04-29 14:33:24 +08:00
else
2013-09-10 14:02:54 +08:00
result = result . where ( 'topics.category_id IS NULL' )
2013-04-29 14:33:24 +08:00
end
end
2013-02-26 00:42:20 +08:00
result
2013-02-06 03:16:51 +08:00
end
2013-07-17 03:20:18 +08:00
def new_results ( options = { } )
2013-08-09 01:18:52 +08:00
result = TopicQuery . new_filter ( default_results ( options . reverse_merge ( :unordered = > true ) ) ,
@user . treat_as_new_topic_start_date )
suggested_ordering ( result , options )
2013-07-17 03:20:18 +08:00
end
def unread_results ( options = { } )
2013-07-19 02:47:59 +08:00
result = TopicQuery . unread_filter ( default_results ( options . reverse_merge ( :unordered = > true ) ) )
. order ( 'CASE WHEN topics.user_id = tu.user_id THEN 1 ELSE 2 END' )
2013-08-09 01:18:52 +08:00
suggested_ordering ( result , options )
2013-07-17 03:20:18 +08:00
end
2013-07-13 02:38:20 +08:00
def random_suggested ( topic , count )
2013-07-17 03:20:18 +08:00
result = default_results ( unordered : true , per_page : count )
2013-02-06 03:16:51 +08:00
2013-07-13 02:38:20 +08:00
# If we are in a category, prefer it for the random results
2013-07-17 03:20:18 +08:00
if topic . category_id
2013-07-19 02:47:59 +08:00
result = result . order ( " CASE WHEN topics.category_id = #{ topic . category_id . to_i } THEN 0 ELSE 1 END " )
2013-02-28 07:30:14 +08:00
end
2013-07-13 02:38:20 +08:00
result . order ( " RANDOM() " )
2013-02-06 03:16:51 +08:00
end
2013-08-09 01:18:52 +08:00
def suggested_ordering ( result , options )
# Prefer unread in the same category
if options [ :topic ] && options [ :topic ] . category_id
result = result . order ( " CASE WHEN topics.category_id = #{ options [ :topic ] . category_id . to_i } THEN 0 ELSE 1 END " )
end
result . order ( TopicQuery . order_nocategory_with_pinned_sql )
end
2013-02-06 03:16:51 +08:00
end