From 66f433176a0d3f0829794110ae75b6403c3f1cc4 Mon Sep 17 00:00:00 2001 From: joshua stein Date: Fri, 18 Oct 2013 15:49:20 -0500 Subject: [PATCH] add an invitiation request queue the user tree is pretty big to look through now, so let users submit a request for an invitation, which logged-in users can browse and instantly send invites to --- app/controllers/invitations_controller.rb | 65 ++++++++++++++++++- app/mailers/invitation_request_mailer.rb | 14 ++++ app/models/invitation_request.rb | 25 +++++++ .../invitation_request.text.erb | 15 +++++ app/views/invitations/build.html.erb | 39 +++++++++++ app/views/invitations/index.html.erb | 40 ++++++++++++ app/views/login/index.html.erb | 7 +- config/routes.rb | 9 ++- .../20131018201413_add_invitation_requests.rb | 17 +++++ db/schema.rb | 13 +++- 10 files changed, 238 insertions(+), 6 deletions(-) create mode 100644 app/mailers/invitation_request_mailer.rb create mode 100644 app/models/invitation_request.rb create mode 100644 app/views/invitation_request_mailer/invitation_request.text.erb create mode 100644 app/views/invitations/build.html.erb create mode 100644 app/views/invitations/index.html.erb create mode 100644 db/migrate/20131018201413_add_invitation_requests.rb diff --git a/app/controllers/invitations_controller.rb b/app/controllers/invitations_controller.rb index cf415b6..e3f2b08 100644 --- a/app/controllers/invitations_controller.rb +++ b/app/controllers/invitations_controller.rb @@ -1,5 +1,27 @@ class InvitationsController < ApplicationController - before_filter :require_logged_in_user + before_filter :require_logged_in_user, + :except => [ :build, :create_by_request, :confirm_email ] + + def build + end + + def index + @invitation_requests = InvitationRequest.where(:is_verified => true) + end + + def confirm_email + if !(ir = InvitationRequest.find_by_code(params[:code].to_s)) + flash[:error] = "Invalid or expired invitation request" + return redirect_to "/invitations/request" + end + + ir.is_verified = true + ir.save! + + flash[:success] = "Your invitiation request has been validated and " << + "will now be shown to other logged-in users." + return redirect_to "/invitations/request" + end def create i = Invitation.new @@ -26,4 +48,45 @@ class InvitationsController < ApplicationController return redirect_to "/settings" end end + + def create_by_request + ir = InvitationRequest.new + ir.name = params[:name] + ir.email = params[:email] + ir.memo = params[:memo] + + ir.ip_address = request.remote_ip + + if ir.save + flash[:success] = "You have been e-mailed a confirmation to " << + params[:email].to_s << "." + return redirect_to "/invitations/request" + else + render :action => :build + end + end + + def send_for_request + if !(ir = InvitationRequest.find_by_code(params[:code].to_s)) + flash[:error] = "Invalid or expired invitation request" + return redirect_to "/invitations" + end + + if ir.save + i = Invitation.new + i.user_id = @user.id + i.email = ir.email + + if i.save + i.send_email + ir.destroy + flash[:success] = "Successfully e-mailed invitation to " << + ir.name.to_s << "." + end + + return redirect_to "/invitations" + else + render :action => :build + end + end end diff --git a/app/mailers/invitation_request_mailer.rb b/app/mailers/invitation_request_mailer.rb new file mode 100644 index 0000000..9c6d02a --- /dev/null +++ b/app/mailers/invitation_request_mailer.rb @@ -0,0 +1,14 @@ +class InvitationRequestMailer < ActionMailer::Base + default :from => "#{Rails.application.name} " << + "" + + def invitation_request(invitation_request) + @invitation_request = invitation_request + + mail( + :to => invitation_request.email, + subject: "[#{Rails.application.name}] Confirm your invitation " << + "request to " << Rails.application.name + ) + end +end diff --git a/app/models/invitation_request.rb b/app/models/invitation_request.rb new file mode 100644 index 0000000..20c37b1 --- /dev/null +++ b/app/models/invitation_request.rb @@ -0,0 +1,25 @@ +class InvitationRequest < ActiveRecord::Base + attr_accessible nil + + validates :name, :presence => true + validates :email, :format => { :with => /\A[^@ ]+@[^@ ]+\.[^@ ]+\Z/ } + + before_validation :create_code + after_create :send_email + + def create_code + (1...10).each do |tries| + if tries == 10 + raise "too many hash collisions" + end + + if !InvitationRequest.find_by_code(self.code = Utils.random_str(15)) + break + end + end + end + + def send_email + InvitationRequestMailer.invitation_request(self).deliver + end +end diff --git a/app/views/invitation_request_mailer/invitation_request.text.erb b/app/views/invitation_request_mailer/invitation_request.text.erb new file mode 100644 index 0000000..345cf89 --- /dev/null +++ b/app/views/invitation_request_mailer/invitation_request.text.erb @@ -0,0 +1,15 @@ +Hello <%= @invitation_request.email %>, + +Someone at <%= @invitation_request.ip_address %> has submitted an invitiation request to +<%= Rails.application.name %>. + + Name: <%= @invitation_request.name %> + E-mail: <%= @invitation_request.email %> (won't be displayed to other users) + Memo: <%= @invitation_request.memo %> + +If this is you, visit the URL below to confirm your request and +display it to other logged-in users. + + <%= root_url %>invitations/confirm/<%= @invitation_request.code %> + +If this is not you, you can delete this message. diff --git a/app/views/invitations/build.html.erb b/app/views/invitations/build.html.erb new file mode 100644 index 0000000..0e16a31 --- /dev/null +++ b/app/views/invitations/build.html.erb @@ -0,0 +1,39 @@ +
+
+ Request an Invitation +
+ +

+ If you don't know (or can't find) an existing user from + which to request an invitiation, you can make a public request for one. This + will display your name and memo to all other logged-in users who can then + send you an invitation if they recognize you. +

+

+ Your e-mail address must be valid and confirmed by visiting a URL e-mailed to + you before your request will be displayed. Your e-mail address will not be + shown to any other users. +

+ + <%= form_tag create_invitation_by_request_url do %> +

+ <%= label_tag :name, "Name:" %> + <%= text_field_tag :name, "", :size => 30 %> +
+ + <%= label_tag :email, "E-mail Address:" %> + <%= text_field_tag :email, "", :size => 30 %> +
+ + <%= label_tag :memo, "Memo:" %> + <%= text_field_tag :memo, "", :size => 75 %> +
+ + URL to your website, Github account, etc. +

+ +

+ <%= submit_tag "Request Invitiation" %> +

+ <% end %> +
diff --git a/app/views/invitations/index.html.erb b/app/views/invitations/index.html.erb new file mode 100644 index 0000000..f116abb --- /dev/null +++ b/app/views/invitations/index.html.erb @@ -0,0 +1,40 @@ +
+
+ Requested Invitations +
+ +

+ These persons have requested invitations and confirmed their e-mail + addresses. If you recognize anyone, feel free to send them an invitation and + remove their request. Spam will be cleared out by a moderator. +

+ + + + + + + + + <% bit = 0 %> + <% @invitation_requests.each do |ir| %> + + + + + + + <% bit = (bit == 1 ? 0 : 1) %> + <% end %> + <% if @invitation_requests.count == 0 %> + + + + <% end %> +
Date/TimeNameMemo
<%= ir.created_at.strftime("%Y-%m-%d %H:%M:%S") %><%= ir.name %><%= ir.memo %><%= form_tag send_invitation_for_request_url, :confirm => "Are " << + "you sure you want to invite this person and remove this request?" do %> + <%= hidden_field_tag "code", ir.code %> + <%= submit_tag "Send Invitiation" %> + <% end %>
No invitation + requests
+
diff --git a/app/views/login/index.html.erb b/app/views/login/index.html.erb index 5a559c4..7cd7a18 100644 --- a/app/views/login/index.html.erb +++ b/app/views/login/index.html.erb @@ -24,9 +24,10 @@

- Not a user yet? Signup is currently by invitation only to combat spam and - increase accountability. If you know a current user of - the site, ask them for an invitation. + Not a user yet? Signup is by invitation only to combat spam and increase + accountability. If you know a current user of the site, + ask them for an invitation or request one + publicly.

<% end %> diff --git a/config/routes.rb b/config/routes.rb index e893dc8..3339e1f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ Lobsters::Application.routes.draw do root :to => "home#index", :protocol => (Rails.env == "production" ? "https://" : "http://") - + get "/rss" => "home#index", :format => "rss" get "/hottest.json" => "home#index", :format => "json" @@ -77,6 +77,13 @@ Lobsters::Application.routes.draw do post "/filters" => "filters#update" post "/invitations" => "invitations#create" + get "/invitations" => "invitations#index" + get "/invitations/request" => "invitations#build" + post "/invitations/create_by_request" => "invitations#create_by_request", + :as => "create_invitation_by_request" + get "/invitations/confirm/:code" => "invitations#confirm_email" + post "/invitations/send_for_request" => "invitations#send_for_request", + :as => "send_invitation_for_request" get "/invitations/:invitation_code" => "signup#invited" get "/moderations" => "moderations#index" diff --git a/db/migrate/20131018201413_add_invitation_requests.rb b/db/migrate/20131018201413_add_invitation_requests.rb new file mode 100644 index 0000000..20c99be --- /dev/null +++ b/db/migrate/20131018201413_add_invitation_requests.rb @@ -0,0 +1,17 @@ +class AddInvitationRequests < ActiveRecord::Migration + def up + create_table :invitation_requests do |t| + t.string :code + t.boolean :is_verified, :default => false + t.string :email + t.string :name + t.text :memo + t.string :ip_address + t.timestamps + end + end + + def down + drop_table :invitation_requests + end +end diff --git a/db/schema.rb b/db/schema.rb index 5a00104..a61d8bd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130622021035) do +ActiveRecord::Schema.define(:version => 20131018201413) do create_table "comments", :force => true do |t| t.datetime "created_at", :null => false @@ -35,6 +35,17 @@ ActiveRecord::Schema.define(:version => 20130622021035) do add_index "comments", ["story_id", "short_id"], :name => "story_id_short_id" add_index "comments", ["thread_id"], :name => "thread_id" + create_table "invitation_requests", :force => true do |t| + t.string "code" + t.boolean "is_verified", :default => false + t.string "email" + t.string "name" + t.text "memo" + t.string "ip_address" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "invitations", :force => true do |t| t.integer "user_id" t.string "email"