Likes in comments, cache counter disabled for now.

This commit is contained in:
danielgrippi 2011-07-08 11:28:17 -07:00 committed by Raphael Sofaer
parent e59f49aace
commit 2e15b6a61e
28 changed files with 135 additions and 74 deletions

View file

@ -31,8 +31,6 @@ class AdminsController < ApplicationController
def stats
@popular_tags = ActsAsTaggableOn::Tagging.joins(:tag).limit(15).count(:group => :tag, :order => 'count(taggings.id) DESC')
@most_liked_posts = Post.where(:type => ['StatusMessage', 'ActivityStreams::Photo'],
:public => true).order('likes_count DESC').limit(15).all
@new_posts = Post.where(:type => ['StatusMessage','ActivityStreams::Photo'],
:public => true).order('created_at DESC').limit(15).all
end

View file

@ -9,10 +9,15 @@ class LikesController < ApplicationController
respond_to :html, :mobile, :json
def create
target = current_user.find_visible_post_by_id params[:post_id]
if params[:post_id]
target = current_user.find_visible_post_by_id params[:post_id]
else
target = Comment.find(params[:comment_id])
end
positive = (params[:positive] == 'true') ? true : false
if target
@like = current_user.build_like(:positive => positive, :post => target)
@like = current_user.build_like(:positive => positive, :target => target)
if @like.save
Rails.logger.info("event=create type=like user=#{current_user.diaspora_handle} status=success like=#{@like.id} positive=#{positive}")
@ -32,7 +37,9 @@ class LikesController < ApplicationController
end
def destroy
if @like = Like.where(:id => params[:id], :author_id => current_user.person.id, :post_id => params[:post_id]).first
target_id = params[:post_id] || params[:comment_id]
if @like = Like.where(:id => params[:id], :author_id => current_user.person.id, :target_id => target_id).first
current_user.retract(@like)
else
respond_to do |format|

View file

@ -8,11 +8,25 @@ module LikesHelper
links.join(", ").html_safe
end
def like_action(post, current_user=current_user)
if current_user.liked?(post)
link_to t('shared.stream_element.unlike'), post_like_path(post, current_user.like_for(post)), :method => :delete, :class => 'unlike', :remote => true
def like_action(target, current_user=current_user)
target = target.model if target.instance_of?(PostsFake::Fake)
if target.instance_of?(Comment)
if current_user.liked?(target)
link_to t('shared.stream_element.unlike'), comment_like_path(target, current_user.like_for(target)), :method => :delete, :class => 'unlike', :remote => true
else
link_to t('shared.stream_element.like'), comment_likes_path(target, :positive => 'true'), :method => :post, :class => 'like', :remote => true
end
else
link_to t('shared.stream_element.like'), post_likes_path(post, :positive => 'true'), :method => :post, :class => 'like', :remote => true
if current_user.liked?(target)
link_to t('shared.stream_element.unlike'), post_like_path(target, current_user.like_for(target)), :method => :delete, :class => 'unlike', :remote => true
else
link_to t('shared.stream_element.like'), post_likes_path(target, :positive => 'true'), :method => :post, :class => 'like', :remote => true
end
end
end
end

View file

@ -25,6 +25,8 @@ class Comment < ActiveRecord::Base
belongs_to :post, :touch => true
belongs_to :author, :class_name => 'Person'
has_many :likes, :foreign_key => :target_id, :conditions => {:positive => true}, :dependent => :delete_all
validates_presence_of :text, :post
validates_length_of :text, :maximum => 2500

View file

@ -8,6 +8,8 @@ class Like < ActiveRecord::Base
include Diaspora::Webhooks
include Diaspora::Guid
xml_attr :target_type
include Diaspora::Relayable
include Diaspora::Socketable
@ -15,11 +17,11 @@ class Like < ActiveRecord::Base
xml_attr :positive
xml_attr :diaspora_handle
belongs_to :post, :counter_cache => true
belongs_to :target, :polymorphic => true #, :counter_cache => true
belongs_to :author, :class_name => 'Person'
validates_uniqueness_of :post_id, :scope => :author_id
validates_presence_of :author, :post
validates_uniqueness_of :target_id, :scope => :author_id
validates_presence_of :author, :target
def diaspora_handle
self.author.diaspora_handle
@ -30,18 +32,18 @@ class Like < ActiveRecord::Base
end
def parent_class
Post
self.target_type.constantize
end
def parent
self.post
self.target
end
def parent= parent
self.post = parent
self.target = parent
end
def notification_type(user, person)
Notifications::Liked if self.post.author == user.person && user.person != person
Notifications::Liked if self.target.author == user.person && user.person != person
end
end

View file

@ -19,7 +19,7 @@ class Notification < ActiveRecord::Base
if target.respond_to? :notification_type
if note_type = target.notification_type(recipient, actor)
if(target.is_a? Comment) || (target.is_a? Like)
n = note_type.concatenate_or_create(recipient, target.post, actor, note_type)
n = note_type.concatenate_or_create(recipient, target.parent, actor, note_type)
else
n = note_type.make_notification(recipient, target, actor, note_type)
end

View file

@ -14,8 +14,8 @@ class Post < ActiveRecord::Base
xml_attr :created_at
has_many :comments, :dependent => :destroy
has_many :likes, :conditions => {:positive => true}, :dependent => :delete_all
has_many :dislikes, :conditions => {:positive => false}, :class_name => 'Like', :dependent => :delete_all
has_many :likes, :conditions => {:positive => true}, :dependent => :delete_all, :as => :target
has_many :dislikes, :conditions => {:positive => false}, :class_name => 'Like', :dependent => :delete_all, :as => :target
has_many :aspect_visibilities
has_many :aspects, :through => :aspect_visibilities

View file

@ -183,26 +183,26 @@ class User < ActiveRecord::Base
# Check whether the user has liked a post. Extremely inefficient if the post's likes are not loaded.
# @param [Post] post
def liked?(post)
if post.likes.loaded?
if self.like_for(post)
def liked?(target)
if target.likes.loaded?
if self.like_for(target)
return true
else
return false
end
else
Like.exists?(:author_id => self.person.id, :post_id => post.id)
Like.exists?(:author_id => self.person.id, :target_type => target.class.base_class.to_s, :target_id => target.id)
end
end
# Get the user's like of a post, if there is one. Extremely inefficient if the post's likes are not loaded.
# @param [Post] post
# @return [Like]
def like_for(post)
if post.likes.loaded?
return post.likes.detect{ |like| like.author_id == self.person.id }
def like_for(target)
if target.likes.loaded?
return target.likes.detect{ |like| like.author_id == self.person.id }
else
return Like.where(:author_id => self.person.id, :post_id => post.id).first
return Like.where(:author_id => self.person.id, :target_id => target.id).first
end
end
@ -215,13 +215,20 @@ class User < ActiveRecord::Base
end
######### Posts and Such ###############
def retract(post)
if post.respond_to?(:relayable?) && post.relayable?
aspects = post.parent.aspects
retraction = RelayableRetraction.build(self, post)
def retract(target)
if target.respond_to?(:relayable?) && target.relayable?
parent = if target.parent.instance_of?(Comment)
target.parent.parent
else
target.parent
end
aspects = parent.aspects
retraction = RelayableRetraction.build(self, target)
else
aspects = post.aspects
retraction = Retraction.for(post)
aspects = target.aspects
retraction = Retraction.for(target)
end
mailman = Postzord::Dispatch.new(self, retraction)

View file

@ -19,4 +19,6 @@
%br
%time.timeago{:datetime => comment.created_at}
= comment.created_at ? timeago(comment.created_at) : timeago(Time.now)
- unless (defined?(@commenting_disabled) && @commenting_disabled)
= like_action(comment, current_user)

View file

@ -6,7 +6,7 @@
.likes_container
.likes
= image_tag('icons/heart.svg')
= link_to t('likes.likes.people_like_this', :count => likes_count), post_likes_path(post_id), :class => "expand_likes"
= link_to t('likes.likes.people_like_this', :count => likes_count), post_likes_path(target_id), :class => "expand_likes"
%span.hidden.likes_list
/= render 'likes/likes', :likes => likes

View file

@ -1,4 +1,4 @@
$(".like_action", ".stream_element[data-guid=<%=@like.post_id%>]").html("<%= escape_javascript(like_action(@like.post))%>");
$(".like_action", ".stream_element[data-guid=<%=@like.target_id%>]").html("<%= escape_javascript(like_action(@like.target))%>");
WebSocketReceiver.processLike("<%=@like.post_id%>", "<%= escape_javascript(render("likes/likes_container", :post_id => @like.post_id, :likes_count => @like.post.likes.count)) %>");
WebSocketReceiver.processLike("<%=@like.target_id%>", "<%= escape_javascript(render("likes/likes_container", :target_id => @like.target_id, :likes_count => @like.target.likes.count)) %>");

View file

@ -1,3 +1,3 @@
$(".like_action", ".stream_element[data-guid=<%=@like.post_id%>]").html("<%= escape_javascript(like_action(@like.post))%>");
WebSocketReceiver.processLike("<%=@like.post_id%>", "<%= escape_javascript(render("likes/likes_container", :post_id => @like.post_id, :likes_count => @like.post.likes.count)) %>");
$(".like_action", ".stream_element[data-guid=<%=@like.target_id%>]").html("<%= escape_javascript(like_action(@like.target))%>");
WebSocketReceiver.processLike("<%=@like.target_id%>", "<%= escape_javascript(render("likes/likes_container", :target_id => @like.target_id, :likes_count => @like.target.likes.count)) %>");

View file

@ -4,12 +4,11 @@
= t('.liked', :name => "#{@sender.name} (#{@sender.diaspora_handle})")
%p
= @like.post.formatted_message(:plain_text => true)
= @like.target.formatted_message(:plain_text => true)
%p
%br
= link_to t('.sign_in'), post_url(@like.post)
= link_to t('.sign_in'), post_url(@like.target)
%br
= t('notifier.love')
%br

View file

@ -2,7 +2,7 @@
!= t('notifier.liked.liked', :name => "#{@sender.name} (#{@sender.diaspora_handle})")
!= @like.post.formatted_message(:plain_text => true)
!= @like.target.formatted_message(:plain_text => true)
!= t('notifier.love')
!= t('notifier.diaspora')

View file

@ -65,5 +65,5 @@
#photo_stream.stream.show
%div{:data=>{:guid=>parent.id}}
.likes_container
= render "likes/likes_container", :post_id => parent.id, :likes_count => parent.likes_count
= render "likes/likes_container", :post_id => parent.id, :likes_count => parent.likes.count
= render "comments/comments", :post => parent, :comments => parent.comments, :always_expanded => true

View file

@ -56,7 +56,7 @@
= link_to t('comments.new_comment.comment'), '#', :class => 'focus_comment_textarea'
.likes
- if post.likes_count > 0
= render "likes/likes_container", :post_id => post.id, :likes_count => post.likes_count, :current_user => current_user
- if post.likes.count > 0
= render "likes/likes_container", :target_id => post.id, :likes_count => post.likes.count, :current_user => current_user
= render "comments/comments", :post => post, :current_user => current_user, :commenting_disabled => (defined?(@commenting_disabled) && @commenting_disabled)

View file

@ -19,6 +19,12 @@ Diaspora::Application.routes.draw do
resources :comments, :only => [:create, :destroy, :index]
end
# roll up likes into a nested resource above
resources :comments, :only => [:create, :destroy] do
resources :likes, :only => [:create, :destroy, :index]
end
get 'bookmarklet' => 'status_messages#bookmarklet'
get 'p/:id' => 'publics#post', :as => 'public_post'

View file

@ -0,0 +1,19 @@
class LikesOnComments < ActiveRecord::Migration
def self.up
add_column :likes, :target_type, :string, :null => false
add_column :likes, :target_id, :integer, :null => false
remove_column :posts, :likes_count
execute <<SQL
UPDATE likes
SET target_type = 'Post'
SQL
end
def self.down
add_column :posts, :likes_count, :integer
remove_column :likes, :target_type
rename_column :likes, :target_id, :post_id
add_index :likes, :post_id
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20110707221112) do
ActiveRecord::Schema.define(:version => 20110707234802) do
create_table "aspect_memberships", :force => true do |t|
t.integer "aspect_id", :null => false
@ -117,6 +117,8 @@ ActiveRecord::Schema.define(:version => 20110707221112) do
t.text "parent_author_signature"
t.datetime "created_at"
t.datetime "updated_at"
t.string "target_type", :null => false
t.integer "target_id", :null => false
end
add_index "likes", ["author_id"], :name => "likes_author_id_fk"
@ -266,7 +268,6 @@ ActiveRecord::Schema.define(:version => 20110707221112) do
t.string "actor_url"
t.integer "objectId"
t.string "status_message_guid"
t.integer "likes_count", :default => 0
end
add_index "posts", ["author_id"], :name => "index_posts_on_person_id"

View file

@ -69,16 +69,18 @@ module Diaspora
#sign relayable as model creator
self.author_signature = self.sign_with_key(author.owner.encryption_key)
if !self.post_id.blank? && self.author.owns?(self.parent)
if !self.parent.blank? && self.author.owns?(self.parent)
#sign relayable as parent object owner
self.parent_author_signature = sign_with_key(author.owner.encryption_key)
end
end
# @return [Boolean]
def verify_parent_author_signature
verify_signature(self.parent_author_signature, self.parent.author)
end
# @return [Boolean]
def signature_valid?
verify_signature(self.author_signature, self.author)
end

View file

@ -816,6 +816,7 @@ label
:cursor pointer
#publisher
:z-index 0
:color #999
:position relative

View file

@ -52,7 +52,7 @@ describe LikesController do
end
it "doesn't post multiple times" do
@user1.like(1, :post => @post)
@user1.like(1, :target => @post)
post :create, dislike_hash
response.code.should == '422'
end
@ -81,7 +81,7 @@ describe LikesController do
end
it 'returns an array of likes for a post' do
like = bob.build_like(:positive => true, :post => @message)
like = bob.build_like(:positive => true, :target => @message)
like.save!
get :index, :post_id => @message.id
@ -97,23 +97,23 @@ describe LikesController do
describe '#destroy' do
before do
@message = bob.post(:status_message, :text => "hey", :to => @aspect1.id)
@like = alice.build_like(:positive => true, :post => @message)
@like = alice.build_like(:positive => true, :target => @message)
@like.save
end
it 'lets a user destroy their like' do
expect {
delete :destroy, :format => "js", :post_id => @like.post_id, :id => @like.id
delete :destroy, :format => "js", :post_id => @like.target_id, :id => @like.id
}.should change(Like, :count).by(-1)
response.status.should == 200
end
it 'does not let a user destroy other likes' do
like2 = eve.build_like(:positive => true, :post => @message)
like2 = eve.build_like(:positive => true, :target => @message)
like2.save
expect {
delete :destroy, :format => "js", :post_id => like2.post_id, :id => like2.id
delete :destroy, :format => "js", :post_id => like2.target_id, :id => like2.id
}.should_not change(Like, :count)
response.status.should == 403

View file

@ -37,7 +37,7 @@ end
Factory.define :like do |x|
x.association :author, :factory => :person
x.association :post, :factory => :status_message
x.association :target, :factory => :status_message
end
Factory.define :user do |u|

View file

@ -8,10 +8,10 @@ describe NotificationsHelper do
@person = Factory(:person)
@post = Factory(:status_message, :author => @user.person)
@person2 = Factory(:person)
@notification = Notification.notify(@user, Factory(:like, :author => @person, :post => @post), @person)
@notification = Notification.notify(@user, Factory(:like, :author => @person2, :post => @post), @person2)
@notification = Notification.notify(@user, Factory(:like, :author => @person, :target => @post), @person)
@notification = Notification.notify(@user, Factory(:like, :author => @person2, :target => @post), @person2)
end
describe '#notification_people_link' do
context 'formatting' do
include ActionView::Helpers::SanitizeHelper

View file

@ -102,7 +102,7 @@ describe Notifier do
describe ".liked" do
before do
@sm = Factory(:status_message, :author => alice.person)
@like = @sm.likes.create(:author => bob.person)
@like = @sm.likes.create!(:author => bob.person)
@mail = Notifier.liked(alice.id, @like.author.id, @like.id)
end

View file

@ -21,26 +21,26 @@ describe Like do
describe 'User#like' do
it "should be able to like on one's own status" do
alice.like(1, :post => @status)
alice.like(1, :target => @status)
@status.reload.likes.first.positive.should == true
end
it "should be able to like on a contact's status" do
bob.like(0, :post => @status)
bob.like(0, :target => @status)
@status.reload.dislikes.first.positive.should == false
end
it "does not allow multiple likes" do
lambda {
alice.like(1, :post => @status)
alice.like(0, :post => @status)
alice.like(1, :target => @status)
alice.like(0, :target => @status)
}.should raise_error
end
end
describe '#notification_type' do
before do
@like = @alice.like(1, :post => @status)
@like = @alice.like(1, :target => @status)
end
it 'should be notifications liked if you are the post owner' do
@ -58,8 +58,9 @@ describe Like do
describe 'counter cache' do
it 'increments the counter cache on its post' do
pending
lambda {
@alice.like(1, :post => @status)
@alice.like(1, :target => @status)
}.should change{ @status.reload.likes_count }.by(1)
end
end
@ -70,7 +71,7 @@ describe Like do
@liker_aspect = @liker.aspects.create(:name => "dummies")
connect_users(alice, @alices_aspect, @liker, @liker_aspect)
@post = alice.post :status_message, :text => "huhu", :to => @alices_aspect.id
@like = @liker.like 0, :post => @post
@like = @liker.like 0, :target => @post
@xml = @like.to_xml.to_s
end
it 'serializes the sender handle' do
@ -87,7 +88,7 @@ describe Like do
@marshalled_like.author.should == @liker.person
end
it 'marshals the post' do
@marshalled_like.post.should == @post
@marshalled_like.target.should == @post
end
end
end
@ -98,11 +99,11 @@ describe Like do
@remote_parent = Factory.create(:status_message, :author => @remote_raphael)
@local_parent = @local_luke.post :status_message, :text => "foobar", :to => @local_luke.aspects.first
@object_by_parent_author = @local_luke.like(1, :post => @local_parent)
@object_by_recipient = @local_leia.build_like(:positive => 1, :post => @local_parent)
@object_by_parent_author = @local_luke.like(1, :target => @local_parent)
@object_by_recipient = @local_leia.build_like(:positive => 1, :target => @local_parent)
@dup_object_by_parent_author = @object_by_parent_author.dup
@object_on_remote_parent = @local_luke.like(0, :post => @remote_parent)
@object_on_remote_parent = @local_luke.like(0, :target => @remote_parent)
end
it_should_behave_like 'it is relayable'
end

View file

@ -96,8 +96,8 @@ describe Notification do
it 'concatinates the like notifications' do
p = Factory(:status_message, :author => @user.person)
person2 = Factory(:person)
notification = Notification.notify(@user, Factory(:like, :author => @person, :post => p), @person)
notification2 = Notification.notify(@user, Factory(:like, :author => person2, :post => p), person2)
notification = Notification.notify(@user, Factory(:like, :author => @person, :target => p), @person)
notification2 = Notification.notify(@user, Factory(:like, :author => person2, :target => p), person2)
notification.id.should == notification2.id
end
end

View file

@ -569,8 +569,8 @@ describe User do
before do
@message = alice.post(:status_message, :text => "cool", :to => alice.aspects.first)
@message2 = bob.post(:status_message, :text => "uncool", :to => bob.aspects.first)
@like = alice.like(true, :post => @message)
@like2 = bob.like(true, :post => @message)
@like = alice.like(true, :target => @message)
@like2 = bob.like(true, :target => @message)
end
describe '#like_for' do