Merge branch 'invite_link'

This commit is contained in:
danielgrippi 2012-03-16 17:56:59 -07:00
commit 4a564c0c0e
46 changed files with 688 additions and 794 deletions

View file

@ -15,7 +15,6 @@ gem 'rack-cors', '~> 0.2.4', :require => 'rack/cors'
# authentication
gem 'devise', '~> 1.3.1'
gem 'devise_invitable', '0.5.0'
gem 'jwt'
gem 'oauth2-provider', '0.0.19'

View file

@ -138,9 +138,6 @@ GEM
bcrypt-ruby (~> 2.1.2)
orm_adapter (~> 0.0.3)
warden (~> 1.0.3)
devise_invitable (0.5.0)
devise (~> 1.3.1)
rails (>= 3.0.0, <= 3.2)
diff-lcs (1.1.3)
em-http-request (1.0.2)
addressable (>= 2.2.3)
@ -465,7 +462,6 @@ DEPENDENCIES
cucumber-rails (= 1.2.1)
database_cleaner (= 0.7.1)
devise (~> 1.3.1)
devise_invitable (= 0.5.0)
diaspora-client!
em-synchrony (= 1.0.0)
factory_girl_rails

View file

@ -11,16 +11,24 @@ class AdminsController < ApplicationController
end
def admin_inviter
user = User.find_by_email params[:idenitifer]
inviter = InvitationCode.default_inviter_or(current_user)
email = params[:identifier]
user = User.find_by_email(email)
unless user
Invitation.create(:service => 'email', :identifier => params[:identifier], :admin => true)
flash[:notice] = "invitation sent to #{params[:identifier]}"
EmailInviter.new(email, inviter).send!
flash[:notice] = "invitation sent to #{email}"
else
flash[:notice]= "error sending invite to #{params[:identifier]}"
flash[:notice]= "error sending invite to #{email}"
end
redirect_to user_search_path, :notice => flash[:notice]
end
def add_invites
InvitationCode.find_by_token(params[:invite_code_id]).add_invites!
redirect_to user_search_path
end
def weekly_user_stats
@created_users = User.where("username IS NOT NULL")
@created_users_by_week = Hash.new{ |h,k| h[k] = [] }

View file

@ -0,0 +1,16 @@
class InvitationCodesController < ApplicationController
before_filter :ensure_valid_invite_code
rescue_from ActiveRecord::RecordNotFound do
redirect_to root_url, :notice => "That invite code is no longer valid"
end
def show
sign_out(current_user) if user_signed_in?
redirect_to new_user_registration_path(:invite => {:token => params[:id]})
end
def ensure_valid_invite_code
InvitationCode.find_by_token!(params[:id])
end
end

View file

@ -2,12 +2,12 @@
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
class InvitationsController < Devise::InvitationsController
require Rails.root.join('lib', 'email_inviter')
before_filter :check_token, :only => [:edit, :email]
before_filter :check_if_invites_open, :only =>[:create]
class InvitationsController < ApplicationController
def new
@invite_code = current_user.invitation_code
@sent_invitations = current_user.invitations_from_me.includes(:recipient)
respond_to do |format|
format.html do
@ -16,65 +16,21 @@ class InvitationsController < Devise::InvitationsController
end
end
# this is for legacy invites. We try to look the person who sent them the
# invite, and use their new invite code
# owe will be removing this eventually
# @depreciated
def edit
user = User.find_by_invitation_token(params[:invitation_token])
invitation_code = user.ugly_accept_invitation_code
redirect_to invite_code_path(invitation_code)
end
def create
aspect_id = params[:user].delete(:aspects)
message = params[:user].delete(:invite_messages)
emails = params[:user][:email].to_s.gsub(/\s/, '').split(/, */)
#NOTE should we try and find users by email here? probs
aspect = current_user.aspects.find(aspect_id)
language = params[:user][:language]
invites = Invitation.batch_invite(emails, :message => message, :sender => current_user, :aspect => aspect, :service => 'email', :language => language)
flash[:notice] = extract_messages(invites)
redirect_to :back
end
def update
invitation_token = params[:user][:invitation_token]
if invitation_token.nil? || invitation_token.blank?
redirect_to :back, :error => I18n.t('invitations.check_token.not_found')
return
end
user = User.find_by_invitation_token!(invitation_token)
user.accept_invitation!(params[:user])
if user.persisted? && user.person && user.person.persisted?
user.seed_aspects
flash[:notice] = I18n.t 'registrations.create.success'
sign_in_and_redirect(:user, user)
else
user.errors.delete(:person)
flash[:error] = user.errors.full_messages.join(", ")
redirect_to accept_user_invitation_path(:invitation_token => params[:user][:invitation_token])
end
end
def resend
invitation = current_user.invitations_from_me.where(:id => params[:id]).first
if invitation
Resque.enqueue(Jobs::ResendInvitation, invitation.id)
flash[:notice] = I18n.t('invitations.create.sent') + invitation.recipient.email
end
redirect_to :back
end
def email
@invs = []
@resource = User.find_by_invitation_token(params[:invitation_token])
render 'devise/mailer/invitation_instructions', :layout => false
end
protected
def check_token
if User.find_by_invitation_token(params[:invitation_token]).nil?
render 'invitations/token_not_found'
end
inviter = EmailInviter.new(params[:email_inviter][:emails], current_user, params[:email_inviter])
inviter.send!
redirect_to :back, :notice => "Great! Invites were sent off to #{inviter.emails.join(', ')}"
end
def check_if_invites_open
@ -84,26 +40,4 @@ class InvitationsController < Devise::InvitationsController
return
end
end
# @param invites [Array<Invitation>] Invitations to be sent.
# @return [String] A full list of success and error messages.
def extract_messages(invites)
success_message = "Invites Successfully Sent to: "
failure_message = "There was a problem with: "
following_message = " already are on Diaspora, so you are now sharing with them."
successes, failures = invites.partition{|x| x.persisted? }
followings, real_failures = failures.partition{|x| x.errors[:recipient].present? }
success_message += successes.map{|k| k.identifier }.to_sentence
failure_message += real_failures.map{|k| k.identifier }.to_sentence
following_message += followings.map{|k| k.identifier}.to_sentence
messages = []
messages << success_message if successes.present?
messages << failure_message if failures.present?
messages << following_message if followings.present?
messages.join('\n')
end
end
end

View file

@ -3,10 +3,12 @@
# the COPYRIGHT file.
class RegistrationsController < Devise::RegistrationsController
before_filter :check_registrations_open!
before_filter :check_registrations_open_or_vaild_invite!, :check_valid_invite!
def create
@user = User.build(params[:user])
@user.process_invite_acceptence(invite) if invite.present?
if @user.save
flash[:notice] = I18n.t 'registrations.create.success'
@user.seed_aspects
@ -14,7 +16,7 @@ class RegistrationsController < Devise::RegistrationsController
Rails.logger.info("event=registration status=successful user=#{@user.diaspora_handle}")
else
@user.errors.delete(:person)
flash[:error] = @user.errors.full_messages.join(";")
Rails.logger.info("event=registration status=failure errors='#{@user.errors.full_messages.join(', ')}'")
render :new
@ -26,10 +28,26 @@ class RegistrationsController < Devise::RegistrationsController
end
private
def check_registrations_open!
def check_valid_invite!
return true unless AppConfig[:registrations_closed] #this sucks
return true if invite && invite.can_be_used?
flash[:error] = t('registrations.invalid_invite')
redirect_to new_user_session_path
end
def check_registrations_open_or_vaild_invite!
return true if invite.present?
if AppConfig[:registrations_closed]
flash[:error] = t('registrations.closed')
redirect_to new_user_session_path
end
end
def invite
if params[:invite].present?
@invite ||= InvitationCode.find_by_token(params[:invite][:token])
end
end
helper_method :invite
end

View file

@ -69,54 +69,4 @@ class ServicesController < ApplicationController
@service = current_user.services.where(:type => "Services::#{params[:provider].titleize}").first
@friends = @service ? @service.finder(:remote => params[:remote]).paginate( :page => params[:page], :per_page => 15) : []
end
def inviter
@uid = params[:uid]
if i_id = params[:invitation_id]
invite = Invitation.find(i_id)
invited_user = invite.recipient
else
invite = Invitation.create(:service => params[:provider], :identifier => @uid, :sender => current_user, :aspect => current_user.aspects.find(params[:aspect_id]))
invited_user = invite.attach_recipient!
end
#to make sure a friend you just invited from facebook shows up as invited
service = current_user.services.where(:type => "Services::Facebook").first
su = ServiceUser.where(:service_id => service.id, :uid => @uid).first
su.attach_local_models
su.save
respond_to do |format|
format.html{ invite_redirect_url(invite, invited_user, su)}
format.json{ render :json => invite_redirect_json(invite, invited_user, su) }
end
end
def facebook_message_url(user, facebook_uid)
subject = t('services.inviter.join_me_on_diaspora')
message = <<MSG
#{t('services.inviter.click_link_to_accept_invitation')}:
\n
\n
#{accept_invitation_url(user, :invitation_token => user.invitation_token)}
MSG
"https://www.facebook.com/messages/#{facebook_uid}?msg_prefill=#{message}"
end
def invite_redirect_json(invite, user, service_user)
if invite.email_like_identifer
{:message => t("invitations.create.sent") + service_user.name }
else
{:url => facebook_message_url(user, service_user.uid)}
end
end
def invite_redirect_url(invite, user, service_user)
if invite.email_like_identifer
redirect_to(friend_finder_path(:provider => 'facebook'), :notice => "you re-invited #{service_user.name}")
else
redirect_to(facebook_message_url(user, service_user.uid))
end
end
end
end

View file

@ -0,0 +1,28 @@
module InvitationCodesHelper
def invite_welcome_message
if invite.present?
content_tag(:div) do
person_image_link(invite.user.person) +
I18n.translate('invitation_codes.excited', :name => invite.user.name)
end
end
end
def invite_hidden_tag(invite)
if invite.present?
hidden_field_tag 'invite[token]', invite.token
end
end
def invite_link(invite_code)
text_field_tag :invite_code, invite_code_url(@invite_code), :readonly => true
end
def invited_by_message
inviter = current_user.invited_by
if inviter.present?
contact = current_user.contact_for(inviter.person) || Contact.new
render :partial => 'people/add_contact', :locals => {:inviter => inviter.person, :contact => contact}
end
end
end

View file

@ -25,11 +25,10 @@ module NotifierHelper
end
def invite_email_title
names = @invites.collect{|x| x.sender.person.name}.uniq
if @invites.empty? && names.empty?
"Accept Your Diaspora* invite!"
if @inviter.present?
"#{@inviter.person.name} invited you to Diaspora*"
else
"#{names.to_sentence} invited you to Diaspora*"
"Accept Your Diaspora* invite!"
end
end
end

View file

@ -33,6 +33,21 @@ class Notifier < ActionMailer::Base
mail(default_opts)
end
def invite(email, message, inviter, invitation_code, locale)
@inviter = inviter
@message = message
@locale = locale
@invitation_code = invitation_code
mail_opts = {:to => email, :from => AppConfig[:smtp_sender_address],
:subject => I18n.t('notifier.invited!'),
:host => AppConfig[:pod_uri].host}
I18n.with_locale(locale) do
mail(mail_opts)
end
end
def started_sharing(recipient_id, sender_id)
send_notification(:started_sharing, recipient_id, sender_id)
end

View file

@ -22,8 +22,6 @@ class Invitation < ActiveRecord::Base
validate :sender_owns_aspect?, :unless => :admin?
validates_uniqueness_of :sender_id, :scope => [:identifier, :service], :unless => :admin?
after_create :queue_send! #TODO make this after_commit :queue_saved!, :on => :create
# @note options hash is passed through to [Invitation.new]
# @see [Invitation.new]
@ -33,7 +31,7 @@ class Invitation < ActiveRecord::Base
# @option opts [Aspect] :aspect
# @option opts [String] :service
# @return [Array<Invitation>] An array of [Invitation] models
# the valid ones are saved, and the invalid ones are not.
# the valid optsnes are saved, and the invalid ones are not.
def self.batch_invite(emails, opts)
users_on_pod = User.where(:email => emails, :invitation_token => nil)
@ -66,36 +64,16 @@ class Invitation < ActiveRecord::Base
!email_like_identifer
end
# Attach a recipient [User] to the [Invitation] unless
# there is one already present.
#
# @return [User] The recipient.
def attach_recipient!
unless self.recipient.present?
self.recipient = User.find_or_create_by_invitation(self)
self.save
end
self.recipient
end
# Find or create user, and send that resultant User an
# invitation.
#
# @return [Invitation] self
def send!
self.attach_recipient!
# Sets an instance variable in User (set by devise invitable)
# This determines whether an email should be sent to the recipient.
recipient.skip_invitation = self.skip_email?
recipient.invite!
# Logging the invitation action
log_hash = {:event => :invitation_sent, :to => self[:identifier], :service => self[:service]}
log_hash.merge({:inviter => self.sender.diaspora_handle, :invitier_uid => self.sender.id, :inviter_created_at_unix => self.sender.created_at.to_i}) if self.sender
Rails.logger.info(log_hash)
if email_like_identifer
EmailInviter.new(self.identifier, sender).send!
else
puts "broken facebook invitation_token"
end
self
end
@ -135,19 +113,7 @@ class Invitation < ActiveRecord::Base
when 'email'
self.identifier
when 'facebook'
if username = ServiceUser.username_of_service_user_by_uid(self.identifier)
unless username.include?('profile.php?')
"#{username}@facebook.com"
else
nil
end
end
end
end
def queue_send!
unless self.recipient.present?
Resque.enqueue(Jobs::Mail::InviteUserByEmail, self.id)
false
end
end

View file

@ -0,0 +1,41 @@
class InvitationCode < ActiveRecord::Base
belongs_to :user
validates_presence_of :user
before_create :generate_token, :set_default_invite_count
def to_param
token
end
def can_be_used?
self.count > 0
end
def add_invites!
self.update_attributes(:count => self.count+100)
end
def use!
self.update_attributes(:count => self.count-1)
end
def generate_token
begin
self.token = ActiveSupport::SecureRandom.hex(6)
end while InvitationCode.exists?(:token => self[:token])
end
def self.default_inviter_or(user)
if AppConfig[:admin_account].present?
inviter = User.find_by_username(AppConfig[:admin_account])
end
inviter ||= user
inviter
end
def set_default_invite_count
self.count = AppConfig[:invite_count] || 25
end
end

View file

@ -8,12 +8,11 @@ require 'rest-client'
class User < ActiveRecord::Base
include Encryptor::Private
include Connecting
include Querying
include SocialActions
devise :invitable, :database_authenticatable, :registerable,
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:timeoutable, :token_authenticatable, :lockable,
:lock_strategy => :none, :unlock_strategy => :none
@ -40,7 +39,10 @@ class User < ActiveRecord::Base
has_many :invitations_from_me, :class_name => 'Invitation', :foreign_key => :sender_id
has_many :invitations_to_me, :class_name => 'Invitation', :foreign_key => :recipient_id
has_many :aspects, :order => 'order_id ASC'
belongs_to :auto_follow_back_aspect, :class_name => 'Aspect'
belongs_to :invited_by, :class_name => 'User'
has_many :aspect_memberships, :through => :aspects
has_many :contacts
@ -64,7 +66,6 @@ class User < ActiveRecord::Base
before_save :guard_unconfirmed_email,
:save_person!
before_create :infer_email_from_invitation_provider
attr_accessible :getting_started,
:password,
@ -106,32 +107,23 @@ class User < ActiveRecord::Base
ConversationVisibility.sum(:unread, :conditions => "person_id = #{self.person.id}")
end
# @return [User]
def self.find_by_invitation(invitation)
service = invitation.service
identifier = invitation.identifier
if service == 'email'
existing_user = User.where(:email => identifier).first
else
existing_user = User.joins(:services).where(:services => {:type => "Services::#{service.titleize}", :uid => identifier}).first
#@deprecated
def ugly_accept_invitation_code
begin
self.invitations_to_me.first.sender.invitation_code
rescue Exception => e
nil
end
if existing_user.nil?
i = Invitation.where(:service => service, :identifier => identifier).first
existing_user = i.recipient if i
end
existing_user
end
# @return [User]
def self.find_or_create_by_invitation(invitation)
if existing_user = self.find_by_invitation(invitation)
existing_user
else
self.create_from_invitation!(invitation)
end
def process_invite_acceptence(invite)
self.invited_by = invite.user
invite.use!
end
def invitation_code
InvitationCode.find_or_create_by_user_id(self.id)
end
def hidden_shareables
@ -389,39 +381,6 @@ class User < ActiveRecord::Base
end
end
# This method is called when an invited user accepts his invitation
#
# @param [Hash] opts the options to accept the invitation with
# @option opts [String] :username The username the invited user wants.
# @option opts [String] :password
# @option opts [String] :password_confirmation
def accept_invitation!(opts = {})
log_hash = {:event => :invitation_accepted, :username => opts[:username], :uid => self.id}
log_hash[:inviter] = invitations_to_me.first.sender.diaspora_handle if invitations_to_me.first && invitations_to_me.first.sender
if self.invited?
self.setup(opts)
self.invitation_token = nil
self.password = opts[:password]
self.password_confirmation = opts[:password_confirmation]
self.save
return unless self.errors.empty?
# moved old Invitation#share_with! logic into here,
# but i don't think we want to destroy the invitation
# anymore. we may want to just call self.share_with
invitations_to_me.each do |invitation|
if !invitation.admin? && invitation.sender.share_with(self.person, invitation.aspect)
invitation.destroy
end
end
log_hash[:status] = "success"
Rails.logger.info(log_hash)
self
end
end
###Helpers############
def self.build(opts = {})
@ -511,14 +470,6 @@ class User < ActiveRecord::Base
self.person
end
# Set the User's email to the one they've been invited at, if the user
# is being created via an invitation.
#
# @return [User]
def infer_email_from_invitation_provider
self.email = self.invitation_identifier if self.invitation_service == 'email'
self
end
def no_person_with_same_username
diaspora_id = "#{self.username}#{User.diaspora_id_host}"

View file

@ -1,65 +1,47 @@
.span-24
= render :partial => 'admins/admin_bar.haml'
%br
%br
.span-24.prepend-4
%h3
= form_tag 'admin_inviter', :method => :get do
email to invite:
= text_field_tag 'identifier'
= submit_tag 'invite'
%h3
you currently have
= current_user.invitation_code.count
invites left
= link_to "add_invites", add_invites_path(current_user.invitation_code)
= form_tag 'admin_inviter', :method => :get do
email to invite:
= text_field_tag 'identifier'
= submit_tag 'invite'
%h3
user search
= form_tag 'user_search', :method => :get do
username:
= text_field_tag 'user[username]', params[:user][:username]
%h3
user search
= form_tag 'user_search', :method => :get do
username:
= text_field_tag 'user[username]', params[:user][:username]
email:
= text_field_tag 'user[email]', params[:user][:email]
email:
= text_field_tag 'user[email]', params[:user][:email]
invitation identifier:
= text_field_tag 'user[invitation_identifier]', params[:user][:invitation_identifier]
invitation token:
= text_field_tag 'user[invitation_token]', params[:user][:invitation_token]
= submit_tag 'go'
= submit_tag 'go'
= "#{@users.count} users found"
%br
%br
= for user in @users
= user.inspect
= "#{@users.count} users found"
%br
- if user.person
= user.person.inspect
%br
- @users.each do |user|
= user.inspect
%br
- if user.person.profile
= user.person.profile.inspect
- if user.person
= user.person.inspect
%br
- if user.person.profile
= user.person.profile.inspect
%br
= "invite token: #{invite_code_url(user.invited_by.invite_code)}" if user.invited_by.present?
= link_to "add_invites", add_invites_path(user.invitation_code)
%br
= "invite token: #{accept_invitation_url(user, :invitation_token => user.invitation_token)}" if user.invitation_token
%br
%br
%br
%br
= javascript_include_tag 'apiconsole'
#query
%h3 api console
= text_field_tag :api
= submit_tag 'ping this api', :id => 'api_submit'
response:
%br
%br
#resp
%br
post to Diaspora v1
%br
%br

View file

@ -8,34 +8,35 @@
.description
= t('.if_they_accept_info')
%br
.span-7.append-1
.span-7.append-1.last
#email_invitation
= form_for User.new, :url => invitation_path(User) do |invite|
= form_tag new_user_invitation_path do
%h4
= t('email')
= invite.text_field :email, :title => t('.comma_seperated_plz'), :placeholder => 'foo@bar.com, max@foo.com...'
%br
%h4
= t('.aspect')
= invite.select(:aspects, options_from_collection_for_select(all_aspects, 'id', 'name'))
= text_field_tag 'email_inviter[emails]' ,nil, :title => t('.comma_seperated_plz'), :placeholder => 'foo@bar.com, max@foo.com...'
%br
%br
%h4
= t('.language')
= invite.select(:language, available_language_options, :selected => current_user.language)
= select_tag('email_inviter[locale]', options_from_collection_for_select(available_language_options, "second", "first", :selected => current_user.language))
%br
%br
%h4
= t('.personal_message')
= invite.text_area :invite_messages, :rows => 3, :value => t('.check_out_diaspora')
= text_area_tag 'email_inviter[message]',nil, :rows => 3, :value => t('.check_out_diaspora')
%p
= invite.submit t('.send_an_invitation')
= submit_tag t('.send_an_invitation')
.clearfix
.span-7.prepend-3.last
or paste them this link!
= invite_link(@invite_code)
= "#{@invite_code.count} invites left on this code"
%br
%br

View file

@ -0,0 +1,140 @@
<%- self.extend NotifierHelper -%>
<head>
<title><%=invite_email_title %></title>
</head>
<p style="background-color: rgb(255, 255, 255); text-align: center; font-size: 11px;"><%= t('devise.mailer.invitation_instructions.displaying_correctly', :link => link_to(t('devise.mailer.invitation_instructions.view_in'), invite_email_url(:invitation_code => @invitation_code), :style => "color: #3F8FBA; text-decoration: none;")).html_safe %> </p>
<table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td style="padding: 30px 15px 0pt; background-color: rgb(221, 221, 221);">
<table style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-size: 16px; color: rgb(51, 51, 51);" align="center" border="0" cellpadding="0" cellspacing="0" width="600">
<tbody>
<tr>
<td style="background-color: rgb(0, 0, 0);">
<a style="display: block;" href="<%= invite_code_url(@invitation_code) %>" target="_blank">
<img alt="Diaspora" src="http://dl.dropbox.com/u/15865/diaspora%20logo.png" style="border: 0pt none ; display: block;" height="134" width="600">
</a>
</td>
</tr>
<tr>
<td style="padding: 10px 0pt 0px 20px; background: rgb(255, 255, 255) none repeat scroll 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; font-size: 44px; font-weight: bold; color: rgb(0, 0, 0);">
<%= t('devise.mailer.invitation_instructions.finally') %><br>
</td>
</tr>
<tr><td style="padding: 0px 20px 0pt; background: rgb(255, 255, 255) none repeat scroll 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" width="600" height='389'>
<a href="<%= invite_code_url(@invitation_code)%>" target="_blank"><img src="http://dl.dropbox.com/u/15865/diaspora_shots.jpg" style="border: 0pt none ; display: block;" width="560">
</a></td></tr>
<tr><td style="padding: 0pt 30px; background: rgb(255, 255, 255) none repeat scroll 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; line-height: 20px;">
<%= t('devise.mailer.invitation_instructions.arrived', :strong_diaspora => content_tag(:strong, "DIASPORA*")).html_safe %>
<br>
<br>
<%= link_to(t('devise.mailer.invitation_instructions.sign_up_now').html_safe, invite_code_url(@invitation_code), :style => "color: #3F8FBA; text-decoration: underline; font-weight: bold; font-size: 20px;", :target => "_blank").html_safe %>
</td></tr>
<% if @inviter.present? %>
<%= @inviter.inspect %>
<% end %>
<tr><td style="padding: 20px 20px 0px; background: rgb(255, 255, 255) none repeat scroll 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; font-size: 44px; font-weight: bold; color: rgb(0, 0, 0);">
1. <%= t('devise.mailer.invitation_instructions.get_connected') %></td></tr>
<tr><td>
<table style="padding: 0pt 20px; background: rgb(255, 255, 255) none repeat scroll 0%; font-size: 16px; color: rgb(51, 51, 51); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="center" border="0" cellpadding="0" cellspacing="0" width="600">
<tbody><tr>
<td width="200">
<a href="<%=invite_code_url(@invitation_code) %>" target="_blank"><img style="border: 0pt none ; padding: 0pt 10px 0px 5px; display: block;" src="https://joindiaspora.s3.amazonaws.com/uploads/images/scaled_full_c7506ec0b3ae6694c64d.gif" height="156" width="200"></a>
</td>
<td style="line-height: 20px; width: 360px;">
<%= t('devise.mailer.invitation_instructions.get_connected_paragraph', :strong_diaspora => content_tag(:strong, "DIASPORA*")).html_safe %>
<br>
<!-- <a style="color: #3F8FBA; text-decoration: underline; font-weight: bold; font-size: 20px;" href="https://joindiaspora.com/channels" target="_blank">Tune in to your favorite Channels</a>-->
</td>
</tr>
</tbody></table>
</td></tr>
<tr><td style="padding: 20px 20px 0px; background: rgb(255, 255, 255) none repeat scroll 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; font-size: 44px; font-weight: bold; color: rgb(0, 0, 0);">
2. <%= t('devise.mailer.invitation_instructions.be_yourself') %></td></tr>
<tr><td>
<table style="padding: 0pt 20px; background: rgb(255, 255, 255) none repeat scroll 0%; font-size: 16px; color: rgb(51, 51, 51); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="center" border="0" cellpadding="0" cellspacing="0" width="600">
<tbody><tr>
<td style="line-height: 20px; width: 360px;">
<%= t('devise.mailer.invitation_instructions.be_yourself_paragraph', :strong_diaspora => content_tag(:strong, "DIASPORA*")).html_safe %>
<br>
<!-- <a style="color: #3F8FBA; text-decoration: underline; font-weight: bold; font-size: 20px;" href="#" target="_blank">Watch all the bumpers</a>-->
</td>
<td width="200">
<a href="<%= invite_code_url(@invitation_code)%>" target="_blank"><img style="border: 0pt none ; padding: 0pt 5px 0px 10px; display: block;" src="http://dl.dropbox.com/u/15865/apolonisaphrodisia.png" height="150" width="170"></a>
</td>
</tr>
</tbody></table>
</td></tr>
<tr><td style="padding: 20px 20px 0px; background: rgb(255, 255, 255) none repeat scroll 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; font-size: 44px; font-weight: bold; color: rgb(0, 0, 0);">
3. <%= t('devise.mailer.invitation_instructions.have_fun') %></td></tr>
<tr><td>
<table style="padding: 0pt 20px; background: rgb(255, 255, 255) none repeat scroll 0%; font-size: 16px; color: rgb(51, 51, 51); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="center" border="0" cellpadding="0" cellspacing="0" width="600">
<tbody><tr>
<td width="200">
<a href="<%= invite_code_url(@invitation_code)%>" target="_blank"><img style="border: 0pt none ; padding: 0pt 5px; display: block;" src="http://joindiaspora.com/images/cubbies_screenshot2.png" height="151" width="200"></a>
</td>
<td style="line-height: 20px; width: 360px;">
<%= t('devise.mailer.invitation_instructions.have_fun_paragraph', :strong_diaspora => content_tag(:strong, "DIASPORA*"), :link => link_to(t('devise.mailer.invitation_instructions.cubbies'), "https://cubbi.es", :style => "color: #3F8FBA; text-decoration: underline; font-weight: bold; font-size: 20px;", :target => "_blank")).html_safe %>
<!-- <a style="color: #3F8FBA; text-decoration: underline; font-weight: bold; font-size: 20px;" href="#" target="_blank">Watch all the bumpers</a>-->
</td>
</tr>
</tbody></table>
</td></tr>
<tr><td>
<table style="padding: 0pt 20px; background: rgb(255, 255, 255) none repeat scroll 0%; font-size: 16px; color: rgb(51, 51, 51); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="center" border="0" cellpadding="0" cellspacing="0" width="600">
<tbody>
<tr>
<td style="padding: 40px 20px 20px; background: rgb(255, 255, 255) none repeat scroll 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">
<%= link_to(t('devise.mailer.invitation_instructions.sign_up_now').html_safe, invite_code_url(@invitation_code), :style => "color: #3F8FBA; text-decoration: underline; font-weight: bold; font-size: 20px;", :target => "_blank").html_safe %>
</td>
</tr>
</tbody>
</table>
</td></tr>
<tr><td>
<table style="padding: 0pt 20px; background: rgb(255, 255, 255) none repeat scroll 0%; font-size: 16px; color: rgb(51, 51, 51); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="center" border="0" cellpadding="0" cellspacing="0" width="600">
<tbody>
<tr>
<td style="padding: 20px 20px 0px; background: rgb(255, 255, 255) none repeat scroll 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">
<%= t('devise.mailer.invitation_instructions.made_by_people', :strong_diaspora => content_tag(:strong, "DIASPORA*"), :jointeam => link_to(t('devise.mailer.invitation_instructions.join_team'), "https://github.com/diaspora/diaspora/wiki/Become-a-Contributor", :style =>"color: #3F8FBA; text-decoration: underline; font-weight: bold; font-size: 18px;", :target => "_blank"), :helpfund => link_to(t('devise.mailer.invitation_instructions.help_fund'), "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QG4L6VYD8YGPU", :style =>"color: #3F8FBA; text-decoration: underline; font-weight: bold; font-size: 18px;", :target => "_blank")).html_safe %>
</td>
</tr>
</tbody>
</table>
</td></tr>
<tr><td style="padding: 35px 20px 20px; background: rgb(255, 255, 255) none repeat scroll 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">
<%= t('devise.mailer.invitation_instructions.love') %><br>
<%= t('devise.mailer.invitation_instructions.team_diaspora') %><br>
</td></tr>
<tr><td style="padding: 35px 40px; font-size: 11px; color: rgb(102, 102, 102); line-height: 16px;">
<% if AppConfig[:pod_uri].host.match(/joindiaspora.com/) %>
<%= t('devise.mailer.invitation_instructions.unsubscribe', :link => link_to(t('devise.mailer.invitation_instructions.here'), "http://joindiaspora.us1.list-manage.com/unsubscribe?u=d759919b94f9cdcf39d204f3f&id=7b5ceb2f8b", :style => "color: #3F8FBA; text-decoration: none;")).html_safe %>
<% end %>
<%= t('devise.mailer.invitation_instructions.email_us', :email => link_to(t('devise.mailer.invitation_instructions.email_address'), "mailto:questions@joindiaspora.com", :style => "color: #3F8FBA; text-decoration: none;")).html_safe %>
</td></tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>

View file

@ -0,0 +1,12 @@
Hello!
You have been invited to join Diaspora*!
Click this link to get started
<%= invite_code_url(@invitation_code)%>
Love,
The Diaspora* email robot!

View file

@ -0,0 +1,5 @@
.alert-message.block-message.success
you were invited by
= person_image_link inviter
= person_link inviter
= aspect_membership_dropdown(contact, inviter, false)

View file

@ -11,16 +11,19 @@
= t('welcome')
%h3.accept_invitation_text
= t('.sign_up_message')
- flash.each do |name, msg|
%p{:class => "login_#{name}"}= msg
= image_tag 'diaspora_collage.png', :style => "margin-left:-50px;"
.span-10
%br
%br
%br
%br
= invite_welcome_message
= form_for(resource, :as => resource_name, :html => {:class => 'new_user_form'}, :url => registration_path(resource_name), :validate => true) do |f|
%fieldset
.clearfix
@ -42,6 +45,10 @@
= t('password_confirmation')
= f.password_field :password_confirmation, :title => t('registrations.new.enter_password_again')
.clearfix
= invite_hidden_tag(invite)
.submit_field
= f.submit t('registrations.new.create_my_account'), :class => 'in_aspects'

View file

@ -1,9 +1,6 @@
.stream_element{:id => friend.id}
.float-right
- if friend.already_invited?
= link_to t('.resend'), service_inviter_path(:uid => friend.uid, :provider => 'facebook', :invitation_id => friend.invitation_id), :class => 'button resend'
- elsif friend.on_diaspora?
- if friend.on_diaspora?
= aspect_membership_dropdown(contact_proxy(friend), friend.person, 'left')
- else
= render 'shared/aspect_dropdown', :selected_aspects => contact_proxy(friend).aspects, :person => friend.person, :hang => 'left', :dropdown_class => 'inviter', :service_uid => friend.uid

View file

@ -14,6 +14,8 @@
%p.center
= t(".community_welcome")
= invited_by_message
.clearfix
%br
%br

View file

@ -49,6 +49,12 @@ defaults: &defaults
# Set this to true if you want users to invite as many people as they want
open_invitations: true
#the 'admin' account for your pod... ie for jd.com, this is diasporahq
admin_account: ''
#the default amount of invitiations for an invite link
invite_count: 25
# Set this to true if you don't want your users to follow the diasporahq@joindiaspora.com
# account on account creation. The diasporahq account helps users start with some
# activity in their stream and get news about Diaspora, but if you don't want your server

View file

@ -319,6 +319,8 @@ en:
simplicity_explanation: "Diaspora makes sharing clean and easy and this goes for privacy too. Inherently private, Diaspora doesnt make you wade through pages of settings and options just to keep your profile secure."
learn_about_host: "Learn about how to host your own Diaspora server."
invitation_codes:
excited: "%{name} is excited to see you here."
invitations:
create:
sent: "Invitations have been sent to: "
@ -332,6 +334,7 @@ en:
invite_someone_to_join: "Invite someone to join Diaspora!"
if_they_accept_info: "if they accept, they will be added to the aspect you invited them."
comma_seperated_plz: "You can enter multiple email addresses separated by commas."
check_out_diaspora: "Hey! You should check out Diaspora*"
to: "To"
personal_message: "Personal message"
send_an_invitation: "Send an invitation"
@ -679,6 +682,7 @@ en:
update: "Update"
cancel_my_account: "Cancel my account"
closed: "Signups are closed on this Diaspora pod."
invalid_invite: "The invite link you provided is no longer valid!"
requests:
manage_aspect_contacts:

View file

@ -3,9 +3,7 @@
# the COPYRIGHT file.
Diaspora::Application.routes.draw do
# Posting and Reading
resources :reshares
resources :status_messages, :only => [:new, :create]
@ -33,6 +31,7 @@ Diaspora::Application.routes.draw do
get "liked" => "streams#liked", :as => "liked_stream"
get "commented" => "streams#commented", :as => "commented_stream"
get "aspects" => "streams#aspects", :as => "aspects_stream"
resources :aspects do
put :toggle_contact_visibility
@ -97,11 +96,13 @@ Diaspora::Application.routes.draw do
devise_for :users, :controllers => {:registrations => "registrations",
:password => "devise/passwords",
:sessions => "sessions",
:invitations => "invitations"} do
get 'invitations/resend/:id' => 'invitations#resend', :as => 'invitation_resend'
get 'invitations/email' => 'invitations#email', :as => 'invite_email'
end
:sessions => "sessions"}
#legacy routes to support old invite routes
get 'users/invitations/accept' => 'invitations#edit'
get 'users/invitations/email' => 'invitations#email', :as => 'invite_email'
get 'users/invitations' => 'invitations#new', :as => 'new_user_invitation'
post 'users/invitations' => 'invitations#create', :as => 'new_user_invitation'
get 'login' => redirect('/users/sign_in')
@ -111,6 +112,7 @@ Diaspora::Application.routes.draw do
get :weekly_user_stats
get :correlations
get :stats, :as => 'pod_stats'
get "add_invites/:invite_code_id" => 'admins#add_invites', :as => 'add_invites'
end
resource :profile, :only => [:edit, :update]
@ -122,7 +124,7 @@ Diaspora::Application.routes.draw do
resources :share_visibilities, :only => [:update]
resources :blocks, :only => [:create, :destroy]
get 'community_spotlight' => "contacts#spotlight", :as => 'community_spotlight'
get 'i/:id' => 'invitation_codes#show', :as => 'invite_code'
get 'people/refresh_search' => "people#refresh_search"
resources :people, :except => [:edit, :update] do
@ -189,7 +191,7 @@ Diaspora::Application.routes.draw do
end
end
get 'community_spotlight' => "contacts#spotlight", :as => 'community_spotlight'
# Mobile site
get 'mobile/toggle', :to => 'home#toggle_mobile', :as => 'toggle_mobile'

View file

@ -166,7 +166,6 @@ class CreateImportTables < ActiveRecord::Migration
t.string :language
t.string :email
t.database_authenticatable
t.invitable
t.recoverable
t.rememberable
t.trackable

View file

@ -0,0 +1,15 @@
class CreateInvitationCodes < ActiveRecord::Migration
def self.up
create_table :invitation_codes do |t|
t.string :token
t.integer :user_id
t.integer :count
t.timestamps
end
end
def self.down
drop_table :invitation_codes
end
end

View file

@ -109,6 +109,14 @@ ActiveRecord::Schema.define(:version => 20120301143226) do
add_index "conversations", ["author_id"], :name => "conversations_author_id_fk"
create_table "invitation_codes", :force => true do |t|
t.string "token"
t.integer "user_id"
t.integer "count"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "invitations", :force => true do |t|
t.text "message"
t.integer "sender_id"

View file

@ -19,7 +19,7 @@ Feature: invitation acceptance
Then I should be on the stream page
Scenario: accept invitation from user
Given I have been invited by a user
Given I have been invited by bob
And I am on my acceptance form page
And I fill in the following:
| user_username | ohai |
@ -35,11 +35,15 @@ Feature: invitation acceptance
And I preemptively confirm the alert
And I follow "awesome_button"
Then I should be on the stream page
And I log out
And I sign in as "bob@bob.bob"
And I follow "By email"
Then I should see one less invite
Scenario: sends an invitation
Given a user with email "bob@bob.bob"
When I sign in as "bob@bob.bob"
And I follow "By email"
And I fill in "user_email" with "alex@example.com"
And I fill in "email_inviter_emails" with "alex@example.com"
And I press "Send an invitation"
Then I should have 1 Devise email delivery

