parent
9d2253a010
commit
73b8df5eb7
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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/))
|
||||
|
|
|
@ -23,6 +23,9 @@ class="comment <%= comment.current_vote ? (comment.current_vote[:vote] == 1 ?
|
|||
<% end %>
|
||||
<div class="details">
|
||||
<div class="byline">
|
||||
<% if defined?(was_merged) && was_merged %>
|
||||
<span class="merge"></span>
|
||||
<% end %>
|
||||
<% if comment.previewing %>
|
||||
<a><%= comment.user.username %></a>
|
||||
previewed
|
||||
|
|
|
@ -36,6 +36,25 @@ class="story <%= story.vote == 1 ? "upvoted" : "" %> <%= story.vote == -1 ?
|
|||
<% end %>
|
||||
</span>
|
||||
<span class="domain"><%= story.domain %></span>
|
||||
|
||||
<% if defined?(single_story) && single_story %>
|
||||
<% story.merged_stories.each do |ms| %>
|
||||
<br>
|
||||
<span class="merge"></span>
|
||||
<span class="link">
|
||||
<a href="<%= ms.url_or_comments_url %>"><%= ms.title %></a>
|
||||
</span>
|
||||
<span class="tags">
|
||||
<% ms.taggings.sort_by{|t| t.tag.tag }.sort_by{|t|
|
||||
t.tag.tag == "pdf" ? -1 : 0 }.each do |tagging| %>
|
||||
<a href="<%= tag_url(tagging.tag.tag) %>"
|
||||
class="<%= tagging.tag.css_class %>"
|
||||
title="<%= tagging.tag.description %>"><%= tagging.tag.tag %></a>
|
||||
<% end %>
|
||||
</span>
|
||||
<span class="domain"><%= ms.domain %></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div class="byline">
|
||||
<% if story.previewing %>
|
||||
|
|
|
@ -10,6 +10,13 @@
|
|||
|
||||
<% if @user.is_moderator? && (@story.user_id != @user.id) %>
|
||||
<div class="box">
|
||||
<div class="boxline">
|
||||
<%= f.label :merge_story_short_id, "Merge Into:",
|
||||
:class => "required" %>
|
||||
<%= f.text_field :merge_story_short_id, :autocomplete => "off",
|
||||
:placeholder => "Short id of story into which this story " <<
|
||||
"be merged" %>
|
||||
</div>
|
||||
<div class="boxline">
|
||||
<%= f.label :moderation_reason, "Mod Reason:",
|
||||
:class => "required" %>
|
||||
|
|
|
@ -24,7 +24,9 @@
|
|||
<% while subtree %>
|
||||
<% if (comment = subtree.shift) %>
|
||||
<li>
|
||||
<%= render "comments/comment", :comment => comment %>
|
||||
<%= render "comments/comment", :comment => comment,
|
||||
:show_story => (comment.story_id != @story.id),
|
||||
:was_merged => (comment.story_id != @story.id) %>
|
||||
|
||||
<% if (children = comments_by_parent[comment.id]) %>
|
||||
<% ancestors << subtree %>
|
||||
|
|
6
db/migrate/20140408160306_add_story_merging.rb
Normal file
6
db/migrate/20140408160306_add_story_merging.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
class AddStoryMerging < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :stories, :merged_story_id, :integer
|
||||
add_index "stories", [ "merged_story_id" ]
|
||||
end
|
||||
end
|
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20140221164400) do
|
||||
ActiveRecord::Schema.define(version: 20140408160306) do
|
||||
|
||||
create_table "comments", force: true do |t|
|
||||
t.datetime "created_at", null: false
|
||||
|
@ -103,10 +103,12 @@ ActiveRecord::Schema.define(version: 20140221164400) do
|
|||
t.text "markeddown_description", limit: 16777215
|
||||
t.text "story_cache", limit: 16777215
|
||||
t.integer "comments_count", default: 0, null: false
|
||||
t.integer "merged_story_id"
|
||||
end
|
||||
|
||||
add_index "stories", ["hotness"], name: "hotness_idx", using: :btree
|
||||
add_index "stories", ["is_expired", "is_moderated"], name: "is_idxes", using: :btree
|
||||
add_index "stories", ["merged_story_id"], name: "index_stories_on_merged_story_id", using: :btree
|
||||
add_index "stories", ["short_id"], name: "unique_short_id", unique: true, using: :btree
|
||||
add_index "stories", ["url"], name: "url", length: {"url"=>191}, using: :btree
|
||||
|
||||
|
|
Loading…
Reference in a new issue