class HomeController < ApplicationController STORIES_PER_PAGE = 25 # how many points a story has to have to probably get on the front page HOT_STORY_POINTS = 5 # how many days old a story can be to get on the bottom half of /recent RECENT_DAYS_OLD = 3 # for rss feeds, load the user's tag filters if a token is passed before_filter :find_user_from_rss_token, :only => [ :index, :newest ] def about begin render :action => "about" rescue render :text => "
" << "A mystery." << "
", :layout => "application" end end def hidden @stories = find_stories({ :hidden => true }) @heading = @title = "Hidden Stories" @cur_url = "/hidden" render :action => "index" end def index @stories = find_stories({ :hottest => true }) @rss_link ||= { :title => "RSS 2.0", :href => "/rss#{@user ? "?token=#{@user.rss_token}" : ""}" } @heading = @title = "" @cur_url = "/" respond_to do |format| format.html { render :action => "index" } format.rss { if @user && params[:token].present? @title = "Private feed for #{@user.username}" end render :action => "rss", :layout => false } format.json { render :json => @stories } end end def newest @stories = find_stories({ :newest => true }) @heading = @title = "Newest Stories" @cur_url = "/newest" @rss_link = { :title => "RSS 2.0 - Newest Items", :href => "/newest.rss#{@user ? "?token=#{@user.rss_token}" : ""}" } respond_to do |format| format.html { render :action => "index" } format.rss { if @user && params[:token].present? @title += " - Private feed for #{@user.username}" end render :action => "rss", :layout => false } format.json { render :json => @stories } end end def newest_by_user by_user = User.where(:username => params[:user]).first! @stories = find_stories({ :by_user => by_user }) @heading = @title = "Newest Stories by #{by_user.username}" @cur_url = "/newest/#{by_user.username}" @newest = true @for_user = by_user.username render :action => "index" end def privacy begin render :action => "privacy" rescue render :text => "
" << "You apparently have no privacy." << "
", :layout => "application" end end def recent @stories = find_stories({ :recent => true }) @heading = @title = "Recent Stories" @cur_url = "/recent" # our content changes every page load, so point at /newest.rss to be stable @rss_link = { :title => "RSS 2.0 - Newest Items", :href => "/newest.rss#{@user ? "?token=#{@user.rss_token}" : ""}" } render :action => "index" end def tagged @tag = Tag.where(:tag => params[:tag]).first! @stories = find_stories({ :tag => @tag }) @heading = @title = @tag.description.blank?? @tag.tag : @tag.description @cur_url = tag_url(@tag.tag) @rss_link = { :title => "RSS 2.0 - Tagged #{@tag.tag} (#{@tag.description})", :href => "/t/#{@tag.tag}.rss" } respond_to do |format| format.html { render :action => "index" } format.rss { render :action => "rss", :layout => false } end end TOP_INTVS = { "d" => "Day", "w" => "Week", "m" => "Month", "y" => "Year" } def top @cur_url = "/top" length = { :dur => 1, :intv => "Week" } if m = params[:length].to_s.match(/\A(\d+)([#{TOP_INTVS.keys.join}])\z/) length[:dur] = m[1].to_i length[:intv] = TOP_INTVS[m[2]] @cur_url << "/#{params[:length]}" end @stories = find_stories({ :top => true, :length => length }) if length[:dur] > 1 @heading = @title = "Top Stories of the Past #{length[:dur]} " << length[:intv] << "s" else @heading = @title = "Top Stories of the Past " << length[:intv] end render :action => "index" end private def find_stories(how = {}) @page = how[:page] = 1 if params[:page].to_i > 0 @page = how[:page] = params[:page].to_i end # guest views have caching, but don't bother for logged-in users or dev or # when the user has tag filters if Rails.env.development? || @user || tags_filtered_by_cookie.any? stories, @show_more = _find_stories(how) else stories, @show_more = Rails.cache.fetch("stories " << how.sort.map{|k,v| "#{k}=#{v.to_param}" }.join(" "), :expires_in => 45) do _find_stories(how) end end stories end def _find_stories(how) stories = Story.unmerged.where(:is_expired => false) if @user hidden_arel = Vote.arel_table.where( Vote.arel_table[:user_id].eq(@user.id) ).where( Vote.arel_table[:vote].lteq(0) ).where( Vote.arel_table[:comment_id].eq(nil) ).project( Vote.arel_table[:story_id] ) if how[:hidden] stories = stories.where(Story.arel_table[:id].in(hidden_arel)) elsif !how[:by_user] stories = stories.where(Story.arel_table[:id].not_in(hidden_arel)) end end if how[:tag] || how[:hottest] stories = stories.where("(CAST(upvotes AS integer) - " << "CAST(downvotes AS integer)) >= -2") end if how[:tag] stories = stories.where( Story.arel_table[:id].in( Tagging.arel_table.where( Tagging.arel_table[:tag_id].eq(how[:tag].id) ).project( Tagging.arel_table[:story_id] ) ) ) elsif how[:by_user] stories = stories.where(:user_id => how[:by_user].id) else filtered_tag_ids = [] if @user filtered_tag_ids = @user.tag_filters.map{|tf| tf.tag_id } else filtered_tag_ids = tags_filtered_by_cookie.map{|t| t.id } end if filtered_tag_ids.any? stories = stories.where( Story.arel_table[:id].not_in( Tagging.arel_table.where( Tagging.arel_table[:tag_id].in(filtered_tag_ids) ).project( Tagging.arel_table[:story_id] ) ) ) end end if how[:recent] && how[:page] == 1 # try to help recently-submitted stories that didn't gain traction story_ids = [] 10.times do |x| # grab the list of stories from the past n days, shifting out popular # stories that did gain traction story_ids = stories.select(:id, :upvotes, :downvotes). where(Story.arel_table[:created_at].gt((RECENT_DAYS_OLD + x).days.ago)). order("stories.created_at DESC"). reject{|s| s.score > HOT_STORY_POINTS } if story_ids.length > STORIES_PER_PAGE + 1 # keep the top half (newest stories) keep_ids = story_ids[0 .. ((STORIES_PER_PAGE + 1) * 0.5)] story_ids = story_ids[keep_ids.length - 1 ... story_ids.length] # make the bottom half a random selection of older stories while keep_ids.length <= STORIES_PER_PAGE + 1 story_ids.shuffle! keep_ids.push story_ids.shift end stories = Story.where(:id => keep_ids) break end end elsif how[:top] && how[:length] stories = stories.where("created_at >= (NOW() - INTERVAL " << "#{how[:length][:dur]} #{how[:length][:intv].upcase})") end order = "hotness" if how[:newest] || how[:recent] order = "stories.created_at DESC" elsif how[:top] order = "(CAST(upvotes AS integer) - CAST(downvotes AS integer)) DESC" end stories = stories.includes( :user, :taggings => :tag ).limit( STORIES_PER_PAGE + 1 ).offset( (how[:page] - 1) * STORIES_PER_PAGE ).order( order ).to_a show_more = false if stories.count > STORIES_PER_PAGE show_more = true stories.pop end if @user votes = Vote.votes_by_user_for_stories_hash(@user.id, stories.map(&:id)) stories.each do |s| if votes[s.id] s.vote = votes[s.id] end end end [ stories, show_more ] end end