From 9fe0320881c686f5e91a9b00ac36d254448aff83 Mon Sep 17 00:00:00 2001 From: danielvincent Date: Fri, 25 Feb 2011 11:49:47 -0800 Subject: [PATCH 01/29] added PrivateMessage and PrivateMessageVisibility models and migrations --- .../private_messages_controller.rb | 2 ++ app/helpers/private_messages_helper.rb | 2 ++ app/models/private_message.rb | 5 ++++ app/models/private_message_visibility.rb | 6 +++++ app/views/private_messages/index.haml | 5 ++++ app/views/private_messages/new.haml | 19 ++++++++++++++ app/views/private_messages/show.haml | 5 ++++ .../status_messages/_new_status_message.haml | 9 ------- config/routes.rb | 2 ++ .../20110225190919_create_private_messages.rb | 18 +++++++++++++ ...130_create_private_message_visibilities.rb | 18 +++++++++++++ db/schema.rb | 26 +++++++++++++++++++ .../private_messages_controller_spec.rb | 5 ++++ spec/helpers/private_messages_helper_spec.rb | 15 +++++++++++ spec/models/private_message_spec.rb | 5 ++++ 15 files changed, 133 insertions(+), 9 deletions(-) create mode 100644 app/controllers/private_messages_controller.rb create mode 100644 app/helpers/private_messages_helper.rb create mode 100644 app/models/private_message.rb create mode 100644 app/models/private_message_visibility.rb create mode 100644 app/views/private_messages/index.haml create mode 100644 app/views/private_messages/new.haml create mode 100644 app/views/private_messages/show.haml delete mode 100644 app/views/status_messages/_new_status_message.haml create mode 100644 db/migrate/20110225190919_create_private_messages.rb create mode 100644 db/migrate/20110225193130_create_private_message_visibilities.rb create mode 100644 spec/controllers/private_messages_controller_spec.rb create mode 100644 spec/helpers/private_messages_helper_spec.rb create mode 100644 spec/models/private_message_spec.rb diff --git a/app/controllers/private_messages_controller.rb b/app/controllers/private_messages_controller.rb new file mode 100644 index 00000000000..8e74aa97411 --- /dev/null +++ b/app/controllers/private_messages_controller.rb @@ -0,0 +1,2 @@ +class PrivateMessagesController < ApplicationController +end diff --git a/app/helpers/private_messages_helper.rb b/app/helpers/private_messages_helper.rb new file mode 100644 index 00000000000..1c849b6ab3d --- /dev/null +++ b/app/helpers/private_messages_helper.rb @@ -0,0 +1,2 @@ +module PrivateMessagesHelper +end diff --git a/app/models/private_message.rb b/app/models/private_message.rb new file mode 100644 index 00000000000..bf8079fe389 --- /dev/null +++ b/app/models/private_message.rb @@ -0,0 +1,5 @@ +class PrivateMessage < ActiveRecord::Base + belongs_to :author, :class_name => 'Person' + has_many :private_message_visibilities + has_many :recipients, :class_name => 'Person', :through => :private_message_visibilities, :source => :person +end diff --git a/app/models/private_message_visibility.rb b/app/models/private_message_visibility.rb new file mode 100644 index 00000000000..e4ba8f53b9c --- /dev/null +++ b/app/models/private_message_visibility.rb @@ -0,0 +1,6 @@ +class PrivateMessageVisibility < ActiveRecord::Base + + belongs_to :private_message + belongs_to :person + +end diff --git a/app/views/private_messages/index.haml b/app/views/private_messages/index.haml new file mode 100644 index 00000000000..7fae50376f8 --- /dev/null +++ b/app/views/private_messages/index.haml @@ -0,0 +1,5 @@ +-# Copyright (c) 2010, Diaspora Inc. This file is +-# licensed under the Affero General Public License version 3 or later. See +-# the COPYRIGHT file. + + diff --git a/app/views/private_messages/new.haml b/app/views/private_messages/new.haml new file mode 100644 index 00000000000..ae2a260f6ed --- /dev/null +++ b/app/views/private_messages/new.haml @@ -0,0 +1,19 @@ +-# Copyright (c) 2010, Diaspora Inc. This file is +-# licensed under the Affero General Public License version 3 or later. See +-# the COPYRIGHT file. + + +%h2 + New Message + += form_for PrivateMessage.new do |private_message| + %h4 + to + = private_message.text_field :recipients + + %h4 + message + = private_message.text_area :message, :rows => 5 + + = link_to 'cancel', private_messages_path + = private_message.submit :send diff --git a/app/views/private_messages/show.haml b/app/views/private_messages/show.haml new file mode 100644 index 00000000000..7fae50376f8 --- /dev/null +++ b/app/views/private_messages/show.haml @@ -0,0 +1,5 @@ +-# Copyright (c) 2010, Diaspora Inc. This file is +-# licensed under the Affero General Public License version 3 or later. See +-# the COPYRIGHT file. + + diff --git a/app/views/status_messages/_new_status_message.haml b/app/views/status_messages/_new_status_message.haml deleted file mode 100644 index 00db8eefe1b..00000000000 --- a/app/views/status_messages/_new_status_message.haml +++ /dev/null @@ -1,9 +0,0 @@ --# Copyright (c) 2010, Diaspora Inc. This file is --# licensed under the Affero General Public License version 3 or later. See --# the COPYRIGHT file. - -= form_for StatusMessage.new, :remote => true do |f| - = f.error_messages - %p - = f.text_field :message, :value => t('.tell_me_something_good') - = f.submit t('.oh_yeah'), :class => 'button' diff --git a/config/routes.rb b/config/routes.rb index a3471c3aaf7..88e17c18ec3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,6 +3,8 @@ # the COPYRIGHT file. Diaspora::Application.routes.draw do + resources :private_messages + resources :status_messages, :only => [:create, :destroy, :show] resources :comments, :only => [:create] resources :requests, :only => [:destroy, :create] diff --git a/db/migrate/20110225190919_create_private_messages.rb b/db/migrate/20110225190919_create_private_messages.rb new file mode 100644 index 00000000000..d858aa51aab --- /dev/null +++ b/db/migrate/20110225190919_create_private_messages.rb @@ -0,0 +1,18 @@ +class CreatePrivateMessages < ActiveRecord::Migration + def self.up + create_table :private_messages do |t| + t.integer :author_id, :null => false + t.boolean :unread, :null => false, :default => true + t.string :guid, :null => false + t.text :message, :null => false + + t.timestamps + end + + add_index :private_messages, :author_id + end + + def self.down + drop_table :private_messages + end +end diff --git a/db/migrate/20110225193130_create_private_message_visibilities.rb b/db/migrate/20110225193130_create_private_message_visibilities.rb new file mode 100644 index 00000000000..553e8906c6c --- /dev/null +++ b/db/migrate/20110225193130_create_private_message_visibilities.rb @@ -0,0 +1,18 @@ +class CreatePrivateMessageVisibilities < ActiveRecord::Migration + def self.up + create_table :private_message_visibilities do |t| + t.integer :private_message_id + t.integer :person_id + + t.timestamps + end + + add_index :private_message_visibilities, :person_id + add_index :private_message_visibilities, :private_message_id + add_index :private_message_visibilities, [:private_message_id, :person_id], :name => 'pm_visibilities_on_pm_id_and_person_id', :unique => true + end + + def self.down + drop_table :private_message_visibilities + end +end diff --git a/db/schema.rb b/db/schema.rb index 2dfe5390c1c..4ffd5826908 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,11 @@ # # It's strongly recommended to check this file into your version control system. +<<<<<<< HEAD ActiveRecord::Schema.define(:version => 20110228201109) do +======= +ActiveRecord::Schema.define(:version => 20110225193130) do +>>>>>>> added PrivateMessage and PrivateMessageVisibility models and migrations create_table "aspect_memberships", :force => true do |t| t.integer "aspect_id", :null => false @@ -383,6 +387,28 @@ add_index "posts", ["type", "pending", "id"], :name => "index_posts_on_type_and_pending_and_id" add_index "posts", ["type"], :name => "index_posts_on_type" + create_table "private_message_visibilities", :force => true do |t| + t.integer "private_message_id" + t.integer "person_id" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "private_message_visibilities", ["person_id"], :name => "index_private_message_visibilities_on_person_id" + add_index "private_message_visibilities", ["private_message_id", "person_id"], :name => "pm_visibilities_on_pm_id_and_person_id", :unique => true + add_index "private_message_visibilities", ["private_message_id"], :name => "index_private_message_visibilities_on_private_message_id" + + create_table "private_messages", :force => true do |t| + t.integer "author_id", :null => false + t.boolean "unread", :default => true, :null => false + t.string "guid", :null => false + t.text "message", :null => false + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "private_messages", ["author_id"], :name => "index_private_messages_on_author_id" + create_table "profiles", :force => true do |t| t.string "diaspora_handle" t.string "first_name", :limit => 127 diff --git a/spec/controllers/private_messages_controller_spec.rb b/spec/controllers/private_messages_controller_spec.rb new file mode 100644 index 00000000000..26bf325218e --- /dev/null +++ b/spec/controllers/private_messages_controller_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe PrivateMessagesController do + +end diff --git a/spec/helpers/private_messages_helper_spec.rb b/spec/helpers/private_messages_helper_spec.rb new file mode 100644 index 00000000000..a946519dd96 --- /dev/null +++ b/spec/helpers/private_messages_helper_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +# Specs in this file have access to a helper object that includes +# the PrivateMessagesHelper. For example: +# +# describe PrivateMessagesHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# helper.concat_strings("this","that").should == "this that" +# end +# end +# end +describe PrivateMessagesHelper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/private_message_spec.rb b/spec/models/private_message_spec.rb new file mode 100644 index 00000000000..983209daee2 --- /dev/null +++ b/spec/models/private_message_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe PrivateMessage do + pending "add some examples to (or delete) #{__FILE__}" +end From c5801ffb375186bc0a7c808c2c2d1d507f4238e4 Mon Sep 17 00:00:00 2001 From: danielvincent Date: Fri, 25 Feb 2011 15:02:54 -0800 Subject: [PATCH 02/29] basic views / controller actions for private messages --- .../private_messages_controller.rb | 36 ++++++++++ app/controllers/status_messages_controller.rb | 2 +- app/models/private_message.rb | 9 ++- app/views/private_messages/index.haml | 12 ++++ app/views/private_messages/new.haml | 2 +- app/views/private_messages/show.haml | 24 +++++++ .../private_messages_controller_spec.rb | 65 +++++++++++++++++++ spec/models/private_message_spec.rb | 1 - 8 files changed, 147 insertions(+), 4 deletions(-) diff --git a/app/controllers/private_messages_controller.rb b/app/controllers/private_messages_controller.rb index 8e74aa97411..4128faa1461 100644 --- a/app/controllers/private_messages_controller.rb +++ b/app/controllers/private_messages_controller.rb @@ -1,2 +1,38 @@ class PrivateMessagesController < ApplicationController + before_filter :authenticate_user! + + respond_to :html + + def index + @messages = PrivateMessage.joins(:private_message_visibilities).where( + :private_message_visibilities => {:person_id => current_user.person.id}).all + end + + def create + person_ids = Contact.where(:id => params[:private_message][:contact_ids]).map! do |contact| + contact.person_id + end + + person_ids = person_ids | [current_user.person.id] + + @message = PrivateMessage.new( :author => current_user.person, :participant_ids => person_ids, :message => params[:private_message][:message] ) + + if @message.save + Rails.logger.info("event=create type=private_message chars=#{params[:private_message][:message].length}") + end + + respond_with @message + end + + def show + @message = PrivateMessage.joins(:private_message_visibilities).where(:id => params[:id], + :private_message_visibilities => {:person_id => current_user.person.id}).first + + if @message + respond_with @message + else + redirect_to private_messages_path + end + end + end diff --git a/app/controllers/status_messages_controller.rb b/app/controllers/status_messages_controller.rb index 77a6b3e5a50..27806867159 100644 --- a/app/controllers/status_messages_controller.rb +++ b/app/controllers/status_messages_controller.rb @@ -80,7 +80,7 @@ def destroy def show @status_message = current_user.find_visible_post_by_id params[:id] - if @status_message + if @status_messag @object_aspect_ids = @status_message.aspects.map{|a| a.id} # mark corresponding notification as read diff --git a/app/models/private_message.rb b/app/models/private_message.rb index bf8079fe389..899e55784f8 100644 --- a/app/models/private_message.rb +++ b/app/models/private_message.rb @@ -1,5 +1,12 @@ class PrivateMessage < ActiveRecord::Base + include ROXML + include Diaspora::Guid + belongs_to :author, :class_name => 'Person' has_many :private_message_visibilities - has_many :recipients, :class_name => 'Person', :through => :private_message_visibilities, :source => :person + has_many :participants, :class_name => 'Person', :through => :private_message_visibilities, :source => :person + + def recipients + self.participants - [self.author] + end end diff --git a/app/views/private_messages/index.haml b/app/views/private_messages/index.haml index 7fae50376f8..57780c30238 100644 --- a/app/views/private_messages/index.haml +++ b/app/views/private_messages/index.haml @@ -3,3 +3,15 @@ -# the COPYRIGHT file. += link_to 'new message', new_private_message_path + +%br +%br +%br + +- for message in @messages + %b + = message.author.name + %br + = link_to message.message, message + %hr diff --git a/app/views/private_messages/new.haml b/app/views/private_messages/new.haml index ae2a260f6ed..ccc945125b4 100644 --- a/app/views/private_messages/new.haml +++ b/app/views/private_messages/new.haml @@ -9,7 +9,7 @@ = form_for PrivateMessage.new do |private_message| %h4 to - = private_message.text_field :recipients + = text_field_tag "private_message[contact_ids]" %h4 message diff --git a/app/views/private_messages/show.haml b/app/views/private_messages/show.haml index 7fae50376f8..b80ca5e12df 100644 --- a/app/views/private_messages/show.haml +++ b/app/views/private_messages/show.haml @@ -2,4 +2,28 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. += link_to 'back', private_messages_path +%br +%br +%br +%br + +%h4 + from += @message.author.name + +%br +%br + +%h4 + to +- for recipient in @message.recipients + = recipient.name + +%br +%br + +%h4 + message += @message.message diff --git a/spec/controllers/private_messages_controller_spec.rb b/spec/controllers/private_messages_controller_spec.rb index 26bf325218e..4c30bc1ad22 100644 --- a/spec/controllers/private_messages_controller_spec.rb +++ b/spec/controllers/private_messages_controller_spec.rb @@ -1,5 +1,70 @@ require 'spec_helper' describe PrivateMessagesController do + render_views + before do + @user1 = alice + sign_in :user, @user1 + end + + describe '#new' do + it 'succeeds' do + get :new + response.should be_success + end + end + + describe '#index' do + it 'succeeds' do + get :index + response.should be_success + end + + it 'retrieves all messages for a user' do + @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :author => @user1.person, :message => "cool stuff" } + 3.times do + PrivateMessage.create(@create_hash) + end + + get :index + assigns[:messages].count.should == 3 + end + end + + describe '#create' do + it 'creates a private message' do + message_hash = {:private_message => { + :contact_ids => [@user1.contacts.first.id], + :message => "secret stuff"}} + + + lambda { + post :create, message_hash + }.should change(PrivateMessage, :count).by(1) + end + end + + describe '#show' do + before do + @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :author => @user1.person, :message => "cool stuff" } + @message = PrivateMessage.create(@create_hash) + end + + it 'succeeds' do + get :show, :id => @message.id + response.should be_success + assigns[:message].should == @message + end + + it 'does not let you access messages where you are not a recipient' do + user2 = eve + sign_in :user, user2 + + get :show, :id => @message.id + response.code.should == '302' + end + end end diff --git a/spec/models/private_message_spec.rb b/spec/models/private_message_spec.rb index 983209daee2..b37a57fbe44 100644 --- a/spec/models/private_message_spec.rb +++ b/spec/models/private_message_spec.rb @@ -1,5 +1,4 @@ require 'spec_helper' describe PrivateMessage do - pending "add some examples to (or delete) #{__FILE__}" end From 090c412690e7b8b193dec49b5c41cfe6e6fc67c1 Mon Sep 17 00:00:00 2001 From: danielvincent Date: Fri, 25 Feb 2011 15:19:48 -0800 Subject: [PATCH 03/29] touched up AspectMembership resource --- app/controllers/aspect_memberships_controller.rb | 15 ++------------- config/routes.rb | 4 ++-- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/app/controllers/aspect_memberships_controller.rb b/app/controllers/aspect_memberships_controller.rb index c7be3a84f75..9d5c26a1c33 100644 --- a/app/controllers/aspect_memberships_controller.rb +++ b/app/controllers/aspect_memberships_controller.rb @@ -3,20 +3,10 @@ # the COPYRIGHT file. # -class AspectMembershipsController < ApplicationController +class AspectMembershipsController < ApplicationController before_filter :authenticate_user! - def new - render :nothing => true - end - - def index - raise - end - - - - def destroy + def destroy #note :id is garbage @person_id = params[:person_id] @@ -58,7 +48,6 @@ def create @aspect = current_user.aspects.where(:id => params[:aspect_id]).first @contact = current_user.contact_for(@person) - current_user.add_contact_to_aspect(@contact, @aspect) flash.now[:notice] = I18n.t 'aspects.add_to_aspect.success' diff --git a/config/routes.rb b/config/routes.rb index 88e17c18ec3..508e9a5a623 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,7 +24,7 @@ resources :posts, :only => [:show], :path => '/p/' resources :contacts - resources :aspect_memberships + resources :aspect_memberships, :only => [:destroy, :create] resources :people, :except => [:edit, :update] do resources :status_messages @@ -41,7 +41,7 @@ devise_for :users, :controllers => {:registrations => "registrations", :password => "devise/passwords", :invitations => "invitations"} do - + get 'invitations/resend/:id' => 'invitations#resend', :as => 'invitation_resend' end From 979d9d7fb492dfe1516889d5c315fee62e4e1ff0 Mon Sep 17 00:00:00 2001 From: danielvincent Date: Fri, 25 Feb 2011 16:11:19 -0800 Subject: [PATCH 04/29] added delete actions for private messages --- ...private_message_visibilities_controller.rb | 19 ++++++++++ app/views/private_messages/index.haml | 31 ++++++++++------ app/views/private_messages/new.haml | 2 +- app/views/private_messages/show.haml | 9 +++++ config/routes.rb | 6 +++- ...te_message_visibilities_controller_spec.rb | 35 +++++++++++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 app/controllers/private_message_visibilities_controller.rb create mode 100644 spec/controllers/private_message_visibilities_controller_spec.rb diff --git a/app/controllers/private_message_visibilities_controller.rb b/app/controllers/private_message_visibilities_controller.rb new file mode 100644 index 00000000000..706f7a8669c --- /dev/null +++ b/app/controllers/private_message_visibilities_controller.rb @@ -0,0 +1,19 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. +# + +class PrivateMessageVisibilitiesController < ApplicationController + before_filter :authenticate_user! + + def destroy + @vis = PrivateMessageVisibility.where(:person_id => current_user.person.id, + :private_message_id => params[:private_message_id]).first + if @vis + if @vis.destroy + flash[:notice] = "Message successfully removed" + end + end + redirect_to private_messages_path + end +end diff --git a/app/views/private_messages/index.haml b/app/views/private_messages/index.haml index 57780c30238..a2e91d8c126 100644 --- a/app/views/private_messages/index.haml +++ b/app/views/private_messages/index.haml @@ -3,15 +3,26 @@ -# the COPYRIGHT file. -= link_to 'new message', new_private_message_path +.span-12.last -%br -%br -%br + %h2 + .right + = link_to 'new message', new_private_message_path, :class => 'button' + Message Inbox -- for message in @messages - %b - = message.author.name - %br - = link_to message.message, message - %hr + + - if @messages.count > 0 + .stream + - for message in @messages + .stream_element + .right + = link_to image_tag('deletelabel.png'), private_message_private_message_visibility_path(message), :method => 'delete', :confirm => t('are_you_sure') + + .from + = message.author.name + %p + = link_to message.message, message + + - else + %i + You have no messages diff --git a/app/views/private_messages/new.haml b/app/views/private_messages/new.haml index ccc945125b4..8bff8af7ab5 100644 --- a/app/views/private_messages/new.haml +++ b/app/views/private_messages/new.haml @@ -15,5 +15,5 @@ message = private_message.text_area :message, :rows => 5 - = link_to 'cancel', private_messages_path = private_message.submit :send + = link_to 'cancel', private_messages_path diff --git a/app/views/private_messages/show.haml b/app/views/private_messages/show.haml index b80ca5e12df..43d5baecd30 100644 --- a/app/views/private_messages/show.haml +++ b/app/views/private_messages/show.haml @@ -27,3 +27,12 @@ %h4 message = @message.message + +%br +%br +%br +%br + += link_to t('delete'), private_message_private_message_visibility_path(@message), :method => 'delete', :confirm => t('are_you_sure') + + diff --git a/config/routes.rb b/config/routes.rb index 508e9a5a623..881ebec5e64 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,7 +3,7 @@ # the COPYRIGHT file. Diaspora::Application.routes.draw do - resources :private_messages + resources :status_messages, :only => [:create, :destroy, :show] resources :comments, :only => [:create] @@ -26,6 +26,10 @@ resources :contacts resources :aspect_memberships, :only => [:destroy, :create] + resources :private_messages do + resource :private_message_visibility, :only => [:destroy], :path => '/visibility/' + end + resources :people, :except => [:edit, :update] do resources :status_messages resources :photos diff --git a/spec/controllers/private_message_visibilities_controller_spec.rb b/spec/controllers/private_message_visibilities_controller_spec.rb new file mode 100644 index 00000000000..2dc6c21da12 --- /dev/null +++ b/spec/controllers/private_message_visibilities_controller_spec.rb @@ -0,0 +1,35 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require 'spec_helper' + +describe PrivateMessageVisibilitiesController do + render_views + + before do + @user1 = alice + sign_in :user, @user1 + + @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :author => @user1.person, :message => "cool stuff" } + @message = PrivateMessage.create(@create_hash) + end + + describe '#destroy' do + it 'deletes the visibility' do + lambda { + delete :destroy, :private_message_id => @message.id + }.should change(PrivateMessageVisibility, :count).by(-1) + end + + it 'does not let a user destroy a visibility that is not theirs' do + user2 = eve + sign_in :user, user2 + + lambda { + delete :destroy, :private_message_id => @message.id + }.should_not change(PrivateMessageVisibility, :count) + end + end +end From a179bac6891d41579030a62e432c89fd8e3fda84 Mon Sep 17 00:00:00 2001 From: danielvincent Date: Fri, 25 Feb 2011 16:23:54 -0800 Subject: [PATCH 05/29] fixed migrations, added unread to PrivateMessageVisibilities --- app/controllers/status_messages_controller.rb | 4 ++-- .../20110225190919_create_private_messages.rb | 18 ------------------ ...reate_private_messages_and_visibilities.rb} | 14 +++++++++++++- db/schema.rb | 12 ++++-------- .../aspect_memberships_controller_spec.rb | 7 ------- .../status_messages_controller_spec.rb | 4 ++-- 6 files changed, 21 insertions(+), 38 deletions(-) delete mode 100644 db/migrate/20110225190919_create_private_messages.rb rename db/migrate/{20110225193130_create_private_message_visibilities.rb => 20110225190919_create_private_messages_and_visibilities.rb} (56%) diff --git a/app/controllers/status_messages_controller.rb b/app/controllers/status_messages_controller.rb index 27806867159..51e389bf88a 100644 --- a/app/controllers/status_messages_controller.rb +++ b/app/controllers/status_messages_controller.rb @@ -62,7 +62,7 @@ def create else respond_to do |format| format.js { render :json =>{:errors => @status_message.errors.full_messages}, :status => 406 } - format.html {redirect_to :back} + format.html {redirect_to :back} end end end @@ -80,7 +80,7 @@ def destroy def show @status_message = current_user.find_visible_post_by_id params[:id] - if @status_messag + if @status_message @object_aspect_ids = @status_message.aspects.map{|a| a.id} # mark corresponding notification as read diff --git a/db/migrate/20110225190919_create_private_messages.rb b/db/migrate/20110225190919_create_private_messages.rb deleted file mode 100644 index d858aa51aab..00000000000 --- a/db/migrate/20110225190919_create_private_messages.rb +++ /dev/null @@ -1,18 +0,0 @@ -class CreatePrivateMessages < ActiveRecord::Migration - def self.up - create_table :private_messages do |t| - t.integer :author_id, :null => false - t.boolean :unread, :null => false, :default => true - t.string :guid, :null => false - t.text :message, :null => false - - t.timestamps - end - - add_index :private_messages, :author_id - end - - def self.down - drop_table :private_messages - end -end diff --git a/db/migrate/20110225193130_create_private_message_visibilities.rb b/db/migrate/20110225190919_create_private_messages_and_visibilities.rb similarity index 56% rename from db/migrate/20110225193130_create_private_message_visibilities.rb rename to db/migrate/20110225190919_create_private_messages_and_visibilities.rb index 553e8906c6c..f5fce46f3d4 100644 --- a/db/migrate/20110225193130_create_private_message_visibilities.rb +++ b/db/migrate/20110225190919_create_private_messages_and_visibilities.rb @@ -1,8 +1,18 @@ -class CreatePrivateMessageVisibilities < ActiveRecord::Migration +class CreatePrivateMessagesAndVisibilities < ActiveRecord::Migration def self.up + create_table :private_messages do |t| + t.integer :author_id, :null => false + t.string :guid, :null => false + t.text :message, :null => false + + t.timestamps + end + + create_table :private_message_visibilities do |t| t.integer :private_message_id t.integer :person_id + t.boolean :unread, :null => false, :default => true t.timestamps end @@ -10,9 +20,11 @@ def self.up add_index :private_message_visibilities, :person_id add_index :private_message_visibilities, :private_message_id add_index :private_message_visibilities, [:private_message_id, :person_id], :name => 'pm_visibilities_on_pm_id_and_person_id', :unique => true + add_index :private_messages, :author_id end def self.down + drop_table :private_messages drop_table :private_message_visibilities end end diff --git a/db/schema.rb b/db/schema.rb index 4ffd5826908..bc959b9455b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,11 +10,7 @@ # # It's strongly recommended to check this file into your version control system. -<<<<<<< HEAD ActiveRecord::Schema.define(:version => 20110228201109) do -======= -ActiveRecord::Schema.define(:version => 20110225193130) do ->>>>>>> added PrivateMessage and PrivateMessageVisibility models and migrations create_table "aspect_memberships", :force => true do |t| t.integer "aspect_id", :null => false @@ -390,6 +386,7 @@ create_table "private_message_visibilities", :force => true do |t| t.integer "private_message_id" t.integer "person_id" + t.boolean "unread", :default => true, :null => false t.datetime "created_at" t.datetime "updated_at" end @@ -399,10 +396,9 @@ add_index "private_message_visibilities", ["private_message_id"], :name => "index_private_message_visibilities_on_private_message_id" create_table "private_messages", :force => true do |t| - t.integer "author_id", :null => false - t.boolean "unread", :default => true, :null => false - t.string "guid", :null => false - t.text "message", :null => false + t.integer "author_id", :null => false + t.string "guid", :null => false + t.text "message", :null => false t.datetime "created_at" t.datetime "updated_at" end diff --git a/spec/controllers/aspect_memberships_controller_spec.rb b/spec/controllers/aspect_memberships_controller_spec.rb index 007d914b960..f79429dc293 100644 --- a/spec/controllers/aspect_memberships_controller_spec.rb +++ b/spec/controllers/aspect_memberships_controller_spec.rb @@ -23,13 +23,6 @@ request.env["HTTP_REFERER"] = 'http://' + request.host end - describe "#new" do - it 'succeeds' do - get :new - response.should be_success - end - end - describe '#create' do it 'creates an aspect membership' do @user.should_receive(:add_contact_to_aspect) diff --git a/spec/controllers/status_messages_controller_spec.rb b/spec/controllers/status_messages_controller_spec.rb index 77521f13425..4f3afa575b1 100644 --- a/spec/controllers/status_messages_controller_spec.rb +++ b/spec/controllers/status_messages_controller_spec.rb @@ -65,9 +65,9 @@ end it "dispatches the post to the specified services" do - s1 = Services::Facebook.new + s1 = Services::Facebook.new @user1.services << s1 - @user1.services << Services::Twitter.new + @user1.services << Services::Twitter.new status_message_hash[:services] = ['facebook'] @user1.should_receive(:dispatch_post).with(anything(), hash_including(:services => [s1])) post :create, status_message_hash From 1072806d8f9b491b40e97519bdfd8c52e6868d4d Mon Sep 17 00:00:00 2001 From: danielvincent Date: Fri, 25 Feb 2011 19:34:06 -0800 Subject: [PATCH 06/29] PrivateMessage -> Conversation, Message. --- .../conversation_visibilities_controller.rb | 19 +++++ app/controllers/conversations_controller.rb | 41 +++++++++ ...private_message_visibilities_controller.rb | 19 ----- .../private_messages_controller.rb | 38 --------- app/models/conversation.rb | 22 +++++ app/models/conversation_visibility.rb | 6 ++ app/models/message.rb | 12 +++ app/models/private_message.rb | 12 --- app/models/private_message_visibility.rb | 6 -- app/views/conversations/index.haml | 30 +++++++ app/views/conversations/new.haml | 24 ++++++ app/views/conversations/show.haml | 32 +++++++ app/views/private_messages/index.haml | 28 ------ app/views/private_messages/new.haml | 19 ----- app/views/private_messages/show.haml | 38 --------- config/routes.rb | 4 +- ...ersations_and_messages_and_visibilities.rb | 38 +++++++++ ...reate_private_messages_and_visibilities.rb | 30 ------- db/schema.rb | 52 +++++++----- ...versation_visibilities_controller_spec.rb} | 14 +-- .../conversations_controller_spec.rb | 85 +++++++++++++++++++ .../private_messages_controller_spec.rb | 70 --------------- spec/models/conversation_spec.rb | 42 +++++++++ spec/models/conversation_visibility_spec.rb | 19 +++++ spec/models/private_message_spec.rb | 4 - 25 files changed, 409 insertions(+), 295 deletions(-) create mode 100644 app/controllers/conversation_visibilities_controller.rb create mode 100644 app/controllers/conversations_controller.rb delete mode 100644 app/controllers/private_message_visibilities_controller.rb delete mode 100644 app/controllers/private_messages_controller.rb create mode 100644 app/models/conversation.rb create mode 100644 app/models/conversation_visibility.rb create mode 100644 app/models/message.rb delete mode 100644 app/models/private_message.rb delete mode 100644 app/models/private_message_visibility.rb create mode 100644 app/views/conversations/index.haml create mode 100644 app/views/conversations/new.haml create mode 100644 app/views/conversations/show.haml delete mode 100644 app/views/private_messages/index.haml delete mode 100644 app/views/private_messages/new.haml delete mode 100644 app/views/private_messages/show.haml create mode 100644 db/migrate/20110225190919_create_conversations_and_messages_and_visibilities.rb delete mode 100644 db/migrate/20110225190919_create_private_messages_and_visibilities.rb rename spec/controllers/{private_message_visibilities_controller_spec.rb => conversation_visibilities_controller_spec.rb} (59%) create mode 100644 spec/controllers/conversations_controller_spec.rb delete mode 100644 spec/controllers/private_messages_controller_spec.rb create mode 100644 spec/models/conversation_spec.rb create mode 100644 spec/models/conversation_visibility_spec.rb delete mode 100644 spec/models/private_message_spec.rb diff --git a/app/controllers/conversation_visibilities_controller.rb b/app/controllers/conversation_visibilities_controller.rb new file mode 100644 index 00000000000..b136e38be23 --- /dev/null +++ b/app/controllers/conversation_visibilities_controller.rb @@ -0,0 +1,19 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. +# + +class ConversationVisibilitiesController < ApplicationController + before_filter :authenticate_user! + + def destroy + @vis = ConversationVisibility.where(:person_id => current_user.person.id, + :conversation_id => params[:conversation_id]).first + if @vis + if @vis.destroy + flash[:notice] = "Conversation successfully removed" + end + end + redirect_to conversations_path + end +end diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb new file mode 100644 index 00000000000..72825017ab9 --- /dev/null +++ b/app/controllers/conversations_controller.rb @@ -0,0 +1,41 @@ +class ConversationsController < ApplicationController + before_filter :authenticate_user! + + respond_to :html + + def index + @conversations = Conversation.joins(:conversation_visibilities).where( + :conversation_visibilities => {:person_id => current_user.person.id}).all + end + + def create + person_ids = Contact.where(:id => params[:conversation][:contact_ids]).map! do |contact| + contact.person_id + end + + person_ids = person_ids | [current_user.person.id] + + @conversation = Conversation.new(:subject => params[:conversation][:subject], :participant_ids => person_ids) + + if @conversation.save + @message = Message.new(:text => params[:message][:text], :author => current_user.person, :conversation_id => @conversation.id ) + unless @message.save + @conversation.destroy + end + end + + respond_with @conversation + end + + def show + @conversation = Conversation.joins(:conversation_visibilities).where(:id => params[:id], + :conversation_visibilities => {:person_id => current_user.person.id}).first + + if @conversation + respond_with @conversation + else + redirect_to conversations_path + end + end + +end diff --git a/app/controllers/private_message_visibilities_controller.rb b/app/controllers/private_message_visibilities_controller.rb deleted file mode 100644 index 706f7a8669c..00000000000 --- a/app/controllers/private_message_visibilities_controller.rb +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2010, Diaspora Inc. This file is -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -# - -class PrivateMessageVisibilitiesController < ApplicationController - before_filter :authenticate_user! - - def destroy - @vis = PrivateMessageVisibility.where(:person_id => current_user.person.id, - :private_message_id => params[:private_message_id]).first - if @vis - if @vis.destroy - flash[:notice] = "Message successfully removed" - end - end - redirect_to private_messages_path - end -end diff --git a/app/controllers/private_messages_controller.rb b/app/controllers/private_messages_controller.rb deleted file mode 100644 index 4128faa1461..00000000000 --- a/app/controllers/private_messages_controller.rb +++ /dev/null @@ -1,38 +0,0 @@ -class PrivateMessagesController < ApplicationController - before_filter :authenticate_user! - - respond_to :html - - def index - @messages = PrivateMessage.joins(:private_message_visibilities).where( - :private_message_visibilities => {:person_id => current_user.person.id}).all - end - - def create - person_ids = Contact.where(:id => params[:private_message][:contact_ids]).map! do |contact| - contact.person_id - end - - person_ids = person_ids | [current_user.person.id] - - @message = PrivateMessage.new( :author => current_user.person, :participant_ids => person_ids, :message => params[:private_message][:message] ) - - if @message.save - Rails.logger.info("event=create type=private_message chars=#{params[:private_message][:message].length}") - end - - respond_with @message - end - - def show - @message = PrivateMessage.joins(:private_message_visibilities).where(:id => params[:id], - :private_message_visibilities => {:person_id => current_user.person.id}).first - - if @message - respond_with @message - else - redirect_to private_messages_path - end - end - -end diff --git a/app/models/conversation.rb b/app/models/conversation.rb new file mode 100644 index 00000000000..58ab2ab2f71 --- /dev/null +++ b/app/models/conversation.rb @@ -0,0 +1,22 @@ +class Conversation < ActiveRecord::Base + include ROXML + include Diaspora::Guid + include Diaspora::Webhooks + + xml_attr :subject + xml_attr :messages, :as => [Message] + xml_attr :created_at + xml_reader :participant_handles + + has_many :conversation_visibilities + has_many :participants, :class_name => 'Person', :through => :conversation_visibilities, :source => :person + has_many :messages, :order => 'created_at ASC' + + def recipients + self.participants - [self.author] + end + + def participant_handles + self.participants.map{|p| p.diaspora_handle}.join(";") + end +end diff --git a/app/models/conversation_visibility.rb b/app/models/conversation_visibility.rb new file mode 100644 index 00000000000..da398c7244f --- /dev/null +++ b/app/models/conversation_visibility.rb @@ -0,0 +1,6 @@ +class ConversationVisibility < ActiveRecord::Base + + belongs_to :conversation + belongs_to :person + +end diff --git a/app/models/message.rb b/app/models/message.rb new file mode 100644 index 00000000000..53dff93057c --- /dev/null +++ b/app/models/message.rb @@ -0,0 +1,12 @@ +class Message < ActiveRecord::Base + include ROXML + include Diaspora::Guid + include Diaspora::Webhooks + + xml_attr :text + xml_attr :created_at + + belongs_to :author, :class_name => 'Person' + belongs_to :conversation + +end diff --git a/app/models/private_message.rb b/app/models/private_message.rb deleted file mode 100644 index 899e55784f8..00000000000 --- a/app/models/private_message.rb +++ /dev/null @@ -1,12 +0,0 @@ -class PrivateMessage < ActiveRecord::Base - include ROXML - include Diaspora::Guid - - belongs_to :author, :class_name => 'Person' - has_many :private_message_visibilities - has_many :participants, :class_name => 'Person', :through => :private_message_visibilities, :source => :person - - def recipients - self.participants - [self.author] - end -end diff --git a/app/models/private_message_visibility.rb b/app/models/private_message_visibility.rb deleted file mode 100644 index e4ba8f53b9c..00000000000 --- a/app/models/private_message_visibility.rb +++ /dev/null @@ -1,6 +0,0 @@ -class PrivateMessageVisibility < ActiveRecord::Base - - belongs_to :private_message - belongs_to :person - -end diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml new file mode 100644 index 00000000000..71a8167be46 --- /dev/null +++ b/app/views/conversations/index.haml @@ -0,0 +1,30 @@ +-# Copyright (c) 2010, Diaspora Inc. This file is +-# licensed under the Affero General Public License version 3 or later. See +-# the COPYRIGHT file. + + +.span-12.last + + %h2 + .right + = link_to 'new message', new_conversation_path, :class => 'button' + Inbox + + + - if @conversations.count > 0 + .stream + - for conversation in @conversations + .stream_element + .right + = link_to image_tag('deletelabel.png'), conversation_conversation_visibility_path(conversation), :method => 'delete', :confirm => t('are_you_sure') + + .from + = conversation.messages.last.author.name + %p + = link_to conversation.subject, conversation + %p + = link_to conversation.messages.last, conversation + + - else + %i + You have no messages diff --git a/app/views/conversations/new.haml b/app/views/conversations/new.haml new file mode 100644 index 00000000000..5d13de32308 --- /dev/null +++ b/app/views/conversations/new.haml @@ -0,0 +1,24 @@ +-# Copyright (c) 2010, Diaspora Inc. This file is +-# licensed under the Affero General Public License version 3 or later. See +-# the COPYRIGHT file. + + +%h2 + New Message + += form_for Conversation.new do |conversation| + %h4 + to + = text_field_tag "private_message[contact_ids]" + + %h4 + subject + = conversation.text_field :subject + + = fields_for :message do |message| + %h4 + message + = message.text_area :text, :rows => 5 + + = conversation.submit :send + = link_to 'cancel', conversations_path diff --git a/app/views/conversations/show.haml b/app/views/conversations/show.haml new file mode 100644 index 00000000000..99a8989fa11 --- /dev/null +++ b/app/views/conversations/show.haml @@ -0,0 +1,32 @@ +-# Copyright (c) 2010, Diaspora Inc. This file is +-# licensed under the Affero General Public License version 3 or later. See +-# the COPYRIGHT file. + += link_to 'back', conversations_path + +%br +%br +%br +%br + + +- for participant in @conversation.participants + = participant.name + +- for message in @conversation.messages + %h4 + from + = message.author.name + + %br + %br + + %h4 + message + = message.text + + %hr + += link_to t('delete'), conversation_conversation_visibility_path(@conversation), :method => 'delete', :confirm => t('are_you_sure') + + diff --git a/app/views/private_messages/index.haml b/app/views/private_messages/index.haml deleted file mode 100644 index a2e91d8c126..00000000000 --- a/app/views/private_messages/index.haml +++ /dev/null @@ -1,28 +0,0 @@ --# Copyright (c) 2010, Diaspora Inc. This file is --# licensed under the Affero General Public License version 3 or later. See --# the COPYRIGHT file. - - -.span-12.last - - %h2 - .right - = link_to 'new message', new_private_message_path, :class => 'button' - Message Inbox - - - - if @messages.count > 0 - .stream - - for message in @messages - .stream_element - .right - = link_to image_tag('deletelabel.png'), private_message_private_message_visibility_path(message), :method => 'delete', :confirm => t('are_you_sure') - - .from - = message.author.name - %p - = link_to message.message, message - - - else - %i - You have no messages diff --git a/app/views/private_messages/new.haml b/app/views/private_messages/new.haml deleted file mode 100644 index 8bff8af7ab5..00000000000 --- a/app/views/private_messages/new.haml +++ /dev/null @@ -1,19 +0,0 @@ --# Copyright (c) 2010, Diaspora Inc. This file is --# licensed under the Affero General Public License version 3 or later. See --# the COPYRIGHT file. - - -%h2 - New Message - -= form_for PrivateMessage.new do |private_message| - %h4 - to - = text_field_tag "private_message[contact_ids]" - - %h4 - message - = private_message.text_area :message, :rows => 5 - - = private_message.submit :send - = link_to 'cancel', private_messages_path diff --git a/app/views/private_messages/show.haml b/app/views/private_messages/show.haml deleted file mode 100644 index 43d5baecd30..00000000000 --- a/app/views/private_messages/show.haml +++ /dev/null @@ -1,38 +0,0 @@ --# Copyright (c) 2010, Diaspora Inc. This file is --# licensed under the Affero General Public License version 3 or later. See --# the COPYRIGHT file. - -= link_to 'back', private_messages_path - -%br -%br -%br -%br - -%h4 - from -= @message.author.name - -%br -%br - -%h4 - to -- for recipient in @message.recipients - = recipient.name - -%br -%br - -%h4 - message -= @message.message - -%br -%br -%br -%br - -= link_to t('delete'), private_message_private_message_visibility_path(@message), :method => 'delete', :confirm => t('are_you_sure') - - diff --git a/config/routes.rb b/config/routes.rb index 881ebec5e64..6d82982441c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -26,8 +26,8 @@ resources :contacts resources :aspect_memberships, :only => [:destroy, :create] - resources :private_messages do - resource :private_message_visibility, :only => [:destroy], :path => '/visibility/' + resources :conversations do + resource :conversation_visibility, :only => [:destroy], :path => '/visibility/' end resources :people, :except => [:edit, :update] do diff --git a/db/migrate/20110225190919_create_conversations_and_messages_and_visibilities.rb b/db/migrate/20110225190919_create_conversations_and_messages_and_visibilities.rb new file mode 100644 index 00000000000..397ded1f3c8 --- /dev/null +++ b/db/migrate/20110225190919_create_conversations_and_messages_and_visibilities.rb @@ -0,0 +1,38 @@ +class CreateConversationsAndMessagesAndVisibilities < ActiveRecord::Migration + def self.up + create_table :messages do |t| + t.integer :conversation_id, :null => false + t.integer :author_id, :null => false + t.string :guid, :null => false + t.text :text, :null => false + + t.timestamps + end + + create_table :conversation_visibilities do |t| + t.integer :conversation_id, :null => false + t.integer :person_id, :null => false + t.integer :unread, :null => false, :default => 0 + + t.timestamps + end + + create_table :conversations do |t| + t.string :subject + t.string :guid, :null => false + + t.timestamps + end + + add_index :conversation_visibilities, :person_id + add_index :conversation_visibilities, :conversation_id + add_index :conversation_visibilities, [:conversation_id, :person_id], :unique => true + add_index :messages, :author_id + end + + def self.down + drop_table :messages + drop_table :conversations + drop_table :conversation_visibilities + end +end diff --git a/db/migrate/20110225190919_create_private_messages_and_visibilities.rb b/db/migrate/20110225190919_create_private_messages_and_visibilities.rb deleted file mode 100644 index f5fce46f3d4..00000000000 --- a/db/migrate/20110225190919_create_private_messages_and_visibilities.rb +++ /dev/null @@ -1,30 +0,0 @@ -class CreatePrivateMessagesAndVisibilities < ActiveRecord::Migration - def self.up - create_table :private_messages do |t| - t.integer :author_id, :null => false - t.string :guid, :null => false - t.text :message, :null => false - - t.timestamps - end - - - create_table :private_message_visibilities do |t| - t.integer :private_message_id - t.integer :person_id - t.boolean :unread, :null => false, :default => true - - t.timestamps - end - - add_index :private_message_visibilities, :person_id - add_index :private_message_visibilities, :private_message_id - add_index :private_message_visibilities, [:private_message_id, :person_id], :name => 'pm_visibilities_on_pm_id_and_person_id', :unique => true - add_index :private_messages, :author_id - end - - def self.down - drop_table :private_messages - drop_table :private_message_visibilities - end -end diff --git a/db/schema.rb b/db/schema.rb index bc959b9455b..adea1e3a48a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -70,6 +70,25 @@ add_index "contacts", ["user_id", "pending"], :name => "index_contacts_on_user_id_and_pending" add_index "contacts", ["user_id", "person_id"], :name => "index_contacts_on_user_id_and_person_id", :unique => true + create_table "conversation_visibilities", :force => true do |t| + t.integer "conversation_id", :null => false + t.integer "person_id", :null => false + t.integer "unread", :default => 0, :null => false + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "conversation_visibilities", ["conversation_id", "person_id"], :name => "index_conversation_visibilities_on_conversation_id_and_person_id", :unique => true + add_index "conversation_visibilities", ["conversation_id"], :name => "index_conversation_visibilities_on_conversation_id" + add_index "conversation_visibilities", ["person_id"], :name => "index_conversation_visibilities_on_person_id" + + create_table "conversations", :force => true do |t| + t.string "subject" + t.string "guid", :null => false + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "data_points", :force => true do |t| t.string "key", :null => false t.integer "value", :null => false @@ -104,6 +123,17 @@ add_index "mentions", ["person_id"], :name => "index_mentions_on_person_id" add_index "mentions", ["post_id"], :name => "index_mentions_on_post_id" + create_table "messages", :force => true do |t| + t.integer "conversation_id", :null => false + t.integer "author_id", :null => false + t.string "guid", :null => false + t.text "text", :null => false + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "messages", ["author_id"], :name => "index_messages_on_author_id" + create_table "mongo_aspect_memberships", :force => true do |t| t.string "aspect_mongo_id" t.string "contact_mongo_id" @@ -383,28 +413,6 @@ add_index "posts", ["type", "pending", "id"], :name => "index_posts_on_type_and_pending_and_id" add_index "posts", ["type"], :name => "index_posts_on_type" - create_table "private_message_visibilities", :force => true do |t| - t.integer "private_message_id" - t.integer "person_id" - t.boolean "unread", :default => true, :null => false - t.datetime "created_at" - t.datetime "updated_at" - end - - add_index "private_message_visibilities", ["person_id"], :name => "index_private_message_visibilities_on_person_id" - add_index "private_message_visibilities", ["private_message_id", "person_id"], :name => "pm_visibilities_on_pm_id_and_person_id", :unique => true - add_index "private_message_visibilities", ["private_message_id"], :name => "index_private_message_visibilities_on_private_message_id" - - create_table "private_messages", :force => true do |t| - t.integer "author_id", :null => false - t.string "guid", :null => false - t.text "message", :null => false - t.datetime "created_at" - t.datetime "updated_at" - end - - add_index "private_messages", ["author_id"], :name => "index_private_messages_on_author_id" - create_table "profiles", :force => true do |t| t.string "diaspora_handle" t.string "first_name", :limit => 127 diff --git a/spec/controllers/private_message_visibilities_controller_spec.rb b/spec/controllers/conversation_visibilities_controller_spec.rb similarity index 59% rename from spec/controllers/private_message_visibilities_controller_spec.rb rename to spec/controllers/conversation_visibilities_controller_spec.rb index 2dc6c21da12..c2458ec05f9 100644 --- a/spec/controllers/private_message_visibilities_controller_spec.rb +++ b/spec/controllers/conversation_visibilities_controller_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' -describe PrivateMessageVisibilitiesController do +describe ConversationVisibilitiesController do render_views before do @@ -12,15 +12,15 @@ sign_in :user, @user1 @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], - :author => @user1.person, :message => "cool stuff" } - @message = PrivateMessage.create(@create_hash) + :subject => "cool stuff" } + @conversation = Conversation.create(@create_hash) end describe '#destroy' do it 'deletes the visibility' do lambda { - delete :destroy, :private_message_id => @message.id - }.should change(PrivateMessageVisibility, :count).by(-1) + delete :destroy, :conversation_id => @conversation.id + }.should change(ConversationVisibility, :count).by(-1) end it 'does not let a user destroy a visibility that is not theirs' do @@ -28,8 +28,8 @@ sign_in :user, user2 lambda { - delete :destroy, :private_message_id => @message.id - }.should_not change(PrivateMessageVisibility, :count) + delete :destroy, :conversation_id => @conversation.id + }.should_not change(ConversationVisibility, :count) end end end diff --git a/spec/controllers/conversations_controller_spec.rb b/spec/controllers/conversations_controller_spec.rb new file mode 100644 index 00000000000..0dce62b9dc8 --- /dev/null +++ b/spec/controllers/conversations_controller_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' + +describe ConversationsController do + render_views + + before do + @user1 = alice + sign_in :user, @user1 + end + + describe '#new' do + it 'succeeds' do + get :new + response.should be_success + end + end + + describe '#index' do + it 'succeeds' do + get :index + response.should be_success + end + + it 'retrieves all messages for a user' do + @conversation_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :subject => 'not spam' } + @message_hash = {:author => @user1.person, :text => 'cool stuff'} + + 3.times do + cnv = Conversation.create(@conversation_hash) + Message.create(@message_hash.merge({:conversation_id => cnv.id})) + end + + get :index + assigns[:conversations].count.should == 3 + end + end + + describe '#create' do + before do + @message_hash = {:conversation => { + :contact_ids => [@user1.contacts.first.id], + :subject => "secret stuff"}, + :message => {:text => "text"} + } + end + + it 'creates a conversation' do + lambda { + post :create, @message_hash + }.should change(Conversation, :count).by(1) + end + + it 'creates a message' do + lambda { + post :create, @message_hash + }.should change(Message, :count).by(1) + end + end + + describe '#show' do + before do + conversation_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :subject => 'not spam' } + message_hash = {:author => @user1.person, :text => 'cool stuff'} + + @conversation = Conversation.create(conversation_hash) + @message = Message.create(message_hash.merge({:conversation_id => @conversation.id})) + end + + it 'succeeds' do + get :show, :id => @conversation.id + response.should be_success + assigns[:conversation].should == @conversation + end + + it 'does not let you access conversations where you are not a recipient' do + user2 = eve + sign_in :user, user2 + + get :show, :id => @conversation.id + response.code.should == '302' + end + end +end diff --git a/spec/controllers/private_messages_controller_spec.rb b/spec/controllers/private_messages_controller_spec.rb deleted file mode 100644 index 4c30bc1ad22..00000000000 --- a/spec/controllers/private_messages_controller_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -require 'spec_helper' - -describe PrivateMessagesController do - render_views - - before do - @user1 = alice - sign_in :user, @user1 - end - - describe '#new' do - it 'succeeds' do - get :new - response.should be_success - end - end - - describe '#index' do - it 'succeeds' do - get :index - response.should be_success - end - - it 'retrieves all messages for a user' do - @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], - :author => @user1.person, :message => "cool stuff" } - 3.times do - PrivateMessage.create(@create_hash) - end - - get :index - assigns[:messages].count.should == 3 - end - end - - describe '#create' do - it 'creates a private message' do - message_hash = {:private_message => { - :contact_ids => [@user1.contacts.first.id], - :message => "secret stuff"}} - - - lambda { - post :create, message_hash - }.should change(PrivateMessage, :count).by(1) - end - end - - describe '#show' do - before do - @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], - :author => @user1.person, :message => "cool stuff" } - @message = PrivateMessage.create(@create_hash) - end - - it 'succeeds' do - get :show, :id => @message.id - response.should be_success - assigns[:message].should == @message - end - - it 'does not let you access messages where you are not a recipient' do - user2 = eve - sign_in :user, user2 - - get :show, :id => @message.id - response.code.should == '302' - end - end -end diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb new file mode 100644 index 00000000000..f595adf8d22 --- /dev/null +++ b/spec/models/conversation_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe Conversation do + before do + @user1 = alice + end + + describe 'serialization' do + before do + @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :subject => "cool stuff" } + @cnv = Conversation.new(@create_hash) + @message = Message.new(:author => @user1.person, :text => "stuff") + @cnv.messages << @message + @xml = @cnv.to_diaspora_xml + end + + it 'serializes the message' do + @xml.gsub(/\s/, '').should include(@message.to_xml.to_s.gsub(/\s/, '')) + end + + it 'serializes the participants' do + @create_hash[:participant_ids].each{|id| + @xml.should include(Person.find(id).diaspora_handle) + } + end + + it 'serializes the created_at time' do + @xml.should include(@message.created_at.to_s) + end + end + + describe "#subscribers?" do + it 'returns the recipients for the post owner' do + + end + + it 'returns the author for any other user' do + + end + end +end diff --git a/spec/models/conversation_visibility_spec.rb b/spec/models/conversation_visibility_spec.rb new file mode 100644 index 00000000000..691b61b0e09 --- /dev/null +++ b/spec/models/conversation_visibility_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe ConversationVisibility do + before do + @user = alice + @aspect = @user.aspects.create(:name => 'Boozers') + + @person = Factory(:person) + @post = Factory(:status_message, :person => @person) + end + it 'has an aspect' do + pv = PostVisibility.new(:aspect => @aspect) + pv.aspect.should == @aspect + end + it 'has a post' do + pv = PostVisibility.new(:post => @post) + pv.post.should == @post + end +end diff --git a/spec/models/private_message_spec.rb b/spec/models/private_message_spec.rb deleted file mode 100644 index b37a57fbe44..00000000000 --- a/spec/models/private_message_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'spec_helper' - -describe PrivateMessage do -end From bd908a9b9514f10b71974c77945f6411bd0eb1e1 Mon Sep 17 00:00:00 2001 From: zhitomirskiyi Date: Sat, 26 Feb 2011 20:06:23 -0800 Subject: [PATCH 07/29] conversations federate --- app/models/conversation.rb | 24 +++++++++++++++ app/models/message.rb | 31 +++++++++++++++++++ spec/models/conversation_spec.rb | 47 ++++++++++++++++++++-------- spec/models/message_spec.rb | 53 ++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 spec/models/message_spec.rb diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 58ab2ab2f71..27f375af2ce 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -12,6 +12,10 @@ class Conversation < ActiveRecord::Base has_many :participants, :class_name => 'Person', :through => :conversation_visibilities, :source => :person has_many :messages, :order => 'created_at ASC' + def author + self.messages.first.author + end + def recipients self.participants - [self.author] end @@ -19,4 +23,24 @@ def recipients def participant_handles self.participants.map{|p| p.diaspora_handle}.join(";") end + + def participant_handles= handles + handles.split(';').each do |handle| + self.participants << Webfinger.new(handle).fetch + end + end + def subscribers(user) + self.recipients + end + + def receive(user, person) + cnv = Conversation.find_or_create_by_guid(self.attributes) + self.messages.each do |msg| + msg.conversation_id = cnv.id + msg.receive(user, person) + end + self.participants.each do |participant| + ConversationVisibility.create(:conversation_id => cnv.id, :person_id => participant.id) + end + end end diff --git a/app/models/message.rb b/app/models/message.rb index 53dff93057c..b02765165c1 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -5,8 +5,39 @@ class Message < ActiveRecord::Base xml_attr :text xml_attr :created_at + xml_reader :diaspora_handle + xml_reader :conversation_guid belongs_to :author, :class_name => 'Person' belongs_to :conversation + def diaspora_handle + self.author.diaspora_handle + end + + def diaspora_handle= nh + self.author = Webfinger.new(nh).fetch + end + + def conversation_guid + self.conversation.guid + end + + def conversation_guid= guid + if cnv = Conversation.find_by_guid(guid) + self.conversation_id = cnv.id + end + end + + def receive(user, person) + Message.find_or_create_by_guid(self.attributes) + end + + def subscribers(user) + if self.conversation.author == user.person + p = self.conversation.subscribers(user) + else + p = self.conversation.author + end + end end diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb index f595adf8d22..51683a96172 100644 --- a/spec/models/conversation_spec.rb +++ b/spec/models/conversation_spec.rb @@ -3,18 +3,17 @@ describe Conversation do before do @user1 = alice + @user2 = bob + + @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff" } + @cnv = Conversation.create(@create_hash) + @message = Message.new(:author => @user1.person, :text => "stuff") + @cnv.messages << @message + @message.save + @xml = @cnv.to_diaspora_xml end describe 'serialization' do - before do - @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], - :subject => "cool stuff" } - @cnv = Conversation.new(@create_hash) - @message = Message.new(:author => @user1.person, :text => "stuff") - @cnv.messages << @message - @xml = @cnv.to_diaspora_xml - end - it 'serializes the message' do @xml.gsub(/\s/, '').should include(@message.to_xml.to_s.gsub(/\s/, '')) end @@ -30,13 +29,35 @@ end end - describe "#subscribers?" do + describe '#subscribers' do it 'returns the recipients for the post owner' do - + @cnv.subscribers(@user1).should == @user1.contacts.map{|c| c.person} + end + end + + describe '#receive' do + before do + Conversation.delete_all + Message.delete_all end - it 'returns the author for any other user' do - + it 'creates a message' do + lambda{ + Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) + }.should change(Message, :count).by(1) + end + it 'creates a conversation' do + lambda{ + Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) + }.should change(Conversation, :count).by(1) + end + it 'creates appropriate visibilities' do + lambda{ + Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) + }.should change(ConversationVisibility, :count).by(@cnv.participants.count) + end + it 'does not save before receive' do + Diaspora::Parser.from_xml(@xml).persisted?.should be_false end end end diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb new file mode 100644 index 00000000000..5c5e11f585a --- /dev/null +++ b/spec/models/message_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe Message do + before do + @user1 = alice + @user2 = bob + + @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff" } + @cnv = Conversation.create(@create_hash) + @message = Message.new(:author => @user1.person, :text => "stuff") + @cnv.messages << @message + @message.save + @xml = @message.to_diaspora_xml + end + + describe 'serialization' do + it 'serializes the text' do + @xml.should include(@message.text) + end + + it 'serializes the author_handle' do + @xml.should include(@message.author.diaspora_handle) + end + + it 'serializes the created_at time' do + @xml.should include(@message.created_at.to_s) + end + it 'serializes the conversation_guid time' do + @xml.should include(@message.conversation.guid) + end + end + + describe '#subscribers' do + it 'returns the recipients for the post owner' do + @message.subscribers(@user1).should == @user1.contacts.map{|c| c.person} + end + it 'returns the conversation author for the post owner' do + @message.subscribers(@user2).should == @user1.person + end + end + + describe '#receive' do + before do + Message.delete_all + end + + it 'creates a message' do + lambda{ + Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) + }.should change(Message, :count).by(1) + end + end +end From f4e6d0d82b1414c4e2806c42952fd870baaa09da Mon Sep 17 00:00:00 2001 From: danielvincent Date: Mon, 28 Feb 2011 15:23:24 -0800 Subject: [PATCH 08/29] broke out some comment logic to a replayable module --- app/controllers/comments_controller.rb | 3 +- app/models/comment.rb | 75 +----- app/models/post.rb | 1 - app/models/user.rb | 14 +- ...me_post_to_parent_and_creator_to_author.rb | 11 + db/schema.rb | 14 +- lib/diaspora/relayable.rb | 125 ++++++++++ lib/encryptable.rb | 39 ---- spec/intergration/receiving_spec.rb | 2 +- spec/lib/diaspora/relayable_spec.rb | 58 +++++ spec/misc_spec.rb | 13 ++ spec/models/comment_spec.rb | 215 +++++++----------- spec/models/user/commenting_spec.rb | 43 ---- spec/support/user_methods.rb | 6 +- 14 files changed, 317 insertions(+), 302 deletions(-) create mode 100644 db/migrate/20110228220810_rename_post_to_parent_and_creator_to_author.rb create mode 100644 lib/diaspora/relayable.rb delete mode 100644 lib/encryptable.rb create mode 100644 spec/lib/diaspora/relayable_spec.rb delete mode 100644 spec/models/user/commenting_spec.rb diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 0409f31fff3..6e2ac8f5e40 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -18,8 +18,7 @@ def create if @comment.save Rails.logger.info("event=create type=comment user=#{current_user.diaspora_handle} status=success comment=#{@comment.id} chars=#{params[:text].length}") - - current_user.dispatch_comment(@comment) + Postzord::Dispatch.new(current_user, @comment).post respond_to do |format| format.js{ diff --git a/app/models/comment.rb b/app/models/comment.rb index 33489063232..1fdc4787673 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -7,16 +7,15 @@ class Comment < ActiveRecord::Base require File.join(Rails.root, 'lib/youtube_titles') include YoutubeTitles include ROXML + include Diaspora::Webhooks - include Encryptable - include Diaspora::Socketable + include Diaspora::Relayable include Diaspora::Guid + include Diaspora::Socketable + xml_attr :text xml_attr :diaspora_handle - xml_attr :post_guid - xml_attr :creator_signature - xml_attr :post_creator_signature belongs_to :post, :touch => true belongs_to :person @@ -35,12 +34,6 @@ def diaspora_handle def diaspora_handle= nh self.person = Webfinger.new(nh).fetch end - def post_guid - self.post.guid - end - def post_guid= new_post_guid - self.post = Post.where(:guid => new_post_guid).first - end def notification_type(user, person) if self.post.person == user.person @@ -52,63 +45,15 @@ def notification_type(user, person) end end - def subscribers(user) - if user.owns?(self.post) - p = self.post.subscribers(user) - elsif user.owns?(self) - p = [self.post.person] - end - p + def parent_class + Post end - def receive(user, person) - local_comment = Comment.where(:guid => self.guid).first - comment = local_comment || self - - unless comment.post.person == user.person || comment.verify_post_creator_signature - Rails.logger.info("event=receive status=abort reason='comment signature not valid' recipient=#{user.diaspora_handle} sender=#{self.post.person.diaspora_handle} payload_type=#{self.class} post_id=#{self.post_id}") - return - end - - #sign comment as the post creator if you've been hit UPSTREAM - if user.owns? comment.post - comment.post_creator_signature = comment.sign_with_key(user.encryption_key) - comment.save - end - - #dispatch comment DOWNSTREAM, received it via UPSTREAM - unless user.owns?(comment) - comment.save - user.dispatch_comment(comment) - end - - comment.socket_to_user(user, :aspect_ids => comment.post.aspect_ids) - comment - end - - #ENCRYPTION - - - def signable_accessors - accessors = self.class.roxml_attrs.collect{|definition| - definition.accessor} - accessors.delete 'person' - accessors.delete 'creator_signature' - accessors.delete 'post_creator_signature' - accessors + def parent + self.post end - def signable_string - signable_accessors.collect{|accessor| - (self.send accessor.to_sym).to_s}.join ';' + def parent= parent + self.post = parent end - - def verify_post_creator_signature - verify_signature(post_creator_signature, post.person) - end - - def signature_valid? - verify_signature(creator_signature, person) - end - end diff --git a/app/models/post.rb b/app/models/post.rb index dfff551f596..180e444d85d 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -3,7 +3,6 @@ # the COPYRIGHT file. class Post < ActiveRecord::Base - require File.join(Rails.root, 'lib/encryptable') require File.join(Rails.root, 'lib/diaspora/web_socket') include ApplicationHelper include ROXML diff --git a/app/models/user.rb b/app/models/user.rb index c923a5856a6..0ef27da51d5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -128,8 +128,7 @@ def aspects_from_ids(aspect_ids) end def salmon(post) - created_salmon = Salmon::SalmonSlap.create(self, post.to_diaspora_xml) - created_salmon + Salmon::SalmonSlap.create(self, post.to_diaspora_xml) end ######## Commenting ######## @@ -139,21 +138,16 @@ def build_comment(text, options = {}) :post => options[:on]) comment.set_guid #sign comment as commenter - comment.creator_signature = comment.sign_with_key(self.encryption_key) + comment.author_signature = comment.sign_with_key(self.encryption_key) - if !comment.post_id.blank? && person.owns?(comment.post) + if !comment.post_id.blank? && person.owns?(comment.parent) #sign comment as post owner - comment.post_creator_signature = comment.sign_with_key(self.encryption_key) + comment.parent_author_signature = comment.sign_with_key(self.encryption_key) end comment end - def dispatch_comment(comment) - mailman = Postzord::Dispatch.new(self, comment) - mailman.post - end - ######### Mailer ####################### def mail(job, *args) unless self.disable_mail diff --git a/db/migrate/20110228220810_rename_post_to_parent_and_creator_to_author.rb b/db/migrate/20110228220810_rename_post_to_parent_and_creator_to_author.rb new file mode 100644 index 00000000000..ef43ad57a5d --- /dev/null +++ b/db/migrate/20110228220810_rename_post_to_parent_and_creator_to_author.rb @@ -0,0 +1,11 @@ +class RenamePostToParentAndCreatorToAuthor < ActiveRecord::Migration + def self.up + rename_column :comments, :creator_signature, :author_signature + rename_column :comments, :post_creator_signature, :parent_author_signature + end + + def self.down + rename_column :comments, :author_signature, :creator_signature + rename_column :comments, :parent_author_signature, :post_creator_signature + end +end diff --git a/db/schema.rb b/db/schema.rb index adea1e3a48a..adb41d90ad6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20110228201109) do +ActiveRecord::Schema.define(:version => 20110228220810) do create_table "aspect_memberships", :force => true do |t| t.integer "aspect_id", :null => false @@ -39,12 +39,12 @@ add_index "aspects", ["user_id"], :name => "index_aspects_on_user_id" create_table "comments", :force => true do |t| - t.text "text", :null => false - t.integer "post_id", :null => false - t.integer "person_id", :null => false - t.string "guid", :null => false - t.text "creator_signature" - t.text "post_creator_signature" + t.text "text", :null => false + t.integer "post_id", :null => false + t.integer "person_id", :null => false + t.string "guid", :null => false + t.text "author_signature" + t.text "parent_author_signature" t.text "youtube_titles" t.datetime "created_at" t.datetime "updated_at" diff --git a/lib/diaspora/relayable.rb b/lib/diaspora/relayable.rb new file mode 100644 index 00000000000..2db59176174 --- /dev/null +++ b/lib/diaspora/relayable.rb @@ -0,0 +1,125 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +module Diaspora + module Relayable + + def self.included(model) + model.class_eval do + #these fields must be in the schema for a relayable model + xml_attr :parent_guid + xml_attr :parent_author_signature + xml_attr :author_signature + end + end + + def parent_guid + self.parent.guid + end + def parent_guid= new_parent_guid + self.parent = parent_class.where(:guid => new_parent_guid).first + end + + def subscribers(user) + if user.owns?(self.parent) + self.parent.subscribers(user) + elsif user.owns?(self) + [self.parent.person] + end + end + + def receive(user, person) + object = self.class.where(:guid => self.guid).first || self + + unless object.parent.person == user.person || object.verify_parent_author_signature + Rails.logger.info("event=receive status=abort reason='object signature not valid' recipient=#{user.diaspora_handle} sender=#{self.parent.person.diaspora_handle} payload_type=#{self.class} parent_id=#{self.parent.id}") + return + end + + #sign object as the parent creator if you've been hit UPSTREAM + if user.owns? object.parent + object.parent_author_signature = object.sign_with_key(user.encryption_key) + object.save + end + + #dispatch object DOWNSTREAM, received it via UPSTREAM + unless user.owns?(object) + object.save + Postzord::Dispatch.new(user, object).post + end + + object.socket_to_user(user, :aspect_ids => object.parent.aspect_ids) + object + end + + def signable_string + raise NotImplementedException("Override this in your encryptable class") + end + + def signature_valid? + verify_signature(creator_signature, person) + end + + def verify_signature(signature, person) + if person.nil? + Rails.logger.info("event=verify_signature status=abort reason=no_person guid=#{self.guid} model_id=#{self.id}") + return false + elsif person.public_key.nil? + Rails.logger.info("event=verify_signature status=abort reason=no_key guid=#{self.guid} model_id=#{self.id}") + return false + elsif signature.nil? + Rails.logger.info("event=verify_signature status=abort reason=no_signature guid=#{self.guid} model_id=#{self.id}") + return false + end + log_string = "event=verify_signature status=complete model_id=#{id}" + validity = person.public_key.verify "SHA", Base64.decode64(signature), signable_string + log_string += " validity=#{validity}" + Rails.logger.info(log_string) + validity + end + + def sign_with_key(key) + sig = Base64.encode64(key.sign "SHA", signable_string) + Rails.logger.info("event=sign_with_key status=complete model_id=#{id}") + sig + end + + def signable_accessors + accessors = self.class.roxml_attrs.collect do |definition| + definition.accessor + end + ['person', 'author_signature', 'parent_author_signature'].each do |acc| + accessors.delete acc + end + accessors + end + + def signable_string + signable_accessors.collect{ |accessor| + (self.send accessor.to_sym).to_s + }.join(';') + end + + def verify_parent_author_signature + verify_signature(self.parent_author_signature, parent.person) + end + + def signature_valid? + verify_signature(self.author_signature, person) + end + + + def parent_class + raise NotImplementedError.new('you must override parent_class in order to enable relayable on this model') + end + + def parent + raise NotImplementedError.new('you must override parent in order to enable relayable on this model') + end + + def parent= parent + raise NotImplementedError.new('you must override parent= in order to enable relayable on this model') + end + end +end diff --git a/lib/encryptable.rb b/lib/encryptable.rb deleted file mode 100644 index 06cb31014c0..00000000000 --- a/lib/encryptable.rb +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2010, Diaspora Inc. This file is -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. - -module Encryptable - def signable_string - raise NotImplementedException("Override this in your encryptable class") - end - - def signature_valid? - verify_signature(creator_signature, person) - end - - def verify_signature(signature, person) - if person.nil? - Rails.logger.info("event=verify_signature status=abort reason=no_person guid=#{self.guid} model_id=#{self.id}") - return false - elsif person.public_key.nil? - Rails.logger.info("event=verify_signature status=abort reason=no_key guid=#{self.guid} model_id=#{self.id}") - return false - elsif signature.nil? - Rails.logger.info("event=verify_signature status=abort reason=no_signature guid=#{self.guid} model_id=#{self.id}") - return false - end - log_string = "event=verify_signature status=complete model_id=#{id}" - validity = person.public_key.verify "SHA", Base64.decode64(signature), signable_string - log_string += " validity=#{validity}" - Rails.logger.info(log_string) - validity - end - - def sign_with_key(key) - sig = Base64.encode64(key.sign "SHA", signable_string) - Rails.logger.info("event=sign_with_key status=complete model_id=#{id}") - sig - end - -end - diff --git a/spec/intergration/receiving_spec.rb b/spec/intergration/receiving_spec.rb index 81fbc1ddbe9..5c0b52b03d0 100644 --- a/spec/intergration/receiving_spec.rb +++ b/spec/intergration/receiving_spec.rb @@ -186,7 +186,7 @@ def receive_with_zord(user, person, xml) receive_with_zord(@user3, @user1.person, xml) @comment = @user3.comment('tada',:on => @post) - @comment.post_creator_signature = @comment.sign_with_key(@user1.encryption_key) + @comment.parent_author_signature = @comment.sign_with_key(@user1.encryption_key) @xml = @comment.to_diaspora_xml @comment.delete end diff --git a/spec/lib/diaspora/relayable_spec.rb b/spec/lib/diaspora/relayable_spec.rb new file mode 100644 index 00000000000..450990b9c14 --- /dev/null +++ b/spec/lib/diaspora/relayable_spec.rb @@ -0,0 +1,58 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require 'spec_helper' + +describe Diaspora::Relayable do + + before do + @alices_aspect = alice.aspects.first + @bobs_aspect = bob.aspects.first + @remote_message = bob.post :status_message, :message => "hello", :to => @bobs_aspect.id + @message = alice.post :status_message, :message => "hi", :to => @alices_aspect.id + end + + describe '#parent_author_signature' do + it 'should sign the comment if the user is the post author' do + message = alice.post :status_message, :message => "hi", :to => @alices_aspect.id + alice.comment "Yeah, it was great", :on => message + message.comments.reset + message.comments.first.signature_valid?.should be_true + message.comments.first.verify_parent_author_signature.should be_true + end + + it 'should verify a comment made on a remote post by a different contact' do + comment = Comment.new(:person => bob.person, :text => "cats", :post => @remote_message) + comment.author_signature = comment.send(:sign_with_key, bob.encryption_key) + comment.signature_valid?.should be_true + comment.verify_parent_author_signature.should be_false + comment.parent_author_signature = comment.send(:sign_with_key, alice.encryption_key) + comment.verify_parent_author_signature.should be_true + end + end + + describe '#author_signature' do + it 'should attach the author signature if the user is commenting' do + comment = alice.comment "Yeah, it was great", :on => @remote_message + @remote_message.comments.reset + @remote_message.comments.first.signature_valid?.should be_true + end + + it 'should reject comments on a remote post with only a author sig' do + comment = Comment.new(:person => bob.person, :text => "cats", :post => @remote_message) + comment.author_signature = comment.send(:sign_with_key, bob.encryption_key) + comment.signature_valid?.should be_true + comment.verify_parent_author_signature.should be_false + end + + it 'should receive remote comments on a user post with a author sig' do + comment = Comment.new(:person => bob.person, :text => "cats", :post => @message) + comment.author_signature = comment.send(:sign_with_key, bob.encryption_key) + comment.signature_valid?.should be_true + comment.verify_parent_author_signature.should be_false + end + end + +end + diff --git a/spec/misc_spec.rb b/spec/misc_spec.rb index 3ea7bbc6c8b..149a4e78660 100644 --- a/spec/misc_spec.rb +++ b/spec/misc_spec.rb @@ -49,4 +49,17 @@ @user2.reload.visible_posts.should include message end end + + describe '#comment' do + it "should send a user's comment on a person's post to that person" do + person = Factory.create(:person) + person_status = Factory.create(:status_message, :person => person) + m = mock() + m.stub!(:post) + Postzord::Dispatch.should_receive(:new).and_return(m) + + alice.comment "yo", :on => person_status + end + end + end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index e80cfcf3d4c..edab46f587f 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -8,48 +8,41 @@ before do @alices_aspect = alice.aspects.first @bobs_aspect = bob.aspects.first + + @bob = bob + @eve = eve + @status = alice.post(:status_message, :message => "hello", :to => @alices_aspect.id) end describe 'comment#notification_type' do - before do - @sam = Factory(:user_with_aspect) - connect_users(alice, @alices_aspect, @sam, @sam.aspects.first) - - @alices_post = alice.post(:status_message, :message => "hello", :to => @alices_aspect.id) - end - it "returns 'comment_on_post' if the comment is on a post you own" do - comment = bob.comment("why so formal?", :on => @alices_post) + comment = bob.comment("why so formal?", :on => @status) comment.notification_type(alice, bob.person).should == Notifications::CommentOnPost end it 'returns false if the comment is not on a post you own and no one "also_commented"' do - comment = alice.comment("I simply felt like issuing a greeting. Do step off.", :on => @alices_post) - comment.notification_type(@sam, alice.person).should == false + comment = alice.comment("I simply felt like issuing a greeting. Do step off.", :on => @status) + comment.notification_type(@bob, alice.person).should == false end context "also commented" do before do - bob.comment("a-commenta commenta", :on => @alices_post) - @comment = @sam.comment("I also commented on the first user's post", :on => @alices_post) + @bob.comment("a-commenta commenta", :on => @status) + @comment = @eve.comment("I also commented on the first user's post", :on => @status) end it 'does not return also commented if the user commented' do - @comment.notification_type(@sam, alice.person).should == false + @comment.notification_type(@eve, alice.person).should == false end it "returns 'also_commented' if another person commented on a post you commented on" do - @comment.notification_type(bob, alice.person).should == Notifications::AlsoCommented + @comment.notification_type(@bob, alice.person).should == Notifications::AlsoCommented end end end describe 'User#comment' do - before do - @status = alice.post(:status_message, :message => "hello", :to => @alices_aspect.id) - end - it "should be able to comment on one's own status" do alice.comment("Yeah, it was great", :on => @status) @status.reload.comments.first.text.should == "Yeah, it was great" @@ -59,66 +52,13 @@ bob.comment("sup dog", :on => @status) @status.reload.comments.first.text.should == "sup dog" end - end - - context 'comment propagation' do - before do - @person = Factory.create(:person) - alice.activate_contact(@person, @alices_aspect) - - @person2 = Factory.create(:person) - @person3 = Factory.create(:person) - alice.activate_contact(@person3, @alices_aspect) - - @person_status = Factory.create(:status_message, :person => @person) - - alice.reload - @user_status = alice.post :status_message, :message => "hi", :to => @alices_aspect.id - - @alices_aspect.reload - alice.reload - end - - it 'should send the comment to the postman' do - m = mock() - m.stub!(:post) - Postzord::Dispatch.should_receive(:new).and_return(m) - alice.comment "yo", :on => @person_status - end - - describe '#subscribers' do - it 'returns the posts original audience, if the post is owned by the user' do - comment = alice.build_comment "yo", :on => @person_status - comment.subscribers(alice).should =~ [@person] - end - - it 'returns the owner of the original post, if the user owns the comment' do - comment = alice.build_comment "yo", :on => @user_status - comment.subscribers(alice).map { |s| s.id }.should =~ [@person, @person3, bob.person].map { |s| s.id } - end - end - - context 'testing a method only used for testing' do - it "should send a user's comment on a person's post to that person" do - m = mock() - m.stub!(:post) - Postzord::Dispatch.should_receive(:new).and_return(m) - - alice.comment "yo", :on => @person_status - end - end - - it 'should not clear the aspect post array on receiving a comment' do - @alices_aspect.post_ids.include?(@user_status.id).should be_true - comment = Comment.new(:person_id => @person.id, :text => "cats", :post => @user_status) - - zord = Postzord::Receiver.new(alice, :person => @person) - zord.parse_and_receive(comment.to_diaspora_xml) - - @alices_aspect.reload - @alices_aspect.post_ids.include?(@user_status.id).should be_true + it 'does not multi-post a comment' do + lambda { + alice.comment 'hello', :on => @status + }.should change { Comment.count }.by(1) end end + describe 'xml' do before do @commenter = Factory.create(:user) @@ -146,59 +86,6 @@ end end end - describe 'local commenting' do - before do - @status = alice.post(:status_message, :message => "hello", :to => @alices_aspect.id) - end - it 'does not multi-post a comment' do - lambda { - alice.comment 'hello', :on => @status - }.should change { Comment.count }.by(1) - end - end - describe 'comments' do - before do - @remote_message = bob.post :status_message, :message => "hello", :to => @bobs_aspect.id - @message = alice.post :status_message, :message => "hi", :to => @alices_aspect.id - end - - it 'should attach the creator signature if the user is commenting' do - comment = alice.comment "Yeah, it was great", :on => @remote_message - @remote_message.comments.reset - @remote_message.comments.first.signature_valid?.should be_true - end - - it 'should sign the comment if the user is the post creator' do - message = alice.post :status_message, :message => "hi", :to => @alices_aspect.id - alice.comment "Yeah, it was great", :on => message - message.comments.reset - message.comments.first.signature_valid?.should be_true - message.comments.first.verify_post_creator_signature.should be_true - end - - it 'should verify a comment made on a remote post by a different contact' do - comment = Comment.new(:person => bob.person, :text => "cats", :post => @remote_message) - comment.creator_signature = comment.send(:sign_with_key, bob.encryption_key) - comment.signature_valid?.should be_true - comment.verify_post_creator_signature.should be_false - comment.post_creator_signature = comment.send(:sign_with_key, alice.encryption_key) - comment.verify_post_creator_signature.should be_true - end - - it 'should reject comments on a remote post with only a creator sig' do - comment = Comment.new(:person => bob.person, :text => "cats", :post => @remote_message) - comment.creator_signature = comment.send(:sign_with_key, bob.encryption_key) - comment.signature_valid?.should be_true - comment.verify_post_creator_signature.should be_false - end - - it 'should receive remote comments on a user post with a creator sig' do - comment = Comment.new(:person => bob.person, :text => "cats", :post => @message) - comment.creator_signature = comment.send(:sign_with_key, bob.encryption_key) - comment.signature_valid?.should be_true - comment.verify_post_creator_signature.should be_false - end - end describe 'youtube' do before do @@ -220,4 +107,74 @@ Comment.find(comment.id).youtube_titles.should == {video_id => CGI::escape(expected_title)} end end + + context 'comment propagation' do + before do + @local_luke, @local_leia, @remote_raphael = set_up_friends + @person_status = Factory.create(:status_message, :person => @remote_raphael) + @user_status = @local_luke.post :status_message, :message => "hi", :to => @local_luke.aspects.first + @lukes_aspect = @local_luke.aspects.first + end + + it 'should not clear the aspect post array on receiving a comment' do + @lukes_aspect.post_ids.include?(@user_status.id).should be_true + comment = Comment.new(:person_id => @remote_raphael.id, :text => "cats", :post => @user_status) + + zord = Postzord::Receiver.new(alice, :person => @remote_raphael) + zord.parse_and_receive(comment.to_diaspora_xml) + + @lukes_aspect.reload + @lukes_aspect.post_ids.include?(@user_status.id).should be_true + end + + describe '#receive' do + before do + @comment = @local_luke.comment("yo", :on => @user_status) + @comment2 = @local_leia.build_comment("yo", :on => @user_status) + @new_c = @comment.dup + end + + it 'does not overwrite a comment that is already in the db' do + lambda{ + @new_c.receive(@local_leia, @local_luke.person) + }.should_not change(Comment, :count) + end + + it 'does not process if post_creator_signature is invalid' do + @comment.delete # remove comment from db so we set a creator sig + @new_c.parent_author_signature = "dsfadsfdsa" + @new_c.receive(@local_leia, @local_luke.person).should == nil + end + + it 'signs when the person receiving is the parent author' do + @comment2.save + @comment2.receive(@local_luke, @local_leia.person) + @comment2.reload.parent_author_signature.should_not be_blank + end + + it 'dispatches when the person receiving is the parent author' do + p = Postzord::Dispatch.new(@local_luke, @comment2) + p.should_receive(:post) + Postzord::Dispatch.stub!(:new).and_return(p) + @comment2.receive(@local_luke, @local_leia.person) + end + + it 'sockets to the user' do + @comment2.should_receive(:socket_to_user).exactly(3).times + @comment2.receive(@local_luke, @local_leia.person) + end + end + + describe '#subscribers' do + it 'returns the posts original audience, if the post is owned by the user' do + comment = @local_luke.build_comment "yo", :on => @user_status + comment.subscribers(@local_luke).map(&:id).should =~ [@local_leia.person, @remote_raphael].map(&:id) + end + + it 'returns the owner of the original post, if the user owns the comment' do + comment = @local_leia.build_comment "yo", :on => @user_status + comment.subscribers(@local_leia).map(&:id).should =~ [@local_luke.person].map(&:id) + end + end + end end diff --git a/spec/models/user/commenting_spec.rb b/spec/models/user/commenting_spec.rb deleted file mode 100644 index 8534549e5ce..00000000000 --- a/spec/models/user/commenting_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2010, Diaspora Inc. This file is -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. - -require 'spec_helper' - -describe User do - - let!(:user1){alice} - let!(:user2){bob} - let!(:aspect1){user1.aspects.first} - let!(:aspect2){user2.aspects.first} - - before do - @post = user1.build_post(:status_message, :message => "hey", :to => aspect1.id) - @post.save - user1.dispatch_post(@post, :to => "all") - end - - describe '#dispatch_comment' do - context "post owner's contact is commenting" do - it "doesn't call receive on local users" do - user1.should_not_receive(:receive_comment) - user2.should_not_receive(:receive_comment) - - comment = user2.build_comment "why so formal?", :on => @post - comment.save! - user2.dispatch_comment comment - end - end - - context "post owner is commenting on own post" do - it "doesn't call receive on local users" do - user1.should_not_receive(:receive_comment) - user2.should_not_receive(:receive_comment) - - comment = user1.build_comment "why so formal?", :on => @post - comment.save! - user1.dispatch_comment comment - end - end - end -end diff --git a/spec/support/user_methods.rb b/spec/support/user_methods.rb index e47c2fd6897..3d091d91468 100644 --- a/spec/support/user_methods.rb +++ b/spec/support/user_methods.rb @@ -18,10 +18,7 @@ def post(class_name, opts = {}) fantasy_resque do p = build_post(class_name, opts) if p.save! - raise 'MongoMapper failed to catch a failed save' unless p.id - self.aspects.reload - aspects = self.aspects_from_ids(opts[:to]) add_to_streams(p, aspects) dispatch_post(p, :to => opts[:to]) @@ -34,8 +31,7 @@ def comment(text, options = {}) fantasy_resque do c = build_comment(text, options) if c.save! - raise 'MongoMapper failed to catch a failed save' unless c.id - dispatch_comment(c) + Postzord::Dispatch.new(self, c).post end c end From c62e9db397f85e8795f2e37023ef830017d54aa4 Mon Sep 17 00:00:00 2001 From: danielvincent Date: Mon, 28 Feb 2011 19:26:04 -0800 Subject: [PATCH 09/29] private messages wip --- app/models/comment.rb | 2 +- app/models/conversation.rb | 12 +- app/models/message.rb | 28 +++-- app/models/person.rb | 4 +- ...0110228233419_add_signatures_to_message.rb | 11 ++ db/schema.rb | 12 +- lib/diaspora/relayable.rb | 16 +-- spec/lib/diaspora/relayable_spec.rb | 103 +++++++++++------- spec/models/comment_spec.rb | 73 ++----------- spec/models/conversation_spec.rb | 100 ++++++++++------- spec/models/message_spec.rb | 69 ++++++++---- 11 files changed, 242 insertions(+), 188 deletions(-) create mode 100644 db/migrate/20110228233419_add_signatures_to_message.rb diff --git a/app/models/comment.rb b/app/models/comment.rb index 1fdc4787673..f9e9c2bcb5a 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -18,7 +18,7 @@ class Comment < ActiveRecord::Base xml_attr :diaspora_handle belongs_to :post, :touch => true - belongs_to :person + belongs_to :author, :class_name => 'Person' validates_presence_of :text, :post validates_length_of :text, :maximum => 2500 diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 27f375af2ce..a8741fea122 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -12,6 +12,15 @@ class Conversation < ActiveRecord::Base has_many :participants, :class_name => 'Person', :through => :conversation_visibilities, :source => :person has_many :messages, :order => 'created_at ASC' + def self.create(opts={}) + msg_opts = opts.delete(:message) + + cnv = super(opts) + message = Message.new(msg_opts.merge({:conversation_id => cnv.id})) + message.save + cnv + end + def author self.messages.first.author end @@ -29,10 +38,11 @@ def participant_handles= handles self.participants << Webfinger.new(handle).fetch end end + def subscribers(user) self.recipients end - + def receive(user, person) cnv = Conversation.find_or_create_by_guid(self.attributes) self.messages.each do |msg| diff --git a/app/models/message.rb b/app/models/message.rb index b02765165c1..5d093456da2 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -1,7 +1,9 @@ class Message < ActiveRecord::Base include ROXML + include Diaspora::Guid include Diaspora::Webhooks + include Diaspora::Relayable xml_attr :text xml_attr :created_at @@ -11,6 +13,16 @@ class Message < ActiveRecord::Base belongs_to :author, :class_name => 'Person' belongs_to :conversation + after_initialize do + #sign comment as commenter + self.author_signature = self.sign_with_key(self.author.owner.encryption_key) if self.author.owner + + if !self.parent.blank? && self.parent.author.person.owns?(self.parent) + #sign comment as post owner + self.parent_author_signature = self.sign_with_key( self.parent.author.owner.encryption_key) if self.parent.author.owner + end + end + def diaspora_handle self.author.diaspora_handle end @@ -29,15 +41,15 @@ def conversation_guid= guid end end - def receive(user, person) - Message.find_or_create_by_guid(self.attributes) + def parent_class + Conversation end - def subscribers(user) - if self.conversation.author == user.person - p = self.conversation.subscribers(user) - else - p = self.conversation.author - end + def parent + self.conversation + end + + def parent= parent + self.conversation = parent end end diff --git a/app/models/person.rb b/app/models/person.rb index ac6a5dc88ee..e3c1a03eea7 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -93,8 +93,8 @@ def first_name end end - def owns?(post) - self == post.person + def owns?(obj) + self == obj.author end def url diff --git a/db/migrate/20110228233419_add_signatures_to_message.rb b/db/migrate/20110228233419_add_signatures_to_message.rb new file mode 100644 index 00000000000..e6c72e76175 --- /dev/null +++ b/db/migrate/20110228233419_add_signatures_to_message.rb @@ -0,0 +1,11 @@ +class AddSignaturesToMessage < ActiveRecord::Migration + def self.up + add_column(:messages, :author_signature, :text) + add_column(:messages, :parent_author_signature, :text) + end + + def self.down + remove_column(:messages, :author_signature) + remove_column(:messages, :parent_author_signature) + end +end diff --git a/db/schema.rb b/db/schema.rb index adb41d90ad6..f5dd1653a70 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20110228220810) do +ActiveRecord::Schema.define(:version => 20110228233419) do create_table "aspect_memberships", :force => true do |t| t.integer "aspect_id", :null => false @@ -124,12 +124,14 @@ add_index "mentions", ["post_id"], :name => "index_mentions_on_post_id" create_table "messages", :force => true do |t| - t.integer "conversation_id", :null => false - t.integer "author_id", :null => false - t.string "guid", :null => false - t.text "text", :null => false + t.integer "conversation_id", :null => false + t.integer "author_id", :null => false + t.string "guid", :null => false + t.text "text", :null => false t.datetime "created_at" t.datetime "updated_at" + t.text "author_signature" + t.text "parent_author_signature" end add_index "messages", ["author_id"], :name => "index_messages_on_author_id" diff --git a/lib/diaspora/relayable.rb b/lib/diaspora/relayable.rb index 2db59176174..6916fc7bf1d 100644 --- a/lib/diaspora/relayable.rb +++ b/lib/diaspora/relayable.rb @@ -25,15 +25,15 @@ def subscribers(user) if user.owns?(self.parent) self.parent.subscribers(user) elsif user.owns?(self) - [self.parent.person] + [self.parent.author] end end def receive(user, person) object = self.class.where(:guid => self.guid).first || self - unless object.parent.person == user.person || object.verify_parent_author_signature - Rails.logger.info("event=receive status=abort reason='object signature not valid' recipient=#{user.diaspora_handle} sender=#{self.parent.person.diaspora_handle} payload_type=#{self.class} parent_id=#{self.parent.id}") + unless object.parent.author == user.person || object.verify_parent_author_signature + Rails.logger.info("event=receive status=abort reason='object signature not valid' recipient=#{user.diaspora_handle} sender=#{self.parent.author.diaspora_handle} payload_type=#{self.class} parent_id=#{self.parent.id}") return end @@ -49,7 +49,7 @@ def receive(user, person) Postzord::Dispatch.new(user, object).post end - object.socket_to_user(user, :aspect_ids => object.parent.aspect_ids) + object.socket_to_user(user, :aspect_ids => object.parent.aspect_ids) if object.respond_to? :socket_to_user object end @@ -58,7 +58,7 @@ def signable_string end def signature_valid? - verify_signature(creator_signature, person) + verify_signature(creator_signature, self.author) end def verify_signature(signature, person) @@ -89,7 +89,7 @@ def signable_accessors accessors = self.class.roxml_attrs.collect do |definition| definition.accessor end - ['person', 'author_signature', 'parent_author_signature'].each do |acc| + ['author', 'author_signature', 'parent_author_signature'].each do |acc| accessors.delete acc end accessors @@ -102,11 +102,11 @@ def signable_string end def verify_parent_author_signature - verify_signature(self.parent_author_signature, parent.person) + verify_signature(self.parent_author_signature, self.parent.author) end def signature_valid? - verify_signature(self.author_signature, person) + verify_signature(self.author_signature, self.author) end diff --git a/spec/lib/diaspora/relayable_spec.rb b/spec/lib/diaspora/relayable_spec.rb index 450990b9c14..0f3d99f2589 100644 --- a/spec/lib/diaspora/relayable_spec.rb +++ b/spec/lib/diaspora/relayable_spec.rb @@ -5,54 +5,77 @@ require 'spec_helper' describe Diaspora::Relayable do + shared_examples_for "it is relayable" do + context 'encryption' do + describe '#parent_author_signature' do + it 'should sign the comment if the user is the post author' do + @object_by_parent_author.verify_parent_author_signature.should be_true + end - before do - @alices_aspect = alice.aspects.first - @bobs_aspect = bob.aspects.first - @remote_message = bob.post :status_message, :message => "hello", :to => @bobs_aspect.id - @message = alice.post :status_message, :message => "hi", :to => @alices_aspect.id - end + it 'does not sign as the parent author is not parent' do + @object_by_recipient.author_signature = @object_by_recipient.send(:sign_with_key, @local_leia.encryption_key) + @object_by_recipient.verify_parent_author_signature.should be_false + end - describe '#parent_author_signature' do - it 'should sign the comment if the user is the post author' do - message = alice.post :status_message, :message => "hi", :to => @alices_aspect.id - alice.comment "Yeah, it was great", :on => message - message.comments.reset - message.comments.first.signature_valid?.should be_true - message.comments.first.verify_parent_author_signature.should be_true - end + it 'should verify a comment made on a remote post by a different contact' do + @object_by_recipient.author_signature = @object_by_recipient.send(:sign_with_key, @local_leia.encryption_key) + @object_by_recipient.parent_author_signature = @object_by_recipient.send(:sign_with_key, @local_luke.encryption_key) + @object_by_recipient.verify_parent_author_signature.should be_true + end + end - it 'should verify a comment made on a remote post by a different contact' do - comment = Comment.new(:person => bob.person, :text => "cats", :post => @remote_message) - comment.author_signature = comment.send(:sign_with_key, bob.encryption_key) - comment.signature_valid?.should be_true - comment.verify_parent_author_signature.should be_false - comment.parent_author_signature = comment.send(:sign_with_key, alice.encryption_key) - comment.verify_parent_author_signature.should be_true + describe '#author_signature' do + it 'should sign as the object author' do + @object_on_remote_parent.signature_valid?.should be_true + @object_by_parent_author.signature_valid?.should be_true + @object_by_recipient.signature_valid?.should be_true + end + end end - end - describe '#author_signature' do - it 'should attach the author signature if the user is commenting' do - comment = alice.comment "Yeah, it was great", :on => @remote_message - @remote_message.comments.reset - @remote_message.comments.first.signature_valid?.should be_true - end + context 'propagation' do + describe '#receive' do + it 'does not overwrite a comment that is already in the db' do + lambda{ + @dup_object_by_parent_author.receive(@local_leia, @local_luke.person) + }.should_not change(Comment, :count) + end - it 'should reject comments on a remote post with only a author sig' do - comment = Comment.new(:person => bob.person, :text => "cats", :post => @remote_message) - comment.author_signature = comment.send(:sign_with_key, bob.encryption_key) - comment.signature_valid?.should be_true - comment.verify_parent_author_signature.should be_false - end + it 'does not process if post_creator_signature is invalid' do + @object_by_parent_author.delete # remove comment from db so we set a creator sig + @dup_object_by_parent_author.parent_author_signature = "dsfadsfdsa" + @dup_object_by_parent_author.receive(@local_leia, @local_luke.person).should == nil + end - it 'should receive remote comments on a user post with a author sig' do - comment = Comment.new(:person => bob.person, :text => "cats", :post => @message) - comment.author_signature = comment.send(:sign_with_key, bob.encryption_key) - comment.signature_valid?.should be_true - comment.verify_parent_author_signature.should be_false + it 'signs when the person receiving is the parent author' do + @object_by_recipient.save + @object_by_recipient.receive(@local_luke, @local_leia.person) + @object_by_recipient.reload.parent_author_signature.should_not be_blank + end + + it 'dispatches when the person receiving is the parent author' do + p = Postzord::Dispatch.new(@local_luke, @object_by_recipient) + p.should_receive(:post) + Postzord::Dispatch.stub!(:new).and_return(p) + @object_by_recipient.receive(@local_luke, @local_leia.person) + end + + it 'sockets to the user' do + @object_by_recipient.should_receive(:socket_to_user).exactly(3).times + @object_by_recipient.receive(@local_luke, @local_leia.person) + end + end + + describe '#subscribers' do + it 'returns the posts original audience, if the post is owned by the user' do + @object_by_parent_author.subscribers(@local_luke).map(&:id).should =~ [@local_leia.person, @remote_raphael].map(&:id) + end + + it 'returns the owner of the original post, if the user owns the comment' do + @object_by_recipient.subscribers(@local_leia).map(&:id).should =~ [@local_luke.person].map(&:id) + end + end end end - end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index edab46f587f..895319c30e6 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -3,6 +3,7 @@ # the COPYRIGHT file. require 'spec_helper' +require File.join(Rails.root, "spec", "lib", "diaspora", "relayable_spec") describe Comment do before do @@ -108,73 +109,19 @@ end end - context 'comment propagation' do + describe 'it is relayable' do before do @local_luke, @local_leia, @remote_raphael = set_up_friends - @person_status = Factory.create(:status_message, :person => @remote_raphael) - @user_status = @local_luke.post :status_message, :message => "hi", :to => @local_luke.aspects.first - @lukes_aspect = @local_luke.aspects.first - end - - it 'should not clear the aspect post array on receiving a comment' do - @lukes_aspect.post_ids.include?(@user_status.id).should be_true - comment = Comment.new(:person_id => @remote_raphael.id, :text => "cats", :post => @user_status) - - zord = Postzord::Receiver.new(alice, :person => @remote_raphael) - zord.parse_and_receive(comment.to_diaspora_xml) - - @lukes_aspect.reload - @lukes_aspect.post_ids.include?(@user_status.id).should be_true - end - - describe '#receive' do - before do - @comment = @local_luke.comment("yo", :on => @user_status) - @comment2 = @local_leia.build_comment("yo", :on => @user_status) - @new_c = @comment.dup - end - - it 'does not overwrite a comment that is already in the db' do - lambda{ - @new_c.receive(@local_leia, @local_luke.person) - }.should_not change(Comment, :count) - end - - it 'does not process if post_creator_signature is invalid' do - @comment.delete # remove comment from db so we set a creator sig - @new_c.parent_author_signature = "dsfadsfdsa" - @new_c.receive(@local_leia, @local_luke.person).should == nil - end - - it 'signs when the person receiving is the parent author' do - @comment2.save - @comment2.receive(@local_luke, @local_leia.person) - @comment2.reload.parent_author_signature.should_not be_blank - end + @remote_parent = Factory.create(:status_message, :person => @remote_raphael) + @local_parent = @local_luke.post :status_message, :message => "hi", :to => @local_luke.aspects.first - it 'dispatches when the person receiving is the parent author' do - p = Postzord::Dispatch.new(@local_luke, @comment2) - p.should_receive(:post) - Postzord::Dispatch.stub!(:new).and_return(p) - @comment2.receive(@local_luke, @local_leia.person) - end + @object_by_parent_author = @local_luke.comment("yo", :on => @local_parent) + @object_by_recipient = @local_leia.build_comment("yo", :on => @local_parent) + @dup_object_by_parent_author = @object_by_parent_author.dup - it 'sockets to the user' do - @comment2.should_receive(:socket_to_user).exactly(3).times - @comment2.receive(@local_luke, @local_leia.person) - end - end - - describe '#subscribers' do - it 'returns the posts original audience, if the post is owned by the user' do - comment = @local_luke.build_comment "yo", :on => @user_status - comment.subscribers(@local_luke).map(&:id).should =~ [@local_leia.person, @remote_raphael].map(&:id) - end - - it 'returns the owner of the original post, if the user owns the comment' do - comment = @local_leia.build_comment "yo", :on => @user_status - comment.subscribers(@local_leia).map(&:id).should =~ [@local_luke.person].map(&:id) - end + @object_on_remote_parent = @local_luke.comment("Yeah, it was great", :on => @remote_parent) end + it_should_behave_like 'it is relayable' end + end diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb index 51683a96172..e500d4b85cb 100644 --- a/spec/models/conversation_spec.rb +++ b/spec/models/conversation_spec.rb @@ -1,3 +1,7 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + require 'spec_helper' describe Conversation do @@ -5,59 +9,75 @@ @user1 = alice @user2 = bob - @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff" } - @cnv = Conversation.create(@create_hash) + @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff", + :message => {:author => @user1.person, :text => 'hey'}} +=begin @message = Message.new(:author => @user1.person, :text => "stuff") @cnv.messages << @message @message.save @xml = @cnv.to_diaspora_xml +=end end - describe 'serialization' do - it 'serializes the message' do - @xml.gsub(/\s/, '').should include(@message.to_xml.to_s.gsub(/\s/, '')) - end - - it 'serializes the participants' do - @create_hash[:participant_ids].each{|id| - @xml.should include(Person.find(id).diaspora_handle) - } - end - - it 'serializes the created_at time' do - @xml.should include(@message.created_at.to_s) - end + it 'creates a message on create' do + lambda{ + Conversation.create(@create_hash) + }.should change(Message, :count).by(1) end - describe '#subscribers' do - it 'returns the recipients for the post owner' do - @cnv.subscribers(@user1).should == @user1.contacts.map{|c| c.person} - end - end - - describe '#receive' do + context 'transport' do before do - Conversation.delete_all - Message.delete_all + @cnv = Conversation.create(@create_hash) + @message = @cnv.messages.first + @xml = @cnv.to_diaspora_xml end - it 'creates a message' do - lambda{ - Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) - }.should change(Message, :count).by(1) - end - it 'creates a conversation' do - lambda{ - Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) - }.should change(Conversation, :count).by(1) + describe 'serialization' do + it 'serializes the message' do + @xml.gsub(/\s/, '').should include(@message.to_xml.to_s.gsub(/\s/, '')) + end + + it 'serializes the participants' do + @create_hash[:participant_ids].each{|id| + @xml.should include(Person.find(id).diaspora_handle) + } + end + + it 'serializes the created_at time' do + @xml.should include(@message.created_at.to_s) + end end - it 'creates appropriate visibilities' do - lambda{ - Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) - }.should change(ConversationVisibility, :count).by(@cnv.participants.count) + + describe '#subscribers' do + it 'returns the recipients for the post owner' do + @cnv.subscribers(@user1).should == @user1.contacts.map{|c| c.person} + end end - it 'does not save before receive' do - Diaspora::Parser.from_xml(@xml).persisted?.should be_false + + describe '#receive' do + before do + Conversation.delete_all + Message.delete_all + end + + it 'creates a message' do + lambda{ + Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) + }.should change(Message, :count).by(1) + end + it 'creates a conversation' do + lambda{ + Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) + }.should change(Conversation, :count).by(1) + end + it 'creates appropriate visibilities' do + lambda{ + Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) + }.should change(ConversationVisibility, :count).by(@cnv.participants.count) + end + it 'does not save before receive' do + Diaspora::Parser.from_xml(@xml).persisted?.should be_false + end end end end diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb index 5c5e11f585a..b02b1470cbc 100644 --- a/spec/models/message_spec.rb +++ b/spec/models/message_spec.rb @@ -1,18 +1,39 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + require 'spec_helper' +require File.join(Rails.root, "spec", "lib", "diaspora", "relayable_spec") describe Message do before do @user1 = alice @user2 = bob - @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff" } + @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff", + :message => {:author => @user1.person, :text => "stuff"} } @cnv = Conversation.create(@create_hash) - @message = Message.new(:author => @user1.person, :text => "stuff") - @cnv.messages << @message - @message.save + @message = @cnv.messages.first @xml = @message.to_diaspora_xml end + describe '#after_initialize' do + before do + @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff"} + + @cnv = Conversation.new(@create_hash) + @cnv.save + @msg = Message.new(:text => "21312", :conversation => @cnv) + end + it 'signs the message' do + @msg.author_signature.should_not be_blank + end + + it 'signs the message author if author of conversation' do + @msg.parent_author_signature.should_not be_blank + end + end + describe 'serialization' do it 'serializes the text' do @xml.should include(@message.text) @@ -30,24 +51,32 @@ end end - describe '#subscribers' do - it 'returns the recipients for the post owner' do - @message.subscribers(@user1).should == @user1.contacts.map{|c| c.person} - end - it 'returns the conversation author for the post owner' do - @message.subscribers(@user2).should == @user1.person - end - end - - describe '#receive' do + describe 'it is relayable' do before do - Message.delete_all - end + @local_luke, @local_leia, @remote_raphael = set_up_friends + + cnv_hash = {:subject => 'cool story, bro', :participant_ids => [@local_luke.person, @local_leia.person, @remote_raphael].map(&:id), + :message => {:author => @remote_raphael, :text => 'hey'}} + + @remote_parent = Conversation.create(cnv_hash.dup) + + cnv_hash[:message][:author] = @local_luke.person + @local_parent = Conversation.create(cnv_hash) + + msg_hash = {:author => @local_luke.person, :text => 'yo', :conversation => @local_parent} + @object_by_parent_author = Message.create(msg_hash.dup) + Postzord::Dispatch.new(@local_luke, @object_by_parent_author).post + + msg_hash[:author] = @local_leia.person + @object_by_recipient = Message.create(msg_hash.dup) + + @dup_object_by_parent_author = @object_by_parent_author.dup - it 'creates a message' do - lambda{ - Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) - }.should change(Message, :count).by(1) + msg_hash[:author] = @local_luke.person + msg_hash[:conversation] = @remote_parent + @object_on_remote_parent = Message.create(msg_hash) + Postzord::Dispatch.new(@local_luke, @object_on_remote_parent).post end + it_should_behave_like 'it is relayable' end end From 11309574cff683824bf2e461add215d0a114d19f Mon Sep 17 00:00:00 2001 From: danielvincent Date: Tue, 1 Mar 2011 12:12:08 -0800 Subject: [PATCH 10/29] messages are now relayable, a comment has an author as opposed to a person. --- app/controllers/conversations_controller.rb | 14 ++----- app/helpers/sockets_helper.rb | 2 +- app/models/comment.rb | 8 ++-- app/models/conversation.rb | 20 ++++++---- app/models/message.rb | 6 ++- app/models/post.rb | 7 ++++ app/models/user.rb | 2 +- app/views/comments/_comment.html.haml | 4 +- ...ersations_and_messages_and_visibilities.rb | 1 + .../20110301014507_rename_person_to_author.rb | 13 ++++++ db/schema.rb | 9 +++-- lib/diaspora/relayable.rb | 2 +- lib/diaspora/user/querying.rb | 2 +- lib/fake.rb | 14 +++---- lib/postzord/dispatch.rb | 4 +- spec/controllers/comments_controller_spec.rb | 6 +-- ...nversation_visibilities_controller_spec.rb | 6 +-- .../conversations_controller_spec.rb | 40 ++++++++++--------- spec/factories.rb | 4 +- spec/intergration/receiving_spec.rb | 4 +- .../data_conversion/import_to_mysql_spec.rb | 2 +- spec/lib/diaspora/parser_spec.rb | 2 +- spec/lib/fake_spec.rb | 4 +- spec/lib/postzord/dispatch_spec.rb | 2 +- spec/models/comment_spec.rb | 4 +- spec/models/conversation_spec.rb | 10 +---- spec/models/message_spec.rb | 27 +++++-------- spec/models/person_spec.rb | 4 +- .../relayable.rb} | 1 + 29 files changed, 120 insertions(+), 104 deletions(-) create mode 100644 db/migrate/20110301014507_rename_person_to_author.rb rename spec/{lib/diaspora/relayable_spec.rb => shared_behaviors/relayable.rb} (99%) diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb index 72825017ab9..2718e12f1b5 100644 --- a/app/controllers/conversations_controller.rb +++ b/app/controllers/conversations_controller.rb @@ -9,20 +9,14 @@ def index end def create - person_ids = Contact.where(:id => params[:conversation][:contact_ids]).map! do |contact| + person_ids = Contact.where(:id => params[:conversation].delete(:contact_ids)).map! do |contact| contact.person_id end - person_ids = person_ids | [current_user.person.id] + params[:conversation][:participant_ids] = person_ids | [current_user.person.id] + params[:conversation][:author] = current_user.person - @conversation = Conversation.new(:subject => params[:conversation][:subject], :participant_ids => person_ids) - - if @conversation.save - @message = Message.new(:text => params[:message][:text], :author => current_user.person, :conversation_id => @conversation.id ) - unless @message.save - @conversation.destroy - end - end + @conversation = Conversation.create(params[:conversation]) respond_with @conversation end diff --git a/app/helpers/sockets_helper.rb b/app/helpers/sockets_helper.rb index 1c6ee13ac87..b48f5d9a73f 100644 --- a/app/helpers/sockets_helper.rb +++ b/app/helpers/sockets_helper.rb @@ -48,7 +48,7 @@ def action_hash(user, object, opts={}) v = render_to_string(:partial => 'people/person', :locals => person_hash) elsif object.is_a? Comment - v = render_to_string(:partial => 'comments/comment', :locals => {:comment => object, :person => object.person}) + v = render_to_string(:partial => 'comments/comment', :locals => {:comment => object, :person => object.author}) elsif object.is_a? Notification v = render_to_string(:partial => 'notifications/popup', :locals => {:note => object, :person => opts[:actor]}) diff --git a/app/models/comment.rb b/app/models/comment.rb index f9e9c2bcb5a..08ec011e765 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -29,16 +29,16 @@ class Comment < ActiveRecord::Base self.text.strip! unless self.text.nil? end def diaspora_handle - person.diaspora_handle + self.author.diaspora_handle end def diaspora_handle= nh - self.person = Webfinger.new(nh).fetch + self.author = Webfinger.new(nh).fetch end def notification_type(user, person) - if self.post.person == user.person + if self.post.author == user.person return Notifications::CommentOnPost - elsif self.post.comments.where(:person_id => user.person.id) != [] && self.person_id != user.person.id + elsif self.post.comments.where(:author_id => user.person.id) != [] && self.author_id != user.person.id return Notifications::AlsoCommented else return false diff --git a/app/models/conversation.rb b/app/models/conversation.rb index a8741fea122..d21eccd2ce1 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -4,16 +4,20 @@ class Conversation < ActiveRecord::Base include Diaspora::Webhooks xml_attr :subject - xml_attr :messages, :as => [Message] xml_attr :created_at + xml_attr :messages, :as => [Message] + xml_reader :diaspora_handle xml_reader :participant_handles has_many :conversation_visibilities has_many :participants, :class_name => 'Person', :through => :conversation_visibilities, :source => :person has_many :messages, :order => 'created_at ASC' + belongs_to :author, :class_name => 'Person' + def self.create(opts={}) - msg_opts = opts.delete(:message) + opts = opts.dup + msg_opts = {:author => opts[:author], :text => opts.delete(:text)} cnv = super(opts) message = Message.new(msg_opts.merge({:conversation_id => cnv.id})) @@ -21,18 +25,20 @@ def self.create(opts={}) cnv end - def author - self.messages.first.author - end - def recipients self.participants - [self.author] end + def diaspora_handle + self.author.diaspora_handle + end + def diaspora_handle= nh + self.author = Webfinger.new(nh).fetch + end + def participant_handles self.participants.map{|p| p.diaspora_handle}.join(";") end - def participant_handles= handles handles.split(';').each do |handle| self.participants << Webfinger.new(handle).fetch diff --git a/app/models/message.rb b/app/models/message.rb index 5d093456da2..9ebc262d638 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -13,14 +13,16 @@ class Message < ActiveRecord::Base belongs_to :author, :class_name => 'Person' belongs_to :conversation - after_initialize do + after_create do #sign comment as commenter self.author_signature = self.sign_with_key(self.author.owner.encryption_key) if self.author.owner - if !self.parent.blank? && self.parent.author.person.owns?(self.parent) + if !self.parent.blank? && self.author.owns?(self.parent) #sign comment as post owner self.parent_author_signature = self.sign_with_key( self.parent.author.owner.encryption_key) if self.parent.author.owner end + self.save! + self end def diaspora_handle diff --git a/app/models/post.rb b/app/models/post.rb index 180e444d85d..bd044b5acb9 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -24,6 +24,13 @@ class Post < ActiveRecord::Base after_destroy :propogate_retraction + def author + self.person + end + def author= author + self.person = author + end + def user_refs self.post_visibilities.count end diff --git a/app/models/user.rb b/app/models/user.rb index 0ef27da51d5..3b8f58c5e03 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -133,7 +133,7 @@ def salmon(post) ######## Commenting ######## def build_comment(text, options = {}) - comment = Comment.new(:person_id => self.person.id, + comment = Comment.new(:author_id => self.person.id, :text => text, :post => options[:on]) comment.set_guid diff --git a/app/views/comments/_comment.html.haml b/app/views/comments/_comment.html.haml index 66bb9e906f3..3d8a5617380 100644 --- a/app/views/comments/_comment.html.haml +++ b/app/views/comments/_comment.html.haml @@ -3,10 +3,10 @@ -# the COPYRIGHT file. %li.comment{:data=>{:guid => comment.id}, :class => ("hidden" if(defined? hidden))} - = person_image_link(comment.person) + = person_image_link(comment.author) .content %strong - = person_link(comment.person) + = person_link(comment.author) = markdownify(comment.text, :youtube_maps => comment.youtube_titles) diff --git a/db/migrate/20110225190919_create_conversations_and_messages_and_visibilities.rb b/db/migrate/20110225190919_create_conversations_and_messages_and_visibilities.rb index 397ded1f3c8..7c3f880d7b6 100644 --- a/db/migrate/20110225190919_create_conversations_and_messages_and_visibilities.rb +++ b/db/migrate/20110225190919_create_conversations_and_messages_and_visibilities.rb @@ -20,6 +20,7 @@ def self.up create_table :conversations do |t| t.string :subject t.string :guid, :null => false + t.integer :author_id, :null => false t.timestamps end diff --git a/db/migrate/20110301014507_rename_person_to_author.rb b/db/migrate/20110301014507_rename_person_to_author.rb new file mode 100644 index 00000000000..30bbdadff68 --- /dev/null +++ b/db/migrate/20110301014507_rename_person_to_author.rb @@ -0,0 +1,13 @@ +class RenamePersonToAuthor < ActiveRecord::Migration + def self.up + remove_foreign_key(:comments, :people) + rename_column :comments, :person_id, :author_id + add_foreign_key(:comments, :people, :column => :author_id, :dependent => :delete) + end + + def self.down + remove_foreign_key(:comments, :people, :column => :author_id) + rename_column :comments, :author_id, :person_id + add_foreign_key(:comments, :people, :dependent => :delete) + end +end diff --git a/db/schema.rb b/db/schema.rb index f5dd1653a70..469ad4f92ff 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20110228233419) do +ActiveRecord::Schema.define(:version => 20110301014507) do create_table "aspect_memberships", :force => true do |t| t.integer "aspect_id", :null => false @@ -41,7 +41,7 @@ create_table "comments", :force => true do |t| t.text "text", :null => false t.integer "post_id", :null => false - t.integer "person_id", :null => false + t.integer "author_id", :null => false t.string "guid", :null => false t.text "author_signature" t.text "parent_author_signature" @@ -51,9 +51,9 @@ t.string "mongo_id" end + add_index "comments", ["author_id"], :name => "index_comments_on_person_id" add_index "comments", ["guid"], :name => "index_comments_on_guid", :unique => true add_index "comments", ["mongo_id"], :name => "index_comments_on_mongo_id" - add_index "comments", ["person_id"], :name => "index_comments_on_person_id" add_index "comments", ["post_id"], :name => "index_comments_on_post_id" create_table "contacts", :force => true do |t| @@ -85,6 +85,7 @@ create_table "conversations", :force => true do |t| t.string "subject" t.string "guid", :null => false + t.integer "author_id", :null => false t.datetime "created_at" t.datetime "updated_at" end @@ -511,7 +512,7 @@ add_foreign_key "aspect_memberships", "aspects", :name => "aspect_memberships_aspect_id_fk" add_foreign_key "aspect_memberships", "contacts", :name => "aspect_memberships_contact_id_fk", :dependent => :delete - add_foreign_key "comments", "people", :name => "comments_person_id_fk", :dependent => :delete + add_foreign_key "comments", "people", :name => "comments_author_id_fk", :column => "author_id", :dependent => :delete add_foreign_key "comments", "posts", :name => "comments_post_id_fk", :dependent => :delete add_foreign_key "contacts", "people", :name => "contacts_person_id_fk", :dependent => :delete diff --git a/lib/diaspora/relayable.rb b/lib/diaspora/relayable.rb index 6916fc7bf1d..bbab9696363 100644 --- a/lib/diaspora/relayable.rb +++ b/lib/diaspora/relayable.rb @@ -89,7 +89,7 @@ def signable_accessors accessors = self.class.roxml_attrs.collect do |definition| definition.accessor end - ['author', 'author_signature', 'parent_author_signature'].each do |acc| + ['author_signature', 'parent_author_signature'].each do |acc| accessors.delete acc end accessors diff --git a/lib/diaspora/user/querying.rb b/lib/diaspora/user/querying.rb index 3a23a31c4ec..e2024526752 100644 --- a/lib/diaspora/user/querying.rb +++ b/lib/diaspora/user/querying.rb @@ -7,7 +7,7 @@ module UserModules module Querying def find_visible_post_by_id( id ) - self.raw_visible_posts.where(:id => id).includes({:person => :profile}, {:comments => {:person => :profile}}, :photos).first + self.raw_visible_posts.where(:id => id).includes({:person => :profile}, {:comments => {:author => :profile}}, :photos).first end def raw_visible_posts diff --git a/lib/fake.rb b/lib/fake.rb index bb0475cdc37..fbf25e66bb4 100644 --- a/lib/fake.rb +++ b/lib/fake.rb @@ -6,22 +6,22 @@ def method_missing(method, *args, &block) end def initialize(posts) - person_ids = [] - posts.each do |p| - person_ids << p.person_id + author_ids = [] + posts.each do |p| + author_ids << p.person_id p.comments.each do |c| - person_ids << c.person_id + author_ids << c.author_id end end - people = Person.where(:id => person_ids).includes(:profile) + people = Person.where(:id => author_ids).includes(:profile) @people_hash = {} people.each{|person| @people_hash[person.id] = person} @post_fakes = posts.map do |post| - f = Fake.new(post, self) + f = Fake.new(post, self) f.comments = post.comments.map do |comment| - Fake.new(comment, self) + Fake.new(comment, self) end f end diff --git a/lib/postzord/dispatch.rb b/lib/postzord/dispatch.rb index 244f1bb5d1d..65369d9e4bc 100644 --- a/lib/postzord/dispatch.rb +++ b/lib/postzord/dispatch.rb @@ -26,7 +26,7 @@ def post(opts = {}) user_ids = [*local_people].map{|x| x.owner_id } local_users = User.where(:id => user_ids) self.notify_users(local_users) - local_users << @sender if @object.person.local? + local_users << @sender if @object.author.local? self.socket_to_users(local_users) else self.deliver_to_local(local_people) @@ -73,7 +73,7 @@ def socket_and_notify_users(users) def notify_users(users) users.each do |user| - Resque.enqueue(Job::NotifyLocalUsers, user.id, @object.class.to_s, @object.id, @object.person_id) + Resque.enqueue(Job::NotifyLocalUsers, user.id, @object.class.to_s, @object.id, @object.author.id) end end def socket_to_users(users) diff --git a/spec/controllers/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index 80325a02815..f003ae71c76 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -41,11 +41,11 @@ post :create, comment_hash response.code.should == '201' end - it "doesn't overwrite person_id" do + it "doesn't overwrite author_id" do new_user = Factory.create(:user) - comment_hash[:person_id] = new_user.person.id.to_s + comment_hash[:author_id] = new_user.person.id.to_s post :create, comment_hash - Comment.find_by_text(comment_hash[:text]).person_id.should == @user1.person.id + Comment.find_by_text(comment_hash[:text]).author_id.should == @user1.person.id end it "doesn't overwrite id" do old_comment = @user1.comment("hello", :on => @post) diff --git a/spec/controllers/conversation_visibilities_controller_spec.rb b/spec/controllers/conversation_visibilities_controller_spec.rb index c2458ec05f9..f2002cdd966 100644 --- a/spec/controllers/conversation_visibilities_controller_spec.rb +++ b/spec/controllers/conversation_visibilities_controller_spec.rb @@ -11,9 +11,9 @@ @user1 = alice sign_in :user, @user1 - @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], - :subject => "cool stuff" } - @conversation = Conversation.create(@create_hash) + hash = { :author => @user1.person, :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :subject => 'not spam', :text => 'cool stuff'} + @conversation = Conversation.create(hash) end describe '#destroy' do diff --git a/spec/controllers/conversations_controller_spec.rb b/spec/controllers/conversations_controller_spec.rb index 0dce62b9dc8..d567d9b62de 100644 --- a/spec/controllers/conversations_controller_spec.rb +++ b/spec/controllers/conversations_controller_spec.rb @@ -21,14 +21,12 @@ response.should be_success end - it 'retrieves all messages for a user' do - @conversation_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], - :subject => 'not spam' } - @message_hash = {:author => @user1.person, :text => 'cool stuff'} + it 'retrieves all conversations for a user' do + hash = { :author => @user1.person, :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :subject => 'not spam', :text => 'cool stuff'} 3.times do - cnv = Conversation.create(@conversation_hash) - Message.create(@message_hash.merge({:conversation_id => cnv.id})) + cnv = Conversation.create(hash) end get :index @@ -38,34 +36,38 @@ describe '#create' do before do - @message_hash = {:conversation => { - :contact_ids => [@user1.contacts.first.id], - :subject => "secret stuff"}, - :message => {:text => "text"} - } + @hash = {:conversation => { + :contact_ids => [@user1.contacts.first.id], + :subject => "secret stuff", + :text => 'text'}} end it 'creates a conversation' do lambda { - post :create, @message_hash + post :create, @hash }.should change(Conversation, :count).by(1) end it 'creates a message' do lambda { - post :create, @message_hash + post :create, @hash }.should change(Message, :count).by(1) end + + it 'sets the author to the current_user' do + pending + @hash[:author] = Factory.create(:user) + post :create, @hash + Message.first.author.should == @user1.person + Conversation.first.author.should == @user1.person + end end describe '#show' do before do - conversation_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], - :subject => 'not spam' } - message_hash = {:author => @user1.person, :text => 'cool stuff'} - - @conversation = Conversation.create(conversation_hash) - @message = Message.create(message_hash.merge({:conversation_id => @conversation.id})) + hash = { :author => @user1.person, :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :subject => 'not spam', :text => 'cool stuff'} + @conversation = Conversation.create(hash) end it 'succeeds' do diff --git a/spec/factories.rb b/spec/factories.rb index 1e784e76186..9c6485da7e2 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -85,8 +85,8 @@ def r_str Factory.define(:comment) do |comment| comment.sequence(:text) {|n| "#{n} cats"} - comment.association(:person) - comment.association :post, :factory => :status_message + comment.association(:author, :factory => :person) + comment.association(:post, :factory => :status_message) end Factory.define(:notification) do |n| diff --git a/spec/intergration/receiving_spec.rb b/spec/intergration/receiving_spec.rb index 5c0b52b03d0..703e9707d9f 100644 --- a/spec/intergration/receiving_spec.rb +++ b/spec/intergration/receiving_spec.rb @@ -197,7 +197,7 @@ def receive_with_zord(user, person, xml) post_in_db.comments.should == [] receive_with_zord(@user2, @user1.person, @xml) - post_in_db.comments(true).first.person.should == @user3.person + post_in_db.comments(true).first.author.should == @user3.person end it 'should correctly marshal a stranger for the downstream user' do @@ -225,7 +225,7 @@ def receive_with_zord(user, person, xml) receive_with_zord(@user2, @user1.person, @xml) - post_in_db.comments(true).first.person.should == remote_person + post_in_db.comments(true).first.author.should == remote_person end end diff --git a/spec/lib/data_conversion/import_to_mysql_spec.rb b/spec/lib/data_conversion/import_to_mysql_spec.rb index 639ff7efbc6..8d55ad6a0d3 100644 --- a/spec/lib/data_conversion/import_to_mysql_spec.rb +++ b/spec/lib/data_conversion/import_to_mysql_spec.rb @@ -412,7 +412,7 @@ def delete_everything @migrator.process_raw_comments comment = Comment.first comment.post_id.should == Post.where(:mongo_id => "4d2b6ebecc8cb43cc2000029").first.id - comment.person_id.should == Person.where(:mongo_id => "4d2b6eb7cc8cb43cc2000017").first.id + comment.author_id.should == Person.where(:mongo_id => "4d2b6eb7cc8cb43cc2000017").first.id end end describe "notifications" do diff --git a/spec/lib/diaspora/parser_spec.rb b/spec/lib/diaspora/parser_spec.rb index 5645726dc68..b680c5b3773 100644 --- a/spec/lib/diaspora/parser_spec.rb +++ b/spec/lib/diaspora/parser_spec.rb @@ -20,7 +20,7 @@ describe "parsing compliant XML object" do it 'should be able to correctly parse comment fields' do post = @user1.post :status_message, :message => "hello", :to => @aspect1.id - comment = Factory.create(:comment, :post => post, :person => @person, :diaspora_handle => @person.diaspora_handle, :text => "Freedom!") + comment = Factory.create(:comment, :post => post, :author => @person, :diaspora_handle => @person.diaspora_handle, :text => "Freedom!") comment.delete xml = comment.to_diaspora_xml comment_from_xml = Diaspora::Parser.from_xml(xml) diff --git a/spec/lib/fake_spec.rb b/spec/lib/fake_spec.rb index c8d8c1f981a..9ea25519efc 100644 --- a/spec/lib/fake_spec.rb +++ b/spec/lib/fake_spec.rb @@ -8,7 +8,7 @@ @people << post.person 4.times do comment = Factory(:comment, :post => post) - @people << comment.person + @people << comment.author end @posts << post end @@ -30,7 +30,7 @@ end end describe PostsFake::Fake do - include Rails.application.routes.url_helpers + include Rails.application.routes.url_helpers before do @post = mock() @fakes = mock() diff --git a/spec/lib/postzord/dispatch_spec.rb b/spec/lib/postzord/dispatch_spec.rb index 6886311d79a..6fa20bccb84 100644 --- a/spec/lib/postzord/dispatch_spec.rb +++ b/spec/lib/postzord/dispatch_spec.rb @@ -134,7 +134,7 @@ end context "remote raphael" do before do - @comment = Factory.build(:comment, :person => @remote_raphael, :post => @post) + @comment = Factory.build(:comment, :author => @remote_raphael, :post => @post) @comment.save @mailman = Postzord::Dispatch.new(@local_luke, @comment) end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index 895319c30e6..ee4b662892e 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -3,7 +3,7 @@ # the COPYRIGHT file. require 'spec_helper' -require File.join(Rails.root, "spec", "lib", "diaspora", "relayable_spec") +require File.join(Rails.root, "spec", "shared_behaviors", "relayable") describe Comment do before do @@ -80,7 +80,7 @@ @marshalled_comment = Comment.from_xml(@xml) end it 'marshals the author' do - @marshalled_comment.person.should == @commenter.person + @marshalled_comment.author.should == @commenter.person end it 'marshals the post' do @marshalled_comment.post.should == @post diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb index e500d4b85cb..96ee16a3637 100644 --- a/spec/models/conversation_spec.rb +++ b/spec/models/conversation_spec.rb @@ -9,14 +9,8 @@ @user1 = alice @user2 = bob - @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff", - :message => {:author => @user1.person, :text => 'hey'}} -=begin - @message = Message.new(:author => @user1.person, :text => "stuff") - @cnv.messages << @message - @message.save - @xml = @cnv.to_diaspora_xml -=end + @create_hash = { :author => @user1.person, :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :subject => "cool stuff", :text => 'hey'} end it 'creates a message on create' do diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb index b02b1470cbc..7c09921fbe1 100644 --- a/spec/models/message_spec.rb +++ b/spec/models/message_spec.rb @@ -3,34 +3,28 @@ # the COPYRIGHT file. require 'spec_helper' -require File.join(Rails.root, "spec", "lib", "diaspora", "relayable_spec") +require File.join(Rails.root, "spec", "shared_behaviors", "relayable") describe Message do before do @user1 = alice @user2 = bob - @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff", - :message => {:author => @user1.person, :text => "stuff"} } + @create_hash = { :author => @user1.person, :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :subject => "cool stuff", :text => "stuff"} + @cnv = Conversation.create(@create_hash) @message = @cnv.messages.first @xml = @message.to_diaspora_xml end - describe '#after_initialize' do - before do - @create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff"} - - @cnv = Conversation.new(@create_hash) - @cnv.save - @msg = Message.new(:text => "21312", :conversation => @cnv) - end + describe '#before_create' do it 'signs the message' do - @msg.author_signature.should_not be_blank + @message.author_signature.should_not be_blank end it 'signs the message author if author of conversation' do - @msg.parent_author_signature.should_not be_blank + @message.parent_author_signature.should_not be_blank end end @@ -46,6 +40,7 @@ it 'serializes the created_at time' do @xml.should include(@message.created_at.to_s) end + it 'serializes the conversation_guid time' do @xml.should include(@message.conversation.guid) end @@ -55,12 +50,12 @@ before do @local_luke, @local_leia, @remote_raphael = set_up_friends - cnv_hash = {:subject => 'cool story, bro', :participant_ids => [@local_luke.person, @local_leia.person, @remote_raphael].map(&:id), - :message => {:author => @remote_raphael, :text => 'hey'}} + cnv_hash = {:author => @remote_raphael, :participant_ids => [@local_luke.person, @local_leia.person, @remote_raphael].map(&:id), + :subject => 'cool story, bro', :text => 'hey'} @remote_parent = Conversation.create(cnv_hash.dup) - cnv_hash[:message][:author] = @local_luke.person + cnv_hash[:author] = @local_luke.person @local_parent = Conversation.create(cnv_hash) msg_hash = {:author => @local_luke.person, :text => 'yo', :conversation => @local_parent} diff --git a/spec/models/person_spec.rb b/spec/models/person_spec.rb index 0d799e20705..ab1778f0c36 100644 --- a/spec/models/person_spec.rb +++ b/spec/models/person_spec.rb @@ -154,8 +154,8 @@ end it "deletes a person's comments on person deletion" do - Factory.create(:comment, :person_id => @deleter.id, :diaspora_handle => @deleter.diaspora_handle, :text => "i love you", :post => @other_status) - Factory.create(:comment, :person_id => @person.id,:diaspora_handle => @person.diaspora_handle, :text => "you are creepy", :post => @other_status) + Factory.create(:comment, :author_id => @deleter.id, :diaspora_handle => @deleter.diaspora_handle, :text => "i love you", :post => @other_status) + Factory.create(:comment, :author_id => @person.id,:diaspora_handle => @person.diaspora_handle, :text => "you are creepy", :post => @other_status) lambda {@deleter.destroy}.should change(Comment, :count).by(-1) end diff --git a/spec/lib/diaspora/relayable_spec.rb b/spec/shared_behaviors/relayable.rb similarity index 99% rename from spec/lib/diaspora/relayable_spec.rb rename to spec/shared_behaviors/relayable.rb index 0f3d99f2589..b24a3a50704 100644 --- a/spec/lib/diaspora/relayable_spec.rb +++ b/spec/shared_behaviors/relayable.rb @@ -61,6 +61,7 @@ end it 'sockets to the user' do + pending @object_by_recipient.should_receive(:socket_to_user).exactly(3).times @object_by_recipient.receive(@local_luke, @local_leia.person) end From 21fd546cd05199ec3b0c86f6c869c5d6baa0d55d Mon Sep 17 00:00:00 2001 From: danielvincent Date: Tue, 1 Mar 2011 18:05:05 -0800 Subject: [PATCH 11/29] posts now have authors instead of people --- app/controllers/photos_controller.rb | 12 ++-- app/controllers/posts_controller.rb | 4 +- app/controllers/status_messages_controller.rb | 4 +- app/helpers/notifications_helper.rb | 2 +- app/helpers/sockets_helper.rb | 8 +-- app/mailers/notifier.rb | 2 +- app/models/data_point.rb | 2 +- app/models/mention.rb | 4 +- app/models/person.rb | 2 +- app/models/post.rb | 21 +++---- app/models/post_visibility.rb | 2 +- app/models/retraction.rb | 2 +- app/models/status_message.rb | 4 +- app/models/user.rb | 6 +- app/views/photos/_photo.haml | 2 +- app/views/photos/show.html.haml | 4 +- app/views/shared/_stream_element.html.haml | 8 +-- app/views/shared/_stream_element.mobile.haml | 4 +- app/views/status_messages/show.html.haml | 2 +- app/views/status_messages/show.mobile.haml | 2 +- .../20110301014507_rename_person_to_author.rb | 6 ++ db/schema.rb | 6 +- lib/diaspora/exporter.rb | 4 +- lib/diaspora/user/connecting.rb | 4 +- lib/diaspora/user/querying.rb | 2 +- lib/fake.rb | 2 +- lib/postzord/receiver.rb | 3 +- spec/controllers/photos_controller_spec.rb | 6 +- .../status_messages_controller_spec.rb | 6 +- spec/factories.rb | 6 +- spec/intergration/receiving_spec.rb | 56 +++++++++---------- .../data_conversion/import_to_mysql_spec.rb | 8 +-- spec/lib/fake_spec.rb | 2 +- spec/lib/postzord/dispatch_spec.rb | 4 +- spec/lib/web_hooks_spec.rb | 10 ++-- spec/mailers/notifier_spec.rb | 4 +- spec/misc_spec.rb | 2 +- spec/models/aspect_spec.rb | 2 +- spec/models/comment_spec.rb | 2 +- spec/models/conversation_visibility_spec.rb | 2 +- spec/models/jobs/mail_mentioned_spec.rb | 4 +- spec/models/mention_spec.rb | 2 +- spec/models/person_spec.rb | 8 +-- spec/models/photo_spec.rb | 14 ++--- spec/models/post_spec.rb | 2 +- spec/models/post_visibility_spec.rb | 2 +- spec/models/status_message_spec.rb | 10 ++-- spec/models/user/attack_vectors_spec.rb | 4 +- spec/models/user/querying_spec.rb | 4 +- spec/models/user_spec.rb | 2 +- 50 files changed, 139 insertions(+), 147 deletions(-) diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index e426d4c6094..baf48d0a75a 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -30,7 +30,7 @@ def index end @posts = current_user.visible_photos.where( - :person_id => @person.id + :author_id => @person.id ).paginate(:page => params[:page]) render 'people/show' @@ -94,8 +94,8 @@ def create end def make_profile_photo - person_id = current_user.person.id - @photo = Photo.where(:id => params[:photo_id], :person_id => person_id).first + author_id = current_user.person.id + @photo = Photo.where(:id => params[:photo_id], :author_id => author_id).first if @photo profile_hash = {:image_url => @photo.url(:thumb_large), @@ -108,7 +108,7 @@ def make_profile_photo :image_url => @photo.url(:thumb_large), :image_url_medium => @photo.url(:thumb_medium), :image_url_small => @photo.url(:thumb_small), - :person_id => person_id}, + :author_id => author_id}, :status => 201} end else @@ -139,8 +139,8 @@ def destroy end def show - @photo = current_user.visible_photos.where(:id => params[:id]).includes(:person, :status_message => :photos).first - @photo ||= Photo.where(:public => true, :id => params[:id]).includes(:person, :status_message => :photos).first + @photo = current_user.visible_photos.where(:id => params[:id]).includes(:author, :status_message => :photos).first + @photo ||= Photo.where(:public => true, :id => params[:id]).includes(:author, :status_message => :photos).first if @photo @parent = @photo.status_message diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 0ee777a8334..73ed1f772c5 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -11,11 +11,11 @@ class PostsController < ApplicationController skip_before_filter :set_grammatical_gender def show - @post = Post.where(:id => params[:id], :public => true).includes(:person, :comments => :person).first + @post = Post.where(:id => params[:id], :public => true).includes(:author, :comments => :author).first if @post @landing_page = true - @person = @post.person + @person = @post.author if @person.owner_id I18n.locale = @person.owner.language render "posts/#{@post.class.to_s.underscore}", :layout => true diff --git a/app/controllers/status_messages_controller.rb b/app/controllers/status_messages_controller.rb index 51e389bf88a..da41db3a1cd 100644 --- a/app/controllers/status_messages_controller.rb +++ b/app/controllers/status_messages_controller.rb @@ -47,7 +47,7 @@ def create :partial => 'shared/stream_element', :locals => { :post => @status_message, - :person => @status_message.person, + :author => @status_message.author, :photos => @status_message.photos, :comments => [], :all_aspects => current_user.aspects, @@ -61,7 +61,7 @@ def create end else respond_to do |format| - format.js { render :json =>{:errors => @status_message.errors.full_messages}, :status => 406 } + format.js { render :json =>{:errors => @status_message.errors.full_messages}, :status => 406 } format.html {redirect_to :back} end end diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 788d7ab03e3..44740644a51 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -22,7 +22,7 @@ def object_link(note) elsif note.instance_of?(Notifications::AlsoCommented) post = Post.where(:id => note.target_id).first if post - "#{translation(target_type, post.person.name)} #{link_to t('notifications.post'), object_path(post)}".html_safe + "#{translation(target_type, post.author.name)} #{link_to t('notifications.post'), object_path(post)}".html_safe else t('notifications.also_commented_deleted') end diff --git a/app/helpers/sockets_helper.rb b/app/helpers/sockets_helper.rb index b48f5d9a73f..6e57add60dd 100644 --- a/app/helpers/sockets_helper.rb +++ b/app/helpers/sockets_helper.rb @@ -26,11 +26,11 @@ def action_hash(user, object, opts={}) if object.is_a? StatusMessage post_hash = {:post => object, - :person => object.person, + :author => object.author, :photos => object.photos, :comments => object.comments.map{|c| {:comment => c, - :person => c.person + :author => c.author } }, :current_user => user, @@ -70,12 +70,12 @@ def action_hash(user, object, opts={}) if object.is_a? Comment post = object.post action_hash[:comment_id] = object.id - action_hash[:my_post?] = (post.person.owner_id == uid) + action_hash[:my_post?] = (post.author.owner_id == uid) action_hash[:post_guid] = post.guid end - action_hash[:mine?] = object.person && (object.person.owner_id == uid) if object.respond_to?(:person) + action_hash[:mine?] = object.author && (object.author.owner_id == uid) if object.respond_to?(:author) I18n.locale = old_locale unless user.nil? diff --git a/app/mailers/notifier.rb b/app/mailers/notifier.rb index 13b90c43ef6..9f54eeec53f 100644 --- a/app/mailers/notifier.rb +++ b/app/mailers/notifier.rb @@ -84,7 +84,7 @@ def also_commented(recipient_id, sender_id, comment_id) @receiver = User.find_by_id(recipient_id) @sender = Person.find_by_id(sender_id) @comment = Comment.find_by_id(comment_id) - @post_author_name = @comment.post.person.name + @post_author_name = @comment.post.author.name log_mail(recipient_id, sender_id, 'comment_on_post') diff --git a/app/models/data_point.rb b/app/models/data_point.rb index 980577f6a93..93ee8933878 100644 --- a/app/models/data_point.rb +++ b/app/models/data_point.rb @@ -3,7 +3,7 @@ class DataPoint < ActiveRecord::Base def self.users_with_posts_on_day(time, number) sql = ActiveRecord::Base.connection() - value = sql.execute("SELECT COUNT(*) FROM (SELECT COUNT(*) AS post_sum, person_id FROM posts WHERE created_at >= '#{(time - 1.days).utc.to_datetime}' AND created_at <= '#{time.utc.to_datetime}' GROUP BY person_id) AS t1 WHERE t1.post_sum = #{number};").first[0] + value = sql.execute("SELECT COUNT(*) FROM (SELECT COUNT(*) AS post_sum, author_id FROM posts WHERE created_at >= '#{(time - 1.days).utc.to_datetime}' AND created_at <= '#{time.utc.to_datetime}' GROUP BY author_id) AS t1 WHERE t1.post_sum = #{number};").first[0] self.new(:key => number.to_s, :value => value) end end diff --git a/app/models/mention.rb b/app/models/mention.rb index 86759d08b02..98d0c3ef5fa 100644 --- a/app/models/mention.rb +++ b/app/models/mention.rb @@ -13,8 +13,8 @@ class Mention < ActiveRecord::Base after_destroy :delete_notification def notify_recipient - Rails.logger.info "event=mention_sent id=#{self.id} to=#{person.diaspora_handle} from=#{post.person.diaspora_handle}" - Notification.notify(person.owner, self, post.person) unless person.remote? + Rails.logger.info "event=mention_sent id=#{self.id} to=#{person.diaspora_handle} from=#{post.author.diaspora_handle}" + Notification.notify(person.owner, self, post.author) unless person.remote? end diff --git a/app/models/person.rb b/app/models/person.rb index e3c1a03eea7..5fe05e8f72b 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -26,7 +26,7 @@ def downcase_diaspora_handle end has_many :contacts #Other people's contacts for this person - has_many :posts #his own posts + has_many :posts, :foreign_key => :author_id #his own posts belongs_to :owner, :class_name => 'User' diff --git a/app/models/post.rb b/app/models/post.rb index bd044b5acb9..adbba462912 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -17,35 +17,28 @@ class Post < ActiveRecord::Base has_many :post_visibilities has_many :aspects, :through => :post_visibilities has_many :mentions, :dependent => :destroy - belongs_to :person + belongs_to :author, :class_name => 'Person' cattr_reader :per_page @@per_page = 10 after_destroy :propogate_retraction - def author - self.person - end - def author= author - self.person = author - end - def user_refs self.post_visibilities.count end def diaspora_handle= nd - self.person = Person.where(:diaspora_handle => nd).first + self.author = Person.where(:diaspora_handle => nd).first write_attribute(:diaspora_handle, nd) end def self.diaspora_initialize params new_post = self.new params.to_hash - new_post.person = params[:person] + new_post.author = params[:author] new_post.public = params[:public] if params[:public] new_post.pending = params[:pending] if params[:pending] - new_post.diaspora_handle = new_post.person.diaspora_handle + new_post.diaspora_handle = new_post.author.diaspora_handle new_post end @@ -53,7 +46,7 @@ def as_json(opts={}) { :post => { :id => self.id, - :person => self.person.as_json, + :author => self.author.as_json, } } end @@ -74,7 +67,7 @@ def receive(user, person) #you know about it, and it is not mutable local_post = Post.where(:guid => self.guid).first - if local_post && local_post.person_id == self.person_id + if local_post && local_post.author_id == self.author_id known_post = user.visible_posts(:guid => self.guid).first if known_post if known_post.mutable? @@ -99,7 +92,7 @@ def receive(user, person) protected def propogate_retraction - self.person.owner.retract(self) if self.person.owner + self.author.owner.retract(self) if self.author.owner end end diff --git a/app/models/post_visibility.rb b/app/models/post_visibility.rb index 99b520de645..d922051724b 100644 --- a/app/models/post_visibility.rb +++ b/app/models/post_visibility.rb @@ -10,6 +10,6 @@ class PostVisibility < ActiveRecord::Base belongs_to :post validates_presence_of :post has_one :user, :through => :aspect - has_one :person, :through => :post + has_one :person, :through => :post, :foreign_key => :author_id end diff --git a/app/models/retraction.rb b/app/models/retraction.rb index c1717e4b03e..fa8e9e0b59f 100644 --- a/app/models/retraction.rb +++ b/app/models/retraction.rb @@ -54,7 +54,7 @@ def receive(user, person) return end user.disconnected_by(self.target) - elsif self.target.nil? || self.target.person != self.person + elsif self.target.nil? || self.target.author != self.person Rails.logger.info("event=retraction status=abort reason='no post found authored by retractor' sender=#{person.diaspora_handle} post_guid=#{post_guid}") else self.perform(user) diff --git a/app/models/status_message.rb b/app/models/status_message.rb index 99c94e692c1..0e306d41982 100644 --- a/app/models/status_message.rb +++ b/app/models/status_message.rb @@ -80,8 +80,8 @@ def to_activity <<-XML #{x(self.message)} - - #{person.url}posts/#{self.id} + + #{self.author.url}posts/#{self.id} #{self.created_at.xmlschema} #{self.updated_at.xmlschema} http://activitystrea.ms/schema/1.0/post diff --git a/app/models/user.rb b/app/models/user.rb index 3b8f58c5e03..5373e7ed86a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -87,8 +87,8 @@ def add_contact_to_aspect(contact, aspect) ######## Posting ######## def build_post(class_name, opts = {}) - opts[:person] = self.person - opts[:diaspora_handle] = opts[:person].diaspora_handle + opts[:author] = self.person + opts[:diaspora_handle] = opts[:author].diaspora_handle model_class = class_name.to_s.camelize.constantize model_class.diaspora_initialize(opts) @@ -108,7 +108,7 @@ def update_post(post, post_hash = {}) def add_post_to_aspects(post) Rails.logger.debug("event=add_post_to_aspects user_id=#{self.id} post_id=#{post.id}") - add_to_streams(post, self.aspects_with_person(post.person)) + add_to_streams(post, self.aspects_with_person(post.author)) post end diff --git a/app/views/photos/_photo.haml b/app/views/photos/_photo.haml index a601735ae86..9d8b8ed18b7 100644 --- a/app/views/photos/_photo.haml +++ b/app/views/photos/_photo.haml @@ -10,5 +10,5 @@ %p.photo_description = post.caption -= link_to t('.view_all', :name => post.person.name), person_photos_path(post.person), :class => "small_text" += link_to t('.view_all', :name => post.author.name), person_photos_path(post.author), :class => "small_text" diff --git a/app/views/photos/show.html.haml b/app/views/photos/show.html.haml index 4b825fee371..b2075e701bd 100644 --- a/app/views/photos/show.html.haml +++ b/app/views/photos/show.html.haml @@ -14,7 +14,7 @@ =link_to "#{t('next')} →", @next_photo, :rel => 'prefetch', :id => 'photo_show_right' #original_post_info - = render 'shared/author_info', :person => @photo.person, :post => @photo + = render 'shared/author_info', :person => @photo.author, :post => @photo #photo_container #show_photo{:data=>{:guid=>@photo.id}} @@ -28,7 +28,7 @@ = @photo.caption - if @ownership - .photo_options{:data=>{:actor=>"#{@photo.person.owner.id}",:actor_person=>"#{@photo.person.id}",:image_url=>"#{@photo.url(:thumb_large)}"}} + .photo_options{:data=>{:actor=>"#{@photo.author.owner.id}", :actor_person => "#{@photo.author.id}", :image_url => "#{@photo.url(:thumb_large)}"}} = link_to t('.make_profile_photo'), {:controller => "photos", :action => "make_profile_photo", :photo_id => @photo.id}, :remote => true, :class => 'make_profile_photo' | = link_to t('.edit'), '#', :id => "edit_photo_toggle" diff --git a/app/views/shared/_stream_element.html.haml b/app/views/shared/_stream_element.html.haml index e70d96a2825..18278882de1 100644 --- a/app/views/shared/_stream_element.html.haml +++ b/app/views/shared/_stream_element.html.haml @@ -3,18 +3,18 @@ -# the COPYRIGHT file. .stream_element{:data=>{:guid=>post.id}} - - if post.person.owner_id == current_user.id + - if post.author.owner_id == current_user.id .right.hidden.controls - reshare_aspects = aspects_without_post(all_aspects, post) - unless reshare_aspects.empty? = render 'shared/reshare', :aspects => reshare_aspects, :post => post = link_to image_tag('deletelabel.png'), status_message_path(post), :confirm => t('are_you_sure'), :method => :delete, :remote => true, :class => "delete", :title => t('delete') - = person_image_link(post.person, :size => :thumb_small) + = person_image_link(post.author, :size => :thumb_small) .content %strong - = person_link(post.person) + = person_link(post.author) = render 'status_messages/status_message', :post => post, :photos => post.photos @@ -23,7 +23,7 @@ %span.aspect_badges %span.aspect_badge.public = t('the_world') - - elsif post.person.owner_id == current_user.id + - elsif post.author.owner_id == current_user.id %span.aspect_badges = aspect_badges(aspects_with_post(all_aspects, post)) diff --git a/app/views/shared/_stream_element.mobile.haml b/app/views/shared/_stream_element.mobile.haml index 00746b0a943..b313860d1e3 100644 --- a/app/views/shared/_stream_element.mobile.haml +++ b/app/views/shared/_stream_element.mobile.haml @@ -7,11 +7,11 @@ %span.time = time_ago_in_words(post.created_at) - = person_image_link(post.person, :size => :thumb_small) + = person_image_link(post.author, :size => :thumb_small) .content .from - = person_link(post.person) + = person_link(post.author) = render 'status_messages/status_message', :post => post, :photos => post.photos diff --git a/app/views/status_messages/show.html.haml b/app/views/status_messages/show.html.haml index a5dc196c42c..e79b8947177 100644 --- a/app/views/status_messages/show.html.haml +++ b/app/views/status_messages/show.html.haml @@ -5,7 +5,7 @@ .span-16.append-4.prepend-4.last #original_post_info - = render 'shared/author_info', :person => @status_message.person, :post => @status_message + = render 'shared/author_info', :person => @status_message.author, :post => @status_message #show_text %p diff --git a/app/views/status_messages/show.mobile.haml b/app/views/status_messages/show.mobile.haml index 8efa3f0d771..27585511741 100644 --- a/app/views/status_messages/show.mobile.haml +++ b/app/views/status_messages/show.mobile.haml @@ -3,7 +3,7 @@ -# the COPYRIGHT file. #show_content{:data=>{:guid=>@status_message.id}} - = render 'shared/author_info', :person => @status_message.person, :post => @status_message + = render 'shared/author_info', :person => @status_message.author, :post => @status_message %p = markdownify(@status_message.message, :youtube_maps => @status_message[:youtube_titles]) diff --git a/db/migrate/20110301014507_rename_person_to_author.rb b/db/migrate/20110301014507_rename_person_to_author.rb index 30bbdadff68..1233924455e 100644 --- a/db/migrate/20110301014507_rename_person_to_author.rb +++ b/db/migrate/20110301014507_rename_person_to_author.rb @@ -1,13 +1,19 @@ class RenamePersonToAuthor < ActiveRecord::Migration def self.up remove_foreign_key(:comments, :people) + remove_foreign_key(:posts, :people) rename_column :comments, :person_id, :author_id + rename_column :posts, :person_id, :author_id add_foreign_key(:comments, :people, :column => :author_id, :dependent => :delete) + add_foreign_key(:posts, :people, :column => :author_id, :dependent => :delete) end def self.down remove_foreign_key(:comments, :people, :column => :author_id) + remove_foreign_key(:posts, :people, :column => :author_id) rename_column :comments, :author_id, :person_id + rename_column :posts, :author_id, :person_id add_foreign_key(:comments, :people, :dependent => :delete) + add_foreign_key(:posts, :people, :dependent => :delete) end end diff --git a/db/schema.rb b/db/schema.rb index 469ad4f92ff..e85ca62fd23 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -389,7 +389,7 @@ add_index "post_visibilities", ["post_id"], :name => "index_post_visibilities_on_post_id" create_table "posts", :force => true do |t| - t.integer "person_id", :null => false + t.integer "author_id", :null => false t.boolean "public", :default => false, :null => false t.string "diaspora_handle" t.string "guid", :null => false @@ -408,9 +408,9 @@ t.string "mongo_id" end + add_index "posts", ["author_id"], :name => "index_posts_on_person_id" add_index "posts", ["guid"], :name => "index_posts_on_guid" add_index "posts", ["mongo_id"], :name => "index_posts_on_mongo_id" - add_index "posts", ["person_id"], :name => "index_posts_on_person_id" add_index "posts", ["status_message_id", "pending"], :name => "index_posts_on_status_message_id_and_pending" add_index "posts", ["status_message_id"], :name => "index_posts_on_status_message_id" add_index "posts", ["type", "pending", "id"], :name => "index_posts_on_type_and_pending_and_id" @@ -522,7 +522,7 @@ add_foreign_key "notification_actors", "notifications", :name => "notification_actors_notification_id_fk", :dependent => :delete - add_foreign_key "posts", "people", :name => "posts_person_id_fk", :dependent => :delete + add_foreign_key "posts", "people", :name => "posts_author_id_fk", :column => "author_id", :dependent => :delete add_foreign_key "profiles", "people", :name => "profiles_person_id_fk", :dependent => :delete diff --git a/lib/diaspora/exporter.rb b/lib/diaspora/exporter.rb index f1815141328..adb33d26da7 100644 --- a/lib/diaspora/exporter.rb +++ b/lib/diaspora/exporter.rb @@ -37,7 +37,7 @@ def execute(user) #} xml.post_ids { - aspect.posts.find_all_by_person_id(user_person_id).each do |post| + aspect.posts.find_all_by_author_id(user_person_id).each do |post| xml.post_id post.id end } @@ -64,7 +64,7 @@ def execute(user) } xml.posts { - user.raw_visible_posts.find_all_by_person_id(user_person_id).each do |post| + user.raw_visible_posts.find_all_by_author_id(user_person_id).each do |post| #post.comments.each do |comment| # post_doc << comment.to_xml #end diff --git a/lib/diaspora/user/connecting.rb b/lib/diaspora/user/connecting.rb index e3a0c35fe18..a2101436581 100644 --- a/lib/diaspora/user/connecting.rb +++ b/lib/diaspora/user/connecting.rb @@ -84,9 +84,9 @@ def disconnect(bad_contact) def remove_contact(contact) bad_person_id = contact.person_id - posts = raw_visible_posts.where(:person_id => bad_person_id).all + posts = raw_visible_posts.where(:author_id => bad_person_id).all visibilities = PostVisibility.joins(:post, :aspect).where( - :posts => {:person_id => bad_person_id}, + :posts => {:author_id => bad_person_id}, :aspects => {:user_id => self.id} ) visibility_ids = visibilities.map{|v| v.id} diff --git a/lib/diaspora/user/querying.rb b/lib/diaspora/user/querying.rb index e2024526752..25c90d0f25b 100644 --- a/lib/diaspora/user/querying.rb +++ b/lib/diaspora/user/querying.rb @@ -7,7 +7,7 @@ module UserModules module Querying def find_visible_post_by_id( id ) - self.raw_visible_posts.where(:id => id).includes({:person => :profile}, {:comments => {:author => :profile}}, :photos).first + self.raw_visible_posts.where(:id => id).includes({:author => :profile}, {:comments => {:author => :profile}}, :photos).first end def raw_visible_posts diff --git a/lib/fake.rb b/lib/fake.rb index fbf25e66bb4..8149bda4742 100644 --- a/lib/fake.rb +++ b/lib/fake.rb @@ -8,7 +8,7 @@ def method_missing(method, *args, &block) def initialize(posts) author_ids = [] posts.each do |p| - author_ids << p.person_id + author_ids << p.author_id p.comments.each do |c| author_ids << c.author_id end diff --git a/lib/postzord/receiver.rb b/lib/postzord/receiver.rb index bfc56ab110e..7281729d362 100644 --- a/lib/postzord/receiver.rb +++ b/lib/postzord/receiver.rb @@ -51,7 +51,7 @@ def salmon def xml_author if @object.is_a?(Comment) #if A and B are friends, and A sends B a comment from C, we delegate the validation to the owner of the post being commented on - xml_author = @user.owns?(@object.post) ? @object.diaspora_handle : @object.post.person.diaspora_handle + xml_author = @user.owns?(@object.post) ? @object.diaspora_handle : @object.post.author.diaspora_handle @author = Webfinger.new(@object.diaspora_handle).fetch else xml_author = @object.diaspora_handle @@ -82,6 +82,7 @@ def validate_object end if @author + @object.author = @author if @object.respond_to? :author= @object.person = @author if @object.respond_to? :person= end diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb index e2e4423326b..d7f0ceaf5a5 100644 --- a/spec/controllers/photos_controller_spec.rb +++ b/spec/controllers/photos_controller_spec.rb @@ -18,7 +18,7 @@ @photo2 = @user2.post(:photo, :user_file => uploaded_photo, :to => @aspect2.id, :public => true) @controller.stub!(:current_user).and_return(@user1) - sign_in :user, @user1 + sign_in :user, @user1 request.env["HTTP_REFERER"] = '' end @@ -128,9 +128,9 @@ it "doesn't overwrite random attributes" do new_user = Factory.create(:user) - params = { :caption => "now with lasers!", :person_id => new_user.id } + params = { :caption => "now with lasers!", :author_id => new_user.id } put :update, :id => @photo1.id, :photo => params - @photo1.reload.person_id.should == @user1.person.id + @photo1.reload.author_id.should == @user1.person.id end it 'redirects if you do not have access to the post' do diff --git a/spec/controllers/status_messages_controller_spec.rb b/spec/controllers/status_messages_controller_spec.rb index 4f3afa575b1..f6c2b7591bb 100644 --- a/spec/controllers/status_messages_controller_spec.rb +++ b/spec/controllers/status_messages_controller_spec.rb @@ -73,11 +73,11 @@ post :create, status_message_hash end - it "doesn't overwrite person_id" do - status_message_hash[:status_message][:person_id] = @user2.person.id + it "doesn't overwrite author_id" do + status_message_hash[:status_message][:author_id] = @user2.person.id post :create, status_message_hash new_message = StatusMessage.find_by_message(status_message_hash[:status_message][:message]) - new_message.person_id.should == @user1.person.id + new_message.author_id.should == @user1.person.id end it "doesn't overwrite id" do diff --git a/spec/factories.rb b/spec/factories.rb index 9c6485da7e2..72cc39a056d 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -62,11 +62,11 @@ def r_str aspect.association :user end -Factory.define :status_message do |m| +Factory.define(:status_message) do |m| m.sequence(:message) { |n| "jimmy's #{n} whales" } - m.association :person + m.association :author, :factory => :person m.after_build do|m| - m.diaspora_handle = m.person.diaspora_handle + m.diaspora_handle = m.author.diaspora_handle end end diff --git a/spec/intergration/receiving_spec.rb b/spec/intergration/receiving_spec.rb index 703e9707d9f..16e459086ed 100644 --- a/spec/intergration/receiving_spec.rb +++ b/spec/intergration/receiving_spec.rb @@ -115,35 +115,29 @@ def receive_with_zord(user, person, xml) @user1.raw_visible_posts.should_not include @status_message end - it 'deletes a post if the noone links to it' do - person = Factory(:person) - @user1.activate_contact(person, @aspect) - post = Factory.create(:status_message, :person => person) - post.post_visibilities.should be_empty - receive_with_zord(@user1, person, post.to_diaspora_xml) - @aspect.post_visibilities.reset - @aspect.posts(true).should include(post) - post.post_visibilities.reset - post.post_visibilities.length.should == 1 + context 'dependant delete' do + before do + @person = Factory(:person) + @user1.activate_contact(@person, @aspect) + @post = Factory.create(:status_message, :author => @person) + @post.post_visibilities.should be_empty + receive_with_zord(@user1, @person, @post.to_diaspora_xml) + @aspect.post_visibilities.reset + @aspect.posts(true).should include(@post) + @post.post_visibilities.reset + end - lambda { - @user1.disconnected_by(person) - }.should change(Post, :count).by(-1) - end - it 'deletes post_visibilities on disconnected by' do - person = Factory(:person) - @user1.activate_contact(person, @aspect) - post = Factory.create(:status_message, :person => person) - post.post_visibilities.should be_empty - receive_with_zord(@user1, person, post.to_diaspora_xml) - @aspect.post_visibilities.reset - @aspect.posts(true).should include(post) - post.post_visibilities.reset - post.post_visibilities.length.should == 1 + it 'deletes a post if the noone links to it' do + lambda { + @user1.disconnected_by(@person) + }.should change(Post, :count).by(-1) + end - lambda { - @user1.disconnected_by(person) - }.should change{post.post_visibilities(true).count}.by(-1) + it 'deletes post_visibilities on disconnected by' do + lambda { + @user1.disconnected_by(@person) + }.should change{@post.post_visibilities(true).count}.by(-1) + end end it 'should keep track of user references for one person ' do @status_message.reload @@ -254,11 +248,11 @@ def receive_with_zord(user, person, xml) describe 'receiving mulitple versions of the same post from a remote pod' do before do @local_luke, @local_leia, @remote_raphael = set_up_friends - @post = Factory.build(:status_message, :message => 'hey', :guid => 12313123, :person => @remote_raphael, :created_at => 5.days.ago, :updated_at => 5.days.ago) + @post = Factory.build(:status_message, :message => 'hey', :guid => 12313123, :author=> @remote_raphael, :created_at => 5.days.ago, :updated_at => 5.days.ago) end it 'does not update created_at or updated_at when two people save the same post' do - @post = Factory.build(:status_message, :message => 'hey', :guid => 12313123, :person => @remote_raphael, :created_at => 5.days.ago, :updated_at => 5.days.ago) + @post = Factory.build(:status_message, :message => 'hey', :guid => 12313123, :author=> @remote_raphael, :created_at => 5.days.ago, :updated_at => 5.days.ago) xml = @post.to_diaspora_xml receive_with_zord(@local_luke, @remote_raphael, xml) sleep(2) @@ -269,11 +263,11 @@ def receive_with_zord(user, person, xml) end it 'does not update the post if a new one is sent with a new created_at' do - @post = Factory.build(:status_message, :message => 'hey', :guid => 12313123, :person => @remote_raphael, :created_at => 5.days.ago) + @post = Factory.build(:status_message, :message => 'hey', :guid => 12313123, :author => @remote_raphael, :created_at => 5.days.ago) old_time = @post.created_at xml = @post.to_diaspora_xml receive_with_zord(@local_luke, @remote_raphael, xml) - @post = Factory.build(:status_message, :message => 'hey', :guid => 12313123, :person => @remote_raphael, :created_at => 2.days.ago) + @post = Factory.build(:status_message, :message => 'hey', :guid => 12313123, :author => @remote_raphael, :created_at => 2.days.ago) receive_with_zord(@local_luke, @remote_raphael, xml) (Post.find_by_guid @post.guid).created_at.day.should == old_time.day end diff --git a/spec/lib/data_conversion/import_to_mysql_spec.rb b/spec/lib/data_conversion/import_to_mysql_spec.rb index 8d55ad6a0d3..84020690514 100644 --- a/spec/lib/data_conversion/import_to_mysql_spec.rb +++ b/spec/lib/data_conversion/import_to_mysql_spec.rb @@ -357,8 +357,8 @@ def delete_everything post.image.should be_nil post.mongo_id.should == "4d2b6ebecc8cb43cc2000027" post.guid.should == post.mongo_id - post.person_id.should == Person.where(:mongo_id => mongo_post.person_mongo_id).first.id - post.diaspora_handle.should == post.person.diaspora_handle + post.author_id.should == Person.where(:mongo_id => mongo_post.person_mongo_id).first.id + post.diaspora_handle.should == post.author.diaspora_handle post.message.should == "User2 can see this" post.created_at.should == mongo_post.created_at post.updated_at.should == mongo_post.updated_at @@ -379,8 +379,8 @@ def delete_everything post.image.file.file.should =~ /mUKUIxkYlV4d2b6ebfcc8cb43cc200002d\.png/ post.mongo_id.should == "4d2b6ebfcc8cb43cc200002d" post.guid.should == post.mongo_id - post.person_id.should == Person.where(:mongo_id => mongo_post.person_mongo_id).first.id - post.diaspora_handle.should == post.person.diaspora_handle + post.author_id.should == Person.where(:mongo_id => mongo_post.person_mongo_id).first.id + post.diaspora_handle.should == post.author.diaspora_handle post.message.should be_nil post.created_at.should == mongo_post.created_at post.updated_at.should == mongo_post.updated_at diff --git a/spec/lib/fake_spec.rb b/spec/lib/fake_spec.rb index 9ea25519efc..0cf749568bb 100644 --- a/spec/lib/fake_spec.rb +++ b/spec/lib/fake_spec.rb @@ -5,7 +5,7 @@ @people = [] 4.times do post = Factory(:status_message) - @people << post.person + @people << post.author 4.times do comment = Factory(:comment, :post => post) @people << comment.author diff --git a/spec/lib/postzord/dispatch_spec.rb b/spec/lib/postzord/dispatch_spec.rb index 6fa20bccb84..f46391f27fc 100644 --- a/spec/lib/postzord/dispatch_spec.rb +++ b/spec/lib/postzord/dispatch_spec.rb @@ -181,7 +181,7 @@ end context "remote raphael's post is commented on by local luke" do before do - @post = Factory(:status_message, :person => @remote_raphael) + @post = Factory(:status_message, :author => @remote_raphael) @comment = @local_luke.build_comment "yo", :on => @post @comment.save @mailman = Postzord::Dispatch.new(@local_luke, @comment) @@ -304,7 +304,7 @@ it 'queues Job::NotifyLocalUsers jobs' do @zord.instance_variable_get(:@object).should_receive(:socket_to_user).and_return(false) - Resque.should_receive(:enqueue).with(Job::NotifyLocalUsers, @local_user.id, @sm.class.to_s, @sm.id, @sm.person.id) + Resque.should_receive(:enqueue).with(Job::NotifyLocalUsers, @local_user.id, @sm.class.to_s, @sm.id, @sm.author.id) @zord.send(:socket_and_notify_users, [@local_user]) end end diff --git a/spec/lib/web_hooks_spec.rb b/spec/lib/web_hooks_spec.rb index 359ced22f79..9ee6d4c389c 100644 --- a/spec/lib/web_hooks_spec.rb +++ b/spec/lib/web_hooks_spec.rb @@ -5,12 +5,10 @@ require 'spec_helper' describe Diaspora::Webhooks do - before do - @user = Factory.build(:user) - @post = Factory.build(:status_message, :person => @user.person) - end - it "should add the following methods to Post on inclusion" do - @post.respond_to?(:to_diaspora_xml).should be true + user = Factory.build(:user) + post = Factory.build(:status_message, :author => user.person) + + post.respond_to?(:to_diaspora_xml).should be true end end diff --git a/spec/mailers/notifier_spec.rb b/spec/mailers/notifier_spec.rb index f3db5ab382e..968e71520f8 100644 --- a/spec/mailers/notifier_spec.rb +++ b/spec/mailers/notifier_spec.rb @@ -87,7 +87,7 @@ @sm = Factory(:status_message) @m = Mention.create(:person => @user.person, :post=> @sm) - @mail = Notifier.mentioned(@user.id, @sm.person.id, @m.id) + @mail = Notifier.mentioned(@user.id, @sm.author.id, @m.id) end it 'goes to the right person' do @mail.to.should == [@user.email] @@ -98,7 +98,7 @@ end it 'has the name of person mentioning in the body' do - @mail.body.encoded.include?(@sm.person.name).should be true + @mail.body.encoded.include?(@sm.author.name).should be true end it 'has the post text in the body' do diff --git a/spec/misc_spec.rb b/spec/misc_spec.rb index 149a4e78660..f95f443ae50 100644 --- a/spec/misc_spec.rb +++ b/spec/misc_spec.rb @@ -53,7 +53,7 @@ describe '#comment' do it "should send a user's comment on a person's post to that person" do person = Factory.create(:person) - person_status = Factory.create(:status_message, :person => person) + person_status = Factory.create(:status_message, :author => person) m = mock() m.stub!(:post) Postzord::Dispatch.should_receive(:new).and_return(m) diff --git a/spec/models/aspect_spec.rb b/spec/models/aspect_spec.rb index bf7a2548cbf..9c8f8193902 100644 --- a/spec/models/aspect_spec.rb +++ b/spec/models/aspect_spec.rb @@ -114,7 +114,7 @@ aspect = user.aspects.create(:name => 'losers') contact = aspect.contacts.create(:person => connected_person) - status_message = user.post( :status_message, :message => "hey", :to => aspect.id ) + status_message = user.post(:status_message, :message => "hey", :to => aspect.id) aspect.reload aspect.posts.include?(status_message).should be true diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index ee4b662892e..030755e3b62 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -112,7 +112,7 @@ describe 'it is relayable' do before do @local_luke, @local_leia, @remote_raphael = set_up_friends - @remote_parent = Factory.create(:status_message, :person => @remote_raphael) + @remote_parent = Factory.create(:status_message, :author => @remote_raphael) @local_parent = @local_luke.post :status_message, :message => "hi", :to => @local_luke.aspects.first @object_by_parent_author = @local_luke.comment("yo", :on => @local_parent) diff --git a/spec/models/conversation_visibility_spec.rb b/spec/models/conversation_visibility_spec.rb index 691b61b0e09..fff04d7e115 100644 --- a/spec/models/conversation_visibility_spec.rb +++ b/spec/models/conversation_visibility_spec.rb @@ -6,7 +6,7 @@ @aspect = @user.aspects.create(:name => 'Boozers') @person = Factory(:person) - @post = Factory(:status_message, :person => @person) + @post = Factory(:status_message, :author => @person) end it 'has an aspect' do pv = PostVisibility.new(:aspect => @aspect) diff --git a/spec/models/jobs/mail_mentioned_spec.rb b/spec/models/jobs/mail_mentioned_spec.rb index a186ee623c0..5680a3f3f61 100644 --- a/spec/models/jobs/mail_mentioned_spec.rb +++ b/spec/models/jobs/mail_mentioned_spec.rb @@ -13,9 +13,9 @@ mail_mock = mock() mail_mock.should_receive(:deliver) - Notifier.should_receive(:mentioned).with(user.id, sm.person.id, m.id).and_return(mail_mock) + Notifier.should_receive(:mentioned).with(user.id, sm.author.id, m.id).and_return(mail_mock) - Job::MailMentioned.perform_delegate(user.id, sm.person.id, m.id) + Job::MailMentioned.perform_delegate(user.id, sm.author.id, m.id) end end end diff --git a/spec/models/mention_spec.rb b/spec/models/mention_spec.rb index 8f08abcc476..51aeb7d2954 100644 --- a/spec/models/mention_spec.rb +++ b/spec/models/mention_spec.rb @@ -17,7 +17,7 @@ end it 'notifies the person being mention' do - Notification.should_receive(:notify).with(@user, @m, @sm.person) + Notification.should_receive(:notify).with(@user, @m, @sm.author) @m.save end diff --git a/spec/models/person_spec.rb b/spec/models/person_spec.rb index ab1778f0c36..15b5375e0ef 100644 --- a/spec/models/person_spec.rb +++ b/spec/models/person_spec.rb @@ -101,8 +101,8 @@ end it '#owns? posts' do - person_message = Factory.create(:status_message, :person => @person) - person_two = Factory.create(:person) + person_message = Factory.create(:status_message, :author => @person) + person_two = Factory.create(:person) @person.owns?(person_message).should be true person_two.owns?(person_message).should be false @@ -111,8 +111,8 @@ describe '#remove_all_traces' do before do @deleter = Factory(:person) - @status = Factory.create(:status_message, :person => @deleter) - @other_status = Factory.create(:status_message, :person => @person) + @status = Factory.create(:status_message, :author => @deleter) + @other_status = Factory.create(:status_message, :author => @person) end it "deletes all notifications from a person's actions" do diff --git a/spec/models/photo_spec.rb b/spec/models/photo_spec.rb index 3b19fd14532..692ffb267e8 100644 --- a/spec/models/photo_spec.rb +++ b/spec/models/photo_spec.rb @@ -20,13 +20,13 @@ describe "protected attributes" do it "doesn't allow mass assignment of person" do @photo.save! - @photo.update_attributes(:person => Factory(:person)) - @photo.reload.person.should == @user.person + @photo.update_attributes(:author => Factory(:person)) + @photo.reload.author.should == @user.person end it "doesn't allow mass assignment of person_id" do @photo.save! - @photo.update_attributes(:person_id => Factory(:person).id) - @photo.reload.person.should == @user.person + @photo.update_attributes(:author_id => Factory(:person).id) + @photo.reload.author.should == @user.person end it 'allows assignmant of caption' do @photo.save! @@ -50,14 +50,14 @@ it 'has a constructor' do image = File.open(@fixture_name) photo = Photo.diaspora_initialize( - :person => @user.person, :user_file => image) + :author => @user.person, :user_file => image) photo.created_at.nil?.should be_true photo.image.read.nil?.should be_false end it 'sets a remote url' do image = File.open(@fixture_name) photo = Photo.diaspora_initialize( - :person => @user.person, :user_file => image) + :author => @user.person, :user_file => image) photo.remote_photo_path.should include("http") photo.remote_photo_name.should include(".png") end @@ -139,7 +139,7 @@ xml = @photo.to_diaspora_xml @photo.destroy - zord = Postzord::Receiver.new(user2, :person => @photo.person) + zord = Postzord::Receiver.new(user2, :person => @photo.author) zord.parse_and_receive(xml) new_photo = Photo.where(:guid => @photo.guid).first diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 96b56e22b54..d2b8c7bb5d2 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -12,7 +12,7 @@ describe 'deletion' do it 'should delete a posts comments on delete' do - post = Factory.create(:status_message, :person => @user.person) + post = Factory.create(:status_message, :author => @user.person) @user.comment "hey", :on => post post.destroy Post.where(:id => post.id).empty?.should == true diff --git a/spec/models/post_visibility_spec.rb b/spec/models/post_visibility_spec.rb index 3c2cf91c348..ae8a1141cb2 100644 --- a/spec/models/post_visibility_spec.rb +++ b/spec/models/post_visibility_spec.rb @@ -6,7 +6,7 @@ @aspect = @user.aspects.create(:name => 'Boozers') @person = Factory(:person) - @post = Factory(:status_message, :person => @person) + @post = Factory(:status_message, :author => @person) end it 'has an aspect' do pv = PostVisibility.new(:aspect => @aspect) diff --git a/spec/models/status_message_spec.rb b/spec/models/status_message_spec.rb index f368c2c6b2c..e77556cf0ac 100644 --- a/spec/models/status_message_spec.rb +++ b/spec/models/status_message_spec.rb @@ -21,11 +21,11 @@ end describe '#diaspora_handle=' do - it 'sets #person' do + it 'sets #author' do person = Factory.create(:person) - post = Factory.create(:status_message, :person => @user.person) + post = Factory.create(:status_message, :author => @user.person) post.diaspora_handle = person.diaspora_handle - post.person.should == person + post.author.should == person end end it "should have either a message or at least one photo" do @@ -150,7 +150,7 @@ def controller end describe "XML" do before do - @message = Factory.create(:status_message, :message => "I hate WALRUSES!", :person => @user.person) + @message = Factory.create(:status_message, :message => "I hate WALRUSES!", :author => @user.person) @xml = @message.to_xml.to_s end it 'serializes the unescaped, unprocessed message' do @@ -176,7 +176,7 @@ def controller @marshalled.guid.should == @message.guid end it 'marshals the author' do - @marshalled.person.should == @message.person + @marshalled.author.should == @message.author end it 'marshals the diaspora_handle' do @marshalled.diaspora_handle.should == @message.diaspora_handle diff --git a/spec/models/user/attack_vectors_spec.rb b/spec/models/user/attack_vectors_spec.rb index d40d7d23b4d..de2bfb50de7 100644 --- a/spec/models/user/attack_vectors_spec.rb +++ b/spec/models/user/attack_vectors_spec.rb @@ -67,7 +67,7 @@ zord = Postzord::Receiver.new(user, :salmon_xml => salmon_xml) zord.perform - malicious_message = Factory.build( :status_message, :id => original_message.id, :message => 'BAD!!!', :person => user3.person) + malicious_message = Factory.build(:status_message, :id => original_message.id, :message => 'BAD!!!', :author => user3.person) salmon_xml = user3.salmon(malicious_message).xml_for(user.person) zord = Postzord::Receiver.new(user, :salmon_xml => salmon_xml) zord.perform @@ -83,7 +83,7 @@ zord.perform lambda { - malicious_message = Factory.build( :status_message, :id => original_message.id, :message => 'BAD!!!', :person => user2.person) + malicious_message = Factory.build( :status_message, :id => original_message.id, :message => 'BAD!!!', :author => user2.person) salmon_xml2 = user3.salmon(malicious_message).xml_for(user.person) zord = Postzord::Receiver.new(user, :salmon_xml => salmon_xml) diff --git a/spec/models/user/querying_spec.rb b/spec/models/user/querying_spec.rb index da876409c00..bce6037c459 100644 --- a/spec/models/user/querying_spec.rb +++ b/spec/models/user/querying_spec.rb @@ -47,7 +47,7 @@ describe "#visible_posts" do it "queries by person id" do - query = @user2.visible_posts(:person_id => @user2.person.id) + query = @user2.visible_posts(:author_id => @user2.person.id) query.include?(@status_message1).should == true query.include?(@status_message2).should == true query.include?(@status_message3).should == false @@ -195,7 +195,7 @@ end it 'returns nil if the input is nil' do - @user.contact_for(nil).should be_nil + @user.contact_for(nil).should be_nil end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 078107eef1e..41008ef5445 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -271,7 +271,7 @@ fixture_name = File.join(File.dirname(__FILE__), '..', 'fixtures', fixture_filename) image = File.open(fixture_name) @photo = Photo.diaspora_initialize( - :person => alice.person, :user_file => image) + :author => alice.person, :user_file => image) @photo.save! @params = {:photo => @photo} end From 3812612c8642ae9cc2a0aa77ad7324bbb592abbb Mon Sep 17 00:00:00 2001 From: zhitomirskiyi Date: Tue, 1 Mar 2011 19:24:07 -0800 Subject: [PATCH 12/29] added a validation of participant, as well as messages controller spec wip --- app/models/message.rb | 11 +++++++++++ config/routes.rb | 2 ++ spec/models/message_spec.rb | 5 +++++ 3 files changed, 18 insertions(+) diff --git a/app/models/message.rb b/app/models/message.rb index 9ebc262d638..5f95f30d96e 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -25,6 +25,8 @@ class Message < ActiveRecord::Base self end + validate :participant_of_parent_conversation + def diaspora_handle self.author.diaspora_handle end @@ -54,4 +56,13 @@ def parent def parent= parent self.conversation = parent end + + private + def participant_of_parent_conversation + if self.parent && !self.parent.participants.include?(self.author) + errors[:base] << "Author is not participating in the conversation" + else + true + end + end end diff --git a/config/routes.rb b/config/routes.rb index 6d82982441c..1be26b55bfe 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -26,7 +26,9 @@ resources :contacts resources :aspect_memberships, :only => [:destroy, :create] + resources :conversations do + resources :messages, :only => [:create, :show] resource :conversation_visibility, :only => [:destroy], :path => '/visibility/' end diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb index 7c09921fbe1..faf2bda984d 100644 --- a/spec/models/message_spec.rb +++ b/spec/models/message_spec.rb @@ -18,6 +18,11 @@ @xml = @message.to_diaspora_xml end + it 'validates that the author is a participant in the conversation' do + msg = Message.new(:text => 'yo', :author => eve.person, :conversation_id => @cnv.id) + pp msg.valid? + end + describe '#before_create' do it 'signs the message' do @message.author_signature.should_not be_blank From f58c477673f9424e32f1f7e7306231b8cf449fbe Mon Sep 17 00:00:00 2001 From: zhitomirskiyi Date: Tue, 1 Mar 2011 23:55:11 -0800 Subject: [PATCH 13/29] added the forgotten files --- app/controllers/messages_controller.rb | 32 +++++++++ spec/controllers/messages_controller_spec.rb | 76 ++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 app/controllers/messages_controller.rb create mode 100644 spec/controllers/messages_controller_spec.rb diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb new file mode 100644 index 00000000000..148032fc9db --- /dev/null +++ b/app/controllers/messages_controller.rb @@ -0,0 +1,32 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +class MessagesController < ApplicationController + include ApplicationHelper + before_filter :authenticate_user! + + respond_to :html, :mobile + respond_to :json, :only => :show + + def create + cnv = Conversation.joins(:conversation_visibilities).where(:id => params[:conversation_id], + :conversation_visibilities => {:person_id => current_user.person.id}).first + + if cnv + message = Message.new(:conversation_id => cnv.id, :text => params[:text], :author => current_user.person) + + if message.save + Rails.logger.info("event=create type=comment user=#{current_user.diaspora_handle} status=success message=#{message.id} chars=#{params[:text].length}") + Postzord::Dispatch.new(current_user, message).post + + respond_with cnv + else + render :nothing => true, :status => 406 + end + else + render :nothing => true, :status => 406 + end + end + +end diff --git a/spec/controllers/messages_controller_spec.rb b/spec/controllers/messages_controller_spec.rb new file mode 100644 index 00000000000..45f7d31e701 --- /dev/null +++ b/spec/controllers/messages_controller_spec.rb @@ -0,0 +1,76 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require 'spec_helper' + +describe MessagesController do + render_views + + before do + @user1 = alice + @user2 = bob + + @aspect1 = @user1.aspects.first + @aspect2 = @user2.aspects.first + + sign_in :user, @user1 + end + + describe '#create' do + before do + @create_hash = { :author => @user1.person, :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :subject => "cool stuff", :text => "stuff"} + + end + context "on my own post" do + before do + @cnv = Conversation.create(@create_hash) + @message_hash = {:conversation_id => @cnv.id, :text => "here is something else"} + end + it 'responds to format js' do + lambda{ + post :create, @message_hash + }.should change(Message, :count).by(1) + response.code.should == '201' + response.should redirect_to(@cnv) + end + end + + context "on a post from a contact" do + before do + @create_hash[:author] = @user2.person + @cnv = Conversation.create(@create_hash) + @message_hash = {:conversation_id => @cnv.id, :text => "here is something else"} + end + it 'comments' do + post :create, @message_hash + response.code.should == '201' + end + it "doesn't overwrite author_id" do + new_user = Factory.create(:user) + @message_hash[:author_id] = new_user.person.id.to_s + post :create, @message_hash + Message.find_by_text(@message_hash[:text]).author_id.should == @user1.person.id + end + it "doesn't overwrite id" do + old_message = Message.create(:text => "hello", :author_id => @user1.person.id, :conversation_id => @cnv.id) + @message_hash[:id] = old_message.id + post :create, @message_hash + old_message.reload.text.should == 'hello' + end + end + context 'on a post from a stranger' do + before do + @create_hash[:author] = eve.person + @cnv = Conversation.create(@create_hash) + @message_hash = {:conversation_id => @cnv.id, :text => "here is something else"} + end + it 'posts no comment' do + Message.should_not_receive(:new) + post :create, @message_hash + response.code.should == '406' + end + end + end +end From 9d7611f8d8258d22c4dc793b4140c5d45e1c5510 Mon Sep 17 00:00:00 2001 From: zhitomirskiyi Date: Wed, 2 Mar 2011 12:22:08 -0800 Subject: [PATCH 14/29] addedthe controllers and the views for the message and the conversation inbox, going to pull in some left nav styling --- app/controllers/conversations_controller.rb | 8 ++-- app/controllers/messages_controller.rb | 6 +-- app/views/conversations/_conversation.haml | 20 +++++++++ app/views/conversations/_show.haml | 22 ++++++++++ app/views/conversations/index.haml | 45 +++++++++++--------- app/views/conversations/new.haml | 9 ++-- app/views/conversations/show.haml | 29 +------------ app/views/messages/_message.haml | 17 ++++++++ lib/tasks/db.rake | 13 +----- public/stylesheets/sass/application.sass | 29 ++++++++++++- spec/controllers/messages_controller_spec.rb | 20 ++++----- 11 files changed, 138 insertions(+), 80 deletions(-) create mode 100644 app/views/conversations/_conversation.haml create mode 100644 app/views/conversations/_show.haml create mode 100644 app/views/messages/_message.haml diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb index 2718e12f1b5..b49333446ee 100644 --- a/app/controllers/conversations_controller.rb +++ b/app/controllers/conversations_controller.rb @@ -1,11 +1,13 @@ class ConversationsController < ApplicationController before_filter :authenticate_user! - respond_to :html + respond_to :html, :json def index @conversations = Conversation.joins(:conversation_visibilities).where( :conversation_visibilities => {:person_id => current_user.person.id}).all + @conversation = Conversation.joins(:conversation_visibilities).where( + :conversation_visibilities => {:person_id => current_user.person.id, :conversation_id => params[:conversation_id]}).first end def create @@ -18,7 +20,7 @@ def create @conversation = Conversation.create(params[:conversation]) - respond_with @conversation + redirect_to conversations_path(:conversation_id => @conversation.id) end def show @@ -26,7 +28,7 @@ def show :conversation_visibilities => {:person_id => current_user.person.id}).first if @conversation - respond_with @conversation + render :layout => false else redirect_to conversations_path end diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index 148032fc9db..2dac50dda80 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -14,13 +14,13 @@ def create :conversation_visibilities => {:person_id => current_user.person.id}).first if cnv - message = Message.new(:conversation_id => cnv.id, :text => params[:text], :author => current_user.person) + message = Message.new(:conversation_id => cnv.id, :text => params[:message][:text], :author => current_user.person) if message.save - Rails.logger.info("event=create type=comment user=#{current_user.diaspora_handle} status=success message=#{message.id} chars=#{params[:text].length}") + Rails.logger.info("event=create type=comment user=#{current_user.diaspora_handle} status=success message=#{message.id} chars=#{params[:message][:text].length}") Postzord::Dispatch.new(current_user, message).post - respond_with cnv + redirect_to conversations_path(:conversation_id => cnv.id) else render :nothing => true, :status => 406 end diff --git a/app/views/conversations/_conversation.haml b/app/views/conversations/_conversation.haml new file mode 100644 index 00000000000..cac2ddf0892 --- /dev/null +++ b/app/views/conversations/_conversation.haml @@ -0,0 +1,20 @@ +-# Copyright (c) 2010, Diaspora Inc. This file is +-# licensed under the Affero General Public License version 3 or later. See +-# the COPYRIGHT file. + +.stream_element.conversation{:data=>{:guid=>conversation.id}} + - if conversation.author.owner_id == current_user.id + .right.hidden.controls + /= link_to image_tag('deletelabel.png'), status_message_path(conversation), :confirm => t('are_you_sure'), :method => :delete, :remote => true, :class => "delete", :title => t('delete') + + %strong + = person_link(conversation.author) + + .subject + = conversation.subject + .message + = "#{conversation.messages.first.text[0..20]}..." + + .info + /%span.timeago= link_to(how_long_ago(conversation), status_message_path(conversation)) + diff --git a/app/views/conversations/_show.haml b/app/views/conversations/_show.haml new file mode 100644 index 00000000000..66c59b1959f --- /dev/null +++ b/app/views/conversations/_show.haml @@ -0,0 +1,22 @@ +-# Copyright (c) 2010, Diaspora Inc. This file is +-# licensed under the Affero General Public License version 3 or later. See +-# the COPYRIGHT file. + +.conversation_participants + .right + = link_to t('delete'), conversation_conversation_visibility_path(conversation), :method => 'delete', :confirm => t('are_you_sure') + - for participant in conversation.participants + = person_image_link(participant) + +.stream + = render :partial => 'messages/message', :collection => conversation.messages + + .stream_element.new_message + = owner_image_tag + + .content + = form_for [conversation, Message.new] do |message| + = message.text_area :text, :rows => 5 + .right + = message.submit 'Reply' + = link_to 'Cancel', '#' diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml index 71a8167be46..8ea7f5d71eb 100644 --- a/app/views/conversations/index.haml +++ b/app/views/conversations/index.haml @@ -2,29 +2,36 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. +:javascript + $(document).ready(function(){ + $('.conversation', '.stream').click(function(){ + var conversationGuid = $(this).attr('data-guid'); + $.get("conversations/"+conversationGuid, function(data){ + $('#conversation_show').html(data); + }); + }); + }); -.span-12.last - - %h2 - .right - = link_to 'new message', new_conversation_path, :class => 'button' - Inbox - +.span-24.last{:style => 'position:relative;'} + .right + = link_to 'New Message', new_conversation_path, :class => 'button' + = link_to 'Inbox', conversations_path, :class => 'button' + = link_to 'Sent', conversations_path, :class => 'button' + %br + %br + %br +.span-6.append-1 - if @conversations.count > 0 .stream - - for conversation in @conversations - .stream_element - .right - = link_to image_tag('deletelabel.png'), conversation_conversation_visibility_path(conversation), :method => 'delete', :confirm => t('are_you_sure') - - .from - = conversation.messages.last.author.name - %p - = link_to conversation.subject, conversation - %p - = link_to conversation.messages.last, conversation - + = render :partial => 'conversations/conversation', :collection => @conversations - else %i You have no messages + +#conversation_show.span-17.last + - if @conversation + = render 'conversations/show', :conversation => @conversation + - else + %i + no conversation selected diff --git a/app/views/conversations/new.haml b/app/views/conversations/new.haml index 5d13de32308..e61d21678e1 100644 --- a/app/views/conversations/new.haml +++ b/app/views/conversations/new.haml @@ -9,16 +9,15 @@ = form_for Conversation.new do |conversation| %h4 to - = text_field_tag "private_message[contact_ids]" + = text_field_tag "conversation[contact_ids]" %h4 subject = conversation.text_field :subject - = fields_for :message do |message| - %h4 - message - = message.text_area :text, :rows => 5 + %h4 + message + = text_area_tag "conversation[text]", '', :rows => 5 = conversation.submit :send = link_to 'cancel', conversations_path diff --git a/app/views/conversations/show.haml b/app/views/conversations/show.haml index 99a8989fa11..b5d871dfcc1 100644 --- a/app/views/conversations/show.haml +++ b/app/views/conversations/show.haml @@ -2,31 +2,4 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -= link_to 'back', conversations_path - -%br -%br -%br -%br - - -- for participant in @conversation.participants - = participant.name - -- for message in @conversation.messages - %h4 - from - = message.author.name - - %br - %br - - %h4 - message - = message.text - - %hr - -= link_to t('delete'), conversation_conversation_visibility_path(@conversation), :method => 'delete', :confirm => t('are_you_sure') - - += render 'show', :conversation => @conversation diff --git a/app/views/messages/_message.haml b/app/views/messages/_message.haml new file mode 100644 index 00000000000..bfcf56a4c41 --- /dev/null +++ b/app/views/messages/_message.haml @@ -0,0 +1,17 @@ +-# Copyright (c) 2010, Diaspora Inc. This file is +-# licensed under the Affero General Public License version 3 or later. See +-# the COPYRIGHT file. + +.stream_element{:data=>{:guid=>message.id}} + = person_image_link(message.author, :size => :thumb_small) + + .right + %span.timeago= link_to(how_long_ago(message), status_message_path(message)) + + .content + %strong + = person_link(message.author) + + .message + = message.text + diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 4da04ce865f..77e4b402269 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -38,9 +38,7 @@ namespace :db do puts "Purging the database for #{Rails.env}..." - # Specifiy what models to remove - # No! Drop the fucking database. - MongoMapper::connection.drop_database(MongoMapper::database.name) + Rake::Task['db:rebuild'].invoke puts 'Deleting tmp folder...' `rm -rf #{File.dirname(__FILE__)}/../../public/uploads/*` @@ -51,17 +49,10 @@ namespace :db do puts "Resetting the database for #{Rails.env}".upcase Rake::Task['db:purge'].invoke - Rake::Task['db:seed:dev'].invoke + Rake::Task['db:seed'].invoke puts "Success!" end - task :reset_dev do - puts "making a new base user" - Rake::Task['db:purge'].invoke - Rake::Task['db:seed:dev'].invoke - puts "you did it!" - end - desc "Purge database and then add the first user" task :first_user, :username, :password, :email do |t, args| Rake::Task['db:purge'].invoke diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index 2af5d5534c6..59eb806d92e 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -2480,4 +2480,31 @@ ul.show_comments :position static .public_icon, .service_icon - :cursor pointer \ No newline at end of file + :cursor pointer + +.stream_element + .subject + :font + :weight bold + +.conversation_participants + :background + :color #eee + :border + :bottom 1px solid #999 + :padding 1em + +.stream_element.new_message + :border + :top 1px solid #999 + :bottom none + &:hover + :border + :bottom none + textarea + :margin 0 + :bottom 0.5em + :width 100% + .right + :right -11px + diff --git a/spec/controllers/messages_controller_spec.rb b/spec/controllers/messages_controller_spec.rb index 45f7d31e701..8c9b777bb5b 100644 --- a/spec/controllers/messages_controller_spec.rb +++ b/spec/controllers/messages_controller_spec.rb @@ -21,19 +21,18 @@ before do @create_hash = { :author => @user1.person, :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff", :text => "stuff"} - end context "on my own post" do before do @cnv = Conversation.create(@create_hash) - @message_hash = {:conversation_id => @cnv.id, :text => "here is something else"} + @message_hash = {:conversation_id => @cnv.id, :message => {:text => "here is something else"}} end - it 'responds to format js' do + it 'redirects to conversation' do lambda{ post :create, @message_hash }.should change(Message, :count).by(1) - response.code.should == '201' - response.should redirect_to(@cnv) + response.code.should == '302' + response.should redirect_to(conversations_path(:conversation_id => @cnv)) end end @@ -41,17 +40,18 @@ before do @create_hash[:author] = @user2.person @cnv = Conversation.create(@create_hash) - @message_hash = {:conversation_id => @cnv.id, :text => "here is something else"} + @message_hash = {:conversation_id => @cnv.id, :message => {:text => "here is something else"}} end it 'comments' do post :create, @message_hash - response.code.should == '201' + response.code.should == '302' + response.should redirect_to(conversations_path(:conversation_id => @cnv)) end it "doesn't overwrite author_id" do new_user = Factory.create(:user) @message_hash[:author_id] = new_user.person.id.to_s post :create, @message_hash - Message.find_by_text(@message_hash[:text]).author_id.should == @user1.person.id + Message.find_by_text(@message_hash[:message][:text]).author_id.should == @user1.person.id end it "doesn't overwrite id" do old_message = Message.create(:text => "hello", :author_id => @user1.person.id, :conversation_id => @cnv.id) @@ -63,11 +63,11 @@ context 'on a post from a stranger' do before do @create_hash[:author] = eve.person + @create_hash[:participant_ids] = [eve.person.id, bob.person.id] @cnv = Conversation.create(@create_hash) - @message_hash = {:conversation_id => @cnv.id, :text => "here is something else"} + @message_hash = {:conversation_id => @cnv.id, :message => {:text => "here is something else"}} end it 'posts no comment' do - Message.should_not_receive(:new) post :create, @message_hash response.code.should == '406' end From 2522ab7ee4b3a58290db108731eb3f8156ace67a Mon Sep 17 00:00:00 2001 From: zhitomirskiyi Date: Thu, 3 Mar 2011 10:39:32 -0800 Subject: [PATCH 15/29] refactoring the conversation views, wip --- app/helpers/application_helper.rb | 4 +- app/views/conversations/_conversation.haml | 9 ++- app/views/conversations/_show.haml | 2 + app/views/conversations/index.haml | 10 ++- app/views/messages/_message.haml | 11 ++-- app/views/shared/_stream_element.html.haml | 7 ++- public/stylesheets/sass/application.sass | 71 +++++++++++++++------- 7 files changed, 73 insertions(+), 41 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 81e013ebfae..96e01fffbab 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -133,8 +133,8 @@ def person_image_tag(person, size=:thumb_small) "\"#{h(person.name)}\"".html_safe end - def person_link(person) - " + def person_link(person, opts={}) + " #{h(person.name)} ".html_safe end diff --git a/app/views/conversations/_conversation.haml b/app/views/conversations/_conversation.haml index cac2ddf0892..2c78682d76a 100644 --- a/app/views/conversations/_conversation.haml +++ b/app/views/conversations/_conversation.haml @@ -2,18 +2,17 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -.stream_element.conversation{:data=>{:guid=>conversation.id}} +.stream_element.conversation{:data=>{:guid=>conversation.id}, :class => ('selected' if conversation.id == conversation.id)} - if conversation.author.owner_id == current_user.id .right.hidden.controls /= link_to image_tag('deletelabel.png'), status_message_path(conversation), :confirm => t('are_you_sure'), :method => :delete, :remote => true, :class => "delete", :title => t('delete') - %strong - = person_link(conversation.author) - + .from + = person_link(conversation.author, :class => 'author') .subject = conversation.subject .message - = "#{conversation.messages.first.text[0..20]}..." + = "#{conversation.messages.first.text[0..60]}..." .info /%span.timeago= link_to(how_long_ago(conversation), status_message_path(conversation)) diff --git a/app/views/conversations/_show.haml b/app/views/conversations/_show.haml index 66c59b1959f..aa7f72bfc12 100644 --- a/app/views/conversations/_show.haml +++ b/app/views/conversations/_show.haml @@ -3,6 +3,8 @@ -# the COPYRIGHT file. .conversation_participants + %h3 + = conversation.subject .right = link_to t('delete'), conversation_conversation_visibility_path(conversation), :method => 'delete', :confirm => t('are_you_sure') - for participant in conversation.participants diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml index 8ea7f5d71eb..eeb17e6fb9c 100644 --- a/app/views/conversations/index.haml +++ b/app/views/conversations/index.haml @@ -5,8 +5,12 @@ :javascript $(document).ready(function(){ $('.conversation', '.stream').click(function(){ - var conversationGuid = $(this).attr('data-guid'); + var conversationSummary = $(this), + conversationGuid = conversationSummary.attr('data-guid'); $.get("conversations/"+conversationGuid, function(data){ + + $('.conversation', '.stream').removeClass('selected'); + conversationSummary.addClass('selected'); $('#conversation_show').html(data); }); }); @@ -21,9 +25,9 @@ %br %br -.span-6.append-1 +.span-7 - if @conversations.count > 0 - .stream + .stream.conversations = render :partial => 'conversations/conversation', :collection => @conversations - else %i diff --git a/app/views/messages/_message.haml b/app/views/messages/_message.haml index bfcf56a4c41..d7cd6ab751a 100644 --- a/app/views/messages/_message.haml +++ b/app/views/messages/_message.haml @@ -5,13 +5,12 @@ .stream_element{:data=>{:guid=>message.id}} = person_image_link(message.author, :size => :thumb_small) - .right - %span.timeago= link_to(how_long_ago(message), status_message_path(message)) - .content - %strong - = person_link(message.author) + .from + = person_link(message.author, :class => 'author') + %time.timeago{:datetime => message.created_at} + = how_long_ago(message) - .message + %p = message.text diff --git a/app/views/shared/_stream_element.html.haml b/app/views/shared/_stream_element.html.haml index 18278882de1..5779f1f1ed4 100644 --- a/app/views/shared/_stream_element.html.haml +++ b/app/views/shared/_stream_element.html.haml @@ -13,8 +13,10 @@ = person_image_link(post.author, :size => :thumb_small) .content - %strong - = person_link(post.author) + .from + = person_link(post.author, :class => 'author') + %time.timeago{:datetime => post.created_at} + = link_to(how_long_ago(post), status_message_path(post)) = render 'status_messages/status_message', :post => post, :photos => post.photos @@ -27,7 +29,6 @@ %span.aspect_badges = aspect_badges(aspects_with_post(all_aspects, post)) - %span.timeago= link_to(how_long_ago(post), status_message_path(post)) = link_to t('comments.new_comment.comment').downcase, '#', :class => 'focus_comment_textarea' = render "comments/comments", :post_id => post.id, :comments => post.comments, :current_user => current_user, :condensed => true, :commenting_disabled => defined?(@commenting_disabled) diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index 59eb806d92e..f09b8f52909 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -247,21 +247,12 @@ header .stream .stream_element - :font - :family 'arial', 'helvetica', sans-serif - :padding 10px 14px - :right 75px + :padding 20px 20px :min-height 50px :border :bottom 1px solid #ddd :top 1px solid #fff - :font - :size 13px - :line - :height 19px - - &:hover :border :bottom 1px solid #ddd @@ -273,9 +264,14 @@ header :height 370px :width 500px + time + :font + :weight normal + :size smaller + :position absolute + :right 20px + .from - :text - :shadow 0 1px #fff a :font :weight bold @@ -334,11 +330,15 @@ header .stream_element :position relative :word-wrap break-word - :color #777 + :color #888 - .from - h5 - :display inline + :font + :size 14px + + a.author + :font + :weight bold + :color #444 .content :margin @@ -346,17 +346,17 @@ header :padding :left 60px - :color #444 + :color #666 :font :weight normal p :margin - :bottom 6px - :padding - :right 1em + :bottom 0px :font - :family 'arial', 'helvetica', 'sans-serif' + :size 12px + :line + :height 16px .photo_attachments :margin @@ -406,8 +406,9 @@ header .time, .timeago + :color #ccc a - :color #bbb + :color #ccc :margin :right 1px :text @@ -2485,9 +2486,25 @@ ul.show_comments .stream_element .subject :font + :size 13px :weight bold + .message + :font + :size 12px + + .participants + .avatar + :float none + :height 24px + :width 24px + :margin + :top 3px .conversation_participants + .avatar + :height 30px + :width 30px + :background :color #eee :border @@ -2508,3 +2525,13 @@ ul.show_comments .right :right -11px +.stream_element.conversation + :padding 10px + +.stream.conversations + :border + :right 1px solid #ccc + +.conversation.selected + :background + :color #ccc From 5f55dfa1bccf908843579677f8dce597fc2b837e Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Thu, 3 Mar 2011 17:32:26 -0800 Subject: [PATCH 16/29] private message inbox wip --- Gemfile.lock | 10 +- app/controllers/conversations_controller.rb | 8 +- app/controllers/profiles_controller.rb | 2 +- app/helpers/application_helper.rb | 5 +- app/models/message.rb | 2 +- app/views/conversations/_conversation.haml | 27 ++-- app/views/conversations/_show.haml | 43 +++--- app/views/conversations/index.haml | 51 ++++--- app/views/shared/_stream_element.html.haml | 4 +- config/assets.yml | 2 + public/images/reply.png | Bin 0 -> 1495 bytes public/javascripts/inbox.js | 79 ++++++++++ public/stylesheets/sass/application.sass | 157 +++++++++++++++++--- 13 files changed, 300 insertions(+), 90 deletions(-) create mode 100644 public/images/reply.png create mode 100644 public/javascripts/inbox.js diff --git a/Gemfile.lock b/Gemfile.lock index 72ba7dba1b6..4c40485691f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -119,14 +119,14 @@ GEM erubis extlib highline - json (>= 1.4.4, <= 1.4.6) + json (<= 1.4.6, >= 1.4.4) mixlib-authentication (>= 1.1.0) mixlib-cli (>= 1.1.0) mixlib-config (>= 1.1.2) mixlib-log (>= 1.2.0) moneta ohai (>= 0.5.7) - rest-client (>= 1.0.4, < 1.7.0) + rest-client (< 1.7.0, >= 1.0.4) uuidtools childprocess (0.1.7) ffi (~> 0.6.3) @@ -163,7 +163,7 @@ GEM faraday (0.5.4) addressable (~> 2.2.2) multipart-post (~> 1.1.0) - rack (>= 1.1.0, < 2) + rack (< 2, >= 1.1.0) faraday_middleware (0.3.2) faraday (~> 0.5.4) fastercsv (1.5.4) @@ -271,7 +271,7 @@ GEM multi_json (~> 0.0.4) ohai (0.5.8) extlib - json (>= 1.4.4, <= 1.4.6) + json (<= 1.4.6, >= 1.4.4) mixlib-cli mixlib-config mixlib-log @@ -353,7 +353,7 @@ GEM simple_oauth (0.1.4) sinatra (1.1.3) rack (~> 1.1) - tilt (>= 1.2.2, < 2.0) + tilt (< 2.0, >= 1.2.2) subexec (0.0.4) systemu (1.2.0) term-ansicolor (1.0.5) diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb index b49333446ee..dfe07c023ce 100644 --- a/app/controllers/conversations_controller.rb +++ b/app/controllers/conversations_controller.rb @@ -5,7 +5,9 @@ class ConversationsController < ApplicationController def index @conversations = Conversation.joins(:conversation_visibilities).where( - :conversation_visibilities => {:person_id => current_user.person.id}).all + :conversation_visibilities => {:person_id => current_user.person.id}).paginate( + :page => params[:page], :per_page => 7, :order => 'updated_at DESC') + @conversation = Conversation.joins(:conversation_visibilities).where( :conversation_visibilities => {:person_id => current_user.person.id, :conversation_id => params[:conversation_id]}).first end @@ -34,4 +36,8 @@ def show end end + def new + render :layout => false + end + end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 54c892a9a65..79267c8cc2e 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -14,7 +14,7 @@ def update # upload and set new profile photo params[:profile] ||= {} params[:profile][:searchable] ||= false - params[:profile][:photo] = Photo.where(:person_id => current_user.person.id, + params[:profile][:photo] = Photo.where(:author_id => current_user.person.id, :id => params[:photo_id]).first if params[:photo_id] if current_user.update_profile params[:profile] diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 96e01fffbab..e3927d23731 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -13,11 +13,10 @@ def timeago(time, options = {}) def page_title text=nil title = "" if text.blank? - title = "#{current_user.name} | " if current_user + title = "#{current_user.name}" if current_user else - title = "#{text} | " + title = "#{text}" end - title += "DIASPORA*" end def aspects_with_post aspects, post diff --git a/app/models/message.rb b/app/models/message.rb index 5f95f30d96e..6a2f1adf456 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -11,7 +11,7 @@ class Message < ActiveRecord::Base xml_reader :conversation_guid belongs_to :author, :class_name => 'Person' - belongs_to :conversation + belongs_to :conversation, :touch => true after_create do #sign comment as commenter diff --git a/app/views/conversations/_conversation.haml b/app/views/conversations/_conversation.haml index 2c78682d76a..5e1c450f86a 100644 --- a/app/views/conversations/_conversation.haml +++ b/app/views/conversations/_conversation.haml @@ -2,18 +2,23 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -.stream_element.conversation{:data=>{:guid=>conversation.id}, :class => ('selected' if conversation.id == conversation.id)} - - if conversation.author.owner_id == current_user.id - .right.hidden.controls - /= link_to image_tag('deletelabel.png'), status_message_path(conversation), :confirm => t('are_you_sure'), :method => :delete, :remote => true, :class => "delete", :title => t('delete') +.stream_element.conversation{:data=>{:guid=>conversation.id}} + = person_image_tag(conversation.messages.last.author) - .from - = person_link(conversation.author, :class => 'author') .subject - = conversation.subject - .message - = "#{conversation.messages.first.text[0..60]}..." + .message_count + = conversation.messages.size + + = conversation.subject[0..30] - .info - /%span.timeago= link_to(how_long_ago(conversation), status_message_path(conversation)) + .last_author + .timestamp + = time_ago_in_words conversation.updated_at + = conversation.author.name + - if conversation.participants.size > 2 + %span.participant_count + = "(+#{conversation.participants.size - 1})" + + .message + = "#{conversation.messages.last.text[0..45]}..." diff --git a/app/views/conversations/_show.haml b/app/views/conversations/_show.haml index aa7f72bfc12..b9566615fec 100644 --- a/app/views/conversations/_show.haml +++ b/app/views/conversations/_show.haml @@ -2,23 +2,32 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -.conversation_participants - %h3 - = conversation.subject - .right - = link_to t('delete'), conversation_conversation_visibility_path(conversation), :method => 'delete', :confirm => t('are_you_sure') - - for participant in conversation.participants - = person_image_link(participant) +.conversation_participants.span-16.last + .span-10 + %h3 + = conversation.subject + + .conversation_controls + = link_to (image_tag('reply.png', :height => 14, :width => 14) + ' ' + 'reply'), '#', :id => 'reply_to_conversation' + = link_to (image_tag('deletelabel.png') + ' ' + t('delete').downcase), conversation_conversation_visibility_path(conversation), :method => 'delete', :confirm => t('are_you_sure') -.stream - = render :partial => 'messages/message', :collection => conversation.messages + .span-6.avatars.last + - for participant in conversation.participants + = person_image_link(participant) - .stream_element.new_message - = owner_image_tag +%br +%br +%br +.span-16.last + .stream + = render :partial => 'messages/message', :collection => conversation.messages - .content - = form_for [conversation, Message.new] do |message| - = message.text_area :text, :rows => 5 - .right - = message.submit 'Reply' - = link_to 'Cancel', '#' + .stream_element.new_message + = owner_image_tag + + .content + = form_for [conversation, Message.new] do |message| + = message.text_area :text, :rows => 5 + .right + = message.submit 'Reply' + = link_to 'Cancel', '#' diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml index eeb17e6fb9c..21c51f33051 100644 --- a/app/views/conversations/index.haml +++ b/app/views/conversations/index.haml @@ -2,40 +2,39 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -:javascript - $(document).ready(function(){ - $('.conversation', '.stream').click(function(){ - var conversationSummary = $(this), - conversationGuid = conversationSummary.attr('data-guid'); - $.get("conversations/"+conversationGuid, function(data){ - $('.conversation', '.stream').removeClass('selected'); - conversationSummary.addClass('selected'); - $('#conversation_show').html(data); - }); - }); - }); +- content_for :head do + = include_javascripts :inbox -.span-24.last{:style => 'position:relative;'} - .right - = link_to 'New Message', new_conversation_path, :class => 'button' - = link_to 'Inbox', conversations_path, :class => 'button' - = link_to 'Sent', conversations_path, :class => 'button' +- content_for :page_title do + Message Inbox + +:css + footer{ display:none;} + +.span-5.append-3 + %h3.fixit{:style => 'width:300px;'} + .right + = link_to 'New Message', new_conversation_path, :class => 'button', :rel => 'facebox' + Inbox %br %br %br -.span-7 - - if @conversations.count > 0 - .stream.conversations - = render :partial => 'conversations/conversation', :collection => @conversations - - else - %i - You have no messages + #conversation_inbox + - if @conversations.count > 0 + .stream.conversations + = render :partial => 'conversations/conversation', :collection => @conversations + = will_paginate @conversations + - else + %i + You have no messages -#conversation_show.span-17.last +#conversation_show.span-16.last - if @conversation = render 'conversations/show', :conversation => @conversation - else - %i + #no_conversation_text no conversation selected + #no_conversation_controls + = link_to 'create a new message', new_conversation_path, :rel => 'facebox' diff --git a/app/views/shared/_stream_element.html.haml b/app/views/shared/_stream_element.html.haml index 5779f1f1ed4..ec529a2e08d 100644 --- a/app/views/shared/_stream_element.html.haml +++ b/app/views/shared/_stream_element.html.haml @@ -17,8 +17,8 @@ = person_link(post.author, :class => 'author') %time.timeago{:datetime => post.created_at} = link_to(how_long_ago(post), status_message_path(post)) - - = render 'status_messages/status_message', :post => post, :photos => post.photos + %p + = render 'status_messages/status_message', :post => post, :photos => post.photos .info - if post.public? diff --git a/config/assets.yml b/config/assets.yml index f398ee1d086..e70e461b4cc 100644 --- a/config/assets.yml +++ b/config/assets.yml @@ -56,6 +56,8 @@ javascripts: - public/javascripts/contact-list.js photos: - public/javascripts/photo-show.js + inbox: + - public/javascripts/inbox.js stylesheets: default: diff --git a/public/images/reply.png b/public/images/reply.png new file mode 100644 index 0000000000000000000000000000000000000000..2356dc7791f28e2dae9bb96e62f9d60c7ca3906f GIT binary patch literal 1495 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ za0`Jj+tIX_n~5oC^DMQ#CujeSKy zVsdtBi9%9pdS;%jl7fPQl0s&Rtx~wDuYqrYb81GWM^#a3aFt(3a#eP+Wr~u$9hXgo z6;N|-YDuC(MQ%=Bu~mhw64*>DAR8pCucQE0Qj%?}1aWkPZ-9bxeo?A|iJqZuvVpOQ zf{B@)k-3qjxtWeaaAJvqS7M%mk-37AfdP;(vNANZGBE@?1`L$!xPY`xQA(Oskc%7C zP9V=#DWjyMz)D}gyu4hm+*mKaC|%#s($Z4jz)0W7NEfI=x41H|B(Xv_uUHvk2+SOp z)Z*l#%mQ$5fy_-z$}cUkRZ;?31P4&hB^JOf$}5Hj9xxd7D-sLz4fPE4;U)t$+5iQu zz!8yO6q28xV}~WqY(P3u6d`Oy=udS?EJ?KkhKGf&fswAEd5D3Lm9d$XiD?v)euyG8 z?Y{XbnQ4_s+KqLMOhODTtqcsTOpKt~krY9-+vtM=0x4j?p$_sBnz#ai082@RhgU&q zQ4Tm-Qj+ykb5e6t^Gb?=VP=RLW+};5Y57IDi6wTKxryni`UQFEHu?xbyzYaz8kj7A z$xl!=*;PZz@>&VQYACe906eRt;E%H6L$ABpl1az-T668}$T=Pa2Wgwm!xsa&^7=PO5Qq}KW3nUP8>`zN>sw!Ht%-~X+4 zzDC!JJAQ54=aY7PU)5O}8+_d~a$0X+Z0y3fjVdcwY0W+Vyj|5(_xW!zhOR{kcJnWr z@cLf9v&DX{-^x|1=9e>_d|0r-fG1K%to(Dlp7^%gx($pKlXjIqVBl_Qcv^H*_}g#W zFVdQ&MBb@09JXo(EtDd literal 0 HcmV?d00001 diff --git a/public/javascripts/inbox.js b/public/javascripts/inbox.js new file mode 100644 index 00000000000..ad8478f2155 --- /dev/null +++ b/public/javascripts/inbox.js @@ -0,0 +1,79 @@ +/* Copyright (c) 2010, Diaspora Inc. This file is + * licensed under the Affero General Public License version 3 or later. See + * the COPYRIGHT file. + */ + +$(document).ready(function(){ + + var bindIt = function(element){ + var conversationSummary = element, + conversationGuid = conversationSummary.attr('data-guid'); + $.get("conversations/"+conversationGuid, function(data){ + + $('.conversation', '.stream').removeClass('selected'); + conversationSummary.addClass('selected'); + $('#conversation_show').html(data); + }); + + if (typeof(history.pushState) == 'function') { + history.pushState(null, document.title, '?conversation_id='+conversationGuid); + } + } + + $('.conversation', '.stream').bind('mousedown', function(){ + bindIt($(this)); + }); + + resize(); + $(window).resize(function(){ + resize(); + }); + + $('#conversation_inbox .stream').infinitescroll({ + navSelector : ".pagination", + // selector for the paged navigation (it will be hidden) + nextSelector : ".pagination a.next_page", + // selector for the NEXT link (to page 2) + itemSelector : "#conversation_inbox .conversation", + // selector for all items you'll retrieve + localMode: true, + debug: false, + donetext: "no more.", + loadingText: "", + loadingImg: '/images/ajax-loader.gif' + }, function(){ + $('.conversation', '.stream').bind('mousedown', function(){ + bindIt($(this)); + }); + }); + + // kill scroll binding + $(window).unbind('.infscr'); + + // hook up the manual click guy. + $('a.next_page').click(function(){ + $(document).trigger('retrieve.infscr'); + return false; + }); + + // remove the paginator when we're done. + $(document).ajaxError(function(e,xhr,opt){ + if (xhr.status == 404) $('a.next_page').remove(); + }); + + $('#reply_to_conversation').live('click', function(evt) { + evt.preventDefault(); + $('html, body').animate({scrollTop:$(window).height()}, 'medium', function(){ + $('#message_text').focus(); + }); + }); + +}); + +var resize = function(){ + var inboxSidebar = $('#conversation_inbox'); + inboxSidebarOffset = inboxSidebar.offset().top, + windowHeight = $(window).height(); + + inboxSidebar.css('height', windowHeight - inboxSidebarOffset); +}; diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index f09b8f52909..8c3f721bc3d 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -9,7 +9,7 @@ $background: rgb(252,252,252) body :padding 2em :margin 0 - :top 60px + :top 50px :background-color $background a :color #107FC9 @@ -127,8 +127,8 @@ header :color #111 :color rgba(15,15,15,0.90) - :background -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(30,30,30,0.85)), to(rgba(20,20,20,1))) - :background -moz-linear-gradient(top, rgba(30,30,30,0.85), rgba(20,20,20,0.98)) + :background -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(20,20,20,0.85)), to(rgba(20,20,20,1))) + :background -moz-linear-gradient(top, rgba(20,20,20,0.85), rgba(20,20,20,0.98)) :-webkit-box-shadow 0 1px 3px #111 :-moz-box-shadow 0 1px 2px #111 @@ -254,8 +254,6 @@ header :top 1px solid #fff &:hover - :border - :bottom 1px solid #ddd .right :display inline @@ -2486,30 +2484,63 @@ ul.show_comments .stream_element .subject :font - :size 13px + :size 14px :weight bold - .message + :color #444 + :overflow hidden + :white-space nowrap + + .last_author :font :size 12px + :weight bold + :color #777 - .participants - .avatar - :float none - :height 24px - :width 24px - :margin - :top 3px + .message + :font + :size 12px .conversation_participants + :z-index 3 + :background + :color $background + :position fixed + :margin + :bottom 10px + + :-webkit-box-shadow 0 3px 3px -3px #333 + :-moz-box-shadow 0 3px 3px -3px #333 + + h3 + :margin 0 + :top 6px + :bottom 0px .avatar :height 30px :width 30px - :background - :color #eee + :line + :height 0 + + .conversation_controls + a + :margin + :right 10px + + :margin + :bottom 10px + :border - :bottom 1px solid #999 - :padding 1em + :bottom 1px solid #666 + :padding 5px + :left 10px + :top 90px + :margin + :top -100px + + .avatars + :text + :align right .stream_element.new_message :border @@ -2526,12 +2557,92 @@ ul.show_comments :right -11px .stream_element.conversation - :padding 10px + :padding 5px -.stream.conversations - :border - :right 1px solid #ccc + .message_count + :right 6px + :background + :color #999 + :color #eee + :position absolute + :padding 0 5px + :font + :size 12px + :weight normal + :-webkit-border-radius 3px + + .participant_count + :font + :weight normal + + .timestamp + :position absolute + :right 6px + :font + :weight normal + :color $blue + + .avatar + :display inline + :width 35px + :height 35px + :margin + :right 5px + + .message + :padding + :left 40px + + &:hover:not(.selected) + :background + :color #f0f0f0 + &:hover + :cursor pointer .conversation.selected :background - :color #ccc + :color $blue + .subject + :color #fff + .last_author + :color #fff + .message + :color #eee + .timestamp + :color #eee + :border + :bottom 1px solid darken($blue, 10%) + :top 1px solid darken($blue, 10%) + +#conversation_inbox + :position fixed + :height 100% + :overflow-y auto + :overflow-x none + :background + :color #f8f8f8 + :width 300px + + :border + :right 2px solid #999 + :left 2px solid #eee + +.fixit + :position fixed + +#no_conversation_text + :font + :size 20px + :weight bold + :color #ccc + :text + :align center + :margin + :top 100px + +#no_conversation_controls + :text + :align center + :font + :size 12px + From 48fff29bf6fcfabd480cdef5abbd202f8e9da137 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Fri, 4 Mar 2011 10:53:20 -0800 Subject: [PATCH 17/29] fixed conversation receive. made visibilities on conversation :dependent => :destroy --- app/models/conversation.rb | 13 +++++++++---- lib/diaspora/relayable.rb | 4 ++-- spec/models/conversation_spec.rb | 19 +++++++++++++++---- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/app/models/conversation.rb b/app/models/conversation.rb index d21eccd2ce1..9223a7b46cd 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -9,7 +9,7 @@ class Conversation < ActiveRecord::Base xml_reader :diaspora_handle xml_reader :participant_handles - has_many :conversation_visibilities + has_many :conversation_visibilities, :dependent => :destroy has_many :participants, :class_name => 'Person', :through => :conversation_visibilities, :source => :person has_many :messages, :order => 'created_at ASC' @@ -45,18 +45,23 @@ def participant_handles= handles end end + def last_author + self.messages.last.author if self.messages.size > 0 + end + def subscribers(user) self.recipients end def receive(user, person) cnv = Conversation.find_or_create_by_guid(self.attributes) + + self.participants.each do |participant| + ConversationVisibility.create(:conversation_id => cnv.id, :person_id => participant.id) + end self.messages.each do |msg| msg.conversation_id = cnv.id msg.receive(user, person) end - self.participants.each do |participant| - ConversationVisibility.create(:conversation_id => cnv.id, :person_id => participant.id) - end end end diff --git a/lib/diaspora/relayable.rb b/lib/diaspora/relayable.rb index bbab9696363..0197b8b2e49 100644 --- a/lib/diaspora/relayable.rb +++ b/lib/diaspora/relayable.rb @@ -40,12 +40,12 @@ def receive(user, person) #sign object as the parent creator if you've been hit UPSTREAM if user.owns? object.parent object.parent_author_signature = object.sign_with_key(user.encryption_key) - object.save + object.save! end #dispatch object DOWNSTREAM, received it via UPSTREAM unless user.owns?(object) - object.save + object.save! Postzord::Dispatch.new(user, object).post end diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb index 96ee16a3637..9bfa725bd4f 100644 --- a/spec/models/conversation_spec.rb +++ b/spec/models/conversation_spec.rb @@ -8,8 +8,9 @@ before do @user1 = alice @user2 = bob + @participant_ids = [@user1.contacts.first.person.id, @user1.person.id] - @create_hash = { :author => @user1.person, :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + @create_hash = { :author => @user1.person, :participant_ids => @participant_ids , :subject => "cool stuff", :text => 'hey'} end @@ -19,6 +20,16 @@ }.should change(Message, :count).by(1) end + describe '#last_author' do + it 'returns the last author to a conversation' do + time = Time.now + cnv = Conversation.create(@create_hash) + Message.create(:author => @user2.person, :created_at => time + 1.second, :text => "last", :conversation_id => cnv.id) + cnv.reload.last_author.id.should == @user2.person.id + end + end + + context 'transport' do before do @cnv = Conversation.create(@create_hash) @@ -50,8 +61,8 @@ describe '#receive' do before do - Conversation.delete_all - Message.delete_all + Conversation.destroy_all + Message.destroy_all end it 'creates a message' do @@ -67,7 +78,7 @@ it 'creates appropriate visibilities' do lambda{ Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) - }.should change(ConversationVisibility, :count).by(@cnv.participants.count) + }.should change(ConversationVisibility, :count).by(@participant_ids.size) end it 'does not save before receive' do Diaspora::Parser.from_xml(@xml).persisted?.should be_false From d50863cc90d3d483ecd03ed8a36fb9b735ff8cdb Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Fri, 4 Mar 2011 11:20:01 -0800 Subject: [PATCH 18/29] added the ability to message someone from their profile page --- app/controllers/conversations_controller.rb | 12 +++++-- app/views/conversations/new.haml | 39 +++++++++++++-------- app/views/people/show.html.haml | 10 +++--- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb index dfe07c023ce..3ff512e5267 100644 --- a/app/controllers/conversations_controller.rb +++ b/app/controllers/conversations_controller.rb @@ -6,7 +6,9 @@ class ConversationsController < ApplicationController def index @conversations = Conversation.joins(:conversation_visibilities).where( :conversation_visibilities => {:person_id => current_user.person.id}).paginate( - :page => params[:page], :per_page => 7, :order => 'updated_at DESC') + :page => params[:page], :per_page => 15, :order => 'updated_at DESC') + @authors = {} + @conversations.each{|c| @authors[c.id] = c.last_author} @conversation = Conversation.joins(:conversation_visibilities).where( :conversation_visibilities => {:person_id => current_user.person.id, :conversation_id => params[:conversation_id]}).first @@ -22,7 +24,12 @@ def create @conversation = Conversation.create(params[:conversation]) - redirect_to conversations_path(:conversation_id => @conversation.id) + flash[:notice] = "Message sent" + if params[:profile] + redirect_to person_path(params[:profile]) + else + redirect_to conversations_path(:conversation_id => @conversation.id) + end end def show @@ -37,6 +44,7 @@ def show end def new + @contact = current_user.contacts.find(params[:contact_id]) if params[:contact_id] render :layout => false end diff --git a/app/views/conversations/new.haml b/app/views/conversations/new.haml index e61d21678e1..03542287a7f 100644 --- a/app/views/conversations/new.haml +++ b/app/views/conversations/new.haml @@ -3,21 +3,32 @@ -# the COPYRIGHT file. -%h2 - New Message +#new_message_pane + #facebox_header + %h4 + New Message -= form_for Conversation.new do |conversation| - %h4 - to - = text_field_tag "conversation[contact_ids]" + = form_for Conversation.new do |conversation| - %h4 - subject - = conversation.text_field :subject + - if @contact + send a message to + = @contact.person.name - %h4 - message - = text_area_tag "conversation[text]", '', :rows => 5 + = hidden_field_tag "conversation[contact_ids]", @contact.id + = hidden_field_tag "profile", @contact.person.id - = conversation.submit :send - = link_to 'cancel', conversations_path + -else + %h4 + to + = text_field_tag "conversation[contact_ids]" + + %h4 + subject + = conversation.text_field :subject + + %h4 + message + = text_area_tag "conversation[text]", '', :rows => 5 + + = conversation.submit :send + = link_to 'cancel', conversations_path diff --git a/app/views/people/show.html.haml b/app/views/people/show.html.haml index 928c90a082b..e8385392607 100644 --- a/app/views/people/show.html.haml +++ b/app/views/people/show.html.haml @@ -42,10 +42,12 @@ - else .right - - if @post_type == :photos - = link_to t('layouts.header.view_profile'), person_path(@person) - - else - = link_to t('_photos'), person_photos_path(@person) + = link_to 'Message', new_conversation_path(:contact_id => @contact.id), :class => 'button', :rel => 'facebox' + + /- if @post_type == :photos + / = link_to t('layouts.header.view_profile'), person_path(@person) + /- else + / = link_to t('_photos'), person_photos_path(@person) %h3 = @person.name From 8fafc48d320f640b645f561c4754c8cfaf6ccbaa Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Fri, 4 Mar 2011 18:17:49 -0800 Subject: [PATCH 19/29] more private message wip. --- app/controllers/conversations_controller.rb | 3 ++ app/views/conversations/_conversation.haml | 8 +--- app/views/conversations/_show.haml | 26 ++++++------ app/views/conversations/index.haml | 11 ++++-- app/views/conversations/new.haml | 6 +++ public/stylesheets/sass/application.sass | 44 +++++++++++---------- 6 files changed, 56 insertions(+), 42 deletions(-) diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb index 3ff512e5267..d83b89504d6 100644 --- a/app/controllers/conversations_controller.rb +++ b/app/controllers/conversations_controller.rb @@ -4,6 +4,9 @@ class ConversationsController < ApplicationController respond_to :html, :json def index + + @all_contacts_and_ids = current_user.contacts.map{|c| {:id => c.id, :name => c.person.name}} + @conversations = Conversation.joins(:conversation_visibilities).where( :conversation_visibilities => {:person_id => current_user.person.id}).paginate( :page => params[:page], :per_page => 15, :order => 'updated_at DESC') diff --git a/app/views/conversations/_conversation.haml b/app/views/conversations/_conversation.haml index 5e1c450f86a..bcf865746da 100644 --- a/app/views/conversations/_conversation.haml +++ b/app/views/conversations/_conversation.haml @@ -3,7 +3,7 @@ -# the COPYRIGHT file. .stream_element.conversation{:data=>{:guid=>conversation.id}} - = person_image_tag(conversation.messages.last.author) + = person_image_tag(authors[conversation.id]) .subject .message_count @@ -14,11 +14,7 @@ .last_author .timestamp = time_ago_in_words conversation.updated_at - = conversation.author.name + = authors[conversation.id].name - if conversation.participants.size > 2 %span.participant_count = "(+#{conversation.participants.size - 1})" - - .message - = "#{conversation.messages.last.text[0..45]}..." - diff --git a/app/views/conversations/_show.haml b/app/views/conversations/_show.haml index b9566615fec..e92f8bf9c5f 100644 --- a/app/views/conversations/_show.haml +++ b/app/views/conversations/_show.haml @@ -2,23 +2,25 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -.conversation_participants.span-16.last - .span-10 - %h3 - = conversation.subject - - .conversation_controls - = link_to (image_tag('reply.png', :height => 14, :width => 14) + ' ' + 'reply'), '#', :id => 'reply_to_conversation' - = link_to (image_tag('deletelabel.png') + ' ' + t('delete').downcase), conversation_conversation_visibility_path(conversation), :method => 'delete', :confirm => t('are_you_sure') - .span-6.avatars.last - - for participant in conversation.participants - = person_image_link(participant) +.span-15.last + .conversation_participants + .span-9 + %h3 + = conversation.subject + + .conversation_controls + = link_to (image_tag('reply.png', :height => 14, :width => 14) + ' ' + 'reply'), '#', :id => 'reply_to_conversation' + = link_to (image_tag('deletelabel.png') + ' ' + t('delete').downcase), conversation_conversation_visibility_path(conversation), :method => 'delete', :confirm => t('are_you_sure') + + .span-6.avatars.last + - for participant in conversation.participants + = person_image_link(participant) %br %br %br -.span-16.last +.span-15.last .stream = render :partial => 'messages/message', :collection => conversation.messages diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml index 21c51f33051..2064f25c1d9 100644 --- a/app/views/conversations/index.haml +++ b/app/views/conversations/index.haml @@ -12,8 +12,11 @@ :css footer{ display:none;} -.span-5.append-3 - %h3.fixit{:style => 'width:300px;'} + += hidden_field_tag :contact_json, @all_contacts_and_ids.to_json + +.span-5.append-4 + %h3.fixit{:style => 'width:340px;'} .right = link_to 'New Message', new_conversation_path, :class => 'button', :rel => 'facebox' Inbox @@ -24,13 +27,13 @@ #conversation_inbox - if @conversations.count > 0 .stream.conversations - = render :partial => 'conversations/conversation', :collection => @conversations + = render :partial => 'conversations/conversation', :collection => @conversations, :locals => {:authors => @authors} = will_paginate @conversations - else %i You have no messages -#conversation_show.span-16.last +#conversation_show.span-15.last - if @conversation = render 'conversations/show', :conversation => @conversation - else diff --git a/app/views/conversations/new.haml b/app/views/conversations/new.haml index 03542287a7f..072e88f34a4 100644 --- a/app/views/conversations/new.haml +++ b/app/views/conversations/new.haml @@ -2,6 +2,12 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. +:javascript + $(document).ready(function () { + $("#my-text-input").tokenInput("/url/to/your/script/"); + }); + + #new_message_pane #facebox_header diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index 8c3f721bc3d..1213d33650c 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -52,6 +52,12 @@ form :width 50px :height 50px +#content + :background + :color #fff + :border 1px solid #ccc + :height 100% + #flash_notice, #flash_error, #flash_alert @@ -2271,7 +2277,8 @@ ul.show_comments :position relative :top 10px -#aspect_edit_pane +#aspect_edit_pane, +#new_message_pane :width 400px .person_tiles .tile @@ -2295,6 +2302,10 @@ ul.show_comments &.larger :width 600px +#new_message_pane + input + :width 90% + #facebox_header :padding 1em :background @@ -2484,7 +2495,7 @@ ul.show_comments .stream_element .subject :font - :size 14px + :size 13px :weight bold :color #444 :overflow hidden @@ -2493,12 +2504,8 @@ ul.show_comments .last_author :font :size 12px - :weight bold :color #777 - .message - :font - :size 12px .conversation_participants :z-index 3 @@ -2532,8 +2539,7 @@ ul.show_comments :border :bottom 1px solid #666 - :padding 5px - :left 10px + :padding 0 :top 90px :margin :top -100px @@ -2541,6 +2547,8 @@ ul.show_comments .avatars :text :align right + :margin + :top 9px .stream_element.new_message :border @@ -2557,10 +2565,11 @@ ul.show_comments :right -11px .stream_element.conversation - :padding 5px + :padding 10px + :min-height 0px .message_count - :right 6px + :right 10px :background :color #999 :color #eee @@ -2570,6 +2579,8 @@ ul.show_comments :size 12px :weight normal :-webkit-border-radius 3px + :-moz-border-radius 3px + :border-radius 3px .participant_count :font @@ -2577,7 +2588,7 @@ ul.show_comments .timestamp :position absolute - :right 6px + :right 10px :font :weight normal :color $blue @@ -2587,11 +2598,7 @@ ul.show_comments :width 35px :height 35px :margin - :right 5px - - .message - :padding - :left 40px + :right 10px &:hover:not(.selected) :background @@ -2606,8 +2613,6 @@ ul.show_comments :color #fff .last_author :color #fff - .message - :color #eee .timestamp :color #eee :border @@ -2621,11 +2626,10 @@ ul.show_comments :overflow-x none :background :color #f8f8f8 - :width 300px + :width 358px :border :right 2px solid #999 - :left 2px solid #eee .fixit :position fixed From 97aff091409a43beb056a8ecb7233070b2762003 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Mon, 7 Mar 2011 11:22:59 -0800 Subject: [PATCH 20/29] added autocomplete on message 'to' field, minor css tweaks to inbox --- app/controllers/conversations_controller.rb | 5 +- app/models/conversation.rb | 4 + app/views/conversations/_show.haml | 2 + app/views/conversations/index.haml | 16 +- app/views/conversations/new.haml | 11 +- app/views/layouts/_header.html.haml | 7 +- config/assets.yml | 2 + public/javascripts/inbox.js | 1 - .../javascripts/vendor/jquery.autoSuggest.js | 368 ++++++++++++++++++ public/javascripts/view.js | 8 + public/stylesheets/sass/application.sass | 42 +- public/stylesheets/vendor/autoSuggest.css | 217 +++++++++++ 12 files changed, 654 insertions(+), 29 deletions(-) create mode 100644 public/javascripts/vendor/jquery.autoSuggest.js create mode 100644 public/stylesheets/vendor/autoSuggest.css diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb index d83b89504d6..d803ee65f76 100644 --- a/app/controllers/conversations_controller.rb +++ b/app/controllers/conversations_controller.rb @@ -4,8 +4,7 @@ class ConversationsController < ApplicationController respond_to :html, :json def index - - @all_contacts_and_ids = current_user.contacts.map{|c| {:id => c.id, :name => c.person.name}} + @all_contacts_and_ids = current_user.contacts.map{|c| {:value => c.id, :name => c.person.name}} @conversations = Conversation.joins(:conversation_visibilities).where( :conversation_visibilities => {:person_id => current_user.person.id}).paginate( @@ -18,7 +17,7 @@ def index end def create - person_ids = Contact.where(:id => params[:conversation].delete(:contact_ids)).map! do |contact| + person_ids = Contact.where(:id => params[:contact_ids]).map! do |contact| contact.person_id end diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 9223a7b46cd..640984f16c6 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -49,6 +49,10 @@ def last_author self.messages.last.author if self.messages.size > 0 end + def subject + self[:subject].blank? ? "no subject" : self[:subject] + end + def subscribers(user) self.recipients end diff --git a/app/views/conversations/_show.haml b/app/views/conversations/_show.haml index e92f8bf9c5f..60bee1f3bf8 100644 --- a/app/views/conversations/_show.haml +++ b/app/views/conversations/_show.haml @@ -17,6 +17,8 @@ - for participant in conversation.participants = person_image_link(participant) +%br +%br %br %br %br diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml index 2064f25c1d9..28fecb93cb1 100644 --- a/app/views/conversations/index.haml +++ b/app/views/conversations/index.haml @@ -15,14 +15,12 @@ = hidden_field_tag :contact_json, @all_contacts_and_ids.to_json -.span-5.append-4 - %h3.fixit{:style => 'width:340px;'} - .right - = link_to 'New Message', new_conversation_path, :class => 'button', :rel => 'facebox' - Inbox - %br - %br - %br +#left_pane + #left_pane_header + %h3 + .right + = link_to 'New Message', new_conversation_path, :class => 'button', :rel => 'facebox' + Inbox #conversation_inbox - if @conversations.count > 0 @@ -33,7 +31,7 @@ %i You have no messages -#conversation_show.span-15.last +#conversation_show.span-15.prepend-8.last - if @conversation = render 'conversations/show', :conversation => @conversation - else diff --git a/app/views/conversations/new.haml b/app/views/conversations/new.haml index 072e88f34a4..4eea90612a8 100644 --- a/app/views/conversations/new.haml +++ b/app/views/conversations/new.haml @@ -4,10 +4,13 @@ :javascript $(document).ready(function () { - $("#my-text-input").tokenInput("/url/to/your/script/"); + var data = $.parseJSON( $('#contact_json').val() ); + $("#contact_autocomplete").autoSuggest(data, { + selectedItemProp: "name", + searchObjProps: "name", + asHtmlID: "contact_ids" + }); }); - - #new_message_pane #facebox_header @@ -26,7 +29,7 @@ -else %h4 to - = text_field_tag "conversation[contact_ids]" + = text_field_tag "contact_autocomplete" %h4 subject diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index c4754d5336e..40b3f503128 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -25,7 +25,12 @@ #notification_badge = link_to "", notifications_path, :title => new_notification_text(@notification_count) = image_tag 'icons/mail_grey.png' - #notification_badge_number{:class => ("hidden" if @notification_count == 0)} + .badge_count{:class => ("hidden" if @notification_count == 0)} + = @notification_count + #message_inbox_badge + = link_to "", conversations_path #, :title => new_notification_text(@notification_count) + = image_tag 'icons/mail_grey.png' + .badge_count{:class => ("hidden" if @notification_count == 0)} = @notification_count %ul#user_menu diff --git a/config/assets.yml b/config/assets.yml index e70e461b4cc..0f56bf76436 100644 --- a/config/assets.yml +++ b/config/assets.yml @@ -57,6 +57,7 @@ javascripts: photos: - public/javascripts/photo-show.js inbox: + - public/javascripts/vendor/jquery.autoSuggest.js - public/javascripts/inbox.js stylesheets: @@ -67,4 +68,5 @@ stylesheets: - public/stylesheets/vendor/facebox.css - public/stylesheets/vendor/fileuploader.css - public/stylesheets/vendor/tipsy.css + - public/stylesheets/vendor/autoSuggest.css diff --git a/public/javascripts/inbox.js b/public/javascripts/inbox.js index ad8478f2155..769b6a0c802 100644 --- a/public/javascripts/inbox.js +++ b/public/javascripts/inbox.js @@ -67,7 +67,6 @@ $(document).ready(function(){ $('#message_text').focus(); }); }); - }); var resize = function(){ diff --git a/public/javascripts/vendor/jquery.autoSuggest.js b/public/javascripts/vendor/jquery.autoSuggest.js new file mode 100644 index 00000000000..01f58111a68 --- /dev/null +++ b/public/javascripts/vendor/jquery.autoSuggest.js @@ -0,0 +1,368 @@ + /* + * AutoSuggest + * Copyright 2009-2010 Drew Wilson + * www.drewwilson.com + * code.drewwilson.com/entry/autosuggest-jquery-plugin + * + * Version 1.4 - Updated: Mar. 23, 2010 + * + * This Plug-In will auto-complete or auto-suggest completed search queries + * for you as you type. You can add multiple selections and remove them on + * the fly. It supports keybord navigation (UP + DOWN + RETURN), as well + * as multiple AutoSuggest fields on the same page. + * + * Inspied by the Autocomplete plugin by: Jšrn Zaefferer + * and the Facelist plugin by: Ian Tearle (iantearle.com) + * + * This AutoSuggest jQuery plug-in is dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ + +(function($){ + $.fn.autoSuggest = function(data, options) { + var defaults = { + asHtmlID: false, + startText: "Enter Name Here", + emptyText: "No Results Found", + preFill: {}, + limitText: "No More Selections Are Allowed", + selectedItemProp: "value", //name of object property + selectedValuesProp: "value", //name of object property + searchObjProps: "value", //comma separated list of object property names + queryParam: "q", + retrieveLimit: false, //number for 'limit' param on ajax request + extraParams: "", + matchCase: false, + minChars: 1, + keyDelay: 400, + resultsHighlight: true, + neverSubmit: false, + selectionLimit: false, + showResultList: true, + start: function(){}, + selectionClick: function(elem){}, + selectionAdded: function(elem){}, + selectionRemoved: function(elem){ elem.remove(); }, + formatList: false, //callback function + beforeRetrieve: function(string){ return string; }, + retrieveComplete: function(data){ return data; }, + resultClick: function(data){}, + resultsComplete: function(){} + }; + var opts = $.extend(defaults, options); + + var d_type = "object"; + var d_count = 0; + if(typeof data == "string") { + d_type = "string"; + var req_string = data; + } else { + var org_data = data; + for (k in data) if (data.hasOwnProperty(k)) d_count++; + } + if((d_type == "object" && d_count > 0) || d_type == "string"){ + return this.each(function(x){ + if(!opts.asHtmlID){ + x = x+""+Math.floor(Math.random()*100); //this ensures there will be unique IDs on the page if autoSuggest() is called multiple times + var x_id = "as-input-"+x; + } else { + x = opts.asHtmlID; + var x_id = x; + } + opts.start.call(this); + var input = $(this); + input.attr("autocomplete","off").addClass("as-input").attr("id",x_id).val(opts.startText); + var input_focus = false; + + // Setup basic elements and render them to the DOM + input.wrap('
    ').wrap('
  • '); + var selections_holder = $("#as-selections-"+x); + var org_li = $("#as-original-"+x); + var results_holder = $('
    ').hide(); + var results_ul = $('
      '); + var values_input = $(''); + var prefill_value = ""; + if(typeof opts.preFill == "string"){ + var vals = opts.preFill.split(","); + for(var i=0; i < vals.length; i++){ + var v_data = {}; + v_data[opts.selectedValuesProp] = vals[i]; + if(vals[i] != ""){ + add_selected_item(v_data, "000"+i); + } + } + prefill_value = opts.preFill; + } else { + prefill_value = ""; + var prefill_count = 0; + for (k in opts.preFill) if (opts.preFill.hasOwnProperty(k)) prefill_count++; + if(prefill_count > 0){ + for(var i=0; i < prefill_count; i++){ + var new_v = opts.preFill[i][opts.selectedValuesProp]; + if(new_v == undefined){ new_v = ""; } + prefill_value = prefill_value+new_v+","; + if(new_v != ""){ + add_selected_item(opts.preFill[i], "000"+i); + } + } + } + } + if(prefill_value != ""){ + input.val(""); + var lastChar = prefill_value.substring(prefill_value.length-1); + if(lastChar != ","){ prefill_value = prefill_value+","; } + values_input.val(","+prefill_value); + $("li.as-selection-item", selections_holder).addClass("blur").removeClass("selected"); + } + input.after(values_input); + selections_holder.click(function(){ + input_focus = true; + input.focus(); + }).mousedown(function(){ input_focus = false; }).after(results_holder); + + var timeout = null; + var prev = ""; + var totalSelections = 0; + var tab_press = false; + + // Handle input field events + input.focus(function(){ + if($(this).val() == opts.startText && values_input.val() == ""){ + $(this).val(""); + } else if(input_focus){ + $("li.as-selection-item", selections_holder).removeClass("blur"); + if($(this).val() != ""){ + results_ul.css("width",selections_holder.outerWidth()); + results_holder.show(); + } + } + input_focus = true; + return true; + }).blur(function(){ + if($(this).val() == "" && values_input.val() == "" && prefill_value == ""){ + $(this).val(opts.startText); + } else if(input_focus){ + $("li.as-selection-item", selections_holder).addClass("blur").removeClass("selected"); + results_holder.hide(); + } + }).keydown(function(e) { + // track last key pressed + lastKeyPressCode = e.keyCode; + first_focus = false; + switch(e.keyCode) { + case 38: // up + e.preventDefault(); + moveSelection("up"); + break; + case 40: // down + e.preventDefault(); + moveSelection("down"); + break; + case 8: // delete + if(input.val() == ""){ + var last = values_input.val().split(","); + last = last[last.length - 2]; + selections_holder.children().not(org_li.prev()).removeClass("selected"); + if(org_li.prev().hasClass("selected")){ + values_input.val(values_input.val().replace(","+last+",",",")); + opts.selectionRemoved.call(this, org_li.prev()); + } else { + opts.selectionClick.call(this, org_li.prev()); + org_li.prev().addClass("selected"); + } + } + if(input.val().length == 1){ + results_holder.hide(); + prev = ""; + } + if($(":visible",results_holder).length > 0){ + if (timeout){ clearTimeout(timeout); } + timeout = setTimeout(function(){ keyChange(); }, opts.keyDelay); + } + break; + case 9: case 188: // tab or comma + tab_press = true; + var i_input = input.val().replace(/(,)/g, ""); + if(i_input != "" && values_input.val().search(","+i_input+",") < 0 && i_input.length >= opts.minChars){ + e.preventDefault(); + var n_data = {}; + n_data[opts.selectedItemProp] = i_input; + n_data[opts.selectedValuesProp] = i_input; + var lis = $("li", selections_holder).length; + add_selected_item(n_data, "00"+(lis+1)); + input.val(""); + } + case 13: // return + tab_press = false; + var active = $("li.active:first", results_holder); + if(active.length > 0){ + active.click(); + results_holder.hide(); + } + if(opts.neverSubmit || active.length > 0){ + e.preventDefault(); + } + break; + default: + if(opts.showResultList){ + if(opts.selectionLimit && $("li.as-selection-item", selections_holder).length >= opts.selectionLimit){ + results_ul.html('
    • '+opts.limitText+'
    • '); + results_holder.show(); + } else { + if (timeout){ clearTimeout(timeout); } + timeout = setTimeout(function(){ keyChange(); }, opts.keyDelay); + } + } + break; + } + }); + + function keyChange() { + // ignore if the following keys are pressed: [del] [shift] [capslock] + if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ){ return results_holder.hide(); } + var string = input.val().replace(/[\\]+|[\/]+/g,""); + if (string == prev) return; + prev = string; + if (string.length >= opts.minChars) { + selections_holder.addClass("loading"); + if(d_type == "string"){ + var limit = ""; + if(opts.retrieveLimit){ + limit = "&limit="+encodeURIComponent(opts.retrieveLimit); + } + if(opts.beforeRetrieve){ + string = opts.beforeRetrieve.call(this, string); + } + $.getJSON(req_string+"?"+opts.queryParam+"="+encodeURIComponent(string)+limit+opts.extraParams, function(data){ + d_count = 0; + var new_data = opts.retrieveComplete.call(this, data); + for (k in new_data) if (new_data.hasOwnProperty(k)) d_count++; + processData(new_data, string); + }); + } else { + if(opts.beforeRetrieve){ + string = opts.beforeRetrieve.call(this, string); + } + processData(org_data, string); + } + } else { + selections_holder.removeClass("loading"); + results_holder.hide(); + } + } + var num_count = 0; + function processData(data, query){ + if (!opts.matchCase){ query = query.toLowerCase(); } + var matchCount = 0; + results_holder.html(results_ul.html("")).hide(); + for(var i=0;i').click(function(){ + var raw_data = $(this).data("data"); + var number = raw_data.num; + if($("#as-selection-"+number, selections_holder).length <= 0 && !tab_press){ + var data = raw_data.attributes; + input.val("").focus(); + prev = ""; + add_selected_item(data, number); + opts.resultClick.call(this, raw_data); + results_holder.hide(); + } + tab_press = false; + }).mousedown(function(){ input_focus = false; }).mouseover(function(){ + $("li", results_ul).removeClass("active"); + $(this).addClass("active"); + }).data("data",{attributes: data[num], num: num_count}); + var this_data = $.extend({},data[num]); + if (!opts.matchCase){ + var regx = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "gi"); + } else { + var regx = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "g"); + } + + if(opts.resultsHighlight){ + this_data[opts.selectedItemProp] = this_data[opts.selectedItemProp].replace(regx,"$1"); + } + if(!opts.formatList){ + formatted = formatted.html(this_data[opts.selectedItemProp]); + } else { + formatted = opts.formatList.call(this, this_data, formatted); + } + results_ul.append(formatted); + delete this_data; + matchCount++; + if(opts.retrieveLimit && opts.retrieveLimit == matchCount ){ break; } + } + } + selections_holder.removeClass("loading"); + if(matchCount <= 0){ + results_ul.html('
    • '+opts.emptyText+'
    • '); + } + results_ul.css("width", selections_holder.outerWidth()); + results_holder.show(); + opts.resultsComplete.call(this); + } + + function add_selected_item(data, num){ + values_input.val(values_input.val()+data[opts.selectedValuesProp]+","); + var item = $('
    • ').click(function(){ + opts.selectionClick.call(this, $(this)); + selections_holder.children().removeClass("selected"); + $(this).addClass("selected"); + }).mousedown(function(){ input_focus = false; }); + var close = $('×').click(function(){ + values_input.val(values_input.val().replace(","+data[opts.selectedValuesProp]+",",",")); + opts.selectionRemoved.call(this, item); + input_focus = true; + input.focus(); + return false; + }); + org_li.before(item.html(data[opts.selectedItemProp]).prepend(close)); + opts.selectionAdded.call(this, org_li.prev()); + } + + function moveSelection(direction){ + if($(":visible",results_holder).length > 0){ + var lis = $("li", results_holder); + if(direction == "down"){ + var start = lis.eq(0); + } else { + var start = lis.filter(":last"); + } + var active = $("li.active:first", results_holder); + if(active.length > 0){ + if(direction == "down"){ + start = active.next(); + } else { + start = active.prev(); + } + } + lis.removeClass("active"); + start.addClass("active"); + } + } + + }); + } + } +})(jQuery); diff --git a/public/javascripts/view.js b/public/javascripts/view.js index 198268cccee..6f79f0d63b0 100644 --- a/public/javascripts/view.js +++ b/public/javascripts/view.js @@ -165,6 +165,14 @@ var View = { } }, + conversation_participants: { + bind: function() { + $(".conversation_participants img").tipsy({ + live: true + }); + } + }, + whatIsThis: { bind: function() { $(".what_is_this").tipsy({ diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index 1213d33650c..59bd5304dd4 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -1666,11 +1666,13 @@ h3 span.current_gs_step :background :color #22AAE0 -#notification_badge +#notification_badge, +#message_inbox_badge :position relative :top 5px :display inline - :margin 0 1em + :margin 0 10px + :right 0 :font :weight bold :size smaller @@ -1683,7 +1685,7 @@ h3 span.current_gs_step :width 20px :height 20px -#notification_badge_number +.badge_count :z-index 3 :position absolute :top -10px @@ -2539,8 +2541,9 @@ ul.show_comments :border :bottom 1px solid #666 - :padding 0 - :top 90px + :padding 20px + :top 110px + :bottom 10px :margin :top -100px @@ -2620,19 +2623,36 @@ ul.show_comments :top 1px solid darken($blue, 10%) #conversation_inbox - :position fixed :height 100% :overflow-y auto :overflow-x none :background - :color #f8f8f8 - :width 358px + :color #f3f3f3 + +#left_pane + :position fixed + :width 320px + :background + :color #ddd + + :-webkit-box-shadow 2px 2px 5px -1px #999 :border - :right 2px solid #999 + :right 1px solid #999 + :z-index 4 + + h3 + :padding + :bottom 0 + :margin + :bottom 15px + + #left_pane_header + :padding 10px + :height 22px + :border + :bottom 1px solid #ddd -.fixit - :position fixed #no_conversation_text :font diff --git a/public/stylesheets/vendor/autoSuggest.css b/public/stylesheets/vendor/autoSuggest.css new file mode 100644 index 00000000000..a694cd71ba5 --- /dev/null +++ b/public/stylesheets/vendor/autoSuggest.css @@ -0,0 +1,217 @@ +/* AutoSuggest CSS - Version 1.2 */ + +ul.as-selections { + list-style-type: none; + border-top: 1px solid #888; + border-bottom: 1px solid #b6b6b6; + border-left: 1px solid #aaa; + border-right: 1px solid #aaa; + padding: 4px 0 4px 4px; + margin: 0; + overflow: auto; + background-color: #fff; + box-shadow:inset 0 1px 2px #888; + -webkit-box-shadow:inset 0 1px 2px #888; + -moz-box-shadow:inset 0 1px 2px #888; +} + +ul.as-selections.loading { + background-color: #eee; +} + +ul.as-selections li { + float: left; + margin: 1px 4px 1px 0; +} + +ul.as-selections li.as-selection-item { + color: #2b3840; + font-size: 13px; + font-family: "Lucida Grande", arial, sans-serif; + text-shadow: 0 1px 1px #fff; + background-color: #ddeefe; + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#ddeefe), to(#bfe0f1)); + border: 1px solid #acc3ec; + border-top-color: #c0d9e9; + padding: 2px 7px 2px 10px; + border-radius: 12px; + -webkit-border-radius: 12px; + -moz-border-radius: 12px; + box-shadow: 0 1px 1px #e4edf2; + -webkit-box-shadow: 0 1px 1px #e4edf2; + -moz-box-shadow: 0 1px 1px #e4edf2; +} + +ul.as-selections li.as-selection-item:last-child { + margin-left: 30px; +} + +ul.as-selections li.as-selection-item a.as-close { + float: right; + margin: 1px 0 0 7px; + padding: 0 2px; + cursor: pointer; + color: #5491be; + font-family: "Helvetica", helvetica, arial, sans-serif; + font-size: 14px; + font-weight: bold; + text-shadow: 0 1px 1px #fff; + -webkit-transition: color .1s ease-in; +} + +ul.as-selections li.as-selection-item.blur { + color: #666666; + background-color: #f4f4f4; + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#f4f4f4), to(#d5d5d5)); + border-color: #bbb; + border-top-color: #ccc; + box-shadow: 0 1px 1px #e9e9e9; + -webkit-box-shadow: 0 1px 1px #e9e9e9; + -moz-box-shadow: 0 1px 1px #e9e9e9; +} + +ul.as-selections li.as-selection-item.blur a.as-close { + color: #999; +} + +ul.as-selections li:hover.as-selection-item { + color: #2b3840; + background-color: #bbd4f1; + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#bbd4f1), to(#a3c2e5)); + border-color: #6da0e0; + border-top-color: #8bb7ed; +} + +ul.as-selections li:hover.as-selection-item a.as-close { + color: #4d70b0; +} + +ul.as-selections li.as-selection-item.selected { + border-color: #1f30e4; +} + +ul.as-selections li.as-selection-item a:hover.as-close { + color: #1b3c65; +} + +ul.as-selections li.as-selection-item a:active.as-close { + color: #4d70b0; +} + +ul.as-selections li.as-original { + margin-left: 0; +} + +ul.as-selections li.as-original input { + border: none; + outline: none; + font-size: 13px; + width: 120px; + height: 18px; + padding-top: 3px; +} + +ul.as-list { + position: absolute; + list-style-type: none; + margin: 2px 0 0 0; + padding: 0; + font-size: 14px; + color: #000; + font-family: "Lucida Grande", arial, sans-serif; + background-color: #fff; + background-color: rgba(255,255,255,0.95); + z-index: 2; + box-shadow: 0 2px 12px #222; + -webkit-box-shadow: 0 2px 12px #222; + -moz-box-shadow: 0 2px 12px #222; + border-radius: 5px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; +} + +li.as-result-item, li.as-message { + margin: 0 0 0 0; + padding: 5px 12px; + background-color: transparent; + border: 1px solid #fff; + border-bottom: 1px solid #ddd; + cursor: pointer; + border-radius: 5px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; +} + +li:first-child.as-result-item { + margin: 0; +} + +li.as-message { + margin: 0; + cursor: default; +} + +li.as-result-item.active { + background-color: #3668d9; + background-image: -webkit-gradient(linear, 0% 0%, 0% 64%, from(rgb(110, 129, 245)), to(rgb(62, 82, 242))); + border-color: #3342e8; + color: #fff; + text-shadow: 0 1px 2px #122042; +} + +li.as-result-item em { + font-style: normal; + background: #444; + padding: 0 2px; + color: #fff; +} + +li.as-result-item.active em { + background: #253f7a; + color: #fff; +} + +/* Webkit Hacks */ +@media screen and (-webkit-min-device-pixel-ratio:0) { + ul.as-selections { + border-top-width: 2px; + } + ul.as-selections li.as-selection-item { + padding-top: 3px; + padding-bottom: 3px; + } + ul.as-selections li.as-selection-item a.as-close { + margin-top: -1px; + } + ul.as-selections li.as-original input { + height: 19px; + } +} + +/* Opera Hacks */ +@media all and (-webkit-min-device-pixel-ratio:10000), not all and (-webkit-min-device-pixel-ratio:0) { + ul.as-list { + border: 1px solid #888; + } + ul.as-selections li.as-selection-item a.as-close { + margin-left: 4px; + margin-top: 0; + } +} + +/* IE Hacks */ +ul.as-list { + border: 1px solid #888\9; +} +ul.as-selections li.as-selection-item a.as-close { + margin-left: 4px\9; + margin-top: 0\9; +} + +/* Firefox 3.0 Hacks */ +ul.as-list, x:-moz-any-link, x:default { + border: 1px solid #888; +} +BODY:first-of-type ul.as-list, x:-moz-any-link, x:default { /* Target FF 3.5+ */ + border: none; +} From fca5310c7728c82f2a8242b84e9548a08034d7f5 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Mon, 7 Mar 2011 17:54:25 -0800 Subject: [PATCH 21/29] dispatch the conversation in ConversationsController --- app/controllers/application_controller.rb | 5 +++-- app/controllers/conversations_controller.rb | 16 +++++++++------- app/models/conversation.rb | 2 +- app/models/message.rb | 10 ++++++++++ app/views/conversations/index.haml | 1 - app/views/layouts/_header.html.haml | 4 ++-- lib/diaspora/relayable.rb | 8 +++++++- public/stylesheets/sass/application.sass | 4 ++-- .../conversations_controller_spec.rb | 15 +++++++++++++-- spec/models/message_spec.rb | 11 +++++++++++ spec/shared_behaviors/relayable.rb | 17 +++++++++++------ 11 files changed, 69 insertions(+), 24 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2174c8f2d2f..93ede8c9a0a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,7 +7,7 @@ class ApplicationController < ActionController::Base protect_from_forgery :except => :receive before_filter :ensure_http_referer_is_set - before_filter :set_contacts_notifications_and_status, :except => [:create, :update] + before_filter :set_contacts_notifications_unread_count_and_status, :except => [:create, :update] before_filter :count_requests before_filter :set_invites before_filter :set_locale @@ -22,12 +22,13 @@ def ensure_http_referer_is_set request.env['HTTP_REFERER'] ||= '/aspects' end - def set_contacts_notifications_and_status + def set_contacts_notifications_unread_count_and_status if user_signed_in? @aspect = nil @object_aspect_ids = [] @all_aspects = current_user.aspects.includes(:aspect_memberships, :post_visibilities) @notification_count = Notification.for(current_user, :unread =>true).count + @unread_message_count = ConversationVisibility.sum(:unread, :conditions => "person_id = #{current_user.person.id}") @user_id = current_user.id end end diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb index d803ee65f76..dd083860265 100644 --- a/app/controllers/conversations_controller.rb +++ b/app/controllers/conversations_controller.rb @@ -24,13 +24,15 @@ def create params[:conversation][:participant_ids] = person_ids | [current_user.person.id] params[:conversation][:author] = current_user.person - @conversation = Conversation.create(params[:conversation]) - - flash[:notice] = "Message sent" - if params[:profile] - redirect_to person_path(params[:profile]) - else - redirect_to conversations_path(:conversation_id => @conversation.id) + if @conversation = Conversation.create(params[:conversation]) + Postzord::Dispatch.new(current_user, @conversation).post + + flash[:notice] = "Message sent" + if params[:profile] + redirect_to person_path(params[:profile]) + else + redirect_to conversations_path(:conversation_id => @conversation.id) + end end end diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 640984f16c6..a1e40665d73 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -61,7 +61,7 @@ def receive(user, person) cnv = Conversation.find_or_create_by_guid(self.attributes) self.participants.each do |participant| - ConversationVisibility.create(:conversation_id => cnv.id, :person_id => participant.id) + ConversationVisibility.find_or_create_by_conversation_id_and_person_id(cnv.id, participant.id) end self.messages.each do |msg| msg.conversation_id = cnv.id diff --git a/app/models/message.rb b/app/models/message.rb index 6a2f1adf456..77c5faba026 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -57,6 +57,16 @@ def parent= parent self.conversation = parent end + def after_receive(user, person) + if vis = ConversationVisibility.where(:conversation_id => self.conversation_id, :person_id => user.person.id).first + vis.unread += 1 + vis.save + self + else + raise NotVisibileException("Attempting to access a ConversationVisibility that does not exist!") + end + end + private def participant_of_parent_conversation if self.parent && !self.parent.participants.include?(self.author) diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml index 28fecb93cb1..870833e927e 100644 --- a/app/views/conversations/index.haml +++ b/app/views/conversations/index.haml @@ -12,7 +12,6 @@ :css footer{ display:none;} - = hidden_field_tag :contact_json, @all_contacts_and_ids.to_json #left_pane diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 40b3f503128..7184c98e6cd 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -30,8 +30,8 @@ #message_inbox_badge = link_to "", conversations_path #, :title => new_notification_text(@notification_count) = image_tag 'icons/mail_grey.png' - .badge_count{:class => ("hidden" if @notification_count == 0)} - = @notification_count + .badge_count{:class => ("hidden" if @unread_message_count == 0)} + = @unread_message_count %ul#user_menu .right diff --git a/lib/diaspora/relayable.rb b/lib/diaspora/relayable.rb index 0197b8b2e49..476ae764984 100644 --- a/lib/diaspora/relayable.rb +++ b/lib/diaspora/relayable.rb @@ -50,7 +50,13 @@ def receive(user, person) end object.socket_to_user(user, :aspect_ids => object.parent.aspect_ids) if object.respond_to? :socket_to_user - object + if object.after_receive(user, person) + object + end + end + + def after_receive(user, person) + self end def signable_string diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index 59bd5304dd4..d017757a54e 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -358,9 +358,9 @@ header :margin :bottom 0px :font - :size 12px + :size 13px :line - :height 16px + :height 18px .photo_attachments :margin diff --git a/spec/controllers/conversations_controller_spec.rb b/spec/controllers/conversations_controller_spec.rb index d567d9b62de..50f6ed14a06 100644 --- a/spec/controllers/conversations_controller_spec.rb +++ b/spec/controllers/conversations_controller_spec.rb @@ -37,9 +37,9 @@ describe '#create' do before do @hash = {:conversation => { - :contact_ids => [@user1.contacts.first.id], :subject => "secret stuff", - :text => 'text'}} + :text => 'text'}, + :contact_ids => '@user1.contacts.first.id'} end it 'creates a conversation' do @@ -61,6 +61,17 @@ Message.first.author.should == @user1.person Conversation.first.author.should == @user1.person end + + it 'dispatches the conversation' do + cnv = Conversation.create(@hash[:conversation].merge({ + :author => @user1.person, + :participant_ids => [@user1.contacts.first.person.id]})) + + p = Postzord::Dispatch.new(@user1, cnv) + Postzord::Dispatch.stub!(:new).and_return(p) + p.should_receive(:post) + post :create, @hash + end end describe '#show' do diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb index faf2bda984d..99aca42df6e 100644 --- a/spec/models/message_spec.rb +++ b/spec/models/message_spec.rb @@ -78,5 +78,16 @@ Postzord::Dispatch.new(@local_luke, @object_on_remote_parent).post end it_should_behave_like 'it is relayable' + + describe '#after_receive' do + it 'increments the conversation visiblity for the conversation' do + ConversationVisibility.where(:conversation_id => @object_by_recipient.reload.conversation.id, + :person_id => @local_luke.person.id).first.unread.should == 0 + + @object_by_recipient.receive(@local_luke, @local_leia.person) + ConversationVisibility.where(:conversation_id => @object_by_recipient.reload.conversation.id, + :person_id => @local_luke.person.id).first.unread.should == 1 + end + end end end diff --git a/spec/shared_behaviors/relayable.rb b/spec/shared_behaviors/relayable.rb index b24a3a50704..ba809119a3b 100644 --- a/spec/shared_behaviors/relayable.rb +++ b/spec/shared_behaviors/relayable.rb @@ -8,7 +8,7 @@ shared_examples_for "it is relayable" do context 'encryption' do describe '#parent_author_signature' do - it 'should sign the comment if the user is the post author' do + it 'should sign the object if the user is the post author' do @object_by_parent_author.verify_parent_author_signature.should be_true end @@ -17,7 +17,7 @@ @object_by_recipient.verify_parent_author_signature.should be_false end - it 'should verify a comment made on a remote post by a different contact' do + it 'should verify a object made on a remote post by a different contact' do @object_by_recipient.author_signature = @object_by_recipient.send(:sign_with_key, @local_leia.encryption_key) @object_by_recipient.parent_author_signature = @object_by_recipient.send(:sign_with_key, @local_luke.encryption_key) @object_by_recipient.verify_parent_author_signature.should be_true @@ -35,14 +35,14 @@ context 'propagation' do describe '#receive' do - it 'does not overwrite a comment that is already in the db' do + it 'does not overwrite a object that is already in the db' do lambda{ @dup_object_by_parent_author.receive(@local_leia, @local_luke.person) - }.should_not change(Comment, :count) + }.should_not change(@dup_object_by_parent_author.class, :count) end it 'does not process if post_creator_signature is invalid' do - @object_by_parent_author.delete # remove comment from db so we set a creator sig + @object_by_parent_author.delete # remove object from db so we set a creator sig @dup_object_by_parent_author.parent_author_signature = "dsfadsfdsa" @dup_object_by_parent_author.receive(@local_leia, @local_luke.person).should == nil end @@ -65,6 +65,11 @@ @object_by_recipient.should_receive(:socket_to_user).exactly(3).times @object_by_recipient.receive(@local_luke, @local_leia.person) end + + it 'calls after_receive callback' do + @object_by_recipient.should_receive(:after_receive) + @object_by_recipient.receive(@local_luke, @local_leia.person) + end end describe '#subscribers' do @@ -72,7 +77,7 @@ @object_by_parent_author.subscribers(@local_luke).map(&:id).should =~ [@local_leia.person, @remote_raphael].map(&:id) end - it 'returns the owner of the original post, if the user owns the comment' do + it 'returns the owner of the original post, if the user owns the object' do @object_by_recipient.subscribers(@local_leia).map(&:id).should =~ [@local_luke.person].map(&:id) end end From fb5e5cc341331f082759cb6bb2a09e207b2630e6 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Mon, 7 Mar 2011 20:14:22 -0800 Subject: [PATCH 22/29] cleaned up new message facebox --- app/views/conversations/_show.haml | 4 +- app/views/conversations/index.haml | 2 +- app/views/conversations/new.haml | 57 ++++++++++-------- app/views/people/show.html.haml | 2 +- .../javascripts/vendor/jquery.autoSuggest.js | 10 +++- public/stylesheets/sass/application.sass | 20 +++++-- public/stylesheets/vendor/autoSuggest.css | 59 +++++++++---------- 7 files changed, 86 insertions(+), 68 deletions(-) diff --git a/app/views/conversations/_show.haml b/app/views/conversations/_show.haml index 60bee1f3bf8..bee452285b2 100644 --- a/app/views/conversations/_show.haml +++ b/app/views/conversations/_show.haml @@ -3,7 +3,7 @@ -# the COPYRIGHT file. -.span-15.last +.span-16.last .conversation_participants .span-9 %h3 @@ -22,7 +22,7 @@ %br %br %br -.span-15.last +.span-16.last .stream = render :partial => 'messages/message', :collection => conversation.messages diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml index 870833e927e..0ac8a517d79 100644 --- a/app/views/conversations/index.haml +++ b/app/views/conversations/index.haml @@ -30,7 +30,7 @@ %i You have no messages -#conversation_show.span-15.prepend-8.last +#conversation_show.span-16.prepend-8.last - if @conversation = render 'conversations/show', :conversation => @conversation - else diff --git a/app/views/conversations/new.haml b/app/views/conversations/new.haml index 4eea90612a8..88ec08275da 100644 --- a/app/views/conversations/new.haml +++ b/app/views/conversations/new.haml @@ -4,40 +4,45 @@ :javascript $(document).ready(function () { - var data = $.parseJSON( $('#contact_json').val() ); - $("#contact_autocomplete").autoSuggest(data, { + var data = $.parseJSON( $('#contact_json').val() ), + autocompleteInput = $("#contact_autocomplete"); + + autocompleteInput.autoSuggest(data, { selectedItemProp: "name", searchObjProps: "name", - asHtmlID: "contact_ids" + asHtmlID: "contact_ids", + keyDelay: 0, + startText: '' }); - }); -#new_message_pane - #facebox_header - %h4 - New Message + autocompleteInput.focus(); - = form_for Conversation.new do |conversation| + }); - - if @contact - send a message to - = @contact.person.name +#new_message_pane + .span-12.last + #facebox_header + %h4 + New Message - = hidden_field_tag "conversation[contact_ids]", @contact.id - = hidden_field_tag "profile", @contact.person.id + = form_for Conversation.new do |conversation| + %br - -else - %h4 - to - = text_field_tag "contact_autocomplete" + .span-2 + %h4 + to + .span-10.last + = text_field_tag "contact_autocomplete" - %h4 - subject - = conversation.text_field :subject + .span-2 + %h4 + subject + .span-10.last + = conversation.text_field :subject - %h4 - message - = text_area_tag "conversation[text]", '', :rows => 5 + .span-10.prepend-2.last + = text_area_tag "conversation[text]", '', :rows => 5 - = conversation.submit :send - = link_to 'cancel', conversations_path + .text-right + = conversation.submit :send, :class => 'button' + = link_to 'cancel', conversations_path diff --git a/app/views/people/show.html.haml b/app/views/people/show.html.haml index e8385392607..6e6a73e6b8c 100644 --- a/app/views/people/show.html.haml +++ b/app/views/people/show.html.haml @@ -42,7 +42,7 @@ - else .right - = link_to 'Message', new_conversation_path(:contact_id => @contact.id), :class => 'button', :rel => 'facebox' + = link_to 'Message', new_conversation_path(:contact_id => @contact.id, :name => @contact.person.name, :contact_id => @contact.id), :class => 'button', :rel => 'facebox' /- if @post_type == :photos / = link_to t('layouts.header.view_profile'), person_path(@person) diff --git a/public/javascripts/vendor/jquery.autoSuggest.js b/public/javascripts/vendor/jquery.autoSuggest.js index 01f58111a68..bafd35cbcf6 100644 --- a/public/javascripts/vendor/jquery.autoSuggest.js +++ b/public/javascripts/vendor/jquery.autoSuggest.js @@ -181,7 +181,7 @@ timeout = setTimeout(function(){ keyChange(); }, opts.keyDelay); } break; - case 9: case 188: // tab or comma + /*case 9: case 188: // tab or comma tab_press = true; var i_input = input.val().replace(/(,)/g, ""); if(i_input != "" && values_input.val().search(","+i_input+",") < 0 && i_input.length >= opts.minChars){ @@ -192,8 +192,12 @@ var lis = $("li", selections_holder).length; add_selected_item(n_data, "00"+(lis+1)); input.val(""); - } - case 13: // return + }*/ + case 9: // tab + if(input.val() == ''){ + break; + } + case 13: case 188: // return, comma tab_press = false; var active = $("li.active:first", results_holder); if(active.length > 0){ diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index d017757a54e..af049954b40 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -2279,8 +2279,7 @@ ul.show_comments :position relative :top 10px -#aspect_edit_pane, -#new_message_pane +#aspect_edit_pane :width 400px .person_tiles .tile @@ -2305,8 +2304,15 @@ ul.show_comments :width 600px #new_message_pane - input - :width 90% + input:not([type='submit']), + textarea + :width 377px + :margin + :right 0 + + .as-selections + input + :width 200px #facebox_header :padding 1em @@ -2653,7 +2659,6 @@ ul.show_comments :border :bottom 1px solid #ddd - #no_conversation_text :font :size 20px @@ -2670,3 +2675,8 @@ ul.show_comments :font :size 12px +.text-right + :text + :align right + :margin + :right 5px diff --git a/public/stylesheets/vendor/autoSuggest.css b/public/stylesheets/vendor/autoSuggest.css index a694cd71ba5..225d3c410a2 100644 --- a/public/stylesheets/vendor/autoSuggest.css +++ b/public/stylesheets/vendor/autoSuggest.css @@ -2,17 +2,17 @@ ul.as-selections { list-style-type: none; - border-top: 1px solid #888; - border-bottom: 1px solid #b6b6b6; - border-left: 1px solid #aaa; - border-right: 1px solid #aaa; + border: 1px solid #ccc; padding: 4px 0 4px 4px; margin: 0; overflow: auto; background-color: #fff; - box-shadow:inset 0 1px 2px #888; - -webkit-box-shadow:inset 0 1px 2px #888; - -moz-box-shadow:inset 0 1px 2px #888; + + border-radius: 3px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + + width: 383px; } ul.as-selections.loading { @@ -27,19 +27,21 @@ ul.as-selections li { ul.as-selections li.as-selection-item { color: #2b3840; font-size: 13px; - font-family: "Lucida Grande", arial, sans-serif; text-shadow: 0 1px 1px #fff; background-color: #ddeefe; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#ddeefe), to(#bfe0f1)); border: 1px solid #acc3ec; - border-top-color: #c0d9e9; - padding: 2px 7px 2px 10px; - border-radius: 12px; - -webkit-border-radius: 12px; - -moz-border-radius: 12px; + padding: 0; + padding-left: 6px; + border-radius: 5px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; box-shadow: 0 1px 1px #e4edf2; -webkit-box-shadow: 0 1px 1px #e4edf2; -moz-box-shadow: 0 1px 1px #e4edf2; + line-height: 10px; + margin-top: -1px; + margin-bottom: -1px; } ul.as-selections li.as-selection-item:last-child { @@ -48,8 +50,8 @@ ul.as-selections li.as-selection-item:last-child { ul.as-selections li.as-selection-item a.as-close { float: right; - margin: 1px 0 0 7px; - padding: 0 2px; + margin: 0px 3px 0 0px; + padding: 0 3px; cursor: pointer; color: #5491be; font-family: "Helvetica", helvetica, arial, sans-serif; @@ -107,8 +109,9 @@ ul.as-selections li.as-original input { outline: none; font-size: 13px; width: 120px; - height: 18px; - padding-top: 3px; + height: 14px; + padding: 0; + margin: 0; } ul.as-list { @@ -116,9 +119,8 @@ ul.as-list { list-style-type: none; margin: 2px 0 0 0; padding: 0; - font-size: 14px; + font-size: 13px; color: #000; - font-family: "Lucida Grande", arial, sans-serif; background-color: #fff; background-color: rgba(255,255,255,0.95); z-index: 2; @@ -132,14 +134,14 @@ ul.as-list { li.as-result-item, li.as-message { margin: 0 0 0 0; - padding: 5px 12px; + padding: 5px; background-color: transparent; border: 1px solid #fff; border-bottom: 1px solid #ddd; cursor: pointer; - border-radius: 5px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; + border-radius: 3px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; } li:first-child.as-result-item { @@ -173,9 +175,6 @@ li.as-result-item.active em { /* Webkit Hacks */ @media screen and (-webkit-min-device-pixel-ratio:0) { - ul.as-selections { - border-top-width: 2px; - } ul.as-selections li.as-selection-item { padding-top: 3px; padding-bottom: 3px; @@ -184,14 +183,14 @@ li.as-result-item.active em { margin-top: -1px; } ul.as-selections li.as-original input { - height: 19px; + height: 15px; } } /* Opera Hacks */ @media all and (-webkit-min-device-pixel-ratio:10000), not all and (-webkit-min-device-pixel-ratio:0) { ul.as-list { - border: 1px solid #888; + border: 1px solid #ccc; } ul.as-selections li.as-selection-item a.as-close { margin-left: 4px; @@ -201,7 +200,7 @@ li.as-result-item.active em { /* IE Hacks */ ul.as-list { - border: 1px solid #888\9; + border: 1px solid #ccc\9; } ul.as-selections li.as-selection-item a.as-close { margin-left: 4px\9; @@ -210,7 +209,7 @@ ul.as-selections li.as-selection-item a.as-close { /* Firefox 3.0 Hacks */ ul.as-list, x:-moz-any-link, x:default { - border: 1px solid #888; + border: 1px solid #ccc; } BODY:first-of-type ul.as-list, x:-moz-any-link, x:default { /* Target FF 3.5+ */ border: none; From 8bcf1b49c7a9c2cc3a4023af79d8d70c40d0efed Mon Sep 17 00:00:00 2001 From: zhitomirskiyi Date: Mon, 7 Mar 2011 18:38:06 -0800 Subject: [PATCH 23/29] notifier for the messages, we're so close --- app/mailers/notifier.rb | 18 ++++++++ app/models/jobs/mail_private_message.rb | 15 +++++++ app/models/message.rb | 4 ++ app/models/notification.rb | 4 +- app/models/notifications/private_message.rb | 15 +++++++ app/views/notifier/private_message.html.haml | 16 ++++++++ app/views/notifier/private_message.text.haml | 8 ++++ config/locales/diaspora/en.yml | 4 ++ spec/mailers/notifier_spec.rb | 31 ++++++++++++++ spec/models/jobs/mail_private_message.rb | 27 ++++++++++++ spec/models/notification_spec.rb | 3 +- .../notifications/private_message_spec.rb | 41 +++++++++++++++++++ 12 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 app/models/jobs/mail_private_message.rb create mode 100644 app/models/notifications/private_message.rb create mode 100644 app/views/notifier/private_message.html.haml create mode 100644 app/views/notifier/private_message.text.haml create mode 100644 spec/models/jobs/mail_private_message.rb create mode 100644 spec/models/notifications/private_message_spec.rb diff --git a/app/mailers/notifier.rb b/app/mailers/notifier.rb index 9f54eeec53f..ce10e182545 100644 --- a/app/mailers/notifier.rb +++ b/app/mailers/notifier.rb @@ -97,6 +97,24 @@ def also_commented(recipient_id, sender_id, comment_id) end end + def private_message(recipient_id, sender_id, message_id) + @receiver = User.find_by_id(recipient_id) + @sender = Person.find_by_id(sender_id) + @message = Message.find_by_id(message_id) + @conversation = @message.conversation + @participants = @conversation.participants + + + log_mail(recipient_id, sender_id, 'private_message') + + attachments.inline['logo_caps.png'] = ATTACHMENT + + I18n.with_locale(@receiver.language) do + mail(:to => "\"#{@receiver.name}\" <#{@receiver.email}>", + :subject => I18n.t('notifier.private_message.subject', :name => @sender.name), :host => AppConfig[:pod_uri].host) + end + end + private def log_mail recipient_id, sender_id, type log_string = "event=mail mail_type=#{type} recipient_id=#{recipient_id} sender_id=#{sender_id}" diff --git a/app/models/jobs/mail_private_message.rb b/app/models/jobs/mail_private_message.rb new file mode 100644 index 00000000000..232ed9a6195 --- /dev/null +++ b/app/models/jobs/mail_private_message.rb @@ -0,0 +1,15 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + + +module Job + class MailPrivateMessage < Base + @queue = :mail + def self.perform_delegate(recipient_id, actor_id, target_id) + + Notifier.private_message( recipient_id, actor_id, target_id).deliver + + end + end +end diff --git a/app/models/message.rb b/app/models/message.rb index 77c5faba026..c4d41bb0c4e 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -67,6 +67,10 @@ def after_receive(user, person) end end + def notification_type(user, person) + Notifications::PrivateMessage + end + private def participant_of_parent_conversation if self.parent && !self.parent.participants.include?(self.author) diff --git a/app/models/notification.rb b/app/models/notification.rb index 6af9d9b4bc4..4b1c33f186b 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -19,9 +19,9 @@ def self.notify(recipient, target, actor) if target.respond_to? :notification_type if note_type = target.notification_type(recipient, actor) if target.is_a? Comment - n = concatenate_or_create(recipient, target.post, actor, note_type) + n = note_type.concatenate_or_create(recipient, target.post, actor, note_type) else - n = make_notification(recipient, target, actor, note_type) + n = note_type.make_notification(recipient, target, actor, note_type) end n.email_the_user(target, actor) if n n.socket_to_user(recipient, :actor => actor) if n diff --git a/app/models/notifications/private_message.rb b/app/models/notifications/private_message.rb new file mode 100644 index 00000000000..3044816c866 --- /dev/null +++ b/app/models/notifications/private_message.rb @@ -0,0 +1,15 @@ +class Notifications::PrivateMessage < Notification + def mail_job + Job::MailPrivateMessage + end + def translation_key + 'private_message' + end + def self.make_notification(recipient, target, actor, notification_type) + n = notification_type.new(:target => target, + :recipient_id => recipient.id) + + n.actors << actor + n + end +end diff --git a/app/views/notifier/private_message.html.haml b/app/views/notifier/private_message.html.haml new file mode 100644 index 00000000000..f6d6b48c651 --- /dev/null +++ b/app/views/notifier/private_message.html.haml @@ -0,0 +1,16 @@ +%p + = t('notifier.hello', :name => @receiver.profile.first_name) +%p + = "#{@sender.name} (#{@sender.diaspora_handle})" + = t('.private_message') +%p + = @message.text +%p + + %br + = link_to t('.sign_in'), conversation_url(@cnv) + + %br + = t('notifier.love') + %br + = t('notifier.diaspora') diff --git a/app/views/notifier/private_message.text.haml b/app/views/notifier/private_message.text.haml new file mode 100644 index 00000000000..d86a91780bf --- /dev/null +++ b/app/views/notifier/private_message.text.haml @@ -0,0 +1,8 @@ += t('notifier.hello', :name => @receiver.profile.first_name) += "#{@sender.name} (#{@sender.diaspora_handle})" += t('notifier.private_message.private_message') + += @message.text + += "#{t('notifier.love')} \n" += t('notifier.diaspora') diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml index 162542839b5..2819c4a9ce8 100644 --- a/config/locales/diaspora/en.yml +++ b/config/locales/diaspora/en.yml @@ -488,6 +488,10 @@ en: subject: "%{name} has mentioned you on Diaspora*" mentioned: "mentioned you in a post:" sign_in: "Sign in to view it." + private_message: + subject: "%{name} has sent you a private message yon Diaspora*" + private_message: "has sent you a private message:" + sign_in: "Sign in to view it." home: show: share_what_you_want: "Share what you want, with whom you want." diff --git a/spec/mailers/notifier_spec.rb b/spec/mailers/notifier_spec.rb index 968e71520f8..9ce3bd699b7 100644 --- a/spec/mailers/notifier_spec.rb +++ b/spec/mailers/notifier_spec.rb @@ -110,7 +110,38 @@ end end + describe ".private_message" do + before do + @user2 = bob + @participant_ids = @user2.contacts.map{|c| c.person.id} + [ @user2.person.id] + + @create_hash = { :author => @user2.person, :participant_ids => @participant_ids , + :subject => "cool stuff", :text => 'hey'} + + @cnv = Conversation.create(@create_hash) + @mail = Notifier.private_message(user.id, @cnv.author.id, @cnv.id) + end + it 'goes to the right person' do + @mail.to.should == [user.email] + end + + it 'has the recipients in the body' do + @mail.body.encoded.include?(user.person.first_name).should be true + end + + it 'has the name of the sender in the body' do + @mail.body.encoded.include?(@cnv.author.name).should be true + end + + it 'has the post text in the body' do + @mail.body.encoded.should include(@cnv.messages.first.text) + end + + it 'should not include translation missing' do + @mail.body.encoded.should_not include("missing") + end + end context "comments" do let!(:connect) { connect_users(user, aspect, user2, aspect2)} let!(:sm) {user.post(:status_message, :message => "Sunny outside", :to => :all)} diff --git a/spec/models/jobs/mail_private_message.rb b/spec/models/jobs/mail_private_message.rb new file mode 100644 index 00000000000..dc1c129149e --- /dev/null +++ b/spec/models/jobs/mail_private_message.rb @@ -0,0 +1,27 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require 'spec_helper' + +describe Job::MailPrivateMessage do + describe '#perfom_delegate' do + it 'should call .deliver on the notifier object' do + user1 = alice + user2 = bob + participant_ids = [user1.contacts.first.person.id, user1.person.id] + + create_hash = { :author => user1.person, :participant_ids => participant_ids , + :subject => "cool stuff", :text => 'hey'} + + cnv = Conversation.create(create_hash) + message = cnv.messages.first + + mail_mock = mock() + mail_mock.should_receive(:deliver) + Notifier.should_receive(:mentioned).with(user2.id, user1.person.id, message.id).and_return(mail_mock) + + Job::MailMentioned.perform_delegate(user2.id, user1.person.id, message.id) + end + end +end diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb index 21b3ceaf424..eb6ce5f0995 100644 --- a/spec/models/notification_spec.rb +++ b/spec/models/notification_spec.rb @@ -44,7 +44,8 @@ Notification.should_not_receive(:make_notificatin) Notification.notify(@user, @sm, @person) end - context 'with a request' do + + context 'with a request' do before do @request = Request.diaspora_initialize(:from => @user.person, :to => @user2.person, :into => @aspect) end diff --git a/spec/models/notifications/private_message_spec.rb b/spec/models/notifications/private_message_spec.rb new file mode 100644 index 00000000000..42b05f9ec74 --- /dev/null +++ b/spec/models/notifications/private_message_spec.rb @@ -0,0 +1,41 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require 'spec_helper' + +describe Notifications::PrivateMessage do + before do + @user1 = alice + @user2 = bob + + @create_hash = { :author => @user1.person, :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], + :subject => "cool stuff", :text => "stuff"} + + @cnv = Conversation.create(@create_hash) + @msg = @cnv.messages.first + end + + describe '#make_notifiaction' do + it 'does not save the notification' do + lambda{ + Notification.notify(@user2, @msg, @user1.person) + }.should_not change(Notification, :count) + end + + it 'does email the user' do + opts = { + :actors => [@user1.person], + :recipient_id => @user2.id} + + n = Notifications::PrivateMessage.new(opts) + Notifications::PrivateMessage.stub!(:make_notification).and_return(n) + Notification.notify(@user2, @msg, @user1.person) + n.stub!(:recipient).and_return @user2 + + @user2.should_receive(:mail) + n.email_the_user(@msg, @user1.person) + end + end +end + From 78e30ff459d6bf8cf7a4a08c8bf89dddab6e10f6 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Tue, 8 Mar 2011 11:36:33 -0800 Subject: [PATCH 24/29] update unread count in conversation visibility when hitting conversationscontroller#show --- app/controllers/conversations_controller.rb | 17 ++++++++++++++--- app/controllers/posts_controller.rb | 2 +- app/controllers/publics_controller.rb | 2 +- app/views/conversations/_conversation.haml | 3 ++- app/views/conversations/_show.haml | 2 +- app/views/conversations/index.haml | 4 +--- app/views/conversations/new.haml | 7 +++++-- app/views/people/show.html.haml | 9 +++++++-- config/assets.yml | 1 + public/javascripts/inbox.js | 3 ++- public/stylesheets/sass/application.sass | 6 +++++- 11 files changed, 40 insertions(+), 16 deletions(-) diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb index dd083860265..5806f43589d 100644 --- a/app/controllers/conversations_controller.rb +++ b/app/controllers/conversations_controller.rb @@ -4,11 +4,16 @@ class ConversationsController < ApplicationController respond_to :html, :json def index - @all_contacts_and_ids = current_user.contacts.map{|c| {:value => c.id, :name => c.person.name}} - @conversations = Conversation.joins(:conversation_visibilities).where( :conversation_visibilities => {:person_id => current_user.person.id}).paginate( :page => params[:page], :per_page => 15, :order => 'updated_at DESC') + + @visibilities = ConversationVisibility.where( :person_id => current_user.person.id ).paginate( + :page => params[:page], :per_page => 15, :order => 'updated_at DESC') + + @unread_counts = {} + @visibilities.each{|v| @unread_counts[v.conversation_id] = v.unread} + @authors = {} @conversations.each{|c| @authors[c.id] = c.last_author} @@ -17,7 +22,7 @@ def index end def create - person_ids = Contact.where(:id => params[:contact_ids]).map! do |contact| + person_ids = Contact.where(:id => params[:contact_ids].split(',')).map! do |contact| contact.person_id end @@ -40,6 +45,11 @@ def show @conversation = Conversation.joins(:conversation_visibilities).where(:id => params[:id], :conversation_visibilities => {:person_id => current_user.person.id}).first + if @visibility = ConversationVisibility.where(:conversation_id => params[:id], :person_id => current_user.person.id).first + @visibility.unread = 0 + @visibility.save + end + if @conversation render :layout => false else @@ -48,6 +58,7 @@ def show end def new + @all_contacts_and_ids = current_user.contacts.map{|c| {:value => c.id, :name => c.person.name}} @contact = current_user.contacts.find(params[:contact_id]) if params[:contact_id] render :layout => false end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 73ed1f772c5..9ab13956692 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -3,7 +3,7 @@ # the COPYRIGHT file. class PostsController < ApplicationController - skip_before_filter :set_contacts_notifications_and_status + skip_before_filter :set_contacts_notifications_unread_count_and_status skip_before_filter :count_requests skip_before_filter :set_invites skip_before_filter :set_locale diff --git a/app/controllers/publics_controller.rb b/app/controllers/publics_controller.rb index 64db524f638..85806c76dce 100644 --- a/app/controllers/publics_controller.rb +++ b/app/controllers/publics_controller.rb @@ -6,7 +6,7 @@ class PublicsController < ApplicationController require File.join(Rails.root, '/lib/diaspora/parser') include Diaspora::Parser - skip_before_filter :set_contacts_notifications_and_status, :except => [:create, :update] + skip_before_filter :set_contacts_notifications_unread_count_and_status, :except => [:create, :update] skip_before_filter :count_requests skip_before_filter :set_invites skip_before_filter :set_locale diff --git a/app/views/conversations/_conversation.haml b/app/views/conversations/_conversation.haml index bcf865746da..604ae5125fc 100644 --- a/app/views/conversations/_conversation.haml +++ b/app/views/conversations/_conversation.haml @@ -2,7 +2,7 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -.stream_element.conversation{:data=>{:guid=>conversation.id}} +.stream_element.conversation{:data=>{:guid=>conversation.id}, :class => ('unread' if unread_counts[conversation.id].to_i > 0)} = person_image_tag(authors[conversation.id]) .subject @@ -15,6 +15,7 @@ .timestamp = time_ago_in_words conversation.updated_at = authors[conversation.id].name + - if conversation.participants.size > 2 %span.participant_count = "(+#{conversation.participants.size - 1})" diff --git a/app/views/conversations/_show.haml b/app/views/conversations/_show.haml index bee452285b2..85adf3befb7 100644 --- a/app/views/conversations/_show.haml +++ b/app/views/conversations/_show.haml @@ -33,5 +33,5 @@ = form_for [conversation, Message.new] do |message| = message.text_area :text, :rows => 5 .right - = message.submit 'Reply' + = message.submit 'Reply', :class => 'button' = link_to 'Cancel', '#' diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml index 0ac8a517d79..6fdc0f141e8 100644 --- a/app/views/conversations/index.haml +++ b/app/views/conversations/index.haml @@ -12,8 +12,6 @@ :css footer{ display:none;} -= hidden_field_tag :contact_json, @all_contacts_and_ids.to_json - #left_pane #left_pane_header %h3 @@ -24,7 +22,7 @@ #conversation_inbox - if @conversations.count > 0 .stream.conversations - = render :partial => 'conversations/conversation', :collection => @conversations, :locals => {:authors => @authors} + = render :partial => 'conversations/conversation', :collection => @conversations, :locals => {:authors => @authors, :unread_counts => @unread_counts} = will_paginate @conversations - else %i diff --git a/app/views/conversations/new.haml b/app/views/conversations/new.haml index 88ec08275da..9be57e18195 100644 --- a/app/views/conversations/new.haml +++ b/app/views/conversations/new.haml @@ -12,13 +12,16 @@ searchObjProps: "name", asHtmlID: "contact_ids", keyDelay: 0, - startText: '' + startText: '', + preFill: [{ 'name' : "#{params[:name]}", + 'value' : "#{params[:contact_id]}"}] }); autocompleteInput.focus(); - }); += hidden_field_tag :contact_json, @all_contacts_and_ids.to_json + #new_message_pane .span-12.last #facebox_header diff --git a/app/views/people/show.html.haml b/app/views/people/show.html.haml index 6e6a73e6b8c..d37df6b7cb2 100644 --- a/app/views/people/show.html.haml +++ b/app/views/people/show.html.haml @@ -2,6 +2,10 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. + +- content_for :head do + = include_javascripts :people + - content_for :page_title do = @person.name @@ -41,8 +45,9 @@ - else - .right - = link_to 'Message', new_conversation_path(:contact_id => @contact.id, :name => @contact.person.name, :contact_id => @contact.id), :class => 'button', :rel => 'facebox' + - if @contact.person + .right + = link_to 'Message', new_conversation_path(:contact_id => @contact.id, :name => @contact.person.name, :contact_id => @contact.id), :class => 'button', :rel => 'facebox' /- if @post_type == :photos / = link_to t('layouts.header.view_profile'), person_path(@person) diff --git a/config/assets.yml b/config/assets.yml index 0f56bf76436..4476666a7d6 100644 --- a/config/assets.yml +++ b/config/assets.yml @@ -53,6 +53,7 @@ javascripts: - public/javascripts/aspect-filters.js - public/javascripts/contact-list.js people: + - public/javascripts/vendor/jquery.autoSuggest.js - public/javascripts/contact-list.js photos: - public/javascripts/photo-show.js diff --git a/public/javascripts/inbox.js b/public/javascripts/inbox.js index 769b6a0c802..1172b61c7f7 100644 --- a/public/javascripts/inbox.js +++ b/public/javascripts/inbox.js @@ -11,8 +11,9 @@ $(document).ready(function(){ $.get("conversations/"+conversationGuid, function(data){ $('.conversation', '.stream').removeClass('selected'); - conversationSummary.addClass('selected'); + conversationSummary.addClass('selected').removeClass('unread'); $('#conversation_show').html(data); + Diaspora.widgets.timeago.updateTimeAgo(); }); if (typeof(history.pushState) == 'function') { diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index af049954b40..89045d0bb8d 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -2306,7 +2306,7 @@ ul.show_comments #new_message_pane input:not([type='submit']), textarea - :width 377px + :width 378px :margin :right 0 @@ -2615,6 +2615,10 @@ ul.show_comments &:hover :cursor pointer +.conversation.unread + :background + :color lighten($background,5%) + .conversation.selected :background :color $blue From 9490538e4413b875e02a7e1fc73338f8fa631963 Mon Sep 17 00:00:00 2001 From: zhitomirskiyi Date: Tue, 8 Mar 2011 11:55:17 -0800 Subject: [PATCH 25/29] checking for relayable in the dispatcher and the receiver --- lib/diaspora/relayable.rb | 4 ++++ lib/postzord/dispatch.rb | 2 +- lib/postzord/receiver.rb | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/diaspora/relayable.rb b/lib/diaspora/relayable.rb index 476ae764984..43a4c44c2a1 100644 --- a/lib/diaspora/relayable.rb +++ b/lib/diaspora/relayable.rb @@ -13,6 +13,10 @@ def self.included(model) xml_attr :author_signature end end + + def relayable + true + end def parent_guid self.parent.guid diff --git a/lib/postzord/dispatch.rb b/lib/postzord/dispatch.rb index 65369d9e4bc..4b383303271 100644 --- a/lib/postzord/dispatch.rb +++ b/lib/postzord/dispatch.rb @@ -22,7 +22,7 @@ def post(opts = {}) unless @subscribers == nil remote_people, local_people = @subscribers.partition{ |person| person.owner_id.nil? } - if @object.is_a?(Comment) && @sender.owns?(@object.post) + if @object.respond_to?(:relayable) && @sender.owns?(@object.parent) user_ids = [*local_people].map{|x| x.owner_id } local_users = User.where(:id => user_ids) self.notify_users(local_users) diff --git a/lib/postzord/receiver.rb b/lib/postzord/receiver.rb index 7281729d362..c61039bf01d 100644 --- a/lib/postzord/receiver.rb +++ b/lib/postzord/receiver.rb @@ -49,9 +49,9 @@ def salmon end def xml_author - if @object.is_a?(Comment) + if @object.respond_to?(:relayable) #if A and B are friends, and A sends B a comment from C, we delegate the validation to the owner of the post being commented on - xml_author = @user.owns?(@object.post) ? @object.diaspora_handle : @object.post.author.diaspora_handle + xml_author = @user.owns?(@object.parent) ? @object.diaspora_handle : @object.parent.author.diaspora_handle @author = Webfinger.new(@object.diaspora_handle).fetch else xml_author = @object.diaspora_handle @@ -71,7 +71,7 @@ def validate_object end # abort if we haven't received the post to a comment - if @object.is_a?(Comment) && @object.post.nil? + if @object.respond_to?(:relayable) && @object.parent.nil? Rails.logger.info("event=receive status=abort reason='received a comment but no corresponding post' recipient=#{@user_person.diaspora_handle} sender=#{@sender.diaspora_handle} payload_type=#{@object.class})") return false end From d38e3a6b29bf3f61d9d044de8c0c0c0a9e765514 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Tue, 8 Mar 2011 15:51:46 -0800 Subject: [PATCH 26/29] added translations to all conversation/message views. tweaked css --- app/helpers/conversations_helper.rb | 9 +++++ app/views/comments/_comment.html.haml | 11 +++--- app/views/conversations/_conversation.haml | 2 +- app/views/conversations/_show.haml | 6 ++-- app/views/conversations/index.haml | 10 +++--- app/views/conversations/new.haml | 10 +++--- app/views/layouts/_header.html.haml | 6 ++-- app/views/shared/_stream_element.html.haml | 4 ++- config/locales/diaspora/en.yml | 15 +++++++++ public/images/icons/monotone_flag.png | Bin 0 -> 783 bytes public/stylesheets/sass/application.sass | 37 +++++++++++++++++---- 11 files changed, 79 insertions(+), 31 deletions(-) create mode 100644 app/helpers/conversations_helper.rb create mode 100644 public/images/icons/monotone_flag.png diff --git a/app/helpers/conversations_helper.rb b/app/helpers/conversations_helper.rb new file mode 100644 index 00000000000..4c7e591151a --- /dev/null +++ b/app/helpers/conversations_helper.rb @@ -0,0 +1,9 @@ +module ConversationsHelper + def new_message_text(count) + if count > 0 + t('new_messages', :count => count) + else + t('no_new_messages') + end + end +end diff --git a/app/views/comments/_comment.html.haml b/app/views/comments/_comment.html.haml index 3d8a5617380..ffe72755387 100644 --- a/app/views/comments/_comment.html.haml +++ b/app/views/comments/_comment.html.haml @@ -5,11 +5,10 @@ %li.comment{:data=>{:guid => comment.id}, :class => ("hidden" if(defined? hidden))} = person_image_link(comment.author) .content - %strong + .from = person_link(comment.author) - - = markdownify(comment.text, :youtube_maps => comment.youtube_titles) - - .info - %span.time + %time.timeago{:datetime => comment.created_at} = comment.created_at ? timeago(comment.created_at) : timeago(Time.now) + + %p + = markdownify(comment.text, :youtube_maps => comment.youtube_titles) diff --git a/app/views/conversations/_conversation.haml b/app/views/conversations/_conversation.haml index 604ae5125fc..35657566eaa 100644 --- a/app/views/conversations/_conversation.haml +++ b/app/views/conversations/_conversation.haml @@ -13,7 +13,7 @@ .last_author .timestamp - = time_ago_in_words conversation.updated_at + = time_ago_in_words(conversation.updated_at) = authors[conversation.id].name - if conversation.participants.size > 2 diff --git a/app/views/conversations/_show.haml b/app/views/conversations/_show.haml index 85adf3befb7..9e71f45b76d 100644 --- a/app/views/conversations/_show.haml +++ b/app/views/conversations/_show.haml @@ -10,7 +10,7 @@ = conversation.subject .conversation_controls - = link_to (image_tag('reply.png', :height => 14, :width => 14) + ' ' + 'reply'), '#', :id => 'reply_to_conversation' + = link_to (image_tag('reply.png', :height => 14, :width => 14) + ' ' + t('.reply')), '#', :id => 'reply_to_conversation' = link_to (image_tag('deletelabel.png') + ' ' + t('delete').downcase), conversation_conversation_visibility_path(conversation), :method => 'delete', :confirm => t('are_you_sure') .span-6.avatars.last @@ -33,5 +33,5 @@ = form_for [conversation, Message.new] do |message| = message.text_area :text, :rows => 5 .right - = message.submit 'Reply', :class => 'button' - = link_to 'Cancel', '#' + = message.submit t('.reply').capitalize, :class => 'button' + = link_to t('cancel'), '#' diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml index 6fdc0f141e8..7962d6c841b 100644 --- a/app/views/conversations/index.haml +++ b/app/views/conversations/index.haml @@ -7,7 +7,7 @@ = include_javascripts :inbox - content_for :page_title do - Message Inbox + = t('.message_inbox') :css footer{ display:none;} @@ -16,7 +16,7 @@ #left_pane_header %h3 .right - = link_to 'New Message', new_conversation_path, :class => 'button', :rel => 'facebox' + = link_to t('.new_message'), new_conversation_path, :class => 'button', :rel => 'facebox' Inbox #conversation_inbox @@ -26,13 +26,13 @@ = will_paginate @conversations - else %i - You have no messages + = t('.you_have_no_new_messages') #conversation_show.span-16.prepend-8.last - if @conversation = render 'conversations/show', :conversation => @conversation - else #no_conversation_text - no conversation selected + = t('.no_conversation_selected') #no_conversation_controls - = link_to 'create a new message', new_conversation_path, :rel => 'facebox' + = link_to t('.create_a_new_message'), new_conversation_path, :rel => 'facebox' diff --git a/app/views/conversations/new.haml b/app/views/conversations/new.haml index 9be57e18195..84c343fb002 100644 --- a/app/views/conversations/new.haml +++ b/app/views/conversations/new.haml @@ -26,20 +26,20 @@ .span-12.last #facebox_header %h4 - New Message + = t('conversations.index.new_message') = form_for Conversation.new do |conversation| %br .span-2 %h4 - to + = t('.to') .span-10.last = text_field_tag "contact_autocomplete" .span-2 %h4 - subject + = t('.subject') .span-10.last = conversation.text_field :subject @@ -47,5 +47,5 @@ = text_area_tag "conversation[text]", '', :rows => 5 .text-right - = conversation.submit :send, :class => 'button' - = link_to 'cancel', conversations_path + = conversation.submit t('.send'), :class => 'button' + = link_to t('cancel'), conversations_path diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 7184c98e6cd..589eb60d67c 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -24,12 +24,12 @@ - if @notification_count #notification_badge = link_to "", notifications_path, :title => new_notification_text(@notification_count) - = image_tag 'icons/mail_grey.png' + = image_tag 'icons/monotone_flag.png', :height => 20, :width => 20 .badge_count{:class => ("hidden" if @notification_count == 0)} = @notification_count #message_inbox_badge - = link_to "", conversations_path #, :title => new_notification_text(@notification_count) - = image_tag 'icons/mail_grey.png' + = link_to "", conversations_path , :title => new_message_text(@unread_message_count) + = image_tag 'icons/mail_grey.png', :height => 16, :width => 16 .badge_count{:class => ("hidden" if @unread_message_count == 0)} = @unread_message_count diff --git a/app/views/shared/_stream_element.html.haml b/app/views/shared/_stream_element.html.haml index ec529a2e08d..36a28cbc79c 100644 --- a/app/views/shared/_stream_element.html.haml +++ b/app/views/shared/_stream_element.html.haml @@ -16,7 +16,6 @@ .from = person_link(post.author, :class => 'author') %time.timeago{:datetime => post.created_at} - = link_to(how_long_ago(post), status_message_path(post)) %p = render 'status_messages/status_message', :post => post, :photos => post.photos @@ -29,6 +28,9 @@ %span.aspect_badges = aspect_badges(aspects_with_post(all_aspects, post)) + %span.timeago + = link_to(how_long_ago(post), status_message_path(post)) + = link_to t('comments.new_comment.comment').downcase, '#', :class => 'focus_comment_textarea' = render "comments/comments", :post_id => post.id, :comments => post.comments, :current_user => current_user, :condensed => true, :commenting_disabled => defined?(@commenting_disabled) diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml index 2819c4a9ce8..8131d11b00a 100644 --- a/config/locales/diaspora/en.yml +++ b/config/locales/diaspora/en.yml @@ -27,6 +27,8 @@ en: search: "Search" new_notifications: "%{count} new notifications" no_new_notifications: "no new notifications" + new_messages: "%{count} new messages" + no_new_messages: "no new messages" _home: "Home" _more: "More" _comments: "Comments" @@ -523,3 +525,16 @@ en: fullmonth_day: "%B %d" birthday: "%B %d" birthday_with_year: "%B %d %Y" + + conversations: + index: + message_inbox: "Message Inbox" + new_message: "New Message" + no_conversation_selected: "no conversation selected" + create_a_new_message: "create a new message" + show: + reply: 'reply' + new: + to: 'to' + subject: 'subject' + send: 'Send' diff --git a/public/images/icons/monotone_flag.png b/public/images/icons/monotone_flag.png new file mode 100644 index 0000000000000000000000000000000000000000..baea94936d2e36f689fdf10a49500c3b2cf26efc GIT binary patch literal 783 zcmV+q1MvKbP)&+5Xb-j`QB5|iiM;KrD>Z&5igzuX~9Bo)kC2mp@&K-N(A{#2n{yG%brU^JnRaM z!POoYdeB~a=&6WpD$+v^5`v;&q$b-!*(96rzM1i|yWKCXwFeJAc&GRH&3|V8Gr)g* z_)ibjYW0As9ubk_08X3Pbu;@e#`t5N=gR>6nSk|refQ?(<{5YY5WpMm-upumB$v$W zt5&O3pPrsxdn#~tcJ}oU!bOrNNCq=|Oo501095sR5&107^P5iuR;$$kcfS%scvn>) z1GzuSkR)P^NGUCt*?VJSV}JAl_JPle$O%;iNx1tRGrJ2wMFcTMgb)xy07;~jOhn!y z`EjGs$hyD)05dZ)he?hC5O+ri0q*`2fCm76NGbg$BKt}11+cBEzW_K#a-X}8uB@zF z18}RSFvfV)-Cyh0%-uoqRWn;JiehnmeEibl;^O)B_4N;$&E`k$euv~PGeeeT12M+Y zQpzOH_1DPV;qLoLJ`5r32Y|V`ximF3^>BE2_<52SLI}G7pd;dJ2^eEM*53d#gP9!= z5mnU}`>nIKwnhMd?GV5cFr`##y}Lt1UNN&50K7CmKYy^%Xk?X2MJ6XFo7>ylxvE|U zpdD}a=%E9si^#G5s3Jmle~#qf*4EaIcDubaI5@amtJPX&_A-FC0c0IVd{Y935N@gJ zuOf1&-^?O1U}pDXj3-s~5XpA{Aj`6D?VzgnQ%YZ!QeItMU3T{`02s;s#vBIlr@O<< zpsKyw2?!CH9~v53C;=;#iiybAW_G=6Tz7}6?lH3mJ4cWtLI^Ok1v9ITjErar2mpDW z-!F<{vM7pgNv2Npy#U(o-s&nBk?y422JmVB*p+|)FflRl^Tx);`>Ohhs@?^#3qWx9 zdtDHKB{RFwZnsbETnxZd_F=778!C$8O_EKLp9A>4D2l89wh#aR=Wpy0k`$FnKkfhk N002ovPDHLkV1oK%Zc_jN literal 0 HcmV?d00001 diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index 89045d0bb8d..d5a85aa0b63 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -254,6 +254,7 @@ header .stream .stream_element :padding 20px 20px + :right 70px :min-height 50px :border :bottom 1px solid #ddd @@ -262,6 +263,9 @@ header &:hover .right :display inline + .from + a + :color $blue .youtube-player, .vimeo-player :border none @@ -393,7 +397,7 @@ header :margin :top 2px :color #999 - :font-size smaller + :font-size 11px .comments .info @@ -410,7 +414,7 @@ header .time, .timeago - :color #ccc + :color #999 a :color #ccc :margin @@ -559,6 +563,8 @@ ul.show_comments :line :height 18px + :position relative + textarea :width 100% :height 1.4em @@ -593,9 +599,6 @@ ul.show_comments a :color #444 - div.time - :color #bbb - form :margin :top -5px @@ -634,6 +637,12 @@ ul.show_comments textarea :min-height 2.4em +.comments + time + :margin + :right -15px + .timeago + :color #999 .stream.show ul.comments @@ -693,11 +702,14 @@ a.paginate, #infscr-loading :right 12px &.controls + :z-index 6 :background :color $background :font :size 12px :color #999 + :padding + :left 100px a :color #999 :font @@ -1672,11 +1684,11 @@ h3 span.current_gs_step :top 5px :display inline :margin 0 10px - :right 0 + :right -5px :font :weight bold :size smaller - :width 30px + :width 28px a :z-index 5 @@ -1685,6 +1697,16 @@ h3 span.current_gs_step :width 20px :height 20px + &:hover + .badge_count + :background + :color lighten(#A40802, 5%) + +#notification_badge + img + :position relative + :top 2px + .badge_count :z-index 3 :position absolute @@ -2275,6 +2297,7 @@ ul.show_comments :margin :left 0.5em :right 0.5em + .mark_all_read :position relative :top 10px From c252cfa025d886038faf77d44ae8f1ab2f02e115 Mon Sep 17 00:00:00 2001 From: zhitomirskiyi Date: Tue, 8 Mar 2011 17:42:37 -0800 Subject: [PATCH 27/29] added the email notification, the notification is not persisted, fixed the receive callback spec --- app/models/conversation.rb | 3 ++- app/models/jobs/mail_private_message.rb | 2 -- app/models/message.rb | 2 +- app/views/notifier/private_message.html.haml | 4 +++- app/views/notifier/private_message.text.haml | 1 + config/locales/diaspora/en.yml | 1 + spec/mailers/notifier_spec.rb | 6 +++++- spec/models/conversation_spec.rb | 4 ++++ spec/models/message_spec.rb | 11 ++++++++++- spec/shared_behaviors/relayable.rb | 1 + 10 files changed, 28 insertions(+), 7 deletions(-) diff --git a/app/models/conversation.rb b/app/models/conversation.rb index a1e40665d73..785e80628fe 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -65,7 +65,8 @@ def receive(user, person) end self.messages.each do |msg| msg.conversation_id = cnv.id - msg.receive(user, person) + received_msg = msg.receive(user, person) + Notification.notify(user, received_msg, person) if msg.respond_to?(:notification_type) end end end diff --git a/app/models/jobs/mail_private_message.rb b/app/models/jobs/mail_private_message.rb index 232ed9a6195..d372d990a7f 100644 --- a/app/models/jobs/mail_private_message.rb +++ b/app/models/jobs/mail_private_message.rb @@ -7,9 +7,7 @@ module Job class MailPrivateMessage < Base @queue = :mail def self.perform_delegate(recipient_id, actor_id, target_id) - Notifier.private_message( recipient_id, actor_id, target_id).deliver - end end end diff --git a/app/models/message.rb b/app/models/message.rb index c4d41bb0c4e..abebfc04d46 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -68,7 +68,7 @@ def after_receive(user, person) end def notification_type(user, person) - Notifications::PrivateMessage + Notifications::PrivateMessage unless user.person == person end private diff --git a/app/views/notifier/private_message.html.haml b/app/views/notifier/private_message.html.haml index f6d6b48c651..18c1b335f20 100644 --- a/app/views/notifier/private_message.html.haml +++ b/app/views/notifier/private_message.html.haml @@ -3,12 +3,14 @@ %p = "#{@sender.name} (#{@sender.diaspora_handle})" = t('.private_message') +%p + = t('.message_subject', :subject => @conversation.subject) %p = @message.text %p %br - = link_to t('.sign_in'), conversation_url(@cnv) + = link_to t('.sign_in'), conversation_url(@conversation) %br = t('notifier.love') diff --git a/app/views/notifier/private_message.text.haml b/app/views/notifier/private_message.text.haml index d86a91780bf..58419ab2ca7 100644 --- a/app/views/notifier/private_message.text.haml +++ b/app/views/notifier/private_message.text.haml @@ -2,6 +2,7 @@ = "#{@sender.name} (#{@sender.diaspora_handle})" = t('notifier.private_message.private_message') += t('notifier.private_message.message_subject', :subject => @conversation.subject) = @message.text = "#{t('notifier.love')} \n" diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml index 8131d11b00a..a5f0f617e57 100644 --- a/config/locales/diaspora/en.yml +++ b/config/locales/diaspora/en.yml @@ -493,6 +493,7 @@ en: private_message: subject: "%{name} has sent you a private message yon Diaspora*" private_message: "has sent you a private message:" + message_subject: "Subject: %{subject}" sign_in: "Sign in to view it." home: show: diff --git a/spec/mailers/notifier_spec.rb b/spec/mailers/notifier_spec.rb index 9ce3bd699b7..b2ed38bb337 100644 --- a/spec/mailers/notifier_spec.rb +++ b/spec/mailers/notifier_spec.rb @@ -120,7 +120,7 @@ @cnv = Conversation.create(@create_hash) - @mail = Notifier.private_message(user.id, @cnv.author.id, @cnv.id) + @mail = Notifier.private_message(user.id, @cnv.author.id, @cnv.messages.first.id) end it 'goes to the right person' do @mail.to.should == [user.email] @@ -134,6 +134,10 @@ @mail.body.encoded.include?(@cnv.author.name).should be true end + it 'has the conversation subject in the body' do + @mail.body.encoded.should include(@cnv.subject) + end + it 'has the post text in the body' do @mail.body.encoded.should include(@cnv.messages.first.text) end diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb index 9bfa725bd4f..89ac00a30f7 100644 --- a/spec/models/conversation_spec.rb +++ b/spec/models/conversation_spec.rb @@ -83,6 +83,10 @@ it 'does not save before receive' do Diaspora::Parser.from_xml(@xml).persisted?.should be_false end + it 'notifies for the message' do + Notification.should_receive(:notify).once + Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person) + end end end end diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb index 99aca42df6e..859900f4beb 100644 --- a/spec/models/message_spec.rb +++ b/spec/models/message_spec.rb @@ -20,7 +20,16 @@ it 'validates that the author is a participant in the conversation' do msg = Message.new(:text => 'yo', :author => eve.person, :conversation_id => @cnv.id) - pp msg.valid? + end + + describe '#notification_type' do + it 'does not return anything for the author' do + @message.notification_type(@user1, @user1.person).should be_nil + end + + it 'returns private mesage for an actual receiver' do + @message.notification_type(@user2, @user1.person).should == Notifications::PrivateMessage + end end describe '#before_create' do diff --git a/spec/shared_behaviors/relayable.rb b/spec/shared_behaviors/relayable.rb index ba809119a3b..f6cc2d1a32a 100644 --- a/spec/shared_behaviors/relayable.rb +++ b/spec/shared_behaviors/relayable.rb @@ -68,6 +68,7 @@ it 'calls after_receive callback' do @object_by_recipient.should_receive(:after_receive) + @object_by_recipient.class.stub(:where).and_return([@object_by_recipient]) @object_by_recipient.receive(@local_luke, @local_leia.person) end end From ac738df8f5552ce211e5e01adc147b1a1f2e03fb Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Tue, 8 Mar 2011 19:22:49 -0800 Subject: [PATCH 28/29] added missing translation --- Gemfile.lock | 8 ++++---- app/views/conversations/index.haml | 9 +++++++-- config/locales/diaspora/en.yml | 1 + 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4c40485691f..34fd88c9c06 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -206,7 +206,7 @@ GEM yui-compressor (>= 0.9.1) json (1.4.6) json_pure (1.5.1) - launchy (0.3.7) + launchy (0.4.0) configuration (>= 0.0.5) rake (>= 0.8.1) linecache (0.43) @@ -323,7 +323,7 @@ GEM rspec-core (2.5.1) rspec-expectations (2.5.0) diff-lcs (~> 1.1.2) - rspec-instafail (0.1.6) + rspec-instafail (0.1.7) rspec-mocks (2.5.0) rspec-rails (2.5.0) actionpack (~> 3.0) @@ -343,7 +343,7 @@ GEM rubyntlm (0.1.1) rubyzip (0.9.4) selenium-client (1.2.18) - selenium-rc (2.3.1) + selenium-rc (2.3.2) selenium-client (>= 1.2.18) selenium-webdriver (0.1.3) childprocess (~> 0.1.5) @@ -351,7 +351,7 @@ GEM json_pure rubyzip simple_oauth (0.1.4) - sinatra (1.1.3) + sinatra (1.2.0) rack (~> 1.1) tilt (< 2.0, >= 1.2.2) subexec (0.0.4) diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml index 7962d6c841b..1214816b5ab 100644 --- a/app/views/conversations/index.haml +++ b/app/views/conversations/index.haml @@ -25,8 +25,13 @@ = render :partial => 'conversations/conversation', :collection => @conversations, :locals => {:authors => @authors, :unread_counts => @unread_counts} = will_paginate @conversations - else - %i - = t('.you_have_no_new_messages') + %br + %br + %br + %br + %div{:style => 'text-align:center;'} + %i + = t('.no_messages') #conversation_show.span-16.prepend-8.last - if @conversation diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml index a5f0f617e57..f0a7d180202 100644 --- a/config/locales/diaspora/en.yml +++ b/config/locales/diaspora/en.yml @@ -533,6 +533,7 @@ en: new_message: "New Message" no_conversation_selected: "no conversation selected" create_a_new_message: "create a new message" + no_messages: "no messages" show: reply: 'reply' new: From 81f0cd7f07da067bed67a4c5cf148792e7ee999c Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Tue, 8 Mar 2011 20:42:56 -0800 Subject: [PATCH 29/29] fixed double-javascript loading on people/show --- config/assets.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/config/assets.yml b/config/assets.yml index 4476666a7d6..c719f53297b 100644 --- a/config/assets.yml +++ b/config/assets.yml @@ -54,7 +54,6 @@ javascripts: - public/javascripts/contact-list.js people: - public/javascripts/vendor/jquery.autoSuggest.js - - public/javascripts/contact-list.js photos: - public/javascripts/photo-show.js inbox: