diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 8796fcb..4081f55 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -487,6 +487,15 @@ li .domain { vertical-align: middle; } +.merge { + background: url(data:image/png;base64,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) no-repeat; + width: 11px; + height: 14px; + padding-right: 2px; + display: inline-block; + vertical-align: middle; +} + li .byline { color: #888; font-size: 8.5pt; @@ -695,7 +704,8 @@ div#story_box button#story_fetch_title { padding-bottom: 1px; } div#story_box input#story_title, -div#story_box input#story_moderation_reason { +div#story_box input#story_moderation_reason, +div#story_box input#story_merge_story_short_id { width: 600px; } div#story_box #story_tags_a { diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index a2012e7..b19f1d9 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -174,7 +174,7 @@ private end def _find_stories(how) - stories = Story.where(:is_expired => false) + stories = Story.unmerged.where(:is_expired => false) if @user hidden_arel = Vote.arel_table.where( @@ -186,7 +186,7 @@ private ).project( Vote.arel_table[:story_id] ) - + if how[:hidden] stories = stories.where(Story.arel_table[:id].in(hidden_arel)) elsif !how[:by_user] @@ -218,7 +218,7 @@ private 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( diff --git a/app/controllers/stories_controller.rb b/app/controllers/stories_controller.rb index c6e8259..bba1397 100644 --- a/app/controllers/stories_controller.rb +++ b/app/controllers/stories_controller.rb @@ -52,6 +52,10 @@ class StoriesController < ApplicationController end @title = "Edit Story" + + if @story.merged_into_story + @story.merge_story_short_id = @story.merged_into_story.short_id + end end def fetch_url_title @@ -110,6 +114,11 @@ class StoriesController < ApplicationController def show @story = Story.where(:short_id => params[:id]).first! + if @story.merged_into_story + flash[:success] = "\"#{@story.title}\" has been merged into this story." + return redirect_to @story.merged_into_story.comments_url + end + if @story.can_be_seen_by_user?(@user) @title = @story.title else @@ -118,7 +127,8 @@ class StoriesController < ApplicationController @short_url = @story.short_id_url - @comments = @story.comments.includes(:user).arrange_for_user(@user) + @comments = @story.merged_comments.includes(:user, + :story).arrange_for_user(@user) if params[:comment_short_id] @comments.each do |c,x| @@ -247,13 +257,13 @@ private def story_params p = params.require(:story).permit( :title, :url, :description, :moderation_reason, :seen_previous, - :tags_a => [], + :merge_story_short_id, :tags_a => [], ) if @user.is_moderator? p else - p.except(:moderation_reason) + p.except(:moderation_reason, :merge_story_short_id) end end diff --git a/app/models/story.rb b/app/models/story.rb index 6c84fd7..e20fa80 100644 --- a/app/models/story.rb +++ b/app/models/story.rb @@ -1,11 +1,19 @@ class Story < ActiveRecord::Base belongs_to :user + belongs_to :merged_into_story, + :class_name => "Story", + :foreign_key => "merged_story_id" + has_many :merged_stories, + :class_name => "Story", + :foreign_key => "merged_story_id" has_many :taggings, :autosave => true has_many :comments, :inverse_of => :story has_many :tags, :through => :taggings + scope :unmerged, -> { where(:merged_story_id => nil) } + validates_length_of :title, :in => 3..150 validates_length_of :description, :maximum => (64 * 1024) validates_presence_of :user_id @@ -20,12 +28,13 @@ class Story < ActiveRecord::Base attr_accessor :vote, :already_posted_story, :fetched_content, :previewing, :seen_previous - attr_accessor :editor, :moderation_reason + attr_accessor :editor, :moderation_reason, :merge_story_short_id before_validation :assign_short_id_and_upvote, :on => :create before_save :log_moderation after_create :mark_submitter, :record_initial_upvote + after_save :update_merged_into_story_comments validate do if self.url.present? @@ -284,8 +293,18 @@ class Story < ActiveRecord::Base elsif all_changes["is_expired"] && !self.is_expired? m.action = "undeleted story" else - m.action = all_changes.map{|k,v| "changed #{k} from #{v[0].inspect} " << - "to #{v[1].inspect}" }.join(", ") + m.action = all_changes.map{|k,v| + if k == "merged_story_id" + if v[1] + "merged into #{self.merged_into_story.short_id} " << + "(#{self.merged_into_story.title})" + else + "unmerged from another story" + end + else + "changed #{k} from #{v[0].inspect} to #{v[1].inspect}" + end + }.join(", ") end m.reason = self.moderation_reason @@ -302,6 +321,22 @@ class Story < ActiveRecord::Base Keystore.increment_value_for("user:#{self.user_id}:stories_submitted") end + def merge_into_story!(story) + self.merged_story_id = story.id + self.save! + end + + def merged_comments + # TODO: make this a normal has_many? + Comment.where(:story_id => Story.select(:id). + where(:merged_story_id => self.id) + [ self.id ]) + end + + def merge_story_short_id=(sid) + self.merged_story_id = sid.present?? + Story.where(:short_id => sid).first.id : nil + end + def recalculate_hotness! update_column :hotness, calculated_hotness end @@ -375,12 +410,18 @@ class Story < ActiveRecord::Base end def update_comments_count! - comments = self.comments.arrange_for_user(nil) + comments = self.merged_comments.arrange_for_user(nil) # calculate count after removing deleted comments and threads self.update_column :comments_count, comments.count{|c| !c.is_gone? } end + def update_merged_into_story_comments + if self.merged_into_story + self.merged_into_story.update_comments_count! + end + end + def url=(u) # strip out stupid google analytics parameters if u && (m = u.match(/\A([^\?]+)\?(.+)\z/)) diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index 584fa5f..d9241f2 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -23,6 +23,9 @@ class="comment <%= comment.current_vote ? (comment.current_vote[:vote] == 1 ? <% end %>
+ <% if defined?(was_merged) && was_merged %> + + <% end %> <% if comment.previewing %> <%= comment.user.username %> previewed diff --git a/app/views/stories/_listdetail.html.erb b/app/views/stories/_listdetail.html.erb index e8b25d9..28450ef 100644 --- a/app/views/stories/_listdetail.html.erb +++ b/app/views/stories/_listdetail.html.erb @@ -36,6 +36,25 @@ class="story <%= story.vote == 1 ? "upvoted" : "" %> <%= story.vote == -1 ? <% end %> <%= story.domain %> + + <% if defined?(single_story) && single_story %> + <% story.merged_stories.each do |ms| %> +
+ + + <%= ms.title %> + + + <% ms.taggings.sort_by{|t| t.tag.tag }.sort_by{|t| + t.tag.tag == "pdf" ? -1 : 0 }.each do |tagging| %> + <%= tagging.tag.tag %> + <% end %> + + <%= ms.domain %> + <% end %> + <% end %> <% end %>