diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4e9f94c24..8851f3c8e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -57,11 +57,15 @@ module ApplicationHelper end def person_image_tag(person) + image_tag image_or_default(person), :class => "avatar", :alt => person.real_name, :title => person.real_name, "data-person_id" => person.id + end + + def image_or_default(person) image_location = person.profile.image_url image_location ||= "/images/user/default.png" - - image_tag image_location, :class => "avatar", :alt => person.real_name, :title => person.real_name, "data-person_id" => person.id + image_location end + def person_image_link(person) link_to person_image_tag(person), object_path(person) diff --git a/app/mailers/notifier.rb b/app/mailers/notifier.rb new file mode 100644 index 000000000..95cdff86f --- /dev/null +++ b/app/mailers/notifier.rb @@ -0,0 +1,22 @@ +class Notifier < ActionMailer::Base + default :from => "no-reply@joindiaspora.com" + ATTACHMENT = File.read("#{Rails.root}/public/images/diaspora_caps.png") + + def new_request(recipient, sender) + @receiver = recipient + @sender = sender + attachments["diaspora_white.png"] = ATTACHMENT + + mail(:to => "#{recipient.real_name} <#{recipient.email}>", + :subject => "new Diaspora* friend request from #{@sender.real_name}", :host => APP_CONFIG[:terse_pod_url]) + end + + def request_accepted(recipient, sender, aspect) + @receiver = recipient + @sender = sender + @aspect = aspect + attachments["diaspora_white.png"] = ATTACHMENT + mail(:to => "#{recipient.real_name} <#{recipient.email}>", + :subject => "#{@sender.real_name} has accepted your friend request on Diaspora*", :host => APP_CONFIG[:terse_pod_url]) + end +end diff --git a/app/models/person.rb b/app/models/person.rb index 1e063a743..5a1486653 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -97,9 +97,9 @@ class Person # Raise an error if identifier is not a valid email (generous regexp) raise "Identifier is invalid" if !(identifier =~ /\A.*\@.*\..*\Z/) - query = /#{Regexp.escape(identifier.gsub('acct:', '').to_s)}/i + query = /\A^#{Regexp.escape(identifier.gsub('acct:', '').to_s)}\z/i local_person = Person.first(:diaspora_handle => query) - + if local_person Rails.logger.info("Do not need to webfinger, found a local person #{local_person.real_name}") local_person diff --git a/app/models/status_message.rb b/app/models/status_message.rb index 1c167706e..093316659 100644 --- a/app/models/status_message.rb +++ b/app/models/status_message.rb @@ -3,7 +3,8 @@ # the COPYRIGHT file. class StatusMessage < Post - + + validates_length_of :message, :maximum => 1000, :message => "please make your status messages less than 1000 characters" xml_name :status_message xml_accessor :message diff --git a/app/models/user.rb b/app/models/user.rb index 273cc209b..343cc2d76 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -43,8 +43,7 @@ class User before_validation :strip_username, :on => :create validates_presence_of :username validates_uniqueness_of :username, :case_sensitive => false - validates_format_of :username, :without => /\s/ - + validates_format_of :username, :with => /\A[A-Za-z0-9_.]+\z/ validates_with InvitedUserValidator one :person, :class_name => 'Person', :foreign_key => :owner_id diff --git a/app/views/albums/_new_album.haml b/app/views/albums/_new_album.haml index d7bd2d997..e8cd3e778 100644 --- a/app/views/albums/_new_album.haml +++ b/app/views/albums/_new_album.haml @@ -2,14 +2,11 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -.span-12.last - .modal_title_bar - %h4= t('.add_a_new_album') - = form_for Album.new do |f| - = f.error_messages - %p - = f.label :name - = f.text_field :name - = f.hidden_field :to, :value => aspect - = f.submit t('.create'), :class => 'button' += form_for Album.new do |album| + = album.error_messages + %p + = album.label :name + = album.hidden_field :to, :value => aspect + = album.text_field :name + = album.submit t('.create'), :class => 'button' diff --git a/app/views/albums/index.html.haml b/app/views/albums/index.html.haml index b9e62113f..ea046d7aa 100644 --- a/app/views/albums/index.html.haml +++ b/app/views/albums/index.html.haml @@ -8,22 +8,11 @@ $("#add_album_button").fancybox(); }); -%h2 - = @aspect - .friend_pictures.horizontal - = owner_image_link - - for friend in @friends - = person_image_link(friend) +.span-4.append-1.last + = render "shared/aspect_friends" -.span-24.last - %h3 - = @aspect - Albums - = link_to t('.new_album'), '#new_album_pane', {:class => "button", :id => "add_album_button"} - - .fancybox_content - #new_album_pane - = render "albums/new_album", :aspect => params[:aspect] +.span-15.last + = render "shared/publisher", :type => :album, :aspect => @aspect.id %div - for album in @albums diff --git a/app/views/albums/show.html.haml b/app/views/albums/show.html.haml index d6f834c82..4941a2bee 100644 --- a/app/views/albums/show.html.haml +++ b/app/views/albums/show.html.haml @@ -9,15 +9,12 @@ }); }); -%h2 - = @aspect - .friend_pictures.horizontal - = owner_image_link - - for friend in @friends - = person_image_link(friend) -%h3 - = link_to "#{@aspect} Albums", albums_path(:aspect => @aspect) += render 'shared/author_info', :post => @album + +%ul#breadcrumb + %li= link_to "#{@album.person.profile.first_name}'s Photos", '#' + %li= @album.name .span-19.appends-1.last diff --git a/app/views/aspects/index.html.haml b/app/views/aspects/index.html.haml index 8ffe1a519..dbc8ec95e 100644 --- a/app/views/aspects/index.html.haml +++ b/app/views/aspects/index.html.haml @@ -7,7 +7,7 @@ .span-15.last = render 'aspects/no_friends_message' - = render 'shared/publisher' + = render 'shared/publisher', :type => :status_message, :aspect => @aspect = render 'aspects/no_posts_message' %ul#stream - for post in @posts diff --git a/app/views/aspects/manage.html.haml b/app/views/aspects/manage.html.haml index 3fdedbad9..8ac04b893 100644 --- a/app/views/aspects/manage.html.haml +++ b/app/views/aspects/manage.html.haml @@ -10,7 +10,7 @@ %h2 Manage aspects .right - = link_to(t('.add_a_new_aspect'), "#add_aspect_pane", :class => "new_aspect add_aspect_button button", :title => t('.add_a_new_aspect')) + = link_to("+ #{t('.add_a_new_aspect')}", "#add_aspect_pane", :class => "new_aspect add_aspect_button button", :title => t('.add_a_new_aspect')) .span-4.append-1.last %h3=t('.requests') @@ -18,54 +18,50 @@ .requests %ul.dropzone - if @remote_requests.size < 1 - %li.grey No new requests + %li No new requests - else - for request in @remote_requests - %li.requested_person{:id => request.person.id, :request_id => request.id} + %li.person.request{:data=>{:guid=>request.id, :person_id=>request.person.id}} = person_image_tag(request.person) - .name - = request.person.real_name %h3 Remove from Aspect .aspect_remove %ul.dropzone - %li.grey Drag to remove person from aspect - - %h3=t('.ignore_remove') - .remove - %ul.dropzone - %li.grey Drag to ignore/remove + .draggable_info + Drag to remove person from aspect = render 'shared/invitations', :invites => @invites .span-19.last %ul#aspect_list - for aspect in @aspects - %li.aspect + %li.aspect{:data=>{:guid=>aspect.id}} .aspect_name %span.edit_name_field - %h3{:contenteditable => true}= aspect.name + %h3{:contenteditable=>true} + = aspect.name %span.tip click to edit %ul.tools %li= link_to t('.add_a_new_friend'), "#add_request_pane_#{aspect.id}", :class => 'add_request_button' %li!= remove_link(aspect) - %ul.dropzone{:id => aspect.id} + %ul.dropzone{:data=>{:aspect_id=>aspect.id}} + -for person in aspect.people + %li.person{:data=>{:guid=>person.id, :aspect_id=>aspect.id}} + .delete + .x + X + .circle + = person_image_tag(person) + .draggable_info + Drag to add people - -if aspect.people.size < 1 - %li.grey Drag to add people - -else - -for person in aspect.people - - %li.person{:id => person.id, :class => person.id, :from_aspect_id => aspect.id} - = person_image_tag(person) - .name - = link_to person.real_name, person .fancybox_content %div{:id => "add_request_pane_#{aspect.id}"} = render "requests/new_request", :aspect => aspect + diff --git a/app/views/aspects/show.html.haml b/app/views/aspects/show.html.haml index 8ffe1a519..dbc8ec95e 100644 --- a/app/views/aspects/show.html.haml +++ b/app/views/aspects/show.html.haml @@ -7,7 +7,7 @@ .span-15.last = render 'aspects/no_friends_message' - = render 'shared/publisher' + = render 'shared/publisher', :type => :status_message, :aspect => @aspect = render 'aspects/no_posts_message' %ul#stream - for post in @posts diff --git a/app/views/notifier/new_request.html.haml b/app/views/notifier/new_request.html.haml new file mode 100644 index 000000000..66bb56f1a --- /dev/null +++ b/app/views/notifier/new_request.html.haml @@ -0,0 +1,59 @@ +!!! +%html + %head + %meta{"http-equiv"=>"Content-Type", :content=>"text/html; charset=utf-8"}/ + :css + body{ + width:600px; + font-family:'Arial','Helvetica',sans-serif; + font-size:14px; + color:#333; + } + #container{ + margin-bottom:25px + min-height:400px; + padding-left:15px; + } + header{ + background-color:#333; + padding: 15px; + margin-bottom: 25px; + } + p{ + padding:5px; + } + p.small{ + font-size:smaller; + color:#999; + font-style:italic; + } + a{ + color:#107FC9; + font-weight:bold; + } + a:hover{ + color: #22AAE0; + } + a:active{ + color: #005D9C; + } + .large_text{ + font-size:21px; + font-family:"Helvetica Neue",Arial,Helvetica,sans-serif; + } + %body + %header + = image_tag 'diaspora_white.png' + #container + %p + Hello #{@receiver.profile.first_name}! + %p + = "#{@sender.real_name} (#{@sender.diaspora_handle})" + just sent you a friend request on Diaspora* + You should really think about checking it out. + %br + = link_to "sign in here", new_user_session_url + %br + love, + %br + the diaspora email robot diff --git a/app/views/notifier/new_request.text.haml b/app/views/notifier/new_request.text.haml new file mode 100644 index 000000000..8ab5bc570 --- /dev/null +++ b/app/views/notifier/new_request.text.haml @@ -0,0 +1,9 @@ += "hey #{@receiver.profile.first_name}," += "#{@sender.real_name} (#{@sender.diaspora_handle})" +just sent you a friend request on Diaspora* +You should really think about checking it out. + += "sign in here: #{new_user_session_url}" + +love, +the diaspora email robot diff --git a/app/views/notifier/request_accepted.html.haml b/app/views/notifier/request_accepted.html.haml new file mode 100644 index 000000000..191f8f42c --- /dev/null +++ b/app/views/notifier/request_accepted.html.haml @@ -0,0 +1,59 @@ +!!! +%html + %head + %meta{"http-equiv"=>"Content-Type", :content=>"text/html; charset=utf-8"}/ + :css + body{ + width:600px; + font-family:'Arial','Helvetica',sans-serif; + font-size:14px; + color:#333; + } + #container{ + margin-bottom:25px + min-height:400px; + padding-left:15px; + } + header{ + background-color:#333; + padding: 15px; + margin-bottom: 25px; + } + p{ + padding:5px; + } + p.small{ + font-size:smaller; + color:#999; + font-style:italic; + } + a{ + color:#107FC9; + font-weight:bold; + } + a:hover{ + color: #22AAE0; + } + a:active{ + color: #005D9C; + } + .large_text{ + font-size:21px; + font-family:"Helvetica Neue",Arial,Helvetica,sans-serif; + } + %body + %header + = image_tag 'diaspora_white.png' + #container + %p + Hello #{@receiver.profile.first_name}! + %p + = "#{@sender.real_name} (#{@sender.diaspora_handle})" + has accepted your friend request. They are now in your + = link_to @aspect.name, aspect_url(@aspect) + aspect. + + %br + love, + %br + the diaspora email robot diff --git a/app/views/notifier/request_accepted.text.haml b/app/views/notifier/request_accepted.text.haml new file mode 100644 index 000000000..e10623f16 --- /dev/null +++ b/app/views/notifier/request_accepted.text.haml @@ -0,0 +1,9 @@ += "hey #{@receiver.profile.first_name}," += "#{@sender.real_name} (#{@sender.diaspora_handle})" +has accepted your friend request. They are now in your += "#{@aspect.name} asepct.\n" += "#{aspect_url(@aspect)}" + + +love, \n +the diaspora email robot diff --git a/app/views/photos/show.html.haml b/app/views/photos/show.html.haml index e4ae23170..9b48538e5 100644 --- a/app/views/photos/show.html.haml +++ b/app/views/photos/show.html.haml @@ -55,15 +55,12 @@ });//end document ready -%h2 - = @aspect - .friend_pictures.horizontal - = owner_image_link - - for friend in @friends - = person_image_link(friend) += render 'shared/author_info', :post => @photo -%h3 - = link_to @photo.album.name, @photo.album +%ul#breadcrumb + %li= link_to "#{@album.person.profile.first_name}'s Photos", '#' + %li= link_to @album.name, album_path(@album) + %li= @photo.caption = link_to "<< #{t('.prev')}", url_to_prev(@photo, @album), :rel => 'prefetch' | diff --git a/app/views/publics/hcard.erb b/app/views/publics/hcard.erb index 6b8ab3209..5102897a5 100644 --- a/app/views/publics/hcard.erb +++ b/app/views/publics/hcard.erb @@ -36,13 +36,9 @@
Photo
- +
-
-
Note
-
Diaspora is awesome! vi is better than emacs!
-
diff --git a/app/views/shared/_author_info.html.haml b/app/views/shared/_author_info.html.haml new file mode 100644 index 000000000..6a619c44b --- /dev/null +++ b/app/views/shared/_author_info.html.haml @@ -0,0 +1,15 @@ +#author_info + = owner_image_link + .from + %h2 + = post.person.real_name + .aspect + ➔ + %ul + - if post.public? + the world + - else + - for aspect in current_user.aspects_with_post( post.id ) + %li= link_to aspect.name, aspect + + = link_to "view profile", person_path(post.person) diff --git a/app/views/shared/_publisher.haml b/app/views/shared/_publisher.haml index 3a5a9f468..832c07916 100644 --- a/app/views/shared/_publisher.haml +++ b/app/views/shared/_publisher.haml @@ -9,7 +9,7 @@ }; }); - $("#publisher textarea").live("focus", function(evt){ + $("#publisher textarea, #publisher input").live("focus", function(evt){ $("#publisher .options_and_submit").fadeIn(50); }); @@ -20,28 +20,41 @@ #publisher = owner_image_tag - = form_for StatusMessage.new, :remote => true do |status| - = status.error_messages - %p - = status.label :message, "Post a message to #{@aspect}" - = status.text_area :message, :rows => 2, :value => params[:prefill] + - if( !defined?(type) || type == :status_message ) + = form_for StatusMessage.new, :remote => true do |status| + = status.error_messages + %p + = status.label :message, "Post a message to #{aspect}" + = status.text_area :message, :rows => 2, :value => params[:prefill] - = status.hidden_field :to, :value => (@aspect == :all ? @aspect : @aspect.id) + = status.hidden_field :to, :value => (aspect == :all ? aspect : aspect.id) - .options_and_submit - - - if @aspect == :all - .public_toggle - = status.check_box( :public, :value => false ) - make public - = link_to '(?)', "#question_mark_pane", :class => 'question_mark' + .options_and_submit + + - if aspect == :all + .public_toggle + = status.check_box( :public, :value => false ) + make public + = link_to '(?)', "#question_mark_pane", :class => 'question_mark' - .fancybox_content - #question_mark_pane - = render 'shared/public_explain' + .fancybox_content + #question_mark_pane + = render 'shared/public_explain' - - if @aspect == :all - = status.submit t('.share'), :title => "Share with all aspects" - - else - = status.submit t('.share'), :title => "Share with #{@aspect}" + - if aspect == :all + = status.submit t('.share'), :title => "Share with all aspects" + - else + = status.submit t('.share'), :title => "Share with #{aspect}" + + - else + = form_for Album.new do |album| + = album.error_messages + %p + = album.label :name + = album.text_field :name + + = album.hidden_field :to, :value => aspect + + .options_and_submit + = album.submit "Create", :class => 'button' diff --git a/app/views/shared/_sub_header.haml b/app/views/shared/_sub_header.haml deleted file mode 100644 index 6f4032c1e..000000000 --- a/app/views/shared/_sub_header.haml +++ /dev/null @@ -1,20 +0,0 @@ -#aspect_header - .container - .span-4.last - - if @person - %h2 - = @person.real_name - - else - %h2 - - if @aspect == :all - = link_to "Everyone", root_path - - elsif @aspect == :manage - = link_to t('.manage_aspects'), root_path - - else - = link_to @aspect.name, @aspect - - .page_title - = yield :page_title - - .span-15.last{ :style => "position:relative;" } - = yield :publish diff --git a/app/views/status_messages/show.html.haml b/app/views/status_messages/show.html.haml index 398b3a536..1e0f15438 100644 --- a/app/views/status_messages/show.html.haml +++ b/app/views/status_messages/show.html.haml @@ -3,17 +3,11 @@ -# the COPYRIGHT file. -%h2 - = @aspect - .friend_pictures.horizontal - - for friend in @friends - = person_image_link(friend) += render 'shared/author_info', :post => @status_message .span-14.append-1.last #stream %h1.show_text - = person_image_link(@status_message.person) - = link_to @status_message.person.real_name, @status_message.person = make_links(@status_message.message) = "Posted #{how_long_ago(@status_message)} to" diff --git a/config/initializers/mailer_config.rb b/config/initializers/mailer_config.rb index 73928f740..cbc5f7330 100644 --- a/config/initializers/mailer_config.rb +++ b/config/initializers/mailer_config.rb @@ -3,15 +3,17 @@ # the COPYRIGHT file. Diaspora::Application.configure do - config.action_mailer.delivery_method = :smtp config.action_mailer.default_url_options = {:host => APP_CONFIG[:terse_pod_url]} - config.action_mailer.smtp_settings = { - :address => APP_CONFIG[:smtp_address], - :port => APP_CONFIG[:smtp_port], - :domain => APP_CONFIG[:smtp_domain], - :authentication => APP_CONFIG[:smtp_authentication], - :user_name => APP_CONFIG[:smtp_username], - :password => APP_CONFIG[:smtp_password], - :enable_starttls_auto => true - } + unless Rails.env == 'test' + config.action_mailer.delivery_method = :smtp + config.action_mailer.smtp_settings = { + :address => APP_CONFIG[:smtp_address], + :port => APP_CONFIG[:smtp_port], + :domain => APP_CONFIG[:smtp_domain], + :authentication => APP_CONFIG[:smtp_authentication], + :user_name => APP_CONFIG[:smtp_username], + :password => APP_CONFIG[:smtp_password], + :enable_starttls_auto => true + } + end end diff --git a/config/initializers/setup_mail.rb b/config/initializers/setup_mail.rb new file mode 100644 index 000000000..0d3f958a7 --- /dev/null +++ b/config/initializers/setup_mail.rb @@ -0,0 +1,2 @@ +# if you wish to intercept emails to go to a particuar email address +#ActionMailer::Base.register_interceptor(DevelopmentMailInterceptor) if Rails.env.development? diff --git a/db/seeds/backer.rb b/db/seeds/backer.rb index 877ffc13f..269772f51 100644 --- a/db/seeds/backer.rb +++ b/db/seeds/backer.rb @@ -13,7 +13,8 @@ def create #set pod url username = backer_info[backer_number]['username'].gsub(/ /,'').downcase - set_app_config username + set_app_config username unless File.exists?(Rails.root.join('config', 'app_config.yml')) + require File.join(File.dirname(__FILE__), "..", "..", "config", "initializers", "_load_app_config.rb") # Create seed user diff --git a/db/seeds/dev.rb b/db/seeds/dev.rb index 33f773613..324cf47bd 100644 --- a/db/seeds/dev.rb +++ b/db/seeds/dev.rb @@ -15,7 +15,9 @@ def set_app_config username end username = "tom" -set_app_config username +set_app_config username unless File.exists?(Rails.root.join('config', 'app_config.yml')) + +require Rails.root.join('config', "initializers", "_load_app_config.rb") # Create seed user user = User.build( :email => "tom@tom.joindiaspora.com", diff --git a/db/seeds/tom.rb b/db/seeds/tom.rb index f4c453d81..fe45e2dd0 100644 --- a/db/seeds/tom.rb +++ b/db/seeds/tom.rb @@ -14,7 +14,8 @@ def set_app_config username file.close end -set_app_config "tom" +set_app_config "tom" unless File.exists?(Rails.root.join('config', 'app_config.yml')) + require 'config/initializers/_load_app_config.rb' # Create seed user diff --git a/lib/development_mail_interceptor.rb b/lib/development_mail_interceptor.rb new file mode 100644 index 000000000..efc89596f --- /dev/null +++ b/lib/development_mail_interceptor.rb @@ -0,0 +1,7 @@ +class DevelopmentMailInterceptor + def self.delivering_email(message) + message.subject = "[#{message.to}] #{message.subject}" + message.to = "email@joindiaspora.com" + end +end + diff --git a/lib/diaspora/user/friending.rb b/lib/diaspora/user/friending.rb index 6946910f2..4a19ffd68 100644 --- a/lib/diaspora/user/friending.rb +++ b/lib/diaspora/user/friending.rb @@ -63,19 +63,23 @@ module Diaspora def receive_friend_request(friend_request) Rails.logger.info("receiving friend request #{friend_request.to_json}") + + #response from a friend request you sent if request_from_me?(friend_request) && self.aspect_by_id(friend_request.aspect_id) aspect = self.aspect_by_id(friend_request.aspect_id) activate_friend(friend_request.person, aspect) Rails.logger.info("#{self.real_name}'s friend request has been accepted") - friend_request.destroy + Notifier.request_accepted(self, friend_request.person, aspect).deliver + #this is a new friend request else self.pending_requests << friend_request self.save Rails.logger.info("#{self.real_name} has received a friend request") friend_request.save + Notifier.new_request(self, friend_request.person).deliver end end diff --git a/public/images/user/default.jpg b/public/images/user/default.jpg deleted file mode 100644 index b242c1e1c..000000000 Binary files a/public/images/user/default.jpg and /dev/null differ diff --git a/public/javascripts/aspect-edit.js b/public/javascripts/aspect-edit.js index 825433e0e..9e565e4b2 100644 --- a/public/javascripts/aspect-edit.js +++ b/public/javascripts/aspect-edit.js @@ -4,9 +4,9 @@ */ function decrementRequestsCounter() { - var $new_requests = $(".new_requests"), - request_html = $new_requests.html(), - old_request_count = request_html.match(/\d+/); + var $new_requests = $(".new_requests"); + var request_html = $new_requests.html(); + var old_request_count = request_html.match(/\d+/); if( old_request_count == 1 ) { $new_requests.html( @@ -19,133 +19,142 @@ function decrementRequestsCounter() { } } +// Dragging person between aspects $(function() { - // Multiple classes here won't work $("ul .person").draggable({ - revert: true + revert: true, + start: function(event,ui){ + $(this).children("img").animate({'height':80, 'width':80, 'opacity':0.8},200); + $(".draggable_info").fadeIn(100); + }, + stop: function(event,ui){ + $(this).children("img").animate({'height':70, 'width':70, 'opacity':1},200); + $(".draggable_info").fadeOut(100); + } }); - $("ul .requested_person").draggable({ - revert: true - }); - - $(".aspect ul").droppable({ + $(".aspect ul.dropzone").droppable({ hoverClass: 'active', drop: function(event, ui) { - if ($(ui.draggable[0]).hasClass('requested_person')){ + var dropzone = $(this); + var person = ui.draggable; + + if( person.hasClass('request') ){ $.ajax({ type: "DELETE", - url: "/requests/" + ui.draggable[0].getAttribute('request_id') , - data: {"accept" : true , "aspect_id" : $(this)[0].id }, + url: "/requests/" + person.attr('data-guid'), + data: {"accept" : true, "aspect_id" : dropzone.attr('data-aspect_id') }, success: function(data){ decrementRequestsCounter(); } }); - }; - var dropzone = $(this)[0]; - if ($(this)[0].id == ui.draggable[0].getAttribute('from_aspect_id')){ - ui.draggable.css('background-color','#333'); - } else { - ui.draggable.css('background-color','orange'); - $.ajax({ - url: "/aspects/move_friend/", - data: {"friend_id" : ui.draggable[0].id, - "from" : ui.draggable[0].getAttribute('from_aspect_id'), - "to" : { "to" : dropzone.id }}, - success: function(data){ - ui.draggable.attr('from_aspect_id', dropzone.id); - ui.draggable.css('background-color','#333'); - }}); + if( dropzone.attr('data-aspect_id') != person.attr('data-aspect_id' )){ + $.ajax({ + url: "/aspects/move_friend/", + data: {"friend_id" : person.attr('data-guid'), + "from" : person.attr('data-aspect_id'), + "to" : { "to" : dropzone.attr('data-aspect_id') }}, + success: function(data){ + person.attr('data-aspect_id', dropzone.attr('data-aspect_id')); + }}); } - $(this).closest("ul").append(ui.draggable); + + $(this).closest("ul").append(person); } }); - $(".remove ul").droppable({ - hoverClass: 'active', - drop: function(event, ui) { - - if ($(ui.draggable[0]).hasClass('requested_person')){ - $.ajax({ - type: "DELETE", - url: "/requests/" + ui.draggable.attr('request_id'), - success: function () { - decrementRequestsCounter(); - } - }); - - } else { - $.ajax({ - type: "DELETE", - url: "/people/" + ui.draggable.attr('id'), - success: function () { - alert("Removed Friend, proably want an undo countdown.") - } - }); - - } - - $(ui.draggable[0]).fadeOut('slow'); - $(ui.draggable[0]).remove(); - } - }); $(".aspect_remove ul").droppable({ hoverClass: 'active', drop: function(event, ui) { - if ($( "." + ui.draggable[0].id).length == 1) { + + var person = ui.draggable; + + if ( person.attr('data-guid').length == 1 ) { alert("You can not remove the person from the last aspect"); + } else { - if (!$(ui.draggable[0]).hasClass('requested_person')){ - var aspect = ui.draggable[0].getAttribute('from_aspect_id') - var person_id = ui.draggable[0].id + if( !person.hasClass('request') ){ + $.ajax({ type: "POST", url: "/aspects/remove_from_aspect", data:{ - 'friend_id' : person_id, - 'aspect_id' : aspect - } + 'friend_id' : person.attr('data-guid'), + 'aspect_id' : person.attr('data-aspect_id') } }); } - $(ui.draggable[0]).fadeOut('slow'); - $(ui.draggable[0]).remove(); - - + person.fadeOut('slow', $(this).remove()); } - - } }); - $(".aspect h3").live( 'focus', function() { +}); - var $this = $(this), - id = $this.closest("li").children("ul").attr("id"), - link = "/aspects/"+ id; - $this.keypress(function(e) { - if (e.which == 13) { - e.preventDefault(); - $this.blur(); +// Person deletion +$(".delete").live("click", function() { - //save changes - $.ajax({ - type: "PUT", - url: link, - data: {"aspect" : {"name" : $this.text() }} - }); - } - //update all other aspect links - $this.keyup(function(e) { - $("#aspect_nav a[href='"+link+"']").text($this.text()); + var person = $(this).closest("li.person"); + + if (person.hasClass('request')){ + + if( confirm("Ignore request?") ){ + var request_id = person.attr("data-guid"); + + $.ajax({ + type: "DELETE", + url: "/requests/" + request_id, + success: function () { + decrementRequestsCounter(); + } }); + } + + } else { + + if( confirm("Remove this person from all aspects?") ){ + var person_id = $(this).closest("li.person").attr('data-guid'); + + $.ajax({ + type: "DELETE", + url: "/people/" + person_id, + success: function() { + person.fadeOut(200); + } + }); + } + } +}); + + +// Editing aspect name +$(".aspect h3").live('focus', function() { + + var $this = $(this); + var id = $this.closest("li.aspect").attr("data-guid"); + var link = "/aspects/"+ id; + + $this.keypress(function(e) { + if (e.which == 13) { + e.preventDefault(); + $this.blur(); + + //save changes + $.ajax({ + type: "PUT", + url: link, + data: {"aspect" : {"name" : $this.text() }} + }); + } + //update all other aspect links + $this.keyup(function(e) { + $("#aspect_nav a[href='"+link+"']").text($this.text()); }); }); - }); diff --git a/public/javascripts/view.js b/public/javascripts/view.js index 7e809f978..71fefc347 100644 --- a/public/javascripts/view.js +++ b/public/javascripts/view.js @@ -30,6 +30,7 @@ $(document).ready(function(){ $("#add_request_button").fancybox({ 'titleShow': false , 'hideOnOverlayClick' : false }); $(".invite_user_button").fancybox({ 'titleShow': false , 'hideOnOverlayClick' : false }); $(".add_request_button").fancybox({ 'titleShow': false , 'hideOnOverlayClick' : false }); + $(".remove_person_button").fancybox({ 'titleShow': false , 'hideOnOverlayClick' : false }); $(".question_mark").fancybox({ 'titleShow': false , 'hideOnOverlayClick' : false }); $("input[type='submit']").addClass("button"); diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index bffe9e526..db0b32edc 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -202,6 +202,71 @@ header .avatar :border-radius 5px + +.from + :font + :family 'Helvetica neue', Arial, Helvetica, sans-serif + :text + :shadow 0 1px #fff + + .aspect + :cursor default + :display inline + :color #bbb + :font + :size 85% + a + :font + :weight normal + :color #bbb + + &:hover + :text + :decoration underline + &:active + :color #999 + ul + :display inline + :margin 0 + :padding 0 + :list + :style none + li + :display inline + &:after + :content "," + &:last-child:after + :content "" + + a + :font + :weight bold + +#author_info + :position relative + :padding + :left 65px + :height 70px + + img + :position absolute + :top 8px + :left 0 + + h2 + :margin + :bottom -2px + a + :font + :weight normal + + .from + :font + :size 14px + + .avatar + :border-radius 5px + li.message :position relative :line-height 19px @@ -220,44 +285,6 @@ li.message :padding :left 65px - .from - :font - :family 'Helvetica neue', Arial, Helvetica, sans-serif - :text - :shadow 0 1px #fff - - .aspect - :cursor default - :display inline - :color #bbb - :font - :size 12px - a - :font - :weight normal - :color #bbb - - &:hover - :text - :decoration underline - &:active - :color #999 - ul - :display inline - :margin 0 - :padding 0 - :list - :style none - li - :display inline - &:after - :content "," - &:last-child:after - :content "" - - a - :font - :weight bold :color #444 :font @@ -639,8 +666,11 @@ label :position relative textarea - :width 515px :height 42px + + input[type='text'], + textarea + :width 515px :margin 0 .options_and_submit @@ -873,13 +903,7 @@ h1.big_text :padding 2px -.aspect, -.requests, -.remove, -.aspect_remove - :list - :style none - +.aspect h3 :display inline-block @@ -914,63 +938,115 @@ h1.big_text &:last-child :margin :right 0 - - .grey - :color #999 - :cursor default - :text-shadow 0 1px #fff +.aspect, +.requests, +.aspect_remove + :list + :style none + :color #999 + :cursor default + :text-shadow 0 1px #fff ul.dropzone + :position relative :min-height 20px :margin 0 :bottom 25px - :background - :color #efefef - :border 1px solid #ccc + + :-webkit-border-radius 10px + :-moz-border-radius 10px + :border-radius 10px + :list :style none :padding 15px + :border 2px dashed #ccc &.active :background - :color #fafafa + :color rgba(255,252,127,0.2) - .person, - .requested_person + .draggable_info + :position absolute + :display none + :right 15px + :bottom 10px + :font + :style italic + :size 14px + :color #aaa + + .person :display inline-block - :padding 5px :cursor move - :margin 5px :z-index 10 + :position relative + :padding 0 + :margin 5px - :width 110px - - :background - :color #333 - - :border-radius 5px - :color #ccc - - a - :color #ccc + :color #eee img - :height 40px - :width 40px - :display inline + :height 70px + :width 70px + :border-radius 5px + :-webkit-box-shadow 0 1px 2px #999 - .name - :display inline - :top 0 - :margin - :left 5px + &:hover + .delete + :display inline &:active :z-index 20 - :color #666 - :-webkit-box-shadow 0 1px 3px #000 - :-moz-box-shadow 0 2px 4px #000 - :opacity 0.9 + img + :-webkit-box-shadow 0 1px 3px #000 + :-moz-box-shadow 0 2px 4px #000 + + .delete + :display none + + .delete + :display none + + :position absolute + :top -8px + :left -8px + + .circle + :z-index 1 + :position absolute + :background + :color #333 + + :width 20px + :max-width 20px + :height 20px + :max-height 20px + + :border 1px solid #fff + + :-webkit-border-radius 20px + :-moz-border-radius 20px + :border-radius 20px + + :-webkit-box-shadow 0 1px 3px #000 + + .x + :z-index 2 + :position absolute + :top 2px + :left 7px + + &:hover + :cursor default + .circle + :background + :color rgba(208,49,43,1) + + +.requests + ul.dropzone + :border 2px solid #ccc ul#settings_nav :display inline @@ -1090,6 +1166,10 @@ header h2 :display inline + + .right + :margin + :top 10px .modal_title_bar :width 100% @@ -1136,3 +1216,27 @@ header .controls :display inline +ul#breadcrumb + :list + :style none + :margin 0 + :padding 0 + :font + :size 14px + :weight bold + :line + :height 3em + + a + :font + :weight bold + + > li + :display inline + + &:after + :content ' ››' + + &:last-child + &:after + :content '' diff --git a/spec/controllers/publics_controller_spec.rb b/spec/controllers/publics_controller_spec.rb index cf1a71d88..11a35da95 100644 --- a/spec/controllers/publics_controller_spec.rb +++ b/spec/controllers/publics_controller_spec.rb @@ -6,10 +6,10 @@ require 'spec_helper' describe PublicsController do render_views - let(:user) {Factory.create :user} - let(:user2){Factory.create :user} - let(:aspect1){user.aspect(:name => "foo")} - let(:aspect2){user2.aspect(:name => "far")} + let(:user) { Factory.create :user } + let(:user2) { Factory.create :user } + let(:aspect1) { user.aspect(:name => "foo") } + let(:aspect2) { user2.aspect(:name => "far") } before do sign_in :user, user end @@ -23,7 +23,7 @@ describe PublicsController do it 'should accept a post from another node and save the information' do message = user2.build_post(:status_message, :message => "hi") friend_users(user, aspect1, user2, aspect2) - + user.reload user.visible_post_ids.include?(message.id).should be false @@ -77,10 +77,13 @@ describe PublicsController do end describe 'friend requests' do - let(:aspect2) {user2.aspect(:name => 'disciples')} - let!(:req) {user2.send_friend_request_to(user.person, aspect2)} - let!(:xml) {user2.salmon(req).xml_for(user.person)} + let(:aspect2) { user2.aspect(:name => 'disciples') } + let!(:req) { user2.send_friend_request_to(user.person, aspect2) } + let!(:xml) { user2.salmon(req).xml_for(user.person) } before do + deliverable = Object.new + deliverable.stub!(:deliver) + Notifier.stub!(:new_request).and_return(deliverable) req.delete user2.reload user2.pending_requests.count.should be 1 diff --git a/spec/factories.rb b/spec/factories.rb index c66fa4f86..8d0b76aee 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -8,14 +8,15 @@ #This inclsion, because gpg-agent(not needed) is never run and hence never sets any env. variables on a MAC Factory.define :profile do |p| - p.first_name "Robert" - p.last_name "Grimm" + p.sequence(:first_name){|n| "Robert#{n}"} + p.sequence(:last_name){|n| "Grimm#{n}"} end + Factory.define :person do |p| p.sequence(:diaspora_handle) {|n| "bob-person-#{n}@aol.com"} p.sequence(:url) {|n| "http://google-#{n}.com/"} - p.profile Factory.create(:profile) + p.profile Factory.create(:profile, :first_name => "eugene", :last_name => "weinstien") p.serialized_public_key OpenSSL::PKey::RSA.generate(1024).public_key.export end @@ -32,7 +33,7 @@ Factory.define :user do |u| u.password_confirmation "bluepin7" u.serialized_private_key OpenSSL::PKey::RSA.generate(1024).export u.after_build do |user| - user.person = Factory.build(:person, :owner_id => user._id, + user.person = Factory.build(:person, :profile => Factory.create(:profile), :owner_id => user._id, :serialized_public_key => user.encryption_key.public_key.export, :diaspora_handle => "#{user.username}@#{APP_CONFIG[:pod_url].gsub(/(https?:|www\.)\/\//, '').chop!}") end diff --git a/spec/lib/exporter_spec.rb b/spec/lib/diaspora/exporter_spec.rb similarity index 100% rename from spec/lib/exporter_spec.rb rename to spec/lib/diaspora/exporter_spec.rb diff --git a/spec/lib/importer_spec.rb b/spec/lib/diaspora/importer_spec.rb similarity index 100% rename from spec/lib/importer_spec.rb rename to spec/lib/diaspora/importer_spec.rb diff --git a/spec/lib/ostatus_builder_spec.rb b/spec/lib/diaspora/ostatus_builder.rb similarity index 100% rename from spec/lib/ostatus_builder_spec.rb rename to spec/lib/diaspora/ostatus_builder.rb diff --git a/spec/lib/diaspora_parser_spec.rb b/spec/lib/diaspora/parser_spec.rb similarity index 60% rename from spec/lib/diaspora_parser_spec.rb rename to spec/lib/diaspora/parser_spec.rb index 72b3d96e0..f97a514c3 100644 --- a/spec/lib/diaspora_parser_spec.rb +++ b/spec/lib/diaspora/parser_spec.rb @@ -5,15 +5,15 @@ require 'spec_helper' describe Diaspora::Parser do - let(:user) {Factory.create(:user)} - let(:aspect) {user.aspect(:name => 'spies')} - let(:user2) {Factory.create(:user)} - let(:aspect2){user2.aspect(:name => "pandas")} - let(:user3) {Factory.create :user} - let(:person) {user3.person} + let(:user) { Factory.create(:user) } + let(:aspect) { user.aspect(:name => 'spies') } + let(:user2) { Factory.create(:user) } + let(:aspect2) { user2.aspect(:name => "pandas") } + let(:user3) { Factory.create :user } + let(:person) { user3.person } describe "parsing compliant XML object" do - it 'should be able to correctly handle comments with person in db' do + it 'should be able to correctly handle comments with person in db' do post = user.post :status_message, :message => "hello", :to => aspect.id comment = Factory.build(:comment, :post => post, :person => @person, :text => "Freedom!") xml = comment.to_diaspora_xml @@ -25,7 +25,7 @@ describe Diaspora::Parser do end it 'should be able to correctly handle person on a comment with person not in db' do - friend_users(user, aspect, user2, aspect2) + friend_users(user, aspect, user2, aspect2) post = user.post :status_message, :message => "hello", :to => aspect.id comment = user2.comment "Fool!", :on => post @@ -40,42 +40,50 @@ describe Diaspora::Parser do end it 'should accept retractions' do - friend_users(user, aspect, user2, aspect2) + friend_users(user, aspect, user2, aspect2) message = Factory.create(:status_message, :person => user2.person) retraction = Retraction.for(message) xml = retraction.to_diaspora_xml - proc {user.receive xml, user2.person}.should change(StatusMessage, :count).by(-1) + proc { user.receive xml, user2.person }.should change(StatusMessage, :count).by(-1) end - it "should create a new person upon getting a person request" do - request = Request.instantiate(:to =>"http://www.google.com/", :from => person) + context "friending" do + before do + deliverable = Object.new + deliverable.stub!(:deliver) + Notifier.stub!(:new_request).and_return(deliverable) + end - xml = request.to_diaspora_xml + it "should create a new person upon getting a person request" do + request = Request.instantiate(:to =>"http://www.google.com/", :from => person) - user3.destroy - person.destroy - user - lambda {user.receive xml, person}.should change(Person, :count).by(1) - end + xml = request.to_diaspora_xml - it "should not create a new person if the person is already here" do - request = Request.instantiate(:to =>"http://www.google.com/", :from => user2.person) - original_person_id = user2.person.id - xml = request.to_diaspora_xml - user - lambda {user.receive xml, user2.person}.should_not change(Person, :count) + user3.destroy + person.destroy + user + lambda { user.receive xml, person }.should change(Person, :count).by(1) + end - user2.reload - user2.person.reload - user2.serialized_private_key.include?("PRIVATE").should be true + it "should not create a new person if the person is already here" do + request = Request.instantiate(:to =>"http://www.google.com/", :from => user2.person) + original_person_id = user2.person.id + xml = request.to_diaspora_xml + user + lambda { user.receive xml, user2.person }.should_not change(Person, :count) - url = "http://" + request.callback_url.split("/")[2] + "/" - Person.where(:url => url).first.id.should == original_person_id + user2.reload + user2.person.reload + user2.serialized_private_key.include?("PRIVATE").should be true + + url = "http://" + request.callback_url.split("/")[2] + "/" + Person.where(:url => url).first.id.should == original_person_id + end end it "should activate the Person if I initiated a request to that url" do - request = user.send_friend_request_to( user3.person, aspect) + request = user.send_friend_request_to(user3.person, aspect) user.reload request.reverse_for user3 @@ -95,16 +103,16 @@ describe Diaspora::Parser do end it 'should process retraction for a person' do - friend_users(user, aspect, user2, aspect2) + friend_users(user, aspect, user2, aspect2) retraction = Retraction.for(user2) retraction_xml = retraction.to_diaspora_xml - lambda {user.receive retraction_xml, user2.person}.should change{ - aspect.reload.people.size}.by(-1) + lambda { user.receive retraction_xml, user2.person }.should change { + aspect.reload.people.size }.by(-1) end it 'should marshal a profile for a person' do - friend_users(user, aspect, user2, aspect2) + friend_users(user, aspect, user2, aspect2) #Create person person = user2.person id = person.id @@ -132,9 +140,9 @@ describe Diaspora::Parser do person = Person.first(:id => person.id) person.profile.should_not be nil person.profile.first_name.should == old_profile.first_name - person.profile.last_name.should == old_profile.last_name - person.profile.image_url.should == old_profile.image_url - end + person.profile.last_name.should == old_profile.last_name + person.profile.image_url.should == old_profile.image_url + end end end diff --git a/spec/lib/encryptor_spec.rb b/spec/lib/encryptor_spec.rb new file mode 100644 index 000000000..e993dac45 --- /dev/null +++ b/spec/lib/encryptor_spec.rb @@ -0,0 +1,54 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require 'spec_helper' + +describe 'user encryption' do + before do + @user = Factory.create(:user) + @aspect = @user.aspect(:name => 'dudes') + end + + describe 'key exchange on friending' do + + it 'should receive and marshal a public key from a request' do + remote_user = Factory.build(:user) + remote_user.encryption_key.nil?.should== false + + deliverable = Object.new + deliverable.stub!(:deliver) + Notifier.stub!(:new_request).and_return(deliverable) + Person.should_receive(:by_webfinger).and_return(remote_user.person) + #should move this to friend request, but i found it here + id = remote_user.person.id + original_key = remote_user.exported_key + + request = remote_user.send_friend_request_to( + @user.person, remote_user.aspect(:name => "temp")) + + xml = remote_user.salmon(request).xml_for(@user) + + remote_user.person.delete + remote_user.delete + + person_count = Person.all.count + @user.receive_salmon xml + + Person.all.count.should == person_count + 1 + new_person = Person.first(:id => id) + new_person.exported_key.should == original_key + end + end + + describe 'encryption' do + before do + @string = File.open(File.dirname(__FILE__) + '/../fixtures/fb_status').read + end + it 'should encrypt a string' do + ciphertext = @user.encrypt @string + ciphertext.include?(@string).should be false + @user.decrypt(ciphertext).should == @string + end + end +end diff --git a/spec/mailers/notifier_spec.rb b/spec/mailers/notifier_spec.rb new file mode 100644 index 000000000..88550680f --- /dev/null +++ b/spec/mailers/notifier_spec.rb @@ -0,0 +1,46 @@ + +require 'spec_helper' + +describe Notifier do + + let!(:user) {Factory.create :user} + let!(:aspect) {user.aspect(:name => "science")} + let!(:person) {Factory.create :person} + let!(:request_mail) {Notifier.new_request(user, person)} + let!(:request_accepted_mail) {Notifier.request_accepted(user, person, aspect)} + + + describe "#new_request" do + it 'goes to the right person' do + request_mail.to.should == [user.email] + end + + it 'has the receivers name in the body' do + request_mail.body.encoded.include?(user.person.profile.first_name).should be true + end + + + it 'has the name of person sending the request' do + request_mail.body.encoded.include?(person.real_name).should be true + end + end + + describe "#request_accpeted" do + it 'goes to the right person' do + request_accepted_mail.to.should == [user.email] + end + + it 'has the receivers name in the body' do + request_accepted_mail.body.encoded.include?(user.person.profile.first_name).should be true + end + + + it 'has the name of person sending the request' do + request_accepted_mail.body.encoded.include?(person.real_name).should be true + end + + it 'has the name of the aspect in the body' do + request_accepted_mail.body.encoded.include?(aspect.name).should be true + end + end +end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index 1cd8e9b9d..66056789d 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -97,6 +97,10 @@ describe Comment do user.receive comment.to_diaspora_xml, user2.person end + context 'posts from a remote person' do + before(:all) do + stub_comment_signature_verification + end it 'should not send a comment a person made on his own post to anyone' do User::QUEUE.should_not_receive(:add_post_request) comment = Comment.new(:person_id => @person.id, :text => "balls", :post => @person_status) @@ -108,6 +112,10 @@ describe Comment do comment = Comment.new(:person_id => @person2.id, :text => "balls", :post => @person_status) user.receive comment.to_diaspora_xml, @person end + after(:all) do + unstub_mocha_stubs + end + end it 'should not clear the aspect post array on receiving a comment' do aspect.post_ids.include?(@user_status.id).should be true @@ -130,4 +138,50 @@ describe Comment do comment.to_diaspora_xml.include?(commenter.person.id.to_s).should be true end end + + describe 'comments' do + before do + friend_users(user, aspect, user2, aspect2) + @remote_message = user2.post :status_message, :message => "hello", :to => aspect2.id + + + @message = user.post :status_message, :message => "hi", :to => aspect.id + end + it 'should attach the creator signature if the user is commenting' do + user.comment "Yeah, it was great", :on => @remote_message + @remote_message.comments.first.signature_valid?.should be true + end + + it 'should sign the comment if the user is the post creator' do + message = user.post :status_message, :message => "hi", :to => aspect.id + user.comment "Yeah, it was great", :on => message + 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 friend' do + comment = Comment.new(:person => user2.person, :text => "cats", :post => @remote_message) + comment.creator_signature = comment.send(:sign_with_key,user2.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,user.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 => user2.person, :text => "cats", :post => @remote_message) + comment.creator_signature = comment.send(:sign_with_key,user2.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 => user2.person, :text => "cats", :post => @message) + comment.creator_signature = comment.send(:sign_with_key,user2.encryption_key) + comment.signature_valid?.should be true + comment.verify_post_creator_signature.should be false + end + + end + end diff --git a/spec/models/person_spec.rb b/spec/models/person_spec.rb index 4aae4daad..fa89610af 100644 --- a/spec/models/person_spec.rb +++ b/spec/models/person_spec.rb @@ -187,6 +187,25 @@ describe Person do end end + + it 'should only find people who are exact matches' do + user = Factory(:user, :username => "SaMaNtHa") + person = Factory(:person, :diaspora_handle => "tomtom@tom.joindiaspora.com") + user.person.diaspora_handle = "tom@tom.joindiaspora.com" + user.person.save + Person.by_webfinger("tom@tom.joindiaspora.com").diaspora_handle.should == "tom@tom.joindiaspora.com" + end + + it 'should return nil if there is not an exact match' do + Redfinger.stub!(:finger).and_return(nil) + + person = Factory(:person, :diaspora_handle => "tomtom@tom.joindiaspora.com") + person1 = Factory(:person, :diaspora_handle => "tom@tom.joindiaspora.comm") + #Person.by_webfinger("tom@tom.joindiaspora.com").should_be false + proc{ Person.by_webfinger("tom@tom.joindiaspora.com")}.should raise_error + end + + it 'identifier should be a valid email' do stub_success("joe.valid+email@my-address.com") Proc.new { diff --git a/spec/models/request_spec.rb b/spec/models/request_spec.rb index a7d242ee7..6483c45fa 100644 --- a/spec/models/request_spec.rb +++ b/spec/models/request_spec.rb @@ -28,6 +28,7 @@ describe Request do xml.should include user.person.url xml.should include user.profile.first_name xml.should include user.profile.last_name + xml.should include user.exported_key end it 'should allow me to see only friend requests sent to me' do @@ -56,6 +57,10 @@ describe Request do end it 'recognized when a request is not from me' do + deliverable = Object.new + deliverable.stub!(:deliver) + Notifier.stub!(:new_request).and_return(deliverable) + user2.receive_salmon(user.salmon(request).xml_for(user2.person)) user2.reload user2.request_from_me?(request).should == false @@ -64,6 +69,10 @@ describe Request do context 'quering request through user' do it 'finds requests for that user' do + deliverable = Object.new + deliverable.stub!(:deliver) + Notifier.stub!(:new_request).and_return(deliverable) + user2.receive_salmon(user.salmon(request).xml_for(user2.person)) user2.reload user2.requests_for_me.include?(request).should == true diff --git a/spec/models/status_message_spec.rb b/spec/models/status_message_spec.rb index 8449b964c..086d66e80 100644 --- a/spec/models/status_message_spec.rb +++ b/spec/models/status_message_spec.rb @@ -21,6 +21,15 @@ describe StatusMessage do status = @user.post(:status_message, :message => "Users do things", :to => @aspect.id) end + it 'should require status messages to be less than 1000 characters' do + message = '' + 1001.times do message = message +'1';end + status = Factory.build(:status_message, :message => message) + + status.should_not be_valid + + end + describe "XML" do it 'should serialize to XML' do message = Factory.create(:status_message, :message => "I hate WALRUSES!", :person => @user.person) diff --git a/spec/models/user/attack_vectors_spec.rb b/spec/models/user/attack_vectors_spec.rb index 67235f447..36a586647 100644 --- a/spec/models/user/attack_vectors_spec.rb +++ b/spec/models/user/attack_vectors_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' -describe User do +describe "attack vectors" do let(:user) { Factory(:user) } let(:aspect) { user.aspect(:name => 'heroes') } @@ -73,10 +73,11 @@ describe User do profile.first_name = "Not BOB" user2.reload - user2.profile.first_name.should == "Robert" + + first_name = user2.profile.first_name proc{user.receive_salmon(user3.salmon(profile).xml_for(user.person))}.should raise_error /Malicious Post/ user2.reload - user2.profile.first_name.should == "Robert" + user2.profile.first_name.should == first_name end it 'should not overwrite another persons profile through comment' do diff --git a/spec/models/user/receive_spec.rb b/spec/models/user/receive_spec.rb index 07b481b17..ab83b8c5d 100644 --- a/spec/models/user/receive_spec.rb +++ b/spec/models/user/receive_spec.rb @@ -115,6 +115,7 @@ describe User do comment_id = comment.id comment.delete + comment.post_creator_signature = comment.sign_with_key(user.encryption_key) user3.receive comment.to_diaspora_xml, user.person user3.reload diff --git a/spec/models/user/user_friending_spec.rb b/spec/models/user/user_friending_spec.rb index 2fde487b3..63a472031 100644 --- a/spec/models/user/user_friending_spec.rb +++ b/spec/models/user/user_friending_spec.rb @@ -1,20 +1,28 @@ + # Copyright (c) 2010, Diaspora Inc. This file is # licensed under the Affero General Public License version 3 or later. See # the COPYRIGHT file. require 'spec_helper' -describe User do - let(:user) {Factory.create :user} - let(:aspect) {user.aspect(:name => 'heroes')} - let(:aspect1) {user.aspect(:name => 'other')} +describe Diaspora::UserModules::Friending do + let(:user) { Factory.create :user } + let(:aspect) { user.aspect(:name => 'heroes') } + let(:aspect1) { user.aspect(:name => 'other') } let(:friend) { Factory.create(:person) } - let(:person_one) {Factory.create :person} - let(:person_two) {Factory.create :person} - - let(:user2) { Factory.create :user} - let(:aspect2) { user2.aspect(:name => "aspect two")} + let(:person_one) { Factory.create :person } + let(:person_two) { Factory.create :person } + + let(:user2) { Factory.create :user } + let(:aspect2) { user2.aspect(:name => "aspect two") } + + before do + deliverable = Object.new + deliverable.stub!(:deliver) + Notifier.stub!(:new_request).and_return(deliverable) + Notifier.stub!(:request_accepted).and_return(deliverable) + end context 'friend requesting' do it "should assign a request to a aspect" do @@ -29,9 +37,9 @@ describe User do it "should be able to accept a pending friend request" do r = Request.instantiate(:to => user.receive_url, :from => friend) r.save - - proc {user.accept_friend_request(r.id, aspect.id)}.should change{ - Request.for_user(user).all.count}.by(-1) + + proc { user.accept_friend_request(r.id, aspect.id) }.should change { + Request.for_user(user).all.count }.by(-1) end it 'should be able to ignore a pending friend request' do @@ -39,8 +47,8 @@ describe User do r = Request.instantiate(:to => user.receive_url, :from => friend) r.save - proc{user.ignore_friend_request(r.id)}.should change{ - Request.for_user(user).count}.by(-1) + proc { user.ignore_friend_request(r.id) }.should change { + Request.for_user(user).count }.by(-1) end it 'should not be able to friend request an existing friend' do @@ -54,6 +62,13 @@ describe User do proc { user.send_friend_request_to(nil, aspect) }.should raise_error(RuntimeError, /befriend yourself/) end + it 'should send an email on acceptance if a friend request' do + Notifier.should_receive(:request_accepted) + request = user.send_friend_request_to(user2.person, aspect) + request.reverse_for(user2) + user.receive_friend_request(request) + end + describe 'multiple users accepting/rejecting the same person' do @@ -81,40 +96,52 @@ describe User do user2.receive @req_three_xml, user.person end it 'should befriend the user other user on the same pod' do - proc{ + proc { user2.accept_friend_request @request_three.id, aspect2.id }.should_not change(Person, :count) user2.friends.include?(user.person).should be true end it 'should not delete the ignored user on the same pod' do - proc{ + proc { user2.ignore_friend_request @request_three.id }.should_not change(Person, :count) user2.friends.include?(user.person).should be false end + + it 'sends an email to the receiving user' do + mail_obj = mock("mailer") + mail_obj.should_receive(:deliver) + Notifier.should_receive(:new_request).and_return(mail_obj) + user.receive @req_xml, person_one + end + + end context 'Two users receiving requests from one person' do before do user.receive @req_xml, person_one - user2.receive @req_two_xml, person_one end - it 'should both users should befriend the same person' do - user.accept_friend_request @request.id, aspect.id - user.friends.include?(person_one).should be true - user2.accept_friend_request @request_two.id, aspect2.id - user2.friends.include?(person_one).should be true + describe '#accept_friend_request' do + it 'should both users should befriend the same person' do + user.accept_friend_request @request.id, aspect.id + user.friends.include?(person_one).should be true + + user2.accept_friend_request @request_two.id, aspect2.id + user2.friends.include?(person_one).should be true + end + + it 'should keep the person around if one of the users rejects him' do + user.accept_friend_request @request.id, aspect.id + user.friends.include?(person_one).should be true + + user2.ignore_friend_request @request_two.id + user2.friends.include?(person_one).should be false + end end - it 'should keep the person around if one of the users rejects him' do - user.accept_friend_request @request.id, aspect.id - user.friends.include?(person_one).should be true - - user2.ignore_friend_request @request_two.id - user2.friends.include?(person_one).should be false - end it 'should keep the person around if the users ignores them' do user.ignore_friend_request user.pending_requests.first.id @@ -124,6 +151,8 @@ describe User do user2.friends.include?(person_one).should be false end end + + end describe 'a user accepting rejecting multiple people' do @@ -160,25 +189,25 @@ describe User do describe 'unfriending' do before do - friend_users(user,aspect, user2, aspect2) + friend_users(user, aspect, user2, aspect2) end it 'should unfriend the other user on the same seed' do - lambda {user2.unfriend user.person}.should change{ - user2.friends.count}.by(-1) + lambda { user2.unfriend user.person }.should change { + user2.friends.count }.by(-1) aspect2.reload.people.count.should == 0 end it 'is unfriended by another user' do - lambda {user.unfriended_by user2.person}.should change{ - user.friends.count}.by(-1) + lambda { user.unfriended_by user2.person }.should change { + user.friends.count }.by(-1) aspect.reload.people.count.should == 0 end it 'should remove the friend from all aspects they are in' do user.add_person_to_aspect(user2.person.id, aspect1.id) - lambda {user.unfriended_by user2.person}.should change{ - user.friends.count}.by(-1) + lambda { user.unfriended_by user2.person }.should change { + user.friends.count }.by(-1) aspect.reload.people.count.should == 0 aspect1.reload.people.count.should == 0 end @@ -186,9 +215,9 @@ describe User do context 'with a post' do before do @message = user.post(:status_message, :message => "hi", :to => aspect.id) - user2.receive @message.to_diaspora_xml.to_s, user.person - user2.unfriend user.person - user.unfriended_by user2.person + user2.receive @message.to_diaspora_xml.to_s, user.person + user2.unfriend user.person + user.unfriended_by user2.person end it "deletes the unfriended user's posts from visible_posts" do user.reload.raw_visible_posts.include?(@message.id).should be_false diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 4b2a4d02d..351d5347c 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -10,6 +10,10 @@ describe User do let(:user2) { Factory(:user) } let(:aspect2) { user2.aspect(:name => 'stuff') } + it 'should have a key' do + user.encryption_key.should_not be nil + end + describe "validation" do describe "of associated person" do it "fails if person is not valid" do @@ -69,6 +73,11 @@ describe User do user = Factory.build(:user, :username => "bobby tables") user.should_not be_valid end + + it 'can not contain non url safe characters' do + user = Factory.build(:user, :username => "kittens;") + user.should_not be_valid + end end describe "of email" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 218def68b..29d731b29 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -25,10 +25,6 @@ RSpec.configure do |config| DatabaseCleaner.strategy = :truncation DatabaseCleaner.orm = "mongo_mapper" - config.before(:suite) do - stub_signature_verification - end - config.before(:each) do stub_sockets DatabaseCleaner.clean @@ -49,10 +45,8 @@ ImageUploader.enable_processing = false Diaspora::WebSocket.unstub!(:unsubscribe) end - def stub_signature_verification - (get_models.map{|model| model.camelize.constantize} - [User]).each do |model| - model.any_instance.stubs(:verify_signature).returns(true) - end + def stub_comment_signature_verification + Comment.any_instance.stubs(:verify_signature).returns(true) end def unstub_mocha_stubs @@ -82,11 +76,11 @@ ImageUploader.enable_processing = false aspect2.reload end - def stub_success(address = 'abc@example.com') + def stub_success(address = 'abc@example.com', opts = {}) host = address.split('@')[1] stub_request(:get, "https://#{host}/.well-known/host-meta").to_return(:status => 200, :body => host_xrd) stub_request(:get, "http://#{host}/.well-known/host-meta").to_return(:status => 200, :body => host_xrd) - if host.include?("joindiaspora.com") + if opts[:diaspora] || host.include?("diaspora") stub_request(:get, /webfinger\/\?q=#{address}/).to_return(:status => 200, :body => finger_xrd) stub_request(:get, "http://#{host}/hcard/users/4c8eccce34b7da59ff000002").to_return(:status => 200, :body => hcard_response) else diff --git a/spec/user_encryption_spec.rb b/spec/user_encryption_spec.rb deleted file mode 100644 index 645563b9a..000000000 --- a/spec/user_encryption_spec.rb +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (c) 2010, Diaspora Inc. This file is -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. - -require 'spec_helper' - -describe 'user encryption' do - before do - unstub_mocha_stubs - @user = Factory.create(:user) - @aspect = @user.aspect(:name => 'dudes') - - @user2 = Factory.create(:user) - @aspect2 = @user2.aspect(:name => 'dudes') - end - - after do - stub_signature_verification - #gpgdir = File.expand_path("../../db/gpg-#{Rails.env}", __FILE__) - #ctx = GPGME::Ctx.new - #keys = ctx.keys - #keys.each{|k| ctx.delete_key(k, true)} - end - it 'should have a key' do - @user.encryption_key.should_not be nil - end - describe 'key exchange on friending' do - it 'should send over a public key' do - message_queue.stub!(:add_post_request) - request = @user.send_friend_request_to(Factory.create(:person), @aspect) - request.to_diaspora_xml.include?( @user.exported_key).should be true - end - - it 'should receive and marshal a public key from a request' do - remote_user = Factory.build(:user) - remote_user.encryption_key.nil?.should== false - #should move this to friend request, but i found it here - id = remote_user.person.id - original_key = remote_user.exported_key - - request = remote_user.send_friend_request_to( - @user.person, remote_user.aspect(:name => "temp")) - - xml = request.to_diaspora_xml - - remote_user.person.delete - remote_user.delete - - person_count = Person.all.count - @user.receive xml, remote_user.person - - Person.all.count.should == person_count + 1 - new_person = Person.first(:id => id) - new_person.exported_key.should == original_key - end - end - - describe 'encryption' do - before do - @message = @user.post :status_message, :message => "hi", :to => @aspect.id - end - it 'should encrypt large messages' do - ciphertext = @user.encrypt @message.to_diaspora_xml - ciphertext.include?(@message.to_diaspora_xml).should be false - @user.decrypt(ciphertext).include?(@message.to_diaspora_xml).should be true - end - end - - describe 'comments' do - before do - friend_users(@user, @aspect, @user2, @aspect2) - @remote_message = @user2.post :status_message, :message => "hello", :to => @aspect2.id - - - @message = @user.post :status_message, :message => "hi", :to => @aspect.id - end - it 'should attach the creator signature if the user is commenting' do - @user.comment "Yeah, it was great", :on => @remote_message - @remote_message.comments.first.signature_valid?.should be true - end - - it 'should sign the comment if the user is the post creator' do - message = @user.post :status_message, :message => "hi", :to => @aspect.id - @user.comment "Yeah, it was great", :on => message - 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 friend' do - comment = Comment.new(:person => @user2.person, :text => "cats", :post => @remote_message) - comment.creator_signature = comment.send(:sign_with_key,@user2.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,@user.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 => @user2.person, :text => "cats", :post => @remote_message) - comment.creator_signature = comment.send(:sign_with_key,@user2.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 => @user2.person, :text => "cats", :post => @message) - comment.creator_signature = comment.send(:sign_with_key,@user2.encryption_key) - comment.signature_valid?.should be true - comment.verify_post_creator_signature.should be false - end - - end -end