diff --git a/Gemfile.lock b/Gemfile.lock
index 38a4fbbbf..2f85205a2 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
@@ -344,7 +344,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)
@@ -352,9 +352,9 @@ GEM
json_pure
rubyzip
simple_oauth (0.1.4)
- sinatra (1.1.3)
+ sinatra (1.2.0)
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/application_controller.rb b/app/controllers/application_controller.rb
index 64940e9da..64b9a6970 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 @@ class ApplicationController < ActionController::Base
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/aspect_memberships_controller.rb b/app/controllers/aspect_memberships_controller.rb
index c7be3a84f..9d5c26a1c 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 @@ class AspectMembershipsController < ApplicationController
@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/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb
index 0409f31ff..6e2ac8f5e 100644
--- a/app/controllers/comments_controller.rb
+++ b/app/controllers/comments_controller.rb
@@ -18,8 +18,7 @@ class CommentsController < ApplicationController
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/controllers/conversation_visibilities_controller.rb b/app/controllers/conversation_visibilities_controller.rb
new file mode 100644
index 000000000..b136e38be
--- /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 000000000..5806f4358
--- /dev/null
+++ b/app/controllers/conversations_controller.rb
@@ -0,0 +1,66 @@
+class ConversationsController < ApplicationController
+ before_filter :authenticate_user!
+
+ respond_to :html, :json
+
+ def index
+ @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}
+
+ @conversation = Conversation.joins(:conversation_visibilities).where(
+ :conversation_visibilities => {:person_id => current_user.person.id, :conversation_id => params[:conversation_id]}).first
+ end
+
+ def create
+ person_ids = Contact.where(:id => params[:contact_ids].split(',')).map! do |contact|
+ contact.person_id
+ end
+
+ params[:conversation][:participant_ids] = person_ids | [current_user.person.id]
+ params[:conversation][:author] = current_user.person
+
+ 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
+
+ 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
+ redirect_to conversations_path
+ end
+ 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
+
+end
diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb
new file mode 100644
index 000000000..2dac50dda
--- /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[: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[:message][:text].length}")
+ Postzord::Dispatch.new(current_user, message).post
+
+ redirect_to conversations_path(:conversation_id => cnv.id)
+ else
+ render :nothing => true, :status => 406
+ end
+ else
+ render :nothing => true, :status => 406
+ end
+ end
+
+end
diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb
index e426d4c60..baf48d0a7 100644
--- a/app/controllers/photos_controller.rb
+++ b/app/controllers/photos_controller.rb
@@ -30,7 +30,7 @@ class PhotosController < ApplicationController
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 @@ class PhotosController < ApplicationController
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 @@ class PhotosController < ApplicationController
: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 @@ class PhotosController < ApplicationController
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 0ee777a83..9ab139566 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
@@ -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/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 54c892a9a..79267c8cc 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -14,7 +14,7 @@ class ProfilesController < ApplicationController
# 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/controllers/publics_controller.rb b/app/controllers/publics_controller.rb
index 64db524f6..85806c76d 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/controllers/status_messages_controller.rb b/app/controllers/status_messages_controller.rb
index 7333389b8..33bc0d975 100644
--- a/app/controllers/status_messages_controller.rb
+++ b/app/controllers/status_messages_controller.rb
@@ -52,7 +52,7 @@ class StatusMessagesController < ApplicationController
photos.update_all(:status_message_id => nil)
end
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/application_helper.rb b/app/helpers/application_helper.rb
index 81e013ebf..e3927d237 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -13,11 +13,10 @@ module ApplicationHelper
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
@@ -133,8 +132,8 @@ module ApplicationHelper
"
".html_safe
end
- def person_link(person)
- "
+ def person_link(person, opts={})
+ "
#{h(person.name)}
".html_safe
end
diff --git a/app/helpers/conversations_helper.rb b/app/helpers/conversations_helper.rb
new file mode 100644
index 000000000..4c7e59115
--- /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/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index 788d7ab03..44740644a 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -22,7 +22,7 @@ module NotificationsHelper
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/private_messages_helper.rb b/app/helpers/private_messages_helper.rb
new file mode 100644
index 000000000..1c849b6ab
--- /dev/null
+++ b/app/helpers/private_messages_helper.rb
@@ -0,0 +1,2 @@
+module PrivateMessagesHelper
+end
diff --git a/app/helpers/sockets_helper.rb b/app/helpers/sockets_helper.rb
index 8010d699b..8202ee9f3 100644
--- a/app/helpers/sockets_helper.rb
+++ b/app/helpers/sockets_helper.rb
@@ -26,11 +26,11 @@ module SocketsHelper
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,
@@ -48,7 +48,7 @@ module SocketsHelper
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]})
@@ -69,12 +69,12 @@ module SocketsHelper
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 13b90c43e..ce10e1825 100644
--- a/app/mailers/notifier.rb
+++ b/app/mailers/notifier.rb
@@ -84,7 +84,7 @@ class Notifier < ActionMailer::Base
@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')
@@ -97,6 +97,24 @@ class Notifier < ActionMailer::Base
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/comment.rb b/app/models/comment.rb
index 334890632..08ec011e7 100644
--- a/app/models/comment.rb
+++ b/app/models/comment.rb
@@ -7,19 +7,18 @@ 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
+ belongs_to :author, :class_name => 'Person'
validates_presence_of :text, :post
validates_length_of :text, :maximum => 2500
@@ -30,85 +29,31 @@ 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
- end
- def post_guid
- self.post.guid
- end
- def post_guid= new_post_guid
- self.post = Post.where(:guid => new_post_guid).first
+ 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
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
+ def parent
+ self.post
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= parent
+ self.post = parent
end
-
- def signable_string
- signable_accessors.collect{|accessor|
- (self.send accessor.to_sym).to_s}.join ';'
- 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/conversation.rb b/app/models/conversation.rb
new file mode 100644
index 000000000..785e80628
--- /dev/null
+++ b/app/models/conversation.rb
@@ -0,0 +1,72 @@
+class Conversation < ActiveRecord::Base
+ include ROXML
+ include Diaspora::Guid
+ include Diaspora::Webhooks
+
+ xml_attr :subject
+ xml_attr :created_at
+ xml_attr :messages, :as => [Message]
+ xml_reader :diaspora_handle
+ xml_reader :participant_handles
+
+ has_many :conversation_visibilities, :dependent => :destroy
+ 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={})
+ 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}))
+ message.save
+ cnv
+ 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
+ end
+ end
+
+ 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
+
+ def receive(user, person)
+ cnv = Conversation.find_or_create_by_guid(self.attributes)
+
+ self.participants.each do |participant|
+ 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
+ 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/conversation_visibility.rb b/app/models/conversation_visibility.rb
new file mode 100644
index 000000000..da398c724
--- /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/jobs/mail_private_message.rb b/app/models/jobs/mail_private_message.rb
new file mode 100644
index 000000000..d372d990a
--- /dev/null
+++ b/app/models/jobs/mail_private_message.rb
@@ -0,0 +1,13 @@
+# 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/mention.rb b/app/models/mention.rb
index 8dbc37a3f..fcf2bbaef 100644
--- a/app/models/mention.rb
+++ b/app/models/mention.rb
@@ -8,14 +8,14 @@ class Mention < ActiveRecord::Base
validates_presence_of :post
validates_presence_of :person
+ after_create :notify_recipient
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
-
def notification_type(*args)
Notifications::Mentioned
end
diff --git a/app/models/message.rb b/app/models/message.rb
new file mode 100644
index 000000000..abebfc04d
--- /dev/null
+++ b/app/models/message.rb
@@ -0,0 +1,82 @@
+class Message < ActiveRecord::Base
+ include ROXML
+
+ include Diaspora::Guid
+ include Diaspora::Webhooks
+ include Diaspora::Relayable
+
+ xml_attr :text
+ xml_attr :created_at
+ xml_reader :diaspora_handle
+ xml_reader :conversation_guid
+
+ belongs_to :author, :class_name => 'Person'
+ belongs_to :conversation, :touch => true
+
+ 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.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
+
+ validate :participant_of_parent_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 parent_class
+ Conversation
+ end
+
+ def parent
+ self.conversation
+ end
+
+ 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
+
+ def notification_type(user, person)
+ Notifications::PrivateMessage unless user.person == person
+ 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/app/models/notification.rb b/app/models/notification.rb
index 6af9d9b4b..4b1c33f18 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -19,9 +19,9 @@ class Notification < ActiveRecord::Base
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 000000000..3044816c8
--- /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/models/person.rb b/app/models/person.rb
index d448f2911..94f0bee34 100644
--- a/app/models/person.rb
+++ b/app/models/person.rb
@@ -26,7 +26,7 @@ class Person < ActiveRecord::Base
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'
@@ -93,8 +93,8 @@ class Person < ActiveRecord::Base
end
end
- def owns?(post)
- self == post.person
+ def owns?(obj)
+ self == obj.author
end
def url
diff --git a/app/models/post.rb b/app/models/post.rb
index 5e925e721..dd103edc0 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
@@ -18,7 +17,7 @@ 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
@@ -30,16 +29,16 @@ class Post < ActiveRecord::Base
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
@@ -47,7 +46,7 @@ class Post < ActiveRecord::Base
{
:post => {
:id => self.id,
- :person => self.person.as_json,
+ :author => self.author.as_json,
}
}
end
@@ -68,7 +67,7 @@ class Post < ActiveRecord::Base
#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?
@@ -95,7 +94,7 @@ class Post < ActiveRecord::Base
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 99b520de6..d92205172 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 c1717e4b0..fa8e9e0b5 100644
--- a/app/models/retraction.rb
+++ b/app/models/retraction.rb
@@ -54,7 +54,7 @@ class Retraction
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 3c6d1778c..1e4d3dea0 100644
--- a/app/models/status_message.rb
+++ b/app/models/status_message.rb
@@ -88,7 +88,7 @@ class StatusMessage < Post
<<-XML
#{x(self.formatted_message(:plain_text => true))}
-
+
#{person.url}posts/#{self.id}
#{self.created_at.xmlschema}
#{self.updated_at.xmlschema}
diff --git a/app/models/user.rb b/app/models/user.rb
index ed09e77af..dc366fc60 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -87,8 +87,8 @@ class User < ActiveRecord::Base
######## 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)
@@ -107,16 +107,16 @@ class User < ActiveRecord::Base
end
def notify_if_mentioned(post)
- return unless self.contact_for(post.person) && post.respond_to?(:mentions?)
+ return unless self.contact_for(post.author) && post.respond_to?(:mentions?)
post.notify_person(self.person) if post.mentions? self.person
end
def add_post_to_aspects(post)
- return unless self.contact_for(post.person)
+ return unless self.contact_for(post.author)
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
@@ -136,32 +136,26 @@ class User < ActiveRecord::Base
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 ########
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
#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/app/views/comments/_comment.html.haml b/app/views/comments/_comment.html.haml
index 66bb9e906..ffe727553 100644
--- a/app/views/comments/_comment.html.haml
+++ b/app/views/comments/_comment.html.haml
@@ -3,13 +3,12 @@
-# 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)
-
- = markdownify(comment.text, :youtube_maps => comment.youtube_titles)
-
- .info
- %span.time
+ .from
+ = person_link(comment.author)
+ %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
new file mode 100644
index 000000000..35657566e
--- /dev/null
+++ b/app/views/conversations/_conversation.haml
@@ -0,0 +1,21 @@
+-# 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}, :class => ('unread' if unread_counts[conversation.id].to_i > 0)}
+ = person_image_tag(authors[conversation.id])
+
+ .subject
+ .message_count
+ = conversation.messages.size
+
+ = conversation.subject[0..30]
+
+ .last_author
+ .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
new file mode 100644
index 000000000..9e71f45b7
--- /dev/null
+++ b/app/views/conversations/_show.haml
@@ -0,0 +1,37 @@
+-# Copyright (c) 2010, Diaspora Inc. This file is
+-# licensed under the Affero General Public License version 3 or later. See
+-# the COPYRIGHT file.
+
+
+.span-16.last
+ .conversation_participants
+ .span-9
+ %h3
+ = conversation.subject
+
+ .conversation_controls
+ = 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
+ - for participant in conversation.participants
+ = person_image_link(participant)
+
+%br
+%br
+%br
+%br
+%br
+.span-16.last
+ .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 t('.reply').capitalize, :class => 'button'
+ = link_to t('cancel'), '#'
diff --git a/app/views/conversations/index.haml b/app/views/conversations/index.haml
new file mode 100644
index 000000000..1214816b5
--- /dev/null
+++ b/app/views/conversations/index.haml
@@ -0,0 +1,43 @@
+-# Copyright (c) 2010, Diaspora Inc. This file is
+-# licensed under the Affero General Public License version 3 or later. See
+-# the COPYRIGHT file.
+
+
+- content_for :head do
+ = include_javascripts :inbox
+
+- content_for :page_title do
+ = t('.message_inbox')
+
+:css
+ footer{ display:none;}
+
+#left_pane
+ #left_pane_header
+ %h3
+ .right
+ = link_to t('.new_message'), new_conversation_path, :class => 'button', :rel => 'facebox'
+ Inbox
+
+ #conversation_inbox
+ - if @conversations.count > 0
+ .stream.conversations
+ = render :partial => 'conversations/conversation', :collection => @conversations, :locals => {:authors => @authors, :unread_counts => @unread_counts}
+ = will_paginate @conversations
+ - else
+ %br
+ %br
+ %br
+ %br
+ %div{:style => 'text-align:center;'}
+ %i
+ = t('.no_messages')
+
+#conversation_show.span-16.prepend-8.last
+ - if @conversation
+ = render 'conversations/show', :conversation => @conversation
+ - else
+ #no_conversation_text
+ = t('.no_conversation_selected')
+ #no_conversation_controls
+ = 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
new file mode 100644
index 000000000..84c343fb0
--- /dev/null
+++ b/app/views/conversations/new.haml
@@ -0,0 +1,51 @@
+-# Copyright (c) 2010, Diaspora Inc. This file is
+-# licensed under the Affero General Public License version 3 or later. See
+-# the COPYRIGHT file.
+
+:javascript
+ $(document).ready(function () {
+ var data = $.parseJSON( $('#contact_json').val() ),
+ autocompleteInput = $("#contact_autocomplete");
+
+ autocompleteInput.autoSuggest(data, {
+ selectedItemProp: "name",
+ searchObjProps: "name",
+ asHtmlID: "contact_ids",
+ keyDelay: 0,
+ 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
+ %h4
+ = t('conversations.index.new_message')
+
+ = form_for Conversation.new do |conversation|
+ %br
+
+ .span-2
+ %h4
+ = t('.to')
+ .span-10.last
+ = text_field_tag "contact_autocomplete"
+
+ .span-2
+ %h4
+ = t('.subject')
+ .span-10.last
+ = conversation.text_field :subject
+
+ .span-10.prepend-2.last
+ = text_area_tag "conversation[text]", '', :rows => 5
+
+ .text-right
+ = conversation.submit t('.send'), :class => 'button'
+ = link_to t('cancel'), conversations_path
diff --git a/app/views/conversations/show.haml b/app/views/conversations/show.haml
new file mode 100644
index 000000000..b5d871dfc
--- /dev/null
+++ b/app/views/conversations/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.
+
+= render 'show', :conversation => @conversation
diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml
index 6b255de63..07cac5d00 100644
--- a/app/views/layouts/_header.html.haml
+++ b/app/views/layouts/_header.html.haml
@@ -24,9 +24,14 @@
- if @notification_count
#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)}
+ = 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_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
%ul#user_menu
.right
diff --git a/app/views/messages/_message.haml b/app/views/messages/_message.haml
new file mode 100644
index 000000000..d7cd6ab75
--- /dev/null
+++ b/app/views/messages/_message.haml
@@ -0,0 +1,16 @@
+-# 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)
+
+ .content
+ .from
+ = person_link(message.author, :class => 'author')
+ %time.timeago{:datetime => message.created_at}
+ = how_long_ago(message)
+
+ %p
+ = message.text
+
diff --git a/app/views/notifier/private_message.html.haml b/app/views/notifier/private_message.html.haml
new file mode 100644
index 000000000..18c1b335f
--- /dev/null
+++ b/app/views/notifier/private_message.html.haml
@@ -0,0 +1,18 @@
+%p
+ = t('notifier.hello', :name => @receiver.profile.first_name)
+%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(@conversation)
+
+ %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 000000000..58419ab2c
--- /dev/null
+++ b/app/views/notifier/private_message.text.haml
@@ -0,0 +1,9 @@
+= t('notifier.hello', :name => @receiver.profile.first_name)
+= "#{@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"
+= t('notifier.diaspora')
diff --git a/app/views/people/show.html.haml b/app/views/people/show.html.haml
index 928c90a08..d37df6b7c 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,11 +45,14 @@
- 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)
+ - 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)
+ /- else
+ / = link_to t('_photos'), person_photos_path(@person)
%h3
= @person.name
diff --git a/app/views/photos/_photo.haml b/app/views/photos/_photo.haml
index a601735ae..9d8b8ed18 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 4b825fee3..b2075e701 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 e70d96a28..36a28cbc7 100644
--- a/app/views/shared/_stream_element.html.haml
+++ b/app/views/shared/_stream_element.html.haml
@@ -3,31 +3,34 @@
-# 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)
-
- = render 'status_messages/status_message', :post => post, :photos => post.photos
+ .from
+ = person_link(post.author, :class => 'author')
+ %time.timeago{:datetime => post.created_at}
+ %p
+ = render 'status_messages/status_message', :post => post, :photos => post.photos
.info
- if post.public?
%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))
- %span.timeago= link_to(how_long_ago(post), status_message_path(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/app/views/shared/_stream_element.mobile.haml b/app/views/shared/_stream_element.mobile.haml
index 00746b0a9..b313860d1 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/_new_status_message.haml b/app/views/status_messages/_new_status_message.haml
deleted file mode 100644
index 00db8eefe..000000000
--- 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/app/views/status_messages/create.js.erb b/app/views/status_messages/create.js.erb
index 8eac17206..cfd9a5ef0 100644
--- a/app/views/status_messages/create.js.erb
+++ b/app/views/status_messages/create.js.erb
@@ -2,7 +2,7 @@
: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
diff --git a/app/views/status_messages/show.html.haml b/app/views/status_messages/show.html.haml
index a5dc196c4..e79b89471 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 a5c871976..6961ae3d4 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/config/assets.yml b/config/assets.yml
index b240d33b7..6dc20d3db 100644
--- a/config/assets.yml
+++ b/config/assets.yml
@@ -53,9 +53,12 @@ javascripts:
- public/javascripts/aspect-filters.js
- public/javascripts/contact-list.js
people:
- - public/javascripts/contact-list.js
+ - public/javascripts/vendor/jquery.autoSuggest.js
photos:
- public/javascripts/photo-show.js
+ inbox:
+ - public/javascripts/vendor/jquery.autoSuggest.js
+ - public/javascripts/inbox.js
stylesheets:
default:
@@ -65,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/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml
index 4a91ed354..dafc4743b 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"
@@ -489,6 +491,11 @@ 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:"
+ message_subject: "Subject: %{subject}"
+ sign_in: "Sign in to view it."
home:
show:
share_what_you_want: "Share what you want, with whom you want."
@@ -520,3 +527,17 @@ 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"
+ no_messages: "no messages"
+ show:
+ reply: 'reply'
+ new:
+ to: 'to'
+ subject: 'subject'
+ send: 'Send'
diff --git a/config/routes.rb b/config/routes.rb
index d2bdf9101..793ac7a9c 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,6 +3,8 @@
# the COPYRIGHT file.
Diaspora::Application.routes.draw do
+
+
resources :status_messages, :only => [:create, :destroy, :show]
resources :comments, :only => [:create]
resources :requests, :only => [:destroy, :create]
@@ -20,7 +22,13 @@ Diaspora::Application.routes.draw do
resources :posts, :only => [:show], :path => '/p/'
resources :contacts
- resources :aspect_memberships
+ resources :aspect_memberships, :only => [:destroy, :create]
+
+
+ resources :conversations do
+ resources :messages, :only => [:create, :show]
+ resource :conversation_visibility, :only => [:destroy], :path => '/visibility/'
+ end
resources :people, :except => [:edit, :update] do
resources :status_messages
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 000000000..7c3f880d7
--- /dev/null
+++ b/db/migrate/20110225190919_create_conversations_and_messages_and_visibilities.rb
@@ -0,0 +1,39 @@
+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.integer :author_id, :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/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 000000000..ef43ad57a
--- /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/migrate/20110228233419_add_signatures_to_message.rb b/db/migrate/20110228233419_add_signatures_to_message.rb
new file mode 100644
index 000000000..e6c72e761
--- /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/migrate/20110301014507_rename_person_to_author.rb b/db/migrate/20110301014507_rename_person_to_author.rb
new file mode 100644
index 000000000..123392445
--- /dev/null
+++ b/db/migrate/20110301014507_rename_person_to_author.rb
@@ -0,0 +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 197acb51f..051abe6f7 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -39,21 +39,21 @@ ActiveRecord::Schema.define(:version => 20110301202619) do
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 "author_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"
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|
@@ -70,6 +70,26 @@ ActiveRecord::Schema.define(:version => 20110301202619) do
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.integer "author_id", :null => false
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
create_table "invitations", :force => true do |t|
t.text "message"
t.integer "sender_id", :null => false
@@ -94,6 +114,19 @@ ActiveRecord::Schema.define(:version => 20110301202619) do
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"
+ t.text "author_signature"
+ t.text "parent_author_signature"
+ 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"
@@ -346,7 +379,7 @@ ActiveRecord::Schema.define(:version => 20110301202619) do
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
@@ -365,9 +398,9 @@ ActiveRecord::Schema.define(:version => 20110301202619) do
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"
@@ -462,7 +495,7 @@ ActiveRecord::Schema.define(:version => 20110301202619) do
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
@@ -472,7 +505,7 @@ ActiveRecord::Schema.define(:version => 20110301202619) do
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 f18151413..adb33d26d 100644
--- a/lib/diaspora/exporter.rb
+++ b/lib/diaspora/exporter.rb
@@ -37,7 +37,7 @@ module Diaspora
#}
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 @@ module Diaspora
}
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/relayable.rb b/lib/diaspora/relayable.rb
new file mode 100644
index 000000000..43a4c44c2
--- /dev/null
+++ b/lib/diaspora/relayable.rb
@@ -0,0 +1,135 @@
+# 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 relayable
+ true
+ 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.author]
+ end
+ end
+
+ def receive(user, person)
+ object = self.class.where(:guid => self.guid).first || self
+
+ 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
+
+ #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) if object.respond_to? :socket_to_user
+ if object.after_receive(user, person)
+ object
+ end
+ end
+
+ def after_receive(user, person)
+ self
+ end
+
+ def signable_string
+ raise NotImplementedException("Override this in your encryptable class")
+ end
+
+ def signature_valid?
+ verify_signature(creator_signature, self.author)
+ 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
+ ['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, self.parent.author)
+ end
+
+ def signature_valid?
+ verify_signature(self.author_signature, self.author)
+ 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/diaspora/user/connecting.rb b/lib/diaspora/user/connecting.rb
index e3a0c35fe..a21014365 100644
--- a/lib/diaspora/user/connecting.rb
+++ b/lib/diaspora/user/connecting.rb
@@ -84,9 +84,9 @@ module Diaspora
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 3a23a31c4..25c90d0f2 100644
--- a/lib/diaspora/user/querying.rb
+++ b/lib/diaspora/user/querying.rb
@@ -7,7 +7,7 @@ module Diaspora
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({:author => :profile}, {:comments => {:author => :profile}}, :photos).first
end
def raw_visible_posts
diff --git a/lib/encryptable.rb b/lib/encryptable.rb
deleted file mode 100644
index 06cb31014..000000000
--- 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/lib/fake.rb b/lib/fake.rb
index 27a502de6..099688315 100644
--- a/lib/fake.rb
+++ b/lib/fake.rb
@@ -3,15 +3,15 @@ class PostsFake
delegate :length, :each, :to_ary, :to => :post_fakes
def initialize(posts)
- person_ids = []
+ author_ids = []
posts.each do |p|
- person_ids << p.person_id
+ author_ids << p.author_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}
diff --git a/lib/postzord/dispatch.rb b/lib/postzord/dispatch.rb
index 244f1bb5d..4b3833032 100644
--- a/lib/postzord/dispatch.rb
+++ b/lib/postzord/dispatch.rb
@@ -22,11 +22,11 @@ class Postzord::Dispatch
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)
- 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 @@ class Postzord::Dispatch
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/lib/postzord/receiver.rb b/lib/postzord/receiver.rb
index bfc56ab11..c61039bf0 100644
--- a/lib/postzord/receiver.rb
+++ b/lib/postzord/receiver.rb
@@ -49,9 +49,9 @@ module Postzord
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.person.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 @@ module Postzord
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
@@ -82,6 +82,7 @@ module Postzord
end
if @author
+ @object.author = @author if @object.respond_to? :author=
@object.person = @author if @object.respond_to? :person=
end
diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake
index 4da04ce86..77e4b4022 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/images/icons/monotone_flag.png b/public/images/icons/monotone_flag.png
new file mode 100644
index 000000000..baea94936
Binary files /dev/null and b/public/images/icons/monotone_flag.png differ
diff --git a/public/images/reply.png b/public/images/reply.png
new file mode 100644
index 000000000..2356dc779
Binary files /dev/null and b/public/images/reply.png differ
diff --git a/public/javascripts/inbox.js b/public/javascripts/inbox.js
new file mode 100644
index 000000000..1172b61c7
--- /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').removeClass('unread');
+ $('#conversation_show').html(data);
+ Diaspora.widgets.timeago.updateTimeAgo();
+ });
+
+ 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/javascripts/vendor/jquery.autoSuggest.js b/public/javascripts/vendor/jquery.autoSuggest.js
new file mode 100644
index 000000000..bafd35cbc
--- /dev/null
+++ b/public/javascripts/vendor/jquery.autoSuggest.js
@@ -0,0 +1,372 @@
+ /*
+ * 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 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){
+ 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 198268ccc..6f79f0d63 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 550f05083..1016c158b 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
@@ -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
@@ -127,8 +133,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
@@ -247,35 +253,33 @@ header
.stream
.stream_element
- :font
- :family 'arial', 'helvetica', sans-serif
- :padding 10px 14px
- :right 75px
+ :padding 20px 20px
+ :right 70px
:min-height 50px
:border
:bottom 1px solid #ddd
:top 1px solid #fff
- :font
- :size 13px
- :line
- :height 19px
-
-
&:hover
- :border
- :bottom 1px solid #ddd
.right
:display inline
+ .from
+ a
+ :color $blue
.youtube-player, .vimeo-player
:border none
: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 +338,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 +354,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 13px
+ :line
+ :height 18px
.photo_attachments
:margin
@@ -389,7 +397,7 @@ header
:margin
:top 2px
:color #999
- :font-size smaller
+ :font-size 11px
.comments
.info
@@ -406,8 +414,9 @@ header
.time,
.timeago
+ :color #999
a
- :color #bbb
+ :color #ccc
:margin
:right 1px
:text
@@ -554,6 +563,8 @@ ul.show_comments
:line
:height 18px
+ :position relative
+
textarea
:width 100%
:height 1.4em
@@ -588,9 +599,6 @@ ul.show_comments
a
:color #444
- div.time
- :color #bbb
-
form
:margin
:top -5px
@@ -629,6 +637,12 @@ ul.show_comments
textarea
:min-height 2.4em
+.comments
+ time
+ :margin
+ :right -15px
+ .timeago
+ :color #999
.stream.show
ul.comments
@@ -688,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
@@ -1663,15 +1680,17 @@ 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 -5px
:font
:weight bold
:size smaller
- :width 30px
+ :width 28px
a
:z-index 5
@@ -1680,7 +1699,17 @@ h3 span.current_gs_step
:width 20px
:height 20px
-#notification_badge_number
+ &:hover
+ .badge_count
+ :background
+ :color lighten(#A40802, 5%)
+
+#notification_badge
+ img
+ :position relative
+ :top 2px
+
+.badge_count
:z-index 3
:position absolute
:top -10px
@@ -2270,6 +2299,7 @@ ul.show_comments
:margin
:left 0.5em
:right 0.5em
+
.mark_all_read
:position relative
:top 10px
@@ -2298,6 +2328,17 @@ ul.show_comments
&.larger
:width 600px
+#new_message_pane
+ input:not([type='submit']),
+ textarea
+ :width 378px
+ :margin
+ :right 0
+
+ .as-selections
+ input
+ :width 200px
+
#facebox_header
:padding 1em
:background
@@ -2483,3 +2524,188 @@ ul.show_comments
.public_icon, .service_icon
:cursor pointer
+
+.stream_element
+ .subject
+ :font
+ :size 13px
+ :weight bold
+ :color #444
+ :overflow hidden
+ :white-space nowrap
+
+ .last_author
+ :font
+ :size 12px
+ :color #777
+
+
+.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
+
+ :line
+ :height 0
+
+ .conversation_controls
+ a
+ :margin
+ :right 10px
+
+ :margin
+ :bottom 10px
+
+ :border
+ :bottom 1px solid #666
+ :padding 20px
+ :top 110px
+ :bottom 10px
+ :margin
+ :top -100px
+
+ .avatars
+ :text
+ :align right
+ :margin
+ :top 9px
+
+.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
+
+.stream_element.conversation
+ :padding 10px
+ :min-height 0px
+
+ .message_count
+ :right 10px
+ :background
+ :color #999
+ :color #eee
+ :position absolute
+ :padding 0 5px
+ :font
+ :size 12px
+ :weight normal
+ :-webkit-border-radius 3px
+ :-moz-border-radius 3px
+ :border-radius 3px
+
+ .participant_count
+ :font
+ :weight normal
+
+ .timestamp
+ :position absolute
+ :right 10px
+ :font
+ :weight normal
+ :color $blue
+
+ .avatar
+ :display inline
+ :width 35px
+ :height 35px
+ :margin
+ :right 10px
+
+ &:hover:not(.selected)
+ :background
+ :color #f0f0f0
+ &:hover
+ :cursor pointer
+
+.conversation.unread
+ :background
+ :color lighten($background,5%)
+
+.conversation.selected
+ :background
+ :color $blue
+ .subject
+ :color #fff
+ .last_author
+ :color #fff
+ .timestamp
+ :color #eee
+ :border
+ :bottom 1px solid darken($blue, 10%)
+ :top 1px solid darken($blue, 10%)
+
+#conversation_inbox
+ :height 100%
+ :overflow-y auto
+ :overflow-x none
+ :background
+ :color #f3f3f3
+
+#left_pane
+ :position fixed
+ :width 320px
+ :background
+ :color #ddd
+
+ :-webkit-box-shadow 2px 2px 5px -1px #999
+
+ :border
+ :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
+
+#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
+
+.text-right
+ :text
+ :align right
+ :margin
+ :right 5px
diff --git a/public/stylesheets/vendor/autoSuggest.css b/public/stylesheets/vendor/autoSuggest.css
new file mode 100644
index 000000000..225d3c410
--- /dev/null
+++ b/public/stylesheets/vendor/autoSuggest.css
@@ -0,0 +1,216 @@
+/* AutoSuggest CSS - Version 1.2 */
+
+ul.as-selections {
+ list-style-type: none;
+ border: 1px solid #ccc;
+ padding: 4px 0 4px 4px;
+ margin: 0;
+ overflow: auto;
+ background-color: #fff;
+
+ border-radius: 3px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+
+ width: 383px;
+}
+
+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;
+ 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;
+ 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 {
+ margin-left: 30px;
+}
+
+ul.as-selections li.as-selection-item a.as-close {
+ float: right;
+ margin: 0px 3px 0 0px;
+ padding: 0 3px;
+ 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: 14px;
+ padding: 0;
+ margin: 0;
+}
+
+ul.as-list {
+ position: absolute;
+ list-style-type: none;
+ margin: 2px 0 0 0;
+ padding: 0;
+ font-size: 13px;
+ color: #000;
+ 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;
+ background-color: transparent;
+ border: 1px solid #fff;
+ border-bottom: 1px solid #ddd;
+ cursor: pointer;
+ border-radius: 3px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+}
+
+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 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: 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 #ccc;
+ }
+ ul.as-selections li.as-selection-item a.as-close {
+ margin-left: 4px;
+ margin-top: 0;
+ }
+}
+
+/* IE Hacks */
+ul.as-list {
+ border: 1px solid #ccc\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 #ccc;
+}
+BODY:first-of-type ul.as-list, x:-moz-any-link, x:default { /* Target FF 3.5+ */
+ border: none;
+}
diff --git a/spec/controllers/aspect_memberships_controller_spec.rb b/spec/controllers/aspect_memberships_controller_spec.rb
index 007d914b9..f79429dc2 100644
--- a/spec/controllers/aspect_memberships_controller_spec.rb
+++ b/spec/controllers/aspect_memberships_controller_spec.rb
@@ -23,13 +23,6 @@ describe AspectMembershipsController do
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/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb
index 80325a028..f003ae71c 100644
--- a/spec/controllers/comments_controller_spec.rb
+++ b/spec/controllers/comments_controller_spec.rb
@@ -41,11 +41,11 @@ describe CommentsController do
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
new file mode 100644
index 000000000..f2002cdd9
--- /dev/null
+++ b/spec/controllers/conversation_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 ConversationVisibilitiesController do
+ render_views
+
+ before do
+ @user1 = alice
+ sign_in :user, @user1
+
+ 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
+ it 'deletes the visibility' do
+ lambda {
+ 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
+ user2 = eve
+ sign_in :user, user2
+
+ lambda {
+ 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 000000000..50f6ed14a
--- /dev/null
+++ b/spec/controllers/conversations_controller_spec.rb
@@ -0,0 +1,98 @@
+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 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(hash)
+ end
+
+ get :index
+ assigns[:conversations].count.should == 3
+ end
+ end
+
+ describe '#create' do
+ before do
+ @hash = {:conversation => {
+ :subject => "secret stuff",
+ :text => 'text'},
+ :contact_ids => '@user1.contacts.first.id'}
+ end
+
+ it 'creates a conversation' do
+ lambda {
+ post :create, @hash
+ }.should change(Conversation, :count).by(1)
+ end
+
+ it 'creates a message' do
+ lambda {
+ 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
+
+ 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
+ before do
+ 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
+ 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/messages_controller_spec.rb b/spec/controllers/messages_controller_spec.rb
new file mode 100644
index 000000000..8c9b777bb
--- /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, :message => {:text => "here is something else"}}
+ end
+ it 'redirects to conversation' do
+ lambda{
+ post :create, @message_hash
+ }.should change(Message, :count).by(1)
+ response.code.should == '302'
+ response.should redirect_to(conversations_path(:conversation_id => @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, :message => {:text => "here is something else"}}
+ end
+ it 'comments' do
+ post :create, @message_hash
+ 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[: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)
+ @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
+ @create_hash[:participant_ids] = [eve.person.id, bob.person.id]
+ @cnv = Conversation.create(@create_hash)
+ @message_hash = {:conversation_id => @cnv.id, :message => {:text => "here is something else"}}
+ end
+ it 'posts no comment' do
+ post :create, @message_hash
+ response.code.should == '406'
+ end
+ end
+ end
+end
diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb
index e2e442332..d7f0ceaf5 100644
--- a/spec/controllers/photos_controller_spec.rb
+++ b/spec/controllers/photos_controller_spec.rb
@@ -18,7 +18,7 @@ describe PhotosController do
@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 @@ describe PhotosController do
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 d51fb2ef3..305656dc2 100644
--- a/spec/controllers/status_messages_controller_spec.rb
+++ b/spec/controllers/status_messages_controller_spec.rb
@@ -87,11 +87,11 @@ describe StatusMessagesController do
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 1e784e761..72cc39a05 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -62,11 +62,11 @@ Factory.define :aspect do |aspect|
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
@@ -85,8 +85,8 @@ end
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/helpers/private_messages_helper_spec.rb b/spec/helpers/private_messages_helper_spec.rb
new file mode 100644
index 000000000..a946519dd
--- /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/integration/receiving_spec.rb b/spec/integration/receiving_spec.rb
index 9f81f0371..2070b5427 100644
--- a/spec/integration/receiving_spec.rb
+++ b/spec/integration/receiving_spec.rb
@@ -68,8 +68,9 @@ describe 'a user receives a post' do
alice.visible_posts.count.should == 1
end
- context 'mentions' do
- it 'adds the notifications for the mentioned users regardless of the order they are received' do
+ context 'mentions' do
+ it 'adds the notifications for the mentioned users reguardless of the order they are received' do
+ pending 'this is for mnutt'
Notification.should_receive(:notify).with(@user1, anything(), @user2.person)
Notification.should_receive(:notify).with(@user3, anything(), @user2.person)
@@ -84,32 +85,6 @@ describe 'a user receives a post' do
zord = Postzord::Receiver.new(@user3, :object => @sm, :person => @user2.person)
zord.receive_object
end
-
- it 'notifies users when receiving a mention in a post from a remote user' do
- @remote_person = Factory.create(:person, :diaspora_handle => "foobar@foobar.com")
- Contact.create!(:user => @user1, :person => @remote_person, :aspects => [@aspect], :pending => false)
-
- Notification.should_receive(:notify).with(@user1, anything(), @remote_person)
-
- @sm = Factory.build(:status_message, :message => "hello @{#{@user1.name}; #{@user1.diaspora_handle}}", :diaspora_handle => @remote_person.diaspora_handle, :person => @remote_person)
- @sm.stub!(:socket_to_user)
- @sm.save
-
- zord = Postzord::Receiver.new(@user1, :object => @sm, :person => @user2.person)
- zord.receive_object
- end
-
- it 'does not notify the mentioned user if the mentioned user is not friends with the post author' do
- Notification.should_not_receive(:notify).with(@user1, anything(), @user3.person)
-
- @sm = @user3.build_post(:status_message, :message => "should not notify @{#{@user1.name}; #{@user1.diaspora_handle}}")
- @sm.stub!(:socket_to_user)
- @user3.add_to_streams(@sm, [@user3.aspects.first])
- @sm.save
-
- zord = Postzord::Receiver.new(@user1, :object => @sm, :person => @user2.person)
- zord.receive_object
- end
end
context 'update posts' do
@@ -153,35 +128,29 @@ describe 'a user receives a post' do
@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
@@ -224,7 +193,7 @@ describe 'a user receives a post' do
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
@@ -235,7 +204,7 @@ describe 'a user receives a post' do
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
@@ -263,7 +232,7 @@ describe 'a user receives a post' do
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
@@ -292,11 +261,11 @@ describe 'a user receives a post' do
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)
@@ -307,11 +276,11 @@ describe 'a user receives a post' do
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 639ff7efb..840206905 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 @@ describe DataConversion::ImportToMysql do
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 @@ describe DataConversion::ImportToMysql do
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
@@ -412,7 +412,7 @@ describe DataConversion::ImportToMysql do
@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 5645726dc..b680c5b37 100644
--- a/spec/lib/diaspora/parser_spec.rb
+++ b/spec/lib/diaspora/parser_spec.rb
@@ -20,7 +20,7 @@ describe Diaspora::Parser do
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 c8d8c1f98..0cf749568 100644
--- a/spec/lib/fake_spec.rb
+++ b/spec/lib/fake_spec.rb
@@ -5,10 +5,10 @@ describe PostsFake do
@people = []
4.times do
post = Factory(:status_message)
- @people << post.person
+ @people << post.author
4.times do
comment = Factory(:comment, :post => post)
- @people << comment.person
+ @people << comment.author
end
@posts << post
end
@@ -30,7 +30,7 @@ describe PostsFake do
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 6886311d7..f46391f27 100644
--- a/spec/lib/postzord/dispatch_spec.rb
+++ b/spec/lib/postzord/dispatch_spec.rb
@@ -134,7 +134,7 @@ describe Postzord::Dispatch do
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
@@ -181,7 +181,7 @@ describe Postzord::Dispatch do
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 @@ describe Postzord::Dispatch do
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 359ced22f..9ee6d4c38 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 f3db5ab38..b2ed38bb3 100644
--- a/spec/mailers/notifier_spec.rb
+++ b/spec/mailers/notifier_spec.rb
@@ -87,7 +87,7 @@ describe Notifier do
@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 @@ describe Notifier do
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
@@ -110,7 +110,42 @@ describe Notifier do
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.messages.first.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 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
+
+ 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/misc_spec.rb b/spec/misc_spec.rb
index 3ea7bbc6c..f95f443ae 100644
--- a/spec/misc_spec.rb
+++ b/spec/misc_spec.rb
@@ -49,4 +49,17 @@ describe 'making sure the spec runner works' do
@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, :author => 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/aspect_spec.rb b/spec/models/aspect_spec.rb
index bf7a2548c..9c8f81939 100644
--- a/spec/models/aspect_spec.rb
+++ b/spec/models/aspect_spec.rb
@@ -114,7 +114,7 @@ describe Aspect do
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 e80cfcf3d..030755e3b 100644
--- a/spec/models/comment_spec.rb
+++ b/spec/models/comment_spec.rb
@@ -3,53 +3,47 @@
# the COPYRIGHT file.
require 'spec_helper'
+require File.join(Rails.root, "spec", "shared_behaviors", "relayable")
describe Comment do
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 +53,13 @@ describe Comment do
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)
@@ -139,66 +80,13 @@ describe Comment do
@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
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 +108,20 @@ describe Comment do
Comment.find(comment.id).youtube_titles.should == {video_id => CGI::escape(expected_title)}
end
end
+
+ describe 'it is relayable' do
+ before do
+ @local_luke, @local_leia, @remote_raphael = set_up_friends
+ @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)
+ @object_by_recipient = @local_leia.build_comment("yo", :on => @local_parent)
+ @dup_object_by_parent_author = @object_by_parent_author.dup
+
+ @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
new file mode 100644
index 000000000..89ac00a30
--- /dev/null
+++ b/spec/models/conversation_spec.rb
@@ -0,0 +1,92 @@
+# 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
+ before 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'}
+ end
+
+ it 'creates a message on create' do
+ lambda{
+ Conversation.create(@create_hash)
+ }.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)
+ @message = @cnv.messages.first
+ @xml = @cnv.to_diaspora_xml
+ 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
+ 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
+ before do
+ Conversation.destroy_all
+ Message.destroy_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(@participant_ids.size)
+ end
+ 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/conversation_visibility_spec.rb b/spec/models/conversation_visibility_spec.rb
new file mode 100644
index 000000000..fff04d7e1
--- /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, :author => @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/jobs/mail_mentioned_spec.rb b/spec/models/jobs/mail_mentioned_spec.rb
index a186ee623..5680a3f3f 100644
--- a/spec/models/jobs/mail_mentioned_spec.rb
+++ b/spec/models/jobs/mail_mentioned_spec.rb
@@ -13,9 +13,9 @@ describe Job::MailMentioned do
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/jobs/mail_private_message.rb b/spec/models/jobs/mail_private_message.rb
new file mode 100644
index 000000000..dc1c12914
--- /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/mention_spec.rb b/spec/models/mention_spec.rb
index 9310869d5..77428a541 100644
--- a/spec/models/mention_spec.rb
+++ b/spec/models/mention_spec.rb
@@ -12,12 +12,13 @@ describe Mention do
@mentioned_user = bob
@non_friend = eve
- @sm = @user.build_post(:status_message, :message => "hi @{#{@mentioned_user.name}; #{@mentioned_user.diaspora_handle}}", :to => @user.aspects.first)
- end
+ @sm = Factory(:status_message)
+ @m = Mention.new(:person => @user.person, :post=> @sm)
+ end
it 'notifies the person being mentioned' do
- Notification.should_receive(:notify).with(@mentioned_user, anything(), @sm.person)
- @sm.receive(@mentioned_user, @mentioned_user.person)
+ Notification.should_receive(:notify).with(@user, anything(), @sm.author)
+ @m.save
end
it 'should not notify a user if they do not see the message' do
@@ -39,11 +40,8 @@ describe Mention do
describe 'after destroy' do
it 'destroys a notification' do
@user = alice
- @mentioned_user = bob
-
- @sm = @user.post(:status_message, :message => "hi", :to => @user.aspects.first)
- @m = Mention.create!(:person => @mentioned_user.person, :post => @sm)
- @m.notify_recipient
+ @sm = Factory(:status_message)
+ @m = Mention.create(:person => @user.person, :post=> @sm)
lambda{
@m.destroy
diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb
new file mode 100644
index 000000000..859900f4b
--- /dev/null
+++ b/spec/models/message_spec.rb
@@ -0,0 +1,102 @@
+# 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", "shared_behaviors", "relayable")
+
+describe Message 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)
+ @message = @cnv.messages.first
+ @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)
+ 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
+ it 'signs the message' do
+ @message.author_signature.should_not be_blank
+ end
+
+ it 'signs the message author if author of conversation' do
+ @message.parent_author_signature.should_not be_blank
+ end
+ 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 'it is relayable' do
+ before do
+ @local_luke, @local_leia, @remote_raphael = set_up_friends
+
+ 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[: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
+
+ 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'
+
+ 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/models/notification_spec.rb b/spec/models/notification_spec.rb
index 21b3ceaf4..eb6ce5f09 100644
--- a/spec/models/notification_spec.rb
+++ b/spec/models/notification_spec.rb
@@ -44,7 +44,8 @@ describe Notification do
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 000000000..42b05f9ec
--- /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
+
diff --git a/spec/models/person_spec.rb b/spec/models/person_spec.rb
index 0d799e207..15b5375e0 100644
--- a/spec/models/person_spec.rb
+++ b/spec/models/person_spec.rb
@@ -101,8 +101,8 @@ describe Person do
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 Person do
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
@@ -154,8 +154,8 @@ describe Person do
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/models/photo_spec.rb b/spec/models/photo_spec.rb
index 3b19fd145..692ffb267 100644
--- a/spec/models/photo_spec.rb
+++ b/spec/models/photo_spec.rb
@@ -20,13 +20,13 @@ describe Photo do
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 @@ describe Photo do
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 @@ describe Photo do
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 96b56e22b..d2b8c7bb5 100644
--- a/spec/models/post_spec.rb
+++ b/spec/models/post_spec.rb
@@ -12,7 +12,7 @@ describe Post do
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 3c2cf91c3..ae8a1141c 100644
--- a/spec/models/post_visibility_spec.rb
+++ b/spec/models/post_visibility_spec.rb
@@ -6,7 +6,7 @@ describe PostVisibility do
@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 7beb656e0..697bef70b 100644
--- a/spec/models/status_message_spec.rb
+++ b/spec/models/status_message_spec.rb
@@ -21,11 +21,11 @@ describe StatusMessage do
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
@@ -167,7 +167,7 @@ STR
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
@@ -193,7 +193,7 @@ STR
@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 d40d7d23b..de2bfb50d 100644
--- a/spec/models/user/attack_vectors_spec.rb
+++ b/spec/models/user/attack_vectors_spec.rb
@@ -67,7 +67,7 @@ describe "attack vectors" do
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 @@ describe "attack vectors" do
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/commenting_spec.rb b/spec/models/user/commenting_spec.rb
deleted file mode 100644
index 8534549e5..000000000
--- 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/models/user/querying_spec.rb b/spec/models/user/querying_spec.rb
index da876409c..bce6037c4 100644
--- a/spec/models/user/querying_spec.rb
+++ b/spec/models/user/querying_spec.rb
@@ -47,7 +47,7 @@ describe User do
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 @@ describe User do
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 078107eef..41008ef54 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -271,7 +271,7 @@ describe User do
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
diff --git a/spec/shared_behaviors/relayable.rb b/spec/shared_behaviors/relayable.rb
new file mode 100644
index 000000000..f6cc2d1a3
--- /dev/null
+++ b/spec/shared_behaviors/relayable.rb
@@ -0,0 +1,88 @@
+# 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
+ shared_examples_for "it is relayable" do
+ context 'encryption' do
+ describe '#parent_author_signature' 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
+
+ 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
+
+ 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
+ end
+ end
+
+ 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
+
+ context 'propagation' do
+ describe '#receive' 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(@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 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
+
+ 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
+ pending
+ @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.class.stub(:where).and_return([@object_by_recipient])
+ @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 object' do
+ @object_by_recipient.subscribers(@local_leia).map(&:id).should =~ [@local_luke.person].map(&:id)
+ end
+ end
+ end
+ end
+end
+
diff --git a/spec/support/user_methods.rb b/spec/support/user_methods.rb
index e47c2fd68..3d091d914 100644
--- a/spec/support/user_methods.rb
+++ b/spec/support/user_methods.rb
@@ -18,10 +18,7 @@ class User
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 @@ class User
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