View file

@ -0,0 +1,10 @@
@javascript
Feature: Invitations
Scenario: Accepting an invitation
When I visit alice's invitation code url
Then I should see the "alice is excited" message
When I fill in the new user form
And I press "Create my account!"
Then I should see the "welcome to diaspora" message
And I should be able to friend Alice

View file

@ -11,6 +11,7 @@ Then /^I should see the "(.*)" message$/ do |message|
I18n.translate('profiles.edit.you_are_nsfw')
else
raise "muriel, you don't have that message key, add one here"
end
end
page.should have_content(text)
end
end

View file

@ -26,15 +26,21 @@ Given /^a nsfw user with email "([^\"]*)"$/ do |email|
end
Given /^I have been invited by an admin$/ do
i = Invitation.create!(:admin => true, :service => 'email', :identifier => "new_invitee@example.com")
@me = i.attach_recipient!
admin = Factory(:user)
admin.invitation_code
i = EmailInviter.new("new_invitee@example.com", admin)
i.send!
end
Given /^I have been invited by a user$/ do
@inviter = Factory(:user)
aspect = @inviter.aspects.create(:name => "Rocket Scientists")
i = Invitation.create!(:aspect => aspect, :sender => @inviter, :service => 'email', :identifier => "new_invitee@example.com", :message =>"Hey, tell me about your rockets!")
@me = i.attach_recipient!
Given /^I have been invited by bob$/ do
@inviter = Factory(:user, :email => 'bob@bob.bob')
@inviter_invite_count = @inviter.invitation_code.count
i = EmailInviter.new("new_invitee@example.com", @inviter)
i.send!
end
When /^I should see one less invite$/ do
step "I should see \"#{@inviter_invite_count -1} invites left\""
end
When /^I click on my name$/ do
@ -167,3 +173,22 @@ When /^I view "([^\"]*)"'s first post$/ do |email|
post = user.posts.first
visit post_path(post)
end
Given /^I visit alice's invitation code url$/ do
@alice ||= Factory(:user, :username => 'alice', :getting_started => false)
invite_code = InvitationCode.find_or_create_by_user_id(@alice.id)
visit invite_code_path(invite_code)
end
When /^I fill in the new user form$/ do
step 'I fill in "user_username" with "ohai"'
step 'I fill in "user_email" with "ohai@example.com"'
step 'I fill in "user_password" with "secret"'
step 'I fill in "user_password_confirmation" with "secret"'
end
And /^I should be able to friend Alice$/ do
alice = User.find_by_username 'alice'
step 'I should see "Add contact"'
step "I should see \"#{alice.name}\""
end

View file

@ -20,7 +20,7 @@ module NavigationHelpers
when /^my profile page$/
person_path(@me.person)
when /^my acceptance form page$/
accept_user_invitation_path(:invitation_token => @me.invitation_token)
invite_code_path(InvitationCode.first)
when /^the requestors profile$/
person_path(Request.where(:recipient_id => @me.person.id).first.sender)
when /^"([^\"]*)"'s page$/

30
lib/email_inviter.rb Normal file
View file

@ -0,0 +1,30 @@
class EmailInviter
attr_accessor :emails, :message, :inviter, :locale
def initialize(emails, inviter, options={})
self.message = options[:message]
self.locale = options.fetch(:locale, 'en')
self.inviter = inviter
self.emails = emails
end
def emails=(list)
emails = list.split(%r{[,\s]+})
emails.reject!{|x| x == inviter.email } unless inviter.nil?
@emails = emails
end
def invitation_code
@invitation_code ||= inviter.invitation_code
end
def send!
self.emails.each{ |email| mail(email)}
end
private
def mail(email)
Notifier.invite(email, message, inviter, invitation_code, locale).deliver!
end
end

View file

@ -26,10 +26,15 @@ module RakeHelpers
possible_invite = Invitation.find_by_identifier(backer_email)
possible_user ||= possible_invite.recipient if possible_invite.present?
admin_account = User.find_by_username(AppConfig[:admin_account])
raise "no admin_account in application.yml" unless admin_account.present?
admin_account.invitation_code.count += num_to_process
admin_account.invitation_code.save
unless possible_user
puts "#{n}: sending email to: #{backer_name} #{backer_email}" unless Rails.env == 'test'
unless test
i = Invitation.new(:service => 'email', :identifier => backer_email, :admin => true)
i = EmailInviter.new(backer_email)
i.send!
end
else

View file

@ -73,11 +73,6 @@ describe AdminsController do
AppConfig[:admins] = [@user.username]
end
it 'succeeds' do
get :admin_inviter, :identifier => 'bob@moms.com'
response.should be_redirect
end
it 'does not die if you do it twice' do
get :admin_inviter, :identifier => 'bob@moms.com'
get :admin_inviter, :identifier => 'bob@moms.com'
@ -85,7 +80,7 @@ describe AdminsController do
end
it 'invites a new user' do
Invitation.should_receive(:create)
EmailInviter.should_receive(:new).and_return(stub.as_null_object)
get :admin_inviter, :identifier => 'bob@moms.com'
response.should redirect_to user_search_path
flash.notice.should include("invitation sent")

View file

@ -5,16 +5,11 @@
require 'spec_helper'
describe InvitationsController do
include Devise::TestHelpers
before do
AppConfig[:open_invitations] = true
@user = alice
@aspect = @user.aspects.first
@invite = {:invite_message=>"test", :aspects=> @aspect.id.to_s, :email=>"abc@example.com"}
request.env["devise.mapping"] = Devise.mappings[:user]
Webfinger.stub_chain(:new, :fetch).and_return(Factory(:person))
@invite = {'email_inviter' => {'message' => "test", 'emails' => "abc@example.com"}}
end
describe "#create" do
@ -24,120 +19,26 @@ describe InvitationsController do
request.env["HTTP_REFERER"]= 'http://test.host/cats/foo'
end
it 'saves an invitation' do
expect {
post :create, :user => @invite
}.should change(Invitation, :count).by(1)
it 'creates an EmailInviter' do
inviter = stub(:emails => ['mbs@gmail.com'], :send! => true)
EmailInviter.should_receive(:new).with(@invite['email_inviter']['emails'], @user, @invite['email_inviter']).
and_return(inviter)
post :create, @invite
end
it 'handles a comma-separated list of emails' do
expect{
post :create, :user => @invite.merge(
:email => "foofoofoofoo@example.com, mbs@gmail.com")
}.should change(Invitation, :count).by(2)
end
it 'handles a comma-separated list of emails with whitespace' do
expect {
post :create, :user => @invite.merge(
:email => "foofoofoofoo@example.com , mbs@gmail.com")
}.should change(Invitation, :count).by(2)
end
it "allows invitations without if invitations are open" do
it "redirects if invitations are closed" do
open_bit = AppConfig[:open_invitations]
AppConfig[:open_invitations] = true
AppConfig[:open_invitations] = false
expect{
post :create, :user => @invite
}.to change(Invitation, :count).by(1)
post :create, @invite
response.should be_redirect
AppConfig[:open_invitations] = open_bit
end
it 'returns to the previous page on success' do
post :create, :user => @invite
post :create, @invite
response.should redirect_to("http://test.host/cats/foo")
end
it 'strips out your own email' do
lambda {
post :create, :user => @invite.merge(:email => @user.email)
}.should_not change(Invitation, :count)
expect{
post :create, :user => @invite.merge(:email => "hello@example.org, #{@user.email}")
}.should change(Invitation, :count).by(1)
end
end
describe "#email" do
before do
invites = Invitation.batch_invite(["foo@example.com"], :message => "hi", :sender => @user, :aspect => @user.aspects.first, :service => 'email', :language => "en-US")
invites.first.send!
@invited_user = User.find_by_email("foo@example.com")
end
it "succeeds" do
get :email, :invitation_token => @invited_user.invitation_token
response.should be_success
end
it "shows an error if there's no such invitation token" do
get :email, :invitation_token => "12345"
response.should render_template(:token_not_found)
end
end
describe "#update" do
before do
invite = Factory(:invitation, :sender => @user, :service => 'email', :identifier => "a@a.com")
@invited_user = invite.attach_recipient!
@accept_params = {:user=>
{:password_confirmation =>"password",
:email => "a@a.com",
:username=>"josh",
:password=>"password",
:invitation_token => @invited_user.invitation_token}}
end
context 'success' do
let(:invited) {User.find_by_username(@accept_params[:user][:username])}
it 'creates a user' do
put :update, @accept_params
invited.should_not be_nil
end
it 'seeds the aspects' do
put :update, @accept_params
invited.aspects.count.should == 4
end
it 'adds a contact' do
lambda {
put :update, @accept_params
}.should change(@user.contacts, :count).by(1)
end
end
context 'failure' do
before do
@fail_params = @accept_params
@fail_params[:user][:username] = @user.username
end
it 'stays on the invitation accept form' do
put :update, @fail_params
response.location.include?(accept_user_invitation_path).should be_true
end
it 'keeps the invitation token' do
put :update, @fail_params
response.location.include?("invitation_token=#{@invited_user.invitation_token}").should be_true
end
end
end
describe '#new' do
@ -146,43 +47,4 @@ describe InvitationsController do
get :new
end
end
describe '#resend' do
before do
sign_in :user, @user
@controller.stub!(:current_user).and_return(@user)
request.env["HTTP_REFERER"]= 'http://test.host/cats/foo'
invite = Factory(:invitation, :sender => @user, :service => 'email', :identifier => "a@a.com")
@invited_user = invite.attach_recipient!
end
it 'calls resend invitation if one exists' do
@user.reload.invitations_from_me.count.should == 1
invitation = @user.invitations_from_me.first
Resque.should_receive(:enqueue)
put :resend, :id => invitation.id
end
it 'does not send an invitation for a different user' do
invitation2 = Factory(:invitation, :sender => bob, :service => 'email', :identifier => "a@a.com")
Resque.should_not_receive(:enqueue)
put :resend, :id => invitation2.id
end
end
describe '#extract_messages' do
before do
sign_in alice
end
it 'displays a message that tells the user how many invites were sent, and which REJECTED' do
post :create, :user => @invite.merge(
:email => "mbs@gmail.com, foo@bar.com, foo.com, lala@foo, cool@bar.com")
flash[:notice].should_not be_blank
flash[:notice].should =~ /foo\.com/
flash[:notice].should =~ /lala@foo/
end
end
end

View file

@ -39,10 +39,25 @@ describe RegistrationsController do
flash[:error].should == I18n.t('registrations.closed')
response.should redirect_to new_user_session_path
end
it 'does not redirect if there is a valid invite token' do
i = InvitationCode.create(:user => bob)
get :new, :invite => {:token => i.token}
response.should_not be_redirect
end
it 'does redirect if there is an invalid invite token' do
get :new, :invite => {:token => 'fssdfsd'}
response.should be_redirect
end
end
describe "#create" do
context "with valid parameters" do
before do
AppConfig[:registrations_closed] = false
end
before do
user = Factory.build(:user)
User.stub!(:build).and_return(user)

View file

@ -143,51 +143,4 @@ describe ServicesController do
Nokogiri(response.body).css('.translation_missing').should be_empty
end
end
describe '#inviter' do
before do
@uid = "abc"
fb = Factory(:service, :type => "Services::Facebook", :user => @user)
fb = Services::Facebook.find(fb.id)
@su = Factory(:service_user, :service => fb, :uid => @uid)
@invite_params = {:provider => 'facebook', :uid => @uid, :aspect_id => @user.aspects.first.id}
end
it 'redirects to a prefilled facebook message url' do
put :inviter, @invite_params
response.location.should match(/https:\/\/www\.facebook\.com\/messages\/.*?msg_prefill=.*/)
end
it 'creates an invitation' do
lambda {
put :inviter, @invite_params
}.should change(Invitation, :count).by(1)
end
it 'sets the invitation_id on the service_user' do
post :inviter, @invite_params
@su.reload.invitation.should_not be_nil
end
it 'does not create a duplicate invitation' do
invited_user = Factory.build(:user, :username =>nil)
invited_user.save(:validate => false)
inv = Invitation.create!(:sender => @user, :recipient => invited_user, :aspect => @user.aspects.first, :identifier => eve.email)
@invite_params[:invitation_id] = inv.id
lambda {
put :inviter, @invite_params
}.should_not change(Invitation, :count)
end
it 'disregards the amount of invites if open_invitations are enabled' do
open_bit = AppConfig[:open_invitations]
AppConfig[:open_invitations] = true
lambda {
put :inviter, @invite_params
}.should change(Invitation, :count).by(1)
AppConfig[:open_invitations] = open_bit
end
end
end

View file

@ -130,6 +130,12 @@ FactoryGirl.define do
end
end
factory :invitation_code do
sequence(:token){|n| "sdfsdsf#{n}"}
association :user
count 0
end
factory :service do |service|
nickname "sirrobertking"
type "Services::Twitter"
@ -239,4 +245,4 @@ FactoryGirl.define do
end
factory(:status, :parent => :status_message)
end
end

View file

@ -0,0 +1,15 @@
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the InvitationCodesHelper. For example:
#
# describe InvitationCodesHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end
describe InvitationCodesHelper do
pending "add some examples to (or delete) #{__FILE__}"
end

View file

@ -0,0 +1,56 @@
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'email_inviter')
describe EmailInviter do
before do
@user = stub(:invitation_code => 'coolcodebro', :present? => true,
:email => 'foo@bar.com')
@emails = "mbs333@gmail.com, foo1@bar.com maxwell@dude.com"
end
it 'has a list of emails' do
inviter = EmailInviter.new(@emails, @user)
inviter.emails.should_not be_empty
end
it 'should parse three emails' do
inviter = EmailInviter.new(@emails, @user)
inviter.emails.count.should == 3
end
it 'has an inviter' do
inviter = EmailInviter.new(@emails, @user)
inviter.inviter.should_not be_nil
end
it 'can have a message' do
message = "you guys suck hard"
inviter = EmailInviter.new("emails", @user, :message => message)
inviter.message.should == message
end
describe '#emails' do
it 'rejects the inviter email if present' do
inviter = EmailInviter.new(@emails + " #{@user.email}", @user)
inviter.emails.should_not include(@user.email)
end
end
describe 'language' do
it 'defaults to english' do
inviter = EmailInviter.new(@emails, @user)
inviter.locale.should == 'en'
end
it 'listens to the langauge option' do
inviter = EmailInviter.new(@emails, @user, :locale => 'es')
inviter.locale.should == 'es'
end
end
describe '#invitation_code' do
it 'delegates to the user' do
inviter = EmailInviter.new(@emails, @user)
inviter.invitation_code.should == @user.invitation_code
end
end
end

View file

@ -12,20 +12,18 @@ describe RakeHelpers do
describe '#process_emails' do
before do
Devise.mailer.deliveries = []
end
it 'should send emails to each backer' do
expect{
process_emails(@csv, 100, 1, false)
}.to change(User, :count).by(3)
@old_admin = AppConfig[:admin_account]
AppConfig[:admin_account] = Factory(:user).username
end
it 'should not send the email to the same email twice' do
process_emails(@csv, 100, 1, false)
after do
AppConfig[:admin_account] = @old_admin
end
Devise.mailer.deliveries.count.should == 3
process_emails(@csv, 100, 1, false)
it 'should send emails to each email' do
Devise.mailer.deliveries.count.should == 3
EmailInviter.should_receive(:new).exactly(3).times.and_return(stub.as_null_object)
process_emails(@csv, 100, 1, false)
end
end
end

View file

@ -0,0 +1,42 @@
require 'spec_helper'
describe InvitationCode do
it 'has a valid factory' do
Factory(:invitation_code).should be_valid
end
it 'sets the count to a default value' do
code = Factory(:invitation_code)
code.count.should > 0
end
describe '#use!' do
it 'decrements the count of the code' do
code = Factory(:invitation_code)
expect{
code.use!
}.to change(code, :count).by(-1)
end
end
describe '.default_inviter_or' do
before do
@old_account = AppConfig[:admin_account]
AppConfig[:admin_account] = 'bob'
end
after do
AppConfig[:admin_account] = @old_account
end
it 'grabs the set admin account for the pod...' do
InvitationCode.default_inviter_or(alice).username.should == 'bob'
end
it '..or the given user' do
AppConfig[:admin_account] = ''
InvitationCode.default_inviter_or(alice).username.should == 'alice'
end
end
end

View file

@ -48,44 +48,6 @@ describe Invitation do
@invitation.message.should == "!"
end
describe 'the invite process' do
before do
end
it 'works for a new user' do
invite = Invitation.new(:sender => alice, :aspect => alice.aspects.first, :service => 'email', :identifier => 'foo@bar.com', :language => alice.language)
lambda {
invite.save
invite.send!
}.should change(User, :count).by(1)
end
it 'works for a current user(with the right email)' do
invite = Invitation.create(:sender => alice, :aspect => alice.aspects.first, :service => 'email', :identifier => bob.email, :language => alice.language)
lambda {
invite.send!
}.should_not change(User, :count)
end
it 'works for a current user(with the same fb id)' do
bob.services << Factory.build(:service, :type => "Services::Facebook")
invite = Invitation.create(:sender => alice, :aspect => alice.aspects.first, :service => 'facebook', :identifier => bob.services.first.uid)
lambda {
invite.send!
}.should_not change(User, :count)
end
end
describe '#convert_to_admin!' do
it 'reset sender and aspect to nil, and sets admin flag to true' do
invite = Factory(:invitation)
invite.convert_to_admin!
invite.reload
invite.admin?.should be_true
invite.sender_id.should be_nil
invite.aspect_id.should be_nil
end
end
describe '.batch_invite' do
before do
@ -109,63 +71,4 @@ describe Invitation do
end
end
describe 'send' do
before do
@invitation = Factory(:invitation, :sender => alice, :aspect => alice.aspects.first, :service => 'email', :identifier => 'a@a.com' , :language => alice.language)
end
it 'sends an email' do
lambda {
@invitation.send!
}.should change(Devise.mailer.deliveries, :count).by(1)
end
it 'sends an email with from header' do
@invitation.send!
Devise.mailer.deliveries.first.from.should_not be_blank
end
it 'sends an email with from header' do
@invitation.send!
Devise.mailer.deliveries.first.to.should == ["a@a.com"]
end
context "re-send" do
it 'sends another email' do
lambda {
@invitation.resend
}.should change(Devise.mailer.deliveries, :count).by(1)
end
end
end
describe '#recipient_identifier' do
it 'calls email if the invitation_service is email' do
email = 'abc@abc.com'
invitation = Factory(:invitation, :sender => alice, :service => 'email', :identifier => email, :aspect => alice.aspects.first, :language => alice.language)
invitation.recipient_identifier.should == email
end
context 'facebook' do
before do
@uid = '23526464'
@service = "facebook"
alice.services << Services::Facebook.new(:uid => "13234895")
alice.reload.services(true).first.service_users.create(:uid => @uid, :photo_url => 'url', :name => "Remote User")
end
it 'gets the name if the invitation_service is facebook' do
invitation = Factory(:invitation, :sender => alice, :identifier => @uid, :service => @service, :aspect => alice.aspects.first)
invitation.recipient_identifier.should == "Remote User"
end
it 'does not error if the facebook user is not recorded' do
invitation = Factory(:invitation, :sender => alice, :identifier => @uid, :service => @service, :aspect => alice.aspects.first)
alice.services.first.service_users.delete_all
invitation.recipient_identifier.should == "A Facebook user"
end
end
end
end
end

View file

@ -33,28 +33,6 @@ describe User do
alice.save
end
end
describe '#infer_email_from_invitation_provider' do
it 'sets corresponding email if invitation_service is email' do
addr = '12345@alice.com'
alice.invitation_service = 'email'
alice.invitation_identifier = addr
lambda {
alice.infer_email_from_invitation_provider
}.should change(alice, :email)
end
it 'does not set an email if invitation_service is not email' do
addr = '1233123'
alice.invitation_service = 'facebook'
alice.invitation_identifier = addr
lambda {
alice.infer_email_from_invitation_provider
}.should_not change(alice, :email)
end
end
end
describe 'hidden_shareables' do
@ -400,70 +378,13 @@ describe User do
end
end
describe '.find_by_invitation' do
let(:invited_user) {
inv = Factory.build(:invitation, :recipient => @recipient, :service => @type, :identifier => @identifier)
User.find_by_invitation(inv)
}
context 'send a request to an existing' do
before do
@recipient = alice
end
context 'active user' do
it 'by service' do
@type = 'facebook'
@identifier = '123456'
@recipient.services << Services::Facebook.new(:uid => @identifier)
@recipient.save
invited_user.should == @recipient
end
it 'by email' do
@type = 'email'
@identifier = alice.email
invited_user.should == @recipient
end
end
context 'invited user' do
it 'by service and identifier' do
@identifier = alice.email
@type = 'email'
invited_user.should == alice
end
end
context 'not on server (not yet invited)' do
it 'returns nil' do
@recipient = nil
@identifier = 'foo@bar.com'
@type = 'email'
invited_user.should be_nil
end
end
end
end
describe '.find_or_create_by_invitation'
describe '.create_from_invitation!' do
before do
@identifier = 'max@foobar.com'
@inv = Factory.build(:invitation, :admin => true, :service => 'email', :identifier => @identifier)
@user = User.create_from_invitation!(@inv)
end
it 'creates a persisted user' do
@user.should be_persisted
end
it 'sets the email if the service is email' do
@user.email.should == @inv.identifier
describe '#process_invite_acceptence' do
it 'sets the inviter on user' do
inv = InvitationCode.create(:user => bob)
user = Factory(:user)
user.process_invite_acceptence(inv)
user.invited_by_id.should == bob.id
end
end
@ -842,54 +763,6 @@ describe User do
end
end
describe "#accept_invitation!" do
before do
fantasy_resque do
@invitation = Factory(:invitation, :sender => eve, :identifier => 'invitee@example.org', :aspect => eve.aspects.first)
end
@invitation.reload
@form_params = {
:invitation_token => "abc",
:email => "a@a.com",
:username => "user",
:password => "secret",
:password_confirmation => "secret",
:person => {
:profile => {:first_name => "Bob", :last_name => "Smith"}
}
}
end
context 'after invitation acceptance' do
it 'destroys the invitations' do
user = @invitation.recipient.accept_invitation!(@form_params)
user.invitations_to_me.count.should == 0
end
it "should create the person with the passed in params" do
lambda {
@invitation.recipient.accept_invitation!(@form_params)
}.should change(Person, :count).by(1)
end
it 'resolves incoming invitations into contact requests' do
user = @invitation.recipient.accept_invitation!(@form_params)
eve.contacts.where(:person_id => user.person.id).count.should == 1
end
end
context 'from an admin' do
it 'should work' do
i = nil
fantasy_resque do
i = Invitation.create!(:admin => true, :service => 'email', :identifier => "new_invitee@example.com")
end
i.reload
i.recipient.accept_invitation!(@form_params)
end
end
end
describe '#retract' do
before do

View file