Merge branch 'private_messages'
Conflicts: app/controllers/status_messages_controller.rb app/models/data_point.rb app/models/status_message.rb db/schema.rb lib/fake.rb public/stylesheets/sass/application.sass spec/models/mention_spec.rb
This commit is contained in:
commit
1f5edb1d92
113 changed files with 2682 additions and 625 deletions
14
Gemfile.lock
14
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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
19
app/controllers/conversation_visibilities_controller.rb
Normal file
19
app/controllers/conversation_visibilities_controller.rb
Normal file
|
|
@ -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
|
||||
66
app/controllers/conversations_controller.rb
Normal file
66
app/controllers/conversations_controller.rb
Normal file
|
|
@ -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
|
||||
32
app/controllers/messages_controller.rb
Normal file
32
app/controllers/messages_controller.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|||
"<img alt=\"#{h(person.name)}\" class=\"avatar\" #{("data-owner_id="+@user_id.to_s) if @user_id} data-person_id=\"#{person.id}\" src=\"#{person.profile.image_url(size)}\" title=\"#{h(person.name)}\">".html_safe
|
||||
end
|
||||
|
||||
def person_link(person)
|
||||
"<a href='/people/#{person.id}'>
|
||||
def person_link(person, opts={})
|
||||
"<a href='/people/#{person.id}' class='#{opts[:class]}'>
|
||||
#{h(person.name)}
|
||||
</a>".html_safe
|
||||
end
|
||||
|
|
|
|||
9
app/helpers/conversations_helper.rb
Normal file
9
app/helpers/conversations_helper.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
2
app/helpers/private_messages_helper.rb
Normal file
2
app/helpers/private_messages_helper.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
module PrivateMessagesHelper
|
||||
end
|
||||
|
|
@ -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?
|
||||
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
72
app/models/conversation.rb
Normal file
72
app/models/conversation.rb
Normal file
|
|
@ -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
|
||||
6
app/models/conversation_visibility.rb
Normal file
6
app/models/conversation_visibility.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class ConversationVisibility < ActiveRecord::Base
|
||||
|
||||
belongs_to :conversation
|
||||
belongs_to :person
|
||||
|
||||
end
|
||||
13
app/models/jobs/mail_private_message.rb
Normal file
13
app/models/jobs/mail_private_message.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
82
app/models/message.rb
Normal file
82
app/models/message.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
15
app/models/notifications/private_message.rb
Normal file
15
app/models/notifications/private_message.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class StatusMessage < Post
|
|||
<<-XML
|
||||
<entry>
|
||||
<title>#{x(self.formatted_message(:plain_text => true))}</title>
|
||||
<link rel="alternate" type="text/html" href="#{person.url}p/#{self.id}"/>
|
||||
<link rel="alternate" type="text/html" href="#{self.author.url}status_messages/#{self.id}"/>
|
||||
<id>#{person.url}posts/#{self.id}</id>
|
||||
<published>#{self.created_at.xmlschema}</published>
|
||||
<updated>#{self.updated_at.xmlschema}</updated>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
21
app/views/conversations/_conversation.haml
Normal file
21
app/views/conversations/_conversation.haml
Normal file
|
|
@ -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})"
|
||||
37
app/views/conversations/_show.haml
Normal file
37
app/views/conversations/_show.haml
Normal file
|
|
@ -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'), '#'
|
||||
43
app/views/conversations/index.haml
Normal file
43
app/views/conversations/index.haml
Normal file
|
|
@ -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'
|
||||
51
app/views/conversations/new.haml
Normal file
51
app/views/conversations/new.haml
Normal file
|
|
@ -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
|
||||
5
app/views/conversations/show.haml
Normal file
5
app/views/conversations/show.haml
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
16
app/views/messages/_message.haml
Normal file
16
app/views/messages/_message.haml
Normal file
|
|
@ -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
|
||||
|
||||
18
app/views/notifier/private_message.html.haml
Normal file
18
app/views/notifier/private_message.html.haml
Normal file
|
|
@ -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')
|
||||
9
app/views/notifier/private_message.text.haml
Normal file
9
app/views/notifier/private_message.text.haml
Normal file
|
|
@ -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')
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
11
db/migrate/20110228233419_add_signatures_to_message.rb
Normal file
11
db/migrate/20110228233419_add_signatures_to_message.rb
Normal file
|
|
@ -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
|
||||
19
db/migrate/20110301014507_rename_person_to_author.rb
Normal file
19
db/migrate/20110301014507_rename_person_to_author.rb
Normal file
|
|
@ -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
|
||||
55
db/schema.rb
55
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
135
lib/diaspora/relayable.rb
Normal file
135
lib/diaspora/relayable.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
BIN
public/images/icons/monotone_flag.png
Normal file
BIN
public/images/icons/monotone_flag.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 783 B |
BIN
public/images/reply.png
Normal file
BIN
public/images/reply.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
79
public/javascripts/inbox.js
Normal file
79
public/javascripts/inbox.js
Normal file
|
|
@ -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);
|
||||
};
|
||||
372
public/javascripts/vendor/jquery.autoSuggest.js
vendored
Normal file
372
public/javascripts/vendor/jquery.autoSuggest.js
vendored
Normal file
|
|
@ -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('<ul class="as-selections" id="as-selections-'+x+'"></ul>').wrap('<li class="as-original" id="as-original-'+x+'"></li>');
|
||||
var selections_holder = $("#as-selections-"+x);
|
||||
var org_li = $("#as-original-"+x);
|
||||
var results_holder = $('<div class="as-results" id="as-results-'+x+'"></div>').hide();
|
||||
var results_ul = $('<ul class="as-list"></ul>');
|
||||
var values_input = $('<input type="hidden" class="as-values" name="'+x+'" id="as-values-'+x+'" />');
|
||||
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('<li class="as-message">'+opts.limitText+'</li>');
|
||||
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<d_count;i++){
|
||||
var num = i;
|
||||
num_count++;
|
||||
var forward = false;
|
||||
if(opts.searchObjProps == "value") {
|
||||
var str = data[num].value;
|
||||
} else {
|
||||
var str = "";
|
||||
var names = opts.searchObjProps.split(",");
|
||||
for(var y=0;y<names.length;y++){
|
||||
var name = $.trim(names[y]);
|
||||
str = str+data[num][name]+" ";
|
||||
}
|
||||
}
|
||||
if(str){
|
||||
if (!opts.matchCase){ str = str.toLowerCase(); }
|
||||
if(str.search(query) != -1 && values_input.val().search(","+data[num][opts.selectedValuesProp]+",") == -1){
|
||||
forward = true;
|
||||
}
|
||||
}
|
||||
if(forward){
|
||||
var formatted = $('<li class="as-result-item" id="as-result-item-'+num+'"></li>').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,"<em>$1</em>");
|
||||
}
|
||||
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('<li class="as-message">'+opts.emptyText+'</li>');
|
||||
}
|
||||
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 = $('<li class="as-selection-item" id="as-selection-'+num+'"></li>').click(function(){
|
||||
opts.selectionClick.call(this, $(this));
|
||||
selections_holder.children().removeClass("selected");
|
||||
$(this).addClass("selected");
|
||||
}).mousedown(function(){ input_focus = false; });
|
||||
var close = $('<a class="as-close">×</a>').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);
|
||||
|
|
@ -165,6 +165,14 @@ var View = {
|
|||
}
|
||||
},
|
||||
|
||||
conversation_participants: {
|
||||
bind: function() {
|
||||
$(".conversation_participants img").tipsy({
|
||||
live: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
whatIsThis: {
|
||||
bind: function() {
|
||||
$(".what_is_this").tipsy({
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
216
public/stylesheets/vendor/autoSuggest.css
vendored
Normal file
216
public/stylesheets/vendor/autoSuggest.css
vendored
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
98
spec/controllers/conversations_controller_spec.rb
Normal file
98
spec/controllers/conversations_controller_spec.rb
Normal file
|
|
@ -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
|
||||
76
spec/controllers/messages_controller_spec.rb
Normal file
76
spec/controllers/messages_controller_spec.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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|
|
||||
|
|
|
|||
15
spec/helpers/private_messages_helper_spec.rb
Normal file
15
spec/helpers/private_messages_helper_spec.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
92
spec/models/conversation_spec.rb
Normal file
92
spec/models/conversation_spec.rb
Normal file
|
|
@ -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
|
||||
19
spec/models/conversation_visibility_spec.rb
Normal file
19
spec/models/conversation_visibility_spec.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
27
spec/models/jobs/mail_private_message.rb
Normal file
27
spec/models/jobs/mail_private_message.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
102
spec/models/message_spec.rb
Normal file
102
spec/models/message_spec.rb
Normal file
|
|
@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue