Merge remote branch 'upstream/master'

This commit is contained in:
Alec Leamas 2010-10-23 05:14:10 +02:00
commit 1a49f3fb7d
50 changed files with 917 additions and 524 deletions

View file

@ -57,12 +57,16 @@ 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)
end

22
app/mailers/notifier.rb Normal file
View file

@ -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

View file

@ -97,7 +97,7 @@ 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

View file

@ -4,6 +4,7 @@
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

View file

@ -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

View file

@ -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
= form_for Album.new do |album|
= album.error_messages
%p
= f.label :name
= f.text_field :name
= f.hidden_field :to, :value => aspect
= f.submit t('.create'), :class => 'button'
= album.label :name
= album.hidden_field :to, :value => aspect
= album.text_field :name
= album.submit t('.create'), :class => 'button'

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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}
-if aspect.people.size < 1
%li.grey Drag to add people
-else
%ul.dropzone{:data=>{:aspect_id=>aspect.id}}
-for person in aspect.people
%li.person{:id => person.id, :class => person.id, :from_aspect_id => aspect.id}
%li.person{:data=>{:guid=>person.id, :aspect_id=>aspect.id}}
.delete
.x
X
.circle
= person_image_tag(person)
.name
= link_to person.real_name, person
.draggable_info
Drag to add people
.fancybox_content
%div{:id => "add_request_pane_#{aspect.id}"}
= render "requests/new_request", :aspect => aspect

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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'
|

View file

@ -36,13 +36,9 @@
<dl class="entity_photo">
<dt>Photo</dt>
<dd>
<img class="photo avatar" src="<%= @person.profile.image_url%>" width="100" height="100"/>
<img class="photo avatar" src="<%= image_or_default(@person)%>" width="100" height="100"/>
</dd>
</dl>
<dl class="entity_note">
<dt>Note</dt>
<dd class="note">Diaspora is awesome! vi is better than emacs!</dd>
</dl>
</div>
</div>
</div>

View file

@ -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)

View file

@ -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,17 +20,18 @@
#publisher
= owner_image_tag
- 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.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
- if aspect == :all
.public_toggle
= status.check_box( :public, :value => false )
make public
@ -40,8 +41,20 @@
#question_mark_pane
= render 'shared/public_explain'
- if @aspect == :all
- if aspect == :all
= status.submit t('.share'), :title => "Share with all aspects"
- else
= status.submit t('.share'), :title => "Share with #{@aspect}"
= 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'

View file

@ -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

View file

@ -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"

View file

@ -3,8 +3,9 @@
# 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]}
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],
@ -15,3 +16,4 @@ Diaspora::Application.configure do
:enable_starttls_auto => true
}
end
end

View file

@ -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?

View file

@ -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

View file

@ -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",

View file

@ -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

View file

@ -0,0 +1,7 @@
class DevelopmentMailInterceptor
def self.delivering_email(message)
message.subject = "[#{message.to}] #{message.subject}"
message.to = "email@joindiaspora.com"
end
end

View file

@ -64,18 +64,22 @@ 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

View file

@ -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,115 +19,126 @@ 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');
if( dropzone.attr('data-aspect_id') != person.attr('data-aspect_id' )){
$.ajax({
url: "/aspects/move_friend/",
data: {"friend_id" : ui.draggable[0].id,
"from" : ui.draggable[0].getAttribute('from_aspect_id'),
"to" : { "to" : dropzone.id }},
data: {"friend_id" : person.attr('data-guid'),
"from" : person.attr('data-aspect_id'),
"to" : { "to" : dropzone.attr('data-aspect_id') }},
success: function(data){
ui.draggable.attr('from_aspect_id', dropzone.id);
ui.draggable.css('background-color','#333');
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());
}
}
});
});
// Person deletion
$(".delete").live("click", function() {
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),
id = $this.closest("li").children("ul").attr("id"),
link = "/aspects/"+ id;
var $this = $(this);
var id = $this.closest("li.aspect").attr("data-guid");
var link = "/aspects/"+ id;
$this.keypress(function(e) {
if (e.which == 13) {
@ -147,5 +158,3 @@ $(function() {
});
});
});
});

View file

@ -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");

View file

@ -202,23 +202,6 @@ header
.avatar
:border-radius 5px
li.message
:position relative
:line-height 19px
:font
:family 'Arial', 'Helvetica', sans-serif
:color #777
.avatar
:float left
:margin
:right 15px
.content
:margin
:top -4px
:padding
:left 65px
.from
:font
@ -231,7 +214,7 @@ li.message
:display inline
:color #bbb
:font
:size 12px
:size 85%
a
:font
:weight normal
@ -259,6 +242,50 @@ li.message
: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
:font
:family 'Arial', 'Helvetica', sans-serif
:color #777
.avatar
:float left
:margin
:right 15px
.content
:margin
:top -4px
:padding
:left 65px
:color #444
:font
:weight normal
@ -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
.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
&:hover
.delete
:display inline
:top 0
:margin
:left 5px
&:active
:z-index 20
:color #666
img
:-webkit-box-shadow 0 1px 3px #000
:-moz-box-shadow 0 2px 4px #000
:opacity 0.9
.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
@ -1091,6 +1167,10 @@ header
h2
:display inline
.right
:margin
:top 10px
.modal_title_bar
:width 100%
:background
@ -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 ''

View file

@ -81,6 +81,9 @@ describe PublicsController do
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

View file

@ -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

View file

@ -48,6 +48,13 @@ describe Diaspora::Parser do
proc { user.receive xml, user2.person }.should change(StatusMessage, :count).by(-1)
end
context "friending" do
before do
deliverable = Object.new
deliverable.stub!(:deliver)
Notifier.stub!(:new_request).and_return(deliverable)
end
it "should create a new person upon getting a person request" do
request = Request.instantiate(:to =>"http://www.google.com/", :from => person)
@ -73,6 +80,7 @@ describe Diaspora::Parser do
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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -1,10 +1,11 @@
# 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
describe Diaspora::UserModules::Friending do
let(:user) { Factory.create :user }
let(:aspect) { user.aspect(:name => 'heroes') }
let(:aspect1) { user.aspect(:name => 'other') }
@ -16,6 +17,13 @@ describe User do
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
aspect.requests.size.should == 0
@ -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
@ -93,13 +108,23 @@ describe User do
}.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
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
@ -115,6 +140,8 @@ describe User do
user2.ignore_friend_request @request_two.id
user2.friends.include?(person_one).should be false
end
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

View file

@ -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

View file

@ -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

View file

@ -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