2013-02-25 19:42:20 +03:00
#
2013-02-05 14:16:51 -05:00
# Helps us find topics. Returns a TopicList object containing the topics
# found.
#
require_dependency 'topic_list'
class TopicQuery
2013-03-06 15:17:07 -05: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-01 13:49:35 -04:00
def order_hotness
2013-04-01 14:54:53 -04:00
# When anonymous, don't use topic_user
if @user . blank?
return " CASE
WHEN topics . pinned_at IS NOT NULL THEN 100
ELSE hot_topics . score + ( COALESCE ( categories . hotness , 5 . 0 ) / 11 . 0 )
END DESC "
end
# When logged in take into accounts what pins you've closed
2013-04-01 13:49:35 -04:00
" 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 "
end
2013-03-06 15:17:07 -05:00
# If you've clearned the pin, use bumped_at, otherwise put it at the top
def order_nocategory_with_pinned_sql
" CASE
WHEN topics . category_id IS NULL and ( COALESCE ( topics . pinned_at , '#{lowest_date}' ) > COALESCE ( tu . cleared_pinned_at , '#{lowest_date}' ) )
THEN '#{highest_date}'
ELSE topics . bumped_at
END DESC "
end
# For anonymous users
def order_nocategory_basic_bumped
" CASE WHEN topics.category_id IS NULL and (topics.pinned_at IS NOT NULL) THEN 0 ELSE 1 END, topics.bumped_at DESC "
end
def order_basic_bumped
" CASE WHEN (topics.pinned_at IS NOT NULL) THEN 0 ELSE 1 END, topics.bumped_at DESC "
end
end
2013-02-05 14:16:51 -05:00
def initialize ( user = nil , opts = { } )
2013-02-25 19:42:20 +03:00
@user = user
2013-02-05 14:16:51 -05:00
# Cast to int to avoid sql injection
@user_id = user . id . to_i if @user . present?
@opts = opts
end
# Return a list of suggested topics for a topic
2013-02-25 19:42:20 +03:00
def list_suggested_for ( topic )
2013-02-05 14:16:51 -05:00
exclude_topic_ids = [ topic . id ]
# If not logged in, return some random results, preferably in this category
if @user . blank?
2013-04-02 16:52:51 -04:00
return TopicList . new ( :suggested , @user , random_suggested_results_for ( topic , SiteSetting . suggested_topics , exclude_topic_ids ) )
2013-02-05 14:16:51 -05:00
end
2013-02-19 14:38:59 -05:00
results = unread_results ( per_page : SiteSetting . suggested_topics )
. where ( 'topics.id NOT IN (?)' , exclude_topic_ids )
. where ( closed : false , archived : false , visible : true )
. all
2013-02-05 14:16:51 -05:00
results_left = SiteSetting . suggested_topics - results . size
# If we don't have enough results, go to new posts
if results_left > 0
exclude_topic_ids << results . map { | t | t . id }
exclude_topic_ids . flatten!
2013-02-19 14:38:59 -05:00
results << new_results ( per_page : results_left )
. where ( 'topics.id NOT IN (?)' , exclude_topic_ids )
. where ( closed : false , archived : false , visible : true )
. all
2013-02-05 14:16:51 -05:00
results . flatten!
results_left = SiteSetting . suggested_topics - results . size
# If we STILL don't have enough results, find random topics
if results_left > 0
exclude_topic_ids << results . map { | t | t . id }
exclude_topic_ids . flatten!
2013-02-19 14:38:59 -05:00
results << random_suggested_results_for ( topic , results_left , exclude_topic_ids )
. where ( closed : false , archived : false , visible : true )
. all
2013-02-05 14:16:51 -05:00
results . flatten!
end
end
2013-04-02 16:52:51 -04:00
TopicList . new ( :suggested , @user , results )
2013-02-05 14:16:51 -05:00
end
2013-03-27 16:17:49 -04:00
# The latest view of topics
def list_latest
2013-04-02 16:52:51 -04:00
create_list ( :latest )
2013-02-05 14:16:51 -05:00
end
# The favorited topics
def list_favorited
2013-04-02 16:52:51 -04:00
create_list ( :favorited ) { | topics | topics . where ( 'tu.starred' ) }
2013-02-05 14:16:51 -05:00
end
def list_read
2013-04-02 16:52:51 -04:00
create_list ( :read , unordered : true ) do | topics |
topics . order ( 'COALESCE(tu.last_visited_at, topics.bumped_at) DESC' )
2013-02-05 14:16:51 -05:00
end
end
2013-03-27 16:17:49 -04:00
def list_hot
2013-04-02 16:52:51 -04:00
create_list ( :hot , unordered : true ) do | topics |
topics . joins ( :hot_topic ) . order ( TopicQuery . order_hotness )
2013-03-27 16:17:49 -04:00
end
end
2013-02-05 14:16:51 -05:00
def list_new
2013-04-02 16:52:51 -04:00
TopicList . new ( :new , @user , new_results )
2013-02-05 14:16:51 -05:00
end
def list_unread
2013-04-02 16:52:51 -04:00
TopicList . new ( :unread , @user , unread_results )
2013-02-05 14:16:51 -05:00
end
def list_posted
2013-04-02 16:52:51 -04:00
create_list ( :posted ) { | l | l . where ( 'tu.user_id IS NOT NULL' ) }
2013-02-05 14:16:51 -05:00
end
def list_uncategorized
2013-04-02 16:52:51 -04:00
create_list ( :uncategorized , unordered : true ) do | list |
2013-03-06 15:17:07 -05:00
list = list . where ( category_id : nil )
if @user_id . present?
list . order ( TopicQuery . order_with_pinned_sql )
else
list . order ( TopicQuery . order_nocategory_basic_bumped )
end
end
2013-02-05 14:16:51 -05:00
end
def list_category ( category )
2013-04-02 16:52:51 -04:00
create_list ( :category , unordered : true ) do | list |
2013-03-06 15:17:07 -05:00
list = list . where ( category_id : category . id )
if @user_id . present?
list . order ( TopicQuery . order_with_pinned_sql )
else
list . order ( TopicQuery . order_basic_bumped )
end
end
2013-02-05 14:16:51 -05:00
end
def unread_count
unread_results ( limit : false ) . count
end
def new_count
new_results ( limit : false ) . count
end
2013-02-27 19:36:12 -08:00
def list_new_in_category ( category )
2013-04-02 16:52:51 -04:00
create_list ( :new_in_category ) { | l | l . where ( category_id : category . id ) . by_newest . first ( 25 ) }
2013-02-27 19:36:12 -08:00
end
2013-05-23 15:21:07 +10:00
def self . new_filter ( list , treat_as_new_topic_start_date )
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
2013-05-21 16:39:51 +10:00
def new_results ( list_opts = { } )
2013-05-23 15:21:07 +10:00
TopicQuery . new_filter ( default_list ( list_opts ) , @user . treat_as_new_topic_start_date )
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 16:39:51 +10:00
end
def unread_results ( list_opts = { } )
2013-05-23 15:21:07 +10:00
TopicQuery . unread_filter ( default_list ( list_opts ) )
2013-05-21 16:39:51 +10:00
end
2013-02-05 14:16:51 -05:00
protected
2013-04-02 16:52:51 -04:00
def create_list ( filter , list_opts = { } )
2013-05-28 17:52:52 +10:00
opts = list_opts
if @opts [ :topic_ids ]
opts = opts . dup
opts [ :topic_ids ] = @opts [ :topic_ids ]
end
topics = default_list ( opts )
2013-04-02 16:52:51 -04:00
topics = yield ( topics ) if block_given?
TopicList . new ( filter , @user , topics )
2013-02-05 14:16:51 -05:00
end
# Create a list based on a bunch of detault options
def default_list ( list_opts = { } )
query_opts = @opts . merge ( list_opts )
page_size = query_opts [ :per_page ] || SiteSetting . topics_per_page
2013-03-06 15:17:07 -05:00
# Start with a list of all topics
2013-02-05 14:16:51 -05:00
result = Topic
2013-03-06 15:17:07 -05:00
2013-04-29 16:33:24 +10:00
if @user_id
2013-03-06 15:17:07 -05:00
result = result . joins ( " LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{ @user_id } ) " )
end
unless query_opts [ :unordered ]
# If we're logged in, we have to pay attention to our pinned settings
2013-04-29 16:33:24 +10:00
if @user
2013-03-06 15:17:07 -05:00
result = result . order ( TopicQuery . order_nocategory_with_pinned_sql )
else
2013-03-11 18:29:13 -07:00
result = result . order ( TopicQuery . order_nocategory_basic_bumped )
2013-03-06 15:17:07 -05:00
end
end
2013-05-29 13:28:07 -04:00
2013-03-23 20:32:59 +05:30
result = result . listable_topics . includes ( category : :topic_only_relative_url )
2013-02-05 14:16:51 -05:00
result = result . where ( 'categories.name is null or categories.name <> ?' , query_opts [ :exclude_category ] ) if query_opts [ :exclude_category ]
result = result . where ( 'categories.name = ?' , query_opts [ :only_category ] ) if query_opts [ :only_category ]
2013-02-25 19:42:20 +03:00
result = result . limit ( page_size ) unless query_opts [ :limit ] == false
2013-02-05 14:16:51 -05:00
result = result . visible if @user . blank? or @user . regular?
2013-02-25 19:42:20 +03:00
result = result . where ( 'topics.id <> ?' , query_opts [ :except_topic_id ] ) if query_opts [ :except_topic_id ] . present?
2013-02-05 14:16:51 -05:00
result = result . offset ( query_opts [ :page ] . to_i * page_size ) if query_opts [ :page ] . present?
2013-04-29 16:33:24 +10:00
2013-05-28 17:52:52 +10:00
if list_opts [ :topic_ids ]
result = result . where ( 'topics.id in (?)' , list_opts [ :topic_ids ] )
end
2013-04-29 16:33:24 +10:00
unless @user && @user . moderator?
category_ids = @user . secure_category_ids if @user
if category_ids . present?
result = result . where ( 'categories.secure IS NULL OR categories.secure = ? OR categories.id IN (?)' , false , category_ids )
else
result = result . where ( 'categories.secure IS NULL OR categories.secure = ?' , false )
end
end
2013-02-25 19:42:20 +03:00
result
2013-02-05 14:16:51 -05:00
end
def random_suggested_results_for ( topic , count , exclude_topic_ids )
results = default_list ( unordered : true , per_page : count )
. where ( 'topics.id NOT IN (?)' , exclude_topic_ids )
2013-02-19 14:38:59 -05:00
. where ( closed : false , archived : false , visible : true )
2013-02-05 14:16:51 -05:00
2013-02-27 18:30:14 -05:00
if topic . category_id . present?
2013-04-02 16:52:51 -04:00
return results . order ( " CASE WHEN topics.category_id = #{ topic . category_id . to_i } THEN 0 ELSE 1 END, RANDOM() " )
2013-02-27 18:30:14 -05:00
end
2013-04-02 16:52:51 -04:00
results . order ( " RANDOM() " )
2013-02-05 14:16:51 -05:00
end
end