From ec4cf4dea9c88a7805afb274324ad46a84798c31 Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Sat, 24 Aug 2013 02:18:37 -0700 Subject: [PATCH 01/19] Adding IPNs --- Gemfile | 2 +- Gemfile.lock | 8 ++ app/assets/javascripts/ipn.js.coffee | 3 + app/assets/stylesheets/ipn.css.scss | 3 + app/controllers/ipns_controller.rb | 34 +++++ app/helpers/ipn_helper.rb | 2 + app/models/ipn.rb | 50 +++++++ app/models/payment.rb | 6 +- app/models/user.rb | 4 + app/views/ipns/index.html.erb | 26 ++++ app/views/ipns/new.html.erb | 123 ++++++++++++++++++ app/views/ipns/show.html.erb | 17 +++ app/views/payments/_form.html.erb | 4 + app/views/payments/index.html.erb | 2 + app/views/payments/show.html.erb | 19 ++- config/routes.rb | 2 + db/migrate/20130824062334_create_ipns.rb | 24 ++++ .../20130824072157_add_amount_to_payments.rb | 5 + db/schema.rb | 25 +++- test/fixtures/ipns.yml | 7 + test/functional/ipn_controller_test.rb | 7 + test/unit/helpers/ipn_helper_test.rb | 4 + test/unit/ipn_test.rb | 7 + 23 files changed, 379 insertions(+), 5 deletions(-) create mode 100644 app/assets/javascripts/ipn.js.coffee create mode 100644 app/assets/stylesheets/ipn.css.scss create mode 100644 app/controllers/ipns_controller.rb create mode 100644 app/helpers/ipn_helper.rb create mode 100644 app/models/ipn.rb create mode 100644 app/views/ipns/index.html.erb create mode 100644 app/views/ipns/new.html.erb create mode 100644 app/views/ipns/show.html.erb create mode 100644 db/migrate/20130824062334_create_ipns.rb create mode 100644 db/migrate/20130824072157_add_amount_to_payments.rb create mode 100644 test/fixtures/ipns.yml create mode 100644 test/functional/ipn_controller_test.rb create mode 100644 test/unit/helpers/ipn_helper_test.rb create mode 100644 test/unit/ipn_test.rb diff --git a/Gemfile b/Gemfile index 8ecbde8..6b59f1b 100644 --- a/Gemfile +++ b/Gemfile @@ -40,7 +40,7 @@ gem 'bcrypt-ruby', '~> 3.0.0' # gem 'capistrano' # To use debugger -# gem 'ruby-debug' +gem 'debugger' #gem "paperclip", "~> 3.0" gem 'gravtastic' diff --git a/Gemfile.lock b/Gemfile.lock index 94abf55..d3be24e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,6 +39,13 @@ GEM coffee-script-source execjs coffee-script-source (1.3.3) + columnize (0.3.6) + debugger (1.6.0) + columnize (>= 0.3.1) + debugger-linecache (~> 1.2.0) + debugger-ruby_core_source (~> 1.2.1) + debugger-linecache (1.2.0) + debugger-ruby_core_source (1.2.2) devise (2.1.1) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) @@ -120,6 +127,7 @@ DEPENDENCIES bcrypt-ruby (~> 3.0.0) cancan coffee-rails (~> 3.2.1) + debugger devise gravtastic jquery-rails diff --git a/app/assets/javascripts/ipn.js.coffee b/app/assets/javascripts/ipn.js.coffee new file mode 100644 index 0000000..7615679 --- /dev/null +++ b/app/assets/javascripts/ipn.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/stylesheets/ipn.css.scss b/app/assets/stylesheets/ipn.css.scss new file mode 100644 index 0000000..ee38bc7 --- /dev/null +++ b/app/assets/stylesheets/ipn.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Ipn controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/ipns_controller.rb b/app/controllers/ipns_controller.rb new file mode 100644 index 0000000..a392dd8 --- /dev/null +++ b/app/controllers/ipns_controller.rb @@ -0,0 +1,34 @@ +class IpnsController < ApplicationController + load_and_authorize_resource :ipn, :except => "create" + before_filter :authenticate_user! + + protect_from_forgery :except => [:create] + + def index + @ipns = Ipn.all + end + + def show + end + + def new + end + + def create + #TODO: ensure the request is actually from paypal + @ipn = Ipn.new_from_dynamic_params(params) + @ipn.data = params.to_json + @ipn.save + render :nothing => true + end + + def link + result = @ipn.link_payment + if result.first + redirect_to ipns_url, :notice => 'Payment was successfully linked.' + else + redirect_to ipns_url, :notice => result.last + end + end + +end \ No newline at end of file diff --git a/app/helpers/ipn_helper.rb b/app/helpers/ipn_helper.rb new file mode 100644 index 0000000..a56c555 --- /dev/null +++ b/app/helpers/ipn_helper.rb @@ -0,0 +1,2 @@ +module IpnHelper +end diff --git a/app/models/ipn.rb b/app/models/ipn.rb new file mode 100644 index 0000000..3643066 --- /dev/null +++ b/app/models/ipn.rb @@ -0,0 +1,50 @@ +class Ipn < ActiveRecord::Base + attr_accessible :data + belongs_to :payment + + after_create :create_payment + + def self.new_from_dynamic_params(params) + ipn = Ipn.new() + + ipn.attributes.each do |c| + unless params[c.first.to_sym].nil? + ipn[c.first.to_sym] = params[c.first.to_sym] + end + end + + return ipn + end + + def link_payment + create_payment + end + + private + def create_payment + # find user by email, then by payee + user = User.find_by_email(self.payer_email) + user = User.find_by_payee(self.payer_email) if user.nil? && self.payer_email.present? + + # Only create payments if the amount matches a member level + if user.present? + if User.member_levels[self.payment_gross.to_i].present? + payment = Payment.new + payment.date = self.payment_date + payment.user_id = user.id + payment.amount = self.payment_gross + if payment.save + self.payment_id = payment.id + self.save! + else + return [false, "Unable to link payment. Payment error: #{payment.errors.full_messages.first}"] + end + else + return [false, "Unable to link payment. Couldn't find membership level '#{self.payment_gross.to_i}'."] + end + else + return [false, "Unable to link payment. Couldn't find user/payee '#{self.payer_email}'."] + end + end + +end diff --git a/app/models/payment.rb b/app/models/payment.rb index ef9d681..ea70430 100644 --- a/app/models/payment.rb +++ b/app/models/payment.rb @@ -1,10 +1,12 @@ class Payment < ActiveRecord::Base belongs_to :user - attr_accessible :date, :user_id, :created_by + has_one :ipn + attr_accessible :date, :user_id, :created_by, :amount - validates_presence_of :user_id, :date, :created_by + validates_presence_of :user_id, :date, :amount # not created_by validates_uniqueness_of :date, :scope => :user_id, :message => ' of payment already exists for this user.' + def human_date if date.year < DateTime.now.year date.strftime("%b %e, %y") diff --git a/app/models/user.rb b/app/models/user.rb index 512a0aa..00890e3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -85,6 +85,10 @@ class User < ActiveRecord::Base end end + def self.member_levels + {25 => "Associate", 50 => "Basic", 100 => "Plus"} + end + def member_status case self.member_level.to_i when 0 diff --git a/app/views/ipns/index.html.erb b/app/views/ipns/index.html.erb new file mode 100644 index 0000000..9a6afff --- /dev/null +++ b/app/views/ipns/index.html.erb @@ -0,0 +1,26 @@ + + + + + + + + + <% @ipns.reverse!.each do |ipn| %> + + + + + + + + + <% end %> +
DateNameItemAmount
<%= ipn.payment_date %><%= ipn.first_name %> <%= ipn.last_name %><%= ipn.item_name %><%= ipn.payment_gross %> + <% if ipn.payment.present? %> + <%= link_to "Linked Payment", ipn.payment %> + <% else %> + <%= link_to "Try to link email '#{ipn.payer_email}' at membership level '#{ipn.payment_gross.to_i}'", link_ipn_path(ipn) %> + <% end %> + <%= link_to "Details", ipn %>
+ diff --git a/app/views/ipns/new.html.erb b/app/views/ipns/new.html.erb new file mode 100644 index 0000000..3322347 --- /dev/null +++ b/app/views/ipns/new.html.erb @@ -0,0 +1,123 @@ + <%= form_tag('/ipns') do |f| %> + + + +
+ <%= submit_tag %> +
+
+ <%= label_tag :first_name %> + <%= text_field_tag :first_name, "John" %> +
+
+ <%= label_tag :last_name %> + <%= text_field_tag :last_name, "Smith" %> +
+
+ <%= label_tag :payer_email %> + <%= text_field_tag :payer_email, "jsmith@example.com" %> +
+
+ <%= label_tag :item_name %> + <%= text_field_tag :item_name, "Associate Membership" %> +
+
+ <%= label_tag :payment_gross %> + <%= text_field_tag :payment_gross, "25.00" %> +
+
+ <%= label_tag :transaction_subject %> + <%= text_field_tag :transaction_subject, "" %> +
+
+ <%= label_tag :payment_date %> + <%= text_field_tag :payment_date, Date.today.to_s %> +
+
+ <%= label_tag :txn_type %> + <%= text_field_tag :txn_type, "subscr_payment" %> +
+
+ <%= label_tag :subscr_id %> + <%= text_field_tag :subscr_id, "I-1234567890" %> +
+
+ <%= label_tag :residence_country %> + <%= text_field_tag :residence_country, "US" %> +
+
+ <%= label_tag :mc_currency %> + <%= text_field_tag :mc_currency, "USD" %> +
+
+ <%= label_tag :business %> + <%= text_field_tag :business, "hslfinances@gmail.com" %> +
+
+ <%= label_tag :payment_type %> + <%= text_field_tag :payment_type, "instant" %> +
+
+ <%= label_tag :protection_eligibility %> + <%= text_field_tag :protection_eligibility, "Ineligible" %> +
+
+ <%= label_tag :verify_sign %> + <%= text_field_tag :verify_sign, "12ru9021j9f21j90fj1290fj2910fj0219fj0" %> +
+
+ <%= label_tag :payer_status %> + <%= text_field_tag :payer_status, "verified" %> +
+
+ <%= label_tag :txn_id %> + <%= text_field_tag :txn_id, "1234567890" %> +
+
+ <%= label_tag :receiver_email %> + <%= text_field_tag :receiver_email, "hslfinances@gmail.com" %> +
+
+ <%= label_tag :payer_id %> + <%= text_field_tag :payer_id, "V812314914" %> +
+
+ <%= label_tag :receiver_id %> + <%= text_field_tag :receiver_id, "V90R1280182" %> +
+
+ <%= label_tag :payment_status %> + <%= text_field_tag :payment_status, "Completed" %> +
+
+ <%= label_tag :payment_fee %> + <%= text_field_tag :payment_fee, "0.85" %> +
+
+ <%= label_tag :mc_fee %> + <%= text_field_tag :mc_fee, "0.85" %> +
+
+ <%= label_tag :mc_gross %> + <%= text_field_tag :mc_gross, "25.00" %> +
+
+ <%= label_tag :charset %> + <%= text_field_tag :charset, "windows-1252" %> +
+
+ <%= label_tag :notify_version %> + <%= text_field_tag :notify_version, "3.7" %> +
+
+ <%= label_tag :ipn_track_id %> + <%= text_field_tag :ipn_track_id, "9d3d032d9070" %> +
+
+ <%= submit_tag %> +
+ <% end %> \ No newline at end of file diff --git a/app/views/ipns/show.html.erb b/app/views/ipns/show.html.erb new file mode 100644 index 0000000..8fdc7ec --- /dev/null +++ b/app/views/ipns/show.html.erb @@ -0,0 +1,17 @@ +

IPN Details

+ +<% @ipn.attributes.except("data","payment_id").each do |attr| %> +

+ <%= attr.first.to_s %>: + <%= @ipn.attributes[attr.first] %> +

+<% end %> +

+ <% if @ipn.payment.present? %> + <%= link_to "Payment", @ipn.payment %> + <% else %> + Couldn't link '<%= @ipn.payer_email %>' or payment amount '<%= @ipn.payment_gross.to_i %>' not a valid membership level. Please create payment manually. + <% end %> +

+ +<%= link_to "Back", ipns_path %> \ No newline at end of file diff --git a/app/views/payments/_form.html.erb b/app/views/payments/_form.html.erb index dd04aad..72043e4 100644 --- a/app/views/payments/_form.html.erb +++ b/app/views/payments/_form.html.erb @@ -19,6 +19,10 @@ <%= f.label :date, "Paid for month beginning" %>
<%= f.date_select :date, :default => (DateTime.now - 1.month) %> +
+ <%= f.label :amount %>
+ <%= f.number_field :amount %> +
<%= f.submit %>
diff --git a/app/views/payments/index.html.erb b/app/views/payments/index.html.erb index 9be4eff..58bbd83 100644 --- a/app/views/payments/index.html.erb +++ b/app/views/payments/index.html.erb @@ -6,6 +6,7 @@ User Paid for month
beginning + Amount @@ -15,6 +16,7 @@ <%= link_to payment.user.name_with_payee_and_member_level, payment.user unless payment.user.blank? %> <%= payment.human_date %> + <%= payment.amount %> <%= link_to 'Details', payment %> <%= link_to 'Edit', edit_payment_path(payment) %> diff --git a/app/views/payments/show.html.erb b/app/views/payments/show.html.erb index 59906ce..c735823 100644 --- a/app/views/payments/show.html.erb +++ b/app/views/payments/show.html.erb @@ -8,9 +8,19 @@ <%= @payment.date %>

+

+ Amount: + <%= @payment.amount %> +

+

Last Modified by: - <%= user = @users.find{|u| u.id == @payment.created_by}; link_to user.name, user unless user.blank? %> + <% user = @users.find{|u| u.id == @payment.created_by} %> + <% if user.blank? %> + n/a + <% else %> + <%= link_to user.name, user %> + <% end %>

@@ -23,6 +33,13 @@ <%= @payment.updated_at %>

+<% if @payment.ipn.present? %> +

+ <%= link_to "Paid via PayPal", @payment.ipn %> +

+<% end %> + + <%= link_to 'Edit', edit_payment_path(@payment) %> | <%= link_to 'Destroy', @payment, :confirm => 'Are you sure you want to destroy this payment?', :method => :delete if can? :destroy, @payment %> | diff --git a/config/routes.rb b/config/routes.rb index 518a30a..6c64774 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ Dooraccess::Application.routes.draw do + resources :ipns + match 'ipns/:id/link' => 'ipns#link', :as => :link_ipn resources :payments diff --git a/db/migrate/20130824062334_create_ipns.rb b/db/migrate/20130824062334_create_ipns.rb new file mode 100644 index 0000000..fad43b4 --- /dev/null +++ b/db/migrate/20130824062334_create_ipns.rb @@ -0,0 +1,24 @@ +class CreateIpns < ActiveRecord::Migration + def change + create_table :ipns do |t| + t.integer :payment_id + t.text :data + t.string :txn_id + t.string :txn_type + t.string :first_name + t.string :last_name + t.string :payer_business_name + t.string :payer_email + t.string :payer_id + t.string :auth_amount + t.string :payment_date + t.string :payment_fee + t.string :payment_gross + t.string :payment_status + t.string :item_name + t.string :payment_type + + t.timestamps + end + end +end diff --git a/db/migrate/20130824072157_add_amount_to_payments.rb b/db/migrate/20130824072157_add_amount_to_payments.rb new file mode 100644 index 0000000..37b65e6 --- /dev/null +++ b/db/migrate/20130824072157_add_amount_to_payments.rb @@ -0,0 +1,5 @@ +class AddAmountToPayments < ActiveRecord::Migration + def change + add_column :payments, :amount, :decimal + end +end diff --git a/db/schema.rb b/db/schema.rb index 19fced1..915b9a5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -10,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130212083412) do +ActiveRecord::Schema.define(:version => 20130824072157) do create_table "cards", :force => true do |t| t.string "card_number" @@ -35,6 +36,27 @@ ActiveRecord::Schema.define(:version => 20130212083412) do t.datetime "updated_at", :null => false end + create_table "ipns", :force => true do |t| + t.integer "payment_id" + t.text "data" + t.string "txn_id" + t.string "txn_type" + t.string "first_name" + t.string "last_name" + t.string "payer_business_name" + t.string "payer_email" + t.string "payer_id" + t.string "auth_amount" + t.string "payment_date" + t.string "payment_fee" + t.string "payment_gross" + t.string "payment_status" + t.string "item_name" + t.string "payment_type" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "mac_logs", :force => true do |t| t.string "mac" t.string "ip" @@ -64,6 +86,7 @@ ActiveRecord::Schema.define(:version => 20130212083412) do t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.integer "created_by" + t.decimal "amount" end add_index "payments", ["user_id"], :name => "index_payments_on_user_id" diff --git a/test/fixtures/ipns.yml b/test/fixtures/ipns.yml new file mode 100644 index 0000000..6d2f6e4 --- /dev/null +++ b/test/fixtures/ipns.yml @@ -0,0 +1,7 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html + +one: + data: MyText + +two: + data: MyText diff --git a/test/functional/ipn_controller_test.rb b/test/functional/ipn_controller_test.rb new file mode 100644 index 0000000..0d5ac04 --- /dev/null +++ b/test/functional/ipn_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class IpnControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/unit/helpers/ipn_helper_test.rb b/test/unit/helpers/ipn_helper_test.rb new file mode 100644 index 0000000..be0cbae --- /dev/null +++ b/test/unit/helpers/ipn_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class IpnHelperTest < ActionView::TestCase +end diff --git a/test/unit/ipn_test.rb b/test/unit/ipn_test.rb new file mode 100644 index 0000000..fbdc7fa --- /dev/null +++ b/test/unit/ipn_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class IpnTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From eb782f11d52d9c9e8db7442e5c7063f71fef6eda Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Sat, 24 Aug 2013 03:40:34 -0700 Subject: [PATCH 02/19] Adding IPN validation --- app/controllers/ipns_controller.rb | 12 ++++++- app/models/ipn.rb | 58 ++++++++++++++++++++++++------ app/views/ipns/index.html.erb | 9 ++++- app/views/ipns/new.html.erb | 2 +- app/views/payments/show.html.erb | 2 +- config/routes.rb | 1 + 6 files changed, 69 insertions(+), 15 deletions(-) diff --git a/app/controllers/ipns_controller.rb b/app/controllers/ipns_controller.rb index a392dd8..6018abd 100644 --- a/app/controllers/ipns_controller.rb +++ b/app/controllers/ipns_controller.rb @@ -15,11 +15,21 @@ class IpnsController < ApplicationController end def create - #TODO: ensure the request is actually from paypal @ipn = Ipn.new_from_dynamic_params(params) @ipn.data = params.to_json @ipn.save render :nothing => true + unless @ipn.validate! + Rails.logger.error "Unable to validate IPN: #{@ipn.inspect}" + end + end + + def validate + if @ipn.validate! + redirect_to ipns_url, :notice => 'Valid!' + else + redirect_to ipns_url, :notice => 'INVALID' + end end def link diff --git a/app/models/ipn.rb b/app/models/ipn.rb index 3643066..2b37017 100644 --- a/app/models/ipn.rb +++ b/app/models/ipn.rb @@ -1,3 +1,4 @@ +require 'net/http' class Ipn < ActiveRecord::Base attr_accessible :data belongs_to :payment @@ -16,6 +17,32 @@ class Ipn < ActiveRecord::Base return ipn end + # Post back to Paypal to make sure it's valid + def validate! + uri = URI.parse('https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate') + + http = Net::HTTP.new(uri.host, uri.port) + http.open_timeout = 60 + http.read_timeout = 60 + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + http.use_ssl = true + response = http.post(uri.request_uri, self.data, + 'Content-Length' => "#{self.data.size}", + 'User-Agent' => "Ruby on Rails" + ).body + + unless ["VERIFIED", "INVALID"].include?(response) + Rails.logger.error "Faulty paypal result: #{response}" + return false + end + unless response == "VERIFIED" + Rails.logger.error "Invalid IPN: #{response}" + return false + end + + return true + end + def link_payment create_payment end @@ -26,25 +53,34 @@ class Ipn < ActiveRecord::Base user = User.find_by_email(self.payer_email) user = User.find_by_payee(self.payer_email) if user.nil? && self.payer_email.present? - # Only create payments if the amount matches a member level + # Only create payments if the IPN matches a member if user.present? - if User.member_levels[self.payment_gross.to_i].present? - payment = Payment.new - payment.date = self.payment_date - payment.user_id = user.id - payment.amount = self.payment_gross - if payment.save - self.payment_id = payment.id - self.save! + # And is a payment (not a cancellation, etc) + payment_types = ["subscr_payment","send_money"] + if payment_types.include?(self.txn_type) + # And a member level + if User.member_levels[self.payment_gross.to_i].present? + payment = Payment.new + payment.date = Date.strptime(self.payment_date, "%H:%M:%S %b %e, %Y %Z") + payment.user_id = user.id + payment.amount = self.payment_gross + if payment.save + self.payment_id = payment.id + self.save! + else + return [false, "Unable to link payment. Payment error: #{payment.errors.full_messages.first}"] + end else - return [false, "Unable to link payment. Payment error: #{payment.errors.full_messages.first}"] + return [false, "Unable to link payment. Couldn't find membership level '#{self.payment_gross.to_i}'."] end else - return [false, "Unable to link payment. Couldn't find membership level '#{self.payment_gross.to_i}'."] + return [false, "Unable to link payment. Transaction is a '#{self.txn_type}' instead of '#{payment_types.inspect}'."] end else return [false, "Unable to link payment. Couldn't find user/payee '#{self.payer_email}'."] end + + return [true] end end diff --git a/app/views/ipns/index.html.erb b/app/views/ipns/index.html.erb index 9a6afff..724c670 100644 --- a/app/views/ipns/index.html.erb +++ b/app/views/ipns/index.html.erb @@ -11,7 +11,13 @@ <%= ipn.payment_date %> <%= ipn.first_name %> <%= ipn.last_name %> <%= ipn.item_name %> - <%= ipn.payment_gross %> + + <% if ipn.payment_gross.blank? %> + <%= ipn.txn_type %> + <% else %> + <%= ipn.payment_gross %> + <% end %> + <% if ipn.payment.present? %> <%= link_to "Linked Payment", ipn.payment %> @@ -20,6 +26,7 @@ <% end %> <%= link_to "Details", ipn %> + <%= link_to "Validate", validate_ipn_path(ipn) %> <% end %> diff --git a/app/views/ipns/new.html.erb b/app/views/ipns/new.html.erb index 3322347..8839fb6 100644 --- a/app/views/ipns/new.html.erb +++ b/app/views/ipns/new.html.erb @@ -35,7 +35,7 @@
<%= label_tag :payment_date %> - <%= text_field_tag :payment_date, Date.today.to_s %> + <%= text_field_tag :payment_date, "20:46:54 Jun 20, 2013 PDT" %>
<%= label_tag :txn_type %> diff --git a/app/views/payments/show.html.erb b/app/views/payments/show.html.erb index c735823..316d2ec 100644 --- a/app/views/payments/show.html.erb +++ b/app/views/payments/show.html.erb @@ -1,6 +1,6 @@

User: - <%= @payment.user.name_with_payee_and_member_level unless @payment.user.blank? %> + <%= link_to @payment.user.name_with_payee_and_member_level, @payment.user unless @payment.user.blank? %>

diff --git a/config/routes.rb b/config/routes.rb index 6c64774..4a4f563 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,7 @@ Dooraccess::Application.routes.draw do resources :ipns match 'ipns/:id/link' => 'ipns#link', :as => :link_ipn + match 'ipns/:id/validate' => 'ipns#validate', :as => :validate_ipn resources :payments From f111769b20a263d0942924f90df1d342a0e17fdf Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Sat, 24 Aug 2013 03:43:43 -0700 Subject: [PATCH 03/19] Remove debug, update schema --- Gemfile | 2 +- Gemfile.lock | 8 -------- db/schema.rb | 1 - 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index 6b59f1b..023493f 100644 --- a/Gemfile +++ b/Gemfile @@ -40,7 +40,7 @@ gem 'bcrypt-ruby', '~> 3.0.0' # gem 'capistrano' # To use debugger -gem 'debugger' +#gem 'debugger' #gem "paperclip", "~> 3.0" gem 'gravtastic' diff --git a/Gemfile.lock b/Gemfile.lock index d3be24e..94abf55 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,13 +39,6 @@ GEM coffee-script-source execjs coffee-script-source (1.3.3) - columnize (0.3.6) - debugger (1.6.0) - columnize (>= 0.3.1) - debugger-linecache (~> 1.2.0) - debugger-ruby_core_source (~> 1.2.1) - debugger-linecache (1.2.0) - debugger-ruby_core_source (1.2.2) devise (2.1.1) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) @@ -127,7 +120,6 @@ DEPENDENCIES bcrypt-ruby (~> 3.0.0) cancan coffee-rails (~> 3.2.1) - debugger devise gravtastic jquery-rails diff --git a/db/schema.rb b/db/schema.rb index 915b9a5..ac3f771 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,4 +1,3 @@ -# encoding: UTF-8 # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. From f8f11e3d7efce5eb2ae12fe867e8ab1052c1e88a Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Mon, 26 Aug 2013 15:34:10 -0700 Subject: [PATCH 04/19] Adjust IPN security --- app/controllers/ipns_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/ipns_controller.rb b/app/controllers/ipns_controller.rb index 6018abd..36a9407 100644 --- a/app/controllers/ipns_controller.rb +++ b/app/controllers/ipns_controller.rb @@ -1,5 +1,5 @@ class IpnsController < ApplicationController - load_and_authorize_resource :ipn, :except => "create" + load_and_authorize_resource :ipn, :except => [:new, :create] before_filter :authenticate_user! protect_from_forgery :except => [:create] From 4067477cd467ad1b4bdf715d472578681e76c6c1 Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Mon, 26 Aug 2013 22:05:53 -0700 Subject: [PATCH 05/19] Updating icons, fixing user cert nil error --- app/models/user_certification.rb | 4 ++++ app/views/user_certifications/index.html.erb | 4 ++-- public/copper-coin.png | Bin 0 -> 1317 bytes public/favicon.ico | Bin 0 -> 661 bytes public/gold-coin.png | Bin 0 -> 1272 bytes public/silver-coin.png | Bin 0 -> 1210 bytes 6 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 public/copper-coin.png create mode 100644 public/gold-coin.png create mode 100644 public/silver-coin.png diff --git a/app/models/user_certification.rb b/app/models/user_certification.rb index e29b17c..649c48e 100644 --- a/app/models/user_certification.rb +++ b/app/models/user_certification.rb @@ -5,4 +5,8 @@ class UserCertification < ActiveRecord::Base belongs_to :user belongs_to :certification + + def user_name + self.user.name unless self.user.blank? + end end diff --git a/app/views/user_certifications/index.html.erb b/app/views/user_certifications/index.html.erb index 391f4fc..9996ffe 100644 --- a/app/views/user_certifications/index.html.erb +++ b/app/views/user_certifications/index.html.erb @@ -5,9 +5,9 @@ <% @grouped_user_certs.sort.each do |cert, user_certifications| %>

<%= cert.name %>
- <% user_certifications.sort{|a,b| a.user.name <=> b.user.name}.each do |user_certification| %> + <% user_certifications.sort{|a,b| a.user_name <=> b.user_name}.each do |user_certification| %>
- <%= link_to user_certification.user.name, user_certification %> + <%= link_to user_certification.user_name, user_certification %>
<% end %>
diff --git a/public/copper-coin.png b/public/copper-coin.png new file mode 100644 index 0000000000000000000000000000000000000000..809bbbb0a7a45f9c7b754fd8bfb4fcab48c145ad GIT binary patch literal 1317 zcmV+=1={+FP)|KT+qT<{9D`uI3X!3dZcQLl|=GlbR}Wxon*EtmhYP19 zU@r#cAm@8JXOxNotVqk)^b+*q?l1t(1Mg9=Llk$rfTei}i?SaCxt}{_SaqvNDki-! z5HS4&ed@zn&I7-pfSJ(i_5j#vX{}X7-%k@o1ma=Rh{0?zGuA+Fqze)>tRM$&(SuWT zVKb7vwL1!|1xWz9AICXs_Fg0n2h){Vuy4^qGflXVCzl1^^>rt%|#Q zyv=Rgv4ByPH3TxitHE<5oF>0o8HH1DGsUU;)PbV!!vZ83cBR+tW!3SnBuZAOn)k5Y zdzDp}TPoxg41@H80D|qd6c&s770^4AX#2Ylq|nG)I}9=?2Ox{v^7CI?_u{zsxD|#6 zDtP(&8JgQ6$QZC?Kmz5FYDN&XqP(@t@JC9J+Idpl{`iiBwboiJZmh1s7?V@bH4zX4 zNGyyo&|0HZ3Q%?{0Dz758akV;Twt&kQ;k^&!8fq5z!CsdlH4MbgyH69?p+zs0E1#b zyKFln;Q?NRD_H&e**Yq$3wU`u5>H byaVJvrrzQCXBMWj00000NkvXXu0mjfp=4DJ literal 0 HcmV?d00001 diff --git a/public/favicon.ico b/public/favicon.ico index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f3f28f635730685f2fd650b45ed227d866b7a45e 100644 GIT binary patch literal 661 zcmV;G0&4wPx#24YJ`L;(2!n*a<%my&4!000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iyo6 z2{SA4`Yka400I?BL_t(I%bk-!Y>QzS$A9nlwf)-lvo1wxn4-b5M35!TC>G%$IP}0A zwy={(c32RJ*ogxXM~SG$iHo>lmrW)SVTltF*+>nsh?S|9eqFWSU7LfY#Zz zQuQ8+d)f`R);gZCgTQJa0gM2T01q$>psGmIONT-Y-#FsccCY`%o3n&9rwWy9L56_m zKrhe-z}5g3 znJNhI_;rQ&Ig7hb%M^dq6I{AiVq!|Xzz5(yP_D`N)sj3wsSN2!o33P$^rT%!F!jB5 zd6zAUTZpK%5=(< zGYOCQ4J8*l4ar?L<-!3&yl%u%;2RM8y}Jl7*y!U}_ZC;lO3skn?(<1(l`i2XC8k%| zz!%^IaL|#Hs*z4Q&hi6Yp#`e6y+w~aduO`^d5N) z|EjsvlB{AmI=kGLG?GMvjzd#Tk0RhbsGfV*S_i_GAh4dWzfByIX39dB{<|bDx3}^G1%~%|R8N=s8q#q(- zOw?EbOA$4|Fdu;TOB(p&)!jF*uZ43H;9pH0Qss+$x;dL}d8GtLm8Cy=OxGGT>YEXy z05>hF84i#voprfgUU~5Dp^XW?tQ}F4E9uxl9XvQm@VPY1>{8xnHnmzuOhFY)={|Z!WvR>NP1Ta1)|g?c^%?`- zocPua2S~ry^f_0$_upxc396NkEsSQfR(nl=k@XU$ddmcXkfzF`hmX1U!#(c*@C)fM zA@BZLg0D*Ex*bfjSs#X{iY9-Gv-iV$$*w(nEUFf|jspI={WtUV0aRvq+7D@Lb7fEd zTuj)$D}p>Nk{bOkrf6Ix+tgmD&fnPQc+a0OAG`pLDfuujWvRp-Y^1j-+jsg6!BNBY zx=51j9V{OCz+s%UxHfWxBBPQ~C1}LxXoPBEge8?yk`izoc$s7}#L&|YbXw!;3jvtY z-cjT3!=ei3gi5f9EZd1t8TYCL*EI?#E*S#MJHT618D}sG5H*}Lw!hWlr#oE+gRJmF zeFoP!NEYq^WWp3<1q?|BV3IqOK(ch~hPBzKQmZ-k9iHZo$4^;Ycv4j3J@MRIq*I4C zkZg3%QV`7?4qvcY!LI9c9o={Bj%dw2=hnC1GguiE)ogtOskD&MuoaROcZ(_*mOE4y zT{0Xu*VgH}rh#z`2VPHjbnABMuEcFCV^&>;7FHFb;}AV7$#4z0P3XE_GEpTCJ7WSJ zJ8P_fVsQ zVlkS=S>IVgG@r<(x12EB5NdHi;F|dAUc%WoXzkbrz&{V}5+@HQW=9Ot_I5<{444*g zk=OHRw&c2-U!3a8PD5wQ=hT(q9?BD`K?Uj>gn@?N&n@J4X!x@|hNCd+V~p9S!&0D!1B;EAw)6#Dd_Gc|U7D z01Q#%+0&foy*fS&nV(yNJSo~2$K*4cu`pT^?~aBC?%Y@lxAD^dS{j;GKL-B^&hIe+ zF$2v30q2W90bvD;!Uc2wY+`c0{`SJ-^-voVte(%r2SWuz5kb*@Kb>X7L zjsJq6WZ~xh5&jLnWr+~pLU7@toA>a6`+{A#i3NkzECeA;I_adzbmrW*urn`dQs299 z7Bh41_kH)fXUSBL{w+y%Sx%3nImS7iO46S=7R?hb^yq^0J8|*zI|H- z@Kh;P&tx)JjWN(#gPEa}f`}m5M?`IAekmf2d-v}3|C8X&n>V)ryfem>%H=Y0xg4yu zI6OQ=uh)at8kI@~B9an0jss@)wAPO{Ha2$0@o4|#bW8=#f!-0aw$-! z(}CkS$Ye5z<2ZdUA~-%i#>vS^NksnI-QC@oNziOIZ;QygrKP1}rBVTaRH5(th~qfj z-|rWR=-uAl-mM9X8jZ%B<2YXz78Xh?D=QP~zI^$DFbv_kE{rh~ zaddP9BAT!(pUvSEI-L$aefoq} ztA){Mgwbd;0WKo&eIHR2mEt&l3IME>qGGZ5$Li|pg5x+71pE8@FvftH5r!eIUAu;A zwTe=ygoA?vl*{EQ0uezR$H?dNtINyFzf-MNd(6yNvf1nexV09;;V|`m{KBoZxOC}K zdfDUPB7%Ouk1!0ce*F0HAt|MvxUM^uBqAsj3K04E4=JTE7z}0t&Who12ob51wYK28 z?v#2FL9JFpr_)J(C|GL|1i=KsS+eALG#UZGWwO@tS$mRDN@0C{9qo2Iwaax~XsrR@ zmnRtxhp^Vh1VD#jc&?&+K94(h?%?q75C;bb003r2u~>vLW~O=(K^TUx)v}4oD}?QUrKUa*98$Zsa~(Y55w?tzu%t;*lxGc?RF7GQTnZ1E~f;i zUQgM>%8PETU&cfL{EsQKt#;UnuwT)zG|(vx3;$412`At YACvLQO8>dz`v3p{07*qoM6N<$f+@*CumAu6 literal 0 HcmV?d00001 From 125ad76a1ca286e473a05ba4ece0813b5be70954 Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Mon, 26 Aug 2013 22:07:41 -0700 Subject: [PATCH 06/19] Hacks to allow 1.8.7 --- Gemfile | 2 ++ app/controllers/ipns_controller.rb | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index 023493f..921e64e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,7 @@ source 'https://rubygems.org' +#ruby '1.9.3' + gem 'rails', '3.2.3' # Bundle edge Rails instead: diff --git a/app/controllers/ipns_controller.rb b/app/controllers/ipns_controller.rb index 36a9407..931556a 100644 --- a/app/controllers/ipns_controller.rb +++ b/app/controllers/ipns_controller.rb @@ -1,6 +1,6 @@ class IpnsController < ApplicationController load_and_authorize_resource :ipn, :except => [:new, :create] - before_filter :authenticate_user! + before_filter :authenticate_user!, :except => [:new, :create] protect_from_forgery :except => [:create] @@ -19,9 +19,9 @@ class IpnsController < ApplicationController @ipn.data = params.to_json @ipn.save render :nothing => true - unless @ipn.validate! - Rails.logger.error "Unable to validate IPN: #{@ipn.inspect}" - end + #unless @ipn.validate! + # Rails.logger.error "Unable to validate IPN: #{@ipn.inspect}" + #end end def validate @@ -41,4 +41,4 @@ class IpnsController < ApplicationController end end -end \ No newline at end of file +end From 98be42e9f939634bbfb397b5ce53188aacca6882 Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Mon, 26 Aug 2013 22:11:04 -0700 Subject: [PATCH 07/19] Fixing nil errors --- app/models/user_certification.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/user_certification.rb b/app/models/user_certification.rb index 649c48e..54c2891 100644 --- a/app/models/user_certification.rb +++ b/app/models/user_certification.rb @@ -7,6 +7,10 @@ class UserCertification < ActiveRecord::Base belongs_to :certification def user_name - self.user.name unless self.user.blank? + if self.user.blank? + "" + else + self.user.name + end end end From 59b6e3c838aabc07f8451e0e4cfbfb29e874f1d6 Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Mon, 26 Aug 2013 22:12:45 -0700 Subject: [PATCH 08/19] Updating nav --- app/views/layouts/application.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 5d907ec..6b2658a 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -11,7 +11,7 @@ <%= link_to 'People', users_path if can? :read, User %> <%= link_to 'Access Cards', cards_path if can? :manage, Card %> - <% if can? :manage, UserCertification %> + <% if can? :create, UserCertification %> <%= link_to 'Cert Classes', certifications_path if can? :read, Certification %> <%= link_to 'User Certs', user_certifications_path if can? :create, UserCertification %> <% else %> From 810ff6b034836afc8da96a2b1539c92cf4068421 Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Mon, 26 Aug 2013 22:32:08 -0700 Subject: [PATCH 09/19] De-duplicating cert view --- app/controllers/user_certifications_controller.rb | 2 +- app/views/certifications/show.html.erb | 11 ++++------- app/views/user_certifications/show.html.erb | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/app/controllers/user_certifications_controller.rb b/app/controllers/user_certifications_controller.rb index 6d222ee..aff149a 100644 --- a/app/controllers/user_certifications_controller.rb +++ b/app/controllers/user_certifications_controller.rb @@ -6,7 +6,7 @@ class UserCertificationsController < ApplicationController # Load users and certs based on current ability before_filter :only => [:new, :edit, :create, :update] do - @users = User.where(:hidden => false).accessible_by(current_ability).sort_by(&:name) + @users = User.where(:hidden => [false,nil]).accessible_by(current_ability).sort_by(&:name) @certifications = Certification.accessible_by(current_ability).sort_by(&:name) end diff --git a/app/views/certifications/show.html.erb b/app/views/certifications/show.html.erb index 272deae..c9fce2b 100644 --- a/app/views/certifications/show.html.erb +++ b/app/views/certifications/show.html.erb @@ -8,13 +8,10 @@ <%= simple_format @certification.description %>

-Certified Users: -
    - <% @certification_users.each do |user| %> -
  • <%= link_to user.name, user %>
  • - <% end %> - <% if @certification_users.blank? then %>
  • n/a
  • <% end %> -
+

+ Certified Users: + <%= link_to "Click Here", user_certifications_path %> +

<% if can? :update, @certification %><%= link_to 'Edit', edit_certification_path(@certification) %> |<% end %> <%= link_to 'Back', certifications_path %> diff --git a/app/views/user_certifications/show.html.erb b/app/views/user_certifications/show.html.erb index d12c3be..66b4b5f 100644 --- a/app/views/user_certifications/show.html.erb +++ b/app/views/user_certifications/show.html.erb @@ -1,6 +1,6 @@

User: - <%= @user_certification.user.name %> + <%= link_to @user_certification.user.name, @user_certification.user %>

From 18bc427eb3dc578756d0980866a84925ad196945 Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Mon, 26 Aug 2013 23:01:43 -0700 Subject: [PATCH 10/19] Adding quick-add buttons to user profiles --- app/assets/stylesheets/application.css | 33 ++++++++++++++++++++ app/models/card.rb | 3 +- app/models/user_certification.rb | 1 + app/views/cards/_form.html.erb | 3 +- app/views/user_certifications/_form.html.erb | 5 +-- app/views/users/show.html.erb | 4 +-- 6 files changed, 43 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 38f2c00..43f1ef9 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -12,3 +12,36 @@ *= require_tree . */ .caption { display: inline-block; background-color: #eee; border: 1px solid #333; border-radius: 5px; margin-bottom: 1em; } +.btn { +display: inline-block; +padding: 4px 10px 4px; +margin-bottom: 0; +font-size: 13px; +line-height: 18px; +color: #333; +text-align: center; +text-shadow: 0 1px 1px rgba(255,255,255,.75); +vertical-align: middle; +background-color: #f5f5f5; +background-image: -moz-linear-gradient(top, #fff, #e6e6e6); +background-image: -ms-linear-gradient(top, #fff, #e6e6e6); +background-image: -webkit-gradient(linear,0 0,0 100%,from( #fff),to( #e6e6e6)); +background-image: -webkit-linear-gradient(top, #fff, #e6e6e6); +background-image: -o-linear-gradient(top, #fff, #e6e6e6); +background-image: linear-gradient(top, #fff, #e6e6e6); +background-repeat: repeat-x; +filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); +border-color: #e6e6e6 #e6e6e6 #bfbfbf; +border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25); +filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +border: 1px solid #ccc; +border-bottom-color: #bbb; +-webkit-border-radius: 4px; +-moz-border-radius: 4px; +border-radius: 4px; +-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05); +-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05); +box-shadow: inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05); +cursor: pointer; +filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} diff --git a/app/models/card.rb b/app/models/card.rb index 1416ac3..700cb3b 100644 --- a/app/models/card.rb +++ b/app/models/card.rb @@ -2,7 +2,8 @@ class Card < ActiveRecord::Base require 'open-uri' attr_accessible :id, :user_id, :name, :card_number, :card_permissions - validates_uniqueness_of :id,:card_number + validates_presence_of :user_id, :card_number, :card_permissions + validates_uniqueness_of :id, :card_number belongs_to :user def upload_to_door diff --git a/app/models/user_certification.rb b/app/models/user_certification.rb index 54c2891..8ae9ddb 100644 --- a/app/models/user_certification.rb +++ b/app/models/user_certification.rb @@ -1,6 +1,7 @@ class UserCertification < ActiveRecord::Base attr_accessible :certification_id, :user_id + validates_presence_of :certification_id, :user_id validates_uniqueness_of :certification_id, :scope => :user_id, :message => 'already exists for this user.' # Makes sure users don't get certified twice belongs_to :user diff --git a/app/views/cards/_form.html.erb b/app/views/cards/_form.html.erb index 3a63ed0..6a14351 100644 --- a/app/views/cards/_form.html.erb +++ b/app/views/cards/_form.html.erb @@ -11,9 +11,10 @@

<% end %> + <% @card.user_id = params[:user] if params[:user].present? %>
<%= f.label :user %>
- <%= collection_select(:card, :user_id, User.all.sort_by(&:name), :id, :name) %> + <%= collection_select(:card, :user_id, User.all.sort_by(&:name), :id, :name, :include_blank => true) %>
<%= f.label :name, "Card Note" %>
diff --git a/app/views/user_certifications/_form.html.erb b/app/views/user_certifications/_form.html.erb index 919e8e5..cf571f6 100644 --- a/app/views/user_certifications/_form.html.erb +++ b/app/views/user_certifications/_form.html.erb @@ -11,13 +11,14 @@
<% end %> + <% @user_certification.user_id = params[:user] if params[:user].present? %>
<%= f.label :user_id, "User" %>
- <%= collection_select(:user_certification, :user_id, @users, :id, :name) %> + <%= collection_select(:user_certification, :user_id, @users, :id, :name, :include_blank => true) %>
<%= f.label :certification_id, "Certification" %>
- <%= collection_select(:user_certification, :certification_id, @certifications, :id, :name) %> + <%= collection_select(:user_certification, :certification_id, @certifications, :id, :name, :include_blank => true) %>
<%= f.submit %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 5d2e308..eaa42c7 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -74,7 +74,7 @@

<% end %>

- Card: + Card:<%= link_to "+ Add", (new_card_path+"?user="+@user.id.to_s), :class => 'btn' if can? :create, Card %> <% if current_user.admin? then %> <% @user.cards.each do |c| %> <%= link_to c.card_number, c %><%= "," unless c == @user.cards.last %> @@ -84,7 +84,7 @@ <% end %>

-Certifications: +Certifications:<%= link_to "+ Add", (new_user_certification_path+"?user="+@user.id.to_s), :class => 'btn' if can? :create, UserCertification %>
    <% @user.certifications.each do |certification| %>
  • <%= link_to certification.name, certification %>
  • From 4dba2b8c3d8c5e26933bca716ad6b89bce4dcac5 Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Mon, 26 Aug 2013 23:39:54 -0700 Subject: [PATCH 11/19] Adding status symbols --- app/models/user.rb | 51 +++++++++++++++++++++++------------- public/copper-paid-coin.png | Bin 0 -> 1749 bytes public/gold-paid-coin.png | Bin 0 -> 1706 bytes public/heart-coin.png | Bin 0 -> 1195 bytes public/no-coin.png | Bin 0 -> 1310 bytes public/silver-paid-coin.png | Bin 0 -> 1659 bytes public/timeout-coin.png | Bin 0 -> 1336 bytes 7 files changed, 33 insertions(+), 18 deletions(-) create mode 100644 public/copper-paid-coin.png create mode 100644 public/gold-paid-coin.png create mode 100644 public/heart-coin.png create mode 100644 public/no-coin.png create mode 100644 public/silver-paid-coin.png create mode 100644 public/timeout-coin.png diff --git a/app/models/user.rb b/app/models/user.rb index 00890e3..1da102d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -122,35 +122,50 @@ class User < ActiveRecord::Base end def member_status_symbol + # Begin output buffer + message = "" + icon = "" + flair = "" + + # First status item is level case self.member_level.to_i - when 0 + when 0..9 if self.payments.count > 0 then - ":(" + message = "Former Member (#{(DateTime.now - self.payments.last.date).to_i} days ago)" + icon = :timeout else - "" + message = "Not a Member" + icon = :no end - when 1 - "Unable" when 10..24 - "" + message = "Volunteer" + icon = :heart + when 25..49 + message = member_level_string + icon = :copper + when 50..99 + message = member_level_string + icon = :silver + when 100..999 + message = member_level_string + icon = :gold + end + + # Second status item is payment status + case self.member_level.to_i when 25..999 + # There are payments if self.payments.count > 0 then - if self.payments.last.date < (DateTime.now - 45.days) - "" + # They're on time + if self.payments.last.date > (DateTime.now - 45.days) + flair = "-paid" else - case self.member_level.to_i - when 25..49 - "" - when 50..99 - "" - when 100..999 - "" - end + message = "Last Payment (#{(DateTime.now - self.payments.last.date).to_i} days ago)" end - else - "?" end end + + return "" end private diff --git a/public/copper-paid-coin.png b/public/copper-paid-coin.png new file mode 100644 index 0000000000000000000000000000000000000000..6d40b7b623a15d0147777e6c1ab1bb70d9da153c GIT binary patch literal 1749 zcmV;`1}gc9P)V|&KN#@G%~T}pTrsE9}_ zTB#CrQ6(T{6;+j5sf$Kc_jFbMf^<`%oy7+ zkKfF1=HAoA*fV2nsCrjF&D?W7-+NvQkfju~QqSF2B?npk4%2DD|zAg9kAB*T(3{4f8QJ7vki@fS!gWrYs9-p^z0W zOw@SL=fJ8P+L%M9M*e;L{{rs*%g~rJ@?lc+uJ7JS^1UyTWEz$r2ngbFf>?mj2IoD9 zppD_?+BzR^tZ-$lidgfB82`?x+|@h(6S)0P1K-hB9s9;?(EsC0DKb7_O)f_!+k;XH z=NyeFLI9-{-g&(DhzQnN=CZ;cc0J?rcsQ#9`{v2si|1Yl+;Mcx9g?J)?Nyd0@?-MSKhmkhw`zNxWompb& zrH{>j%uaaWqt~WQ>?i+9GPZu4(TVY8Fz-CB;aUQnZ!}?U@jvG%7mL)2WqxsXfbI9Y zVj}9;zJ+aLD}cJ8KG^io^#AC5oV9D$lIhJZ166BPZr=JF7TOVU4Ue%FlQ4K`-gVwl zepaOTc#dD5$+2ZR*{`+vP@z-{xaIxq0WI?S53gE!y3-5{4Yfetc^axg=?PQ{d&seQ%c@F8JXX%&y_h{}_h8B$fv53cn999Af$xBfP@4Po+)+nW5k5sWcS zwTp$|y{A}w)_P5Mx`)2R09o5hzsZq^lDJA^S@@UiYt<_Ag#!DoWl+N8zRHfR+My2) zK2=Ns<(dk-lIf;Z-=x~sP~?^$FrnshRK-(TI>B9CA+swD~!>^ zlZj=Zrja9_O129ntWycg9io;%epRtiRHM;ouREX&=J+L7!i zwPbCnb@6pae=o4S|dde}Fdp!Fb$hAO) zg-1<69S&phI0^+oH40Z1+nr7`l@Ga>jlgkCUwgN5#UnS)jXUNG1!~pmirhV|&g06C zuv{Uk*YMVn$Rx?9hgu;mOwX(kYK$SjZiJJQPeHhGB75Po7DU8z=-h;(6bMgdW>(C_ zg>b&*Sc@j6;#d_l+d({c9^52cs4UOF_pE<)1M@v_X}AK4L#^RG_4Dx0Rm=IK+v^mo zA(c|8S(S^umFpVB^*YM2GNBIJuPQ&{_U->rc~o3cXahq-B(vQd`Q{v6@yTD;e0IDA z6hy4Jx3A~??2(;yX6K$zepbXS7J>4LUg;w%y+jhVfPTS2b!FdXdb9M64)NZeSuU!1_JQg^Mu2_M?3VlvamTpeBb)mK_h{Fhk7+lJ*JM(@_vj4H#hGCEY2 r8D-71*7~%z=ET_@x30Vhv~>OpShoPc!$ead00000NkvXXu0mjf`*dJ{ literal 0 HcmV?d00001 diff --git a/public/gold-paid-coin.png b/public/gold-paid-coin.png new file mode 100644 index 0000000000000000000000000000000000000000..ffa678aff57b3e3b349a9f1b4452c1a62b45f2f3 GIT binary patch literal 1706 zcmV;b237fqP)3^eZKR?b(=Uryt4eOYyOK<@N!O>crjAuydqu@=T4am2Tu&W z{lTXK?)>fG!|LUjI2qVgfNvFy2XlfH%MeAFI#9&`NmIlE0EsB4z4C{g@?NB6hiWSPFm40vzLZH4FqqPW$isXB1Scs8eU;%R; zyyuymaJ-<1msp*Ab-Hrq)nyD_-c0_cvLzVDF>(j;O@2}iq#K4w21eRon6dEjEqcn` zba(edP$UGf03jf818FpPswq6)1dN=S9DD5Mj?K3LY9h}90|$#uepF6q4sRlq${A%X zuy8A;q`u8a5|7bLClD=x1;hdp*U{<%->DmWW5KWiRrj1A(gxgqq_Pj}f$cu68Ii4b7--s-Mzu8J@m z#^Bqfm{9H_?Wwf$f(7Em2Ho{VoDBKfnBr^&bO|x%(%;vrn^1aN}JUycdW>}i?pAVXB znkb;5kPef%V%dQi5(0`bKH=5@ zYegG3h?c568fQLLb+00}$1ol%Bv`(*QgN#sXt~e^%xexwe4E0xwpo#_%4eg&S7ONy zn?0fg8^@p|5b^~Gwo;ib2SelCyniK&(t9P+L6YVOn7_?-8E845(rnP*x{n1LQ(z1O zFNWm`9c{3@&*g=#@d_6%Rtae)6vgfVDDNcr(GJ^|=XQTe+#toWoFl`Eu>|1FkR)CK z3#%N2z=pwy&rWu6;o>aUW)}g7a(Qw?_mh;ici776eG=2I0JEYRb`>)sF(htu+-`00 zs2E%CEg&+>`F~y@q?wN9M|ME2f8BgquF#4Qz-AO<#tc451!*=q`mZA|s|uph=h!lF zlZ$80Wr8e1!);pEHslK#g&C_KshFJvn5sr-qtOQHD4vcSWuFtHAag4+C*AAXTE4_z zy++|!oRB|HL{C@04Vv&XI7q_`nB7N$3$oricCi#qcr81 zzWe>!SO<69KGcsZXoqqhc4duWQp3+%Db7%M@T0AT%VaGeSZoK zNk{*sJrzQ^ix+$=I^hts9$-P_^S>;=p-y)n=uP-(Um$e#VybOjoS%!Cza9`Rv2}7Q zdBUQhjb`0Ru2*TOLKmOdG zwl4PdDO>Jw(wL1oO!uS>Bu@t@M)?$~^5IFGPYkjYWC%#P~a}uL}rp z-;th&aC%(44D2Ytfv&)p3xu{SuAr(J->I#IqwiBbI$wyFK>SP z)z$vj9TO*?scdnPJS$Eg5HE~G!v0d=YXvAe!HMFX;#K*tI9|l@C-vND&8VpSO+=nN zx$V8nYkl8w5C+(Jw11zd9F|D7iPKLRgaz?>UL$_?%DtDSjP)Jg6>xdoKQ*)i&wiZl zQSZjZF(yuz#B)`=n^D!1>fQ8fyDpu*6VUSdAK|N~c0=u1E&u=k07*qoM6N<$f@beD AoB#j- literal 0 HcmV?d00001 diff --git a/public/heart-coin.png b/public/heart-coin.png new file mode 100644 index 0000000000000000000000000000000000000000..84bfcf6e123994c414edf813d7bf918b4bd67745 GIT binary patch literal 1195 zcmV;c1XTNpP)_L>TDnl7mBvzysqIWBlT7B$ zz2~^NlQ@$f<5^v}m*<@Cob$c!6*FV!5J|2*cFge81a2?lQk+Y0E;aFWoVyeq{&fya zcO-0^;QP{uk~?kUrZtN86Xwt$0OtV#O%XFp0P(kR{(O%11rGkQynBN0$`h%{yoF&U z9UR8;c`yT2Fh#nb)d0hM0Nz(M@FxoUu3y_Z!MA-!)rIrPV5OW6R1oNet7YBT^o$xr z5fsGjCa4|)itH^4j?6LXP-zO$| zPw=IbtS`Ti3=UP2;Zekt2Xl|8URoxod-^7G48L8#4QDW0fk&Ts!h1O=Cq&0uL@zfH zHMH1=6-(8&CWrdRZ!BdeDCEvq7*&#?5yX^xpWkQc>=JS(#Wz5&8V}x$p{b-RN_{3z z=}w7!J0gF-i2I^Ke7OLoxJDh*q~gQybk?GC;>>iWJu-$EB+ChZeYBLds4INKQTy2V z^TU*Wm*2XMm&@3qM>NIPt6)rnh~ou+(tI#(QC7j?T$Nz!;%0F(Aotr=IDjC;Hv&+@ ztu&B0-oLgmdCXy)vV3u4Xz{~jCp_L9rD*#E07=@l2yqKTryb~k<_n;Tm@@QIA3I*l zU7~noZDZqH+ydC^0B=za>Fo31fq-Ul^8|K}6`Htm+*xNs;x#Zy9ZDccdt=|7GxnVw zBKc2^>J2p`d}A>yveRlw+8~-Z96rltS$0*%ju&VjcKqKIkH+p(dcM_j@hr+=Sn5z& zaB-{WrlQJopH`?9+q=SRuFmMp!%hdR(`rTY1;nU3L}w*#_2`P266B1hKkKKO#yi3r zNt2=X7lAH0=@e@DA|Acuc$F*Cj&He@#s+-7n!Zut@ehPXy!9llB%$!mJ%avZi}+bo zz+7-UdgA(=12c?SU#}vX_KKS-dnP>L>rGlo`tMLx(bw)$+!yx>p(|e-gkeig?`dfx#mnyth-yU;mk)zP29YOxiV3d z`9YTlv{#fc-*L6BSqYF1R7lE|nsd=qc<|aOH*R~=zbYkd7tVm6!TE6$I$oRt0?ubY0bve{!dvG2Y;1C||Cw7$ zn^D^*SdVk!(pO3+5q}Et1LmDaB*1xZ&M%1g+2F{+m2Aq}$3OI0n_ewQ&FcUF002ov JPDHLkV1m#SI?Vt8 literal 0 HcmV?d00001 diff --git a/public/no-coin.png b/public/no-coin.png new file mode 100644 index 0000000000000000000000000000000000000000..5205bfddd02997723aa088ec0f43c9fcb0ee0b83 GIT binary patch literal 1310 zcmV+(1>yRMP)8naNZ-4abRqS-`>&W-v?TE0r}wV3-E9);SHmt8HfH+?oOYblo9snp4U4 zWFnDD0s!O@aAt-`U7&~mt%wENw(|;lblv&$(^X8WE~cZ=tspg(?aC%n>GoA{<~pFp zK%r8&H}nFE&peId@&gpSLPC*DJ=i`t*pR_#rQ)^|SxUCIC$nAIMj&CF-8Kxbe~&wlVBo*W)Vd3hOK``&{DA>5dYSX(>?GuRkAec=`XfMMJFE!#?VWIAe0 zt1Our^BekM_VnQIS4V5$zrFJ=GDAZMf&l#4r;s27GsE*d1VNBgfw`|Dky50D4|J~Y z0u)+GLUVHf03H}XdEGipkM2ZzWCS7t@6ea%x-hd!EDVvqmxBVvpp?=(wSL_&&G)mN zoog2}pF?5Wc375$8@+vScf1I#HSV4~f#;5W*H|kN_`VM}=BDse&o88aJyADWCk;1c zu`!p!^~FU1K%~1HN-5mAd>Na+o~VKIL-6X^QptxX9wTOMbD~ZYd#+Efc*S{9JWG)Yr$kg{KbC>4?bM!CGw zQkv@f>nkHj3~X-b_uml|wYwIVa)Vs9U4WCQ@<-M)=$SFcuT z(YFDmO#>~n6=vAB4JZi-N-g;%zom|zZR4M}-UbmN3`2B(bqLXk2^5P(0KlESduz>D z56;4Hq7D?pBW4~imHbr(bz1)X#+z_rF#tgL;}1wLE@E)uGAkXP@7KSbskNpm6ghwoFde2n>IJ4D++IxO?IRq;S8HJu@N^ z2U;6f;?pye21JCl9?##)ebSM&AgE+;ZFm@IR64u&+i$>OSaVCfR6_s9A0hfs+KQ}| zb5KC%`Yy2IgAic?0AfB81VPU8JTNm>1PGO?c>ecucw%v}$%<_ciqJvObZ>QJI$+sW zE-3hiYuEJ-ky}JGS}ZK*{Za`OhHy{)1n`hX=5<{yZgt;{$IzBYZ6~aV)!cY3+#*~#pGYNC*)}ZO zTu`LPO;JZO3k$a%#cCN~byv}BBL12py@yD*ks?i68Kku)(o353c=yuMDLe+`KOCFo U>QgNvdjJ3c07*qoM6N<$g4wN12mk;8 literal 0 HcmV?d00001 diff --git a/public/silver-paid-coin.png b/public/silver-paid-coin.png new file mode 100644 index 0000000000000000000000000000000000000000..4b309518af9f3584ff1b6dfa4f4de9162192e9f9 GIT binary patch literal 1659 zcmV->288*EP)Bz=j=|VzVp@(JMi6X z$pvf7vC&tqfBjr#fUE@+M1KBR=K-wwi+HAAtRSAD{%fB3FV=u36UN%Zqc2}Q{(k{? z{c}@K&FUv&Wv6(CoQc`tMi{E?A!a>l6^w#~#9QWi_A-}q=Xe|x5Kjp;@o*WY^?LG1uKh=G_rUT@n9KP>kt;JdkK$0Y+@&;~Xuk!D<&zTNyV?22z zzW2M&)?FT36FB_w`rYc8e~C505PNyM_IskJmG<^_thG4jC>Dzp3I(jSw70hd;GDxb zhwuA%p2z6A6O6^D5l@zkm3L0YUwu($nEGb>KjL1ee2NkbC3|_t9ip>y6P=x%Da<*? z%*+fy5D1|K~RXO%F(|( zilPRk<#L%?t(HclSS(U5mjTjyl}d#|p}?=dKgcgiZ=h(qG4>NdBn9sHFg^e}vdQ(b zJNYdg9Ub_--(dZ%Tep~k zQe~v{Cal!S5kbTo_-izXZ~Jo^^F!G|S65eqpmUC!H*b>7W&v1ST%^ChALkrNk}xqb zK~GQ5DxhqMByiHgeZ& zQ^?aWRi#?3QZAJlUU(f;F~;mSi0)F9S9OSNHoHpDIY$&lDbP7bwOVa@uh~L7jFPpbG_2 zLnf1HYIP6<^!4>|=gyrplwlYmE0z924{Ic#iXt5b{*fH;l`f}p94bB?8@C2CTuhcq;bK13B!NprK-BO+|uwv9|C!}$1k+5|Ri z*nsDGP1BLdWT*p&Wt#QnJ`=`Y#s7{piF}HP``T6-(g2iHyOeO=6_RVZI z%a$!$(k9+49bH{rd>37zXy!qgI2nKWwNX^naTryu$rzQ&6UyasgW%lUTtl}Bf*^e* z9z1x!%*@OxSRBXXayiaNqbuGChtuYL{f|#hC|tU)Q=F9(lu9L*mzPr|y}iA(w6t*V z-aV$Krc&_aC+>r~Ov8w={cpLwV$Ess zBp8;r*vor(p4aegoSU0V&stks)11~vjws4u)+HZvT7SYkm1htQiqc$eZ;HvWFk*PFhTeZ+$%R@r}=d_n2$h3tY`!r0hRNJOf59 ziO8Wd{ombvR`;5NFu>5q8xDw(BjQWHSo4BGSP;)l7@t3%{n6BMrMcqg0_*E%$6p%6 z;~>`TGM?QmmM*d8iFiB|&(0cS&KS?0JU2XbWi3#B{TI(Df%j17?l}Me002ovPDHLk FV1jHGCQSeU literal 0 HcmV?d00001 diff --git a/public/timeout-coin.png b/public/timeout-coin.png new file mode 100644 index 0000000000000000000000000000000000000000..fbdcea427f80f4ddfc72c522bb6624af9047857f GIT binary patch literal 1336 zcmV-81;_e{P)66jvPn&SiE7mbGj{O^QZrAwU{MThS(HS~a3g ztqT8uiG)XK`qreEcl+2hOd-Aa#qy&rsvRH{KCNvEM_XF)P)a6-WL?uFIe4b$1xK<%NYXxRmh6$KldG z+SxYnZ??lw2J7v&Ks&o&b#;OK z_G_r}qRq_!pf~|Iw{1|*s|OeW%(6mV3$3B^c__+I&166*1Ks%s_?jO^n^2m$Z3C1_ z4nkP8xfvUM;CuE!%+0|*cyQf3%R=i{U&Cr{2DPlv>(tYy;JbH)JKJ?Z07VI~5RJt` z`Lla_5zNeh5)$$z5|6KfZ}5dO2K@67yt^mljaMuJgcPYZ=4y?g1{ObX04g4bH#N1c zbc2A6>iy|y@D&Hv2Oow~Io{c3E^#B%);`NaclyBWcaOci?0lWe( z9z1~j7^7Fv=uPo2a0ff{+{KZ8S$H!qahL)2j z5b5p)joMK2^ALaBg^1kGkaVZe6pM$^9i*fBU*_o0@WY$}C(I_>aErK9Kk`BHsc3n30^DSt05 zFSknH-*o10H?}K|gG@4+H%gs(=j6$I4GTxOZok%gs_MF}L2XKIv0yg#hyhT@WRPE4 z%IhE)>^XkCapBaC=Xpnr5I0p2BxDeTM#E;Ry;2EIIt@{+I#P(i-ebq6w_W Date: Tue, 27 Aug 2013 00:09:54 -0700 Subject: [PATCH 12/19] Adjusting ranking method --- app/models/user.rb | 52 ++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 1da102d..58a4846 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -90,42 +90,22 @@ class User < ActiveRecord::Base end def member_status - case self.member_level.to_i - when 0 - if self.payments.count > 0 then - 2 - else - -1 - end - when 1 - 1 - when 10..24 - 10 - when 25..999 - if self.payments.count > 0 then - if self.payments.last.date < (DateTime.now - 45.days) - 3 - else - case self.member_level.to_i - when 25..49 - 25 - when 50..99 - 50 - when 100..999 - 100 - end - end - else - return 0 - end - end + member_status_calculation[:rank] end def member_status_symbol + results = member_status_calculation + return "" + end + + private + + def member_status_calcuation # Begin output buffer message = "" icon = "" flair = "" + rank = 0 # First status item is level case self.member_level.to_i @@ -133,22 +113,28 @@ class User < ActiveRecord::Base if self.payments.count > 0 then message = "Former Member (#{(DateTime.now - self.payments.last.date).to_i} days ago)" icon = :timeout + rank = 1 else message = "Not a Member" icon = :no + rank = 0 end when 10..24 message = "Volunteer" icon = :heart + rank = 100 when 25..49 message = member_level_string icon = :copper + rank = 250 when 50..99 message = member_level_string icon = :silver + rank = 500 when 100..999 message = member_level_string icon = :gold + rank = 1000 end # Second status item is payment status @@ -161,15 +147,17 @@ class User < ActiveRecord::Base flair = "-paid" else message = "Last Payment (#{(DateTime.now - self.payments.last.date).to_i} days ago)" + rank = rank/10 end + else + message = "No Payments Recorded" + rank = rank/10 end end - return "" + return [:message => message, :icon => icon, :flair => flair, :rank => rank] end - private - def send_new_user_email Rails.logger.info UserMailer.new_user_email(self).deliver end From 0be2834a5ddc6fc056952be2149278f7db4f56dc Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Tue, 27 Aug 2013 00:39:43 -0700 Subject: [PATCH 13/19] Minor status tweak --- app/models/user.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 58a4846..beec17f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -90,17 +90,18 @@ class User < ActiveRecord::Base end def member_status - member_status_calculation[:rank] + results = member_status_calculation + return results[:rank] end def member_status_symbol results = member_status_calculation - return "" + return "" end private - def member_status_calcuation + def member_status_calculation # Begin output buffer message = "" icon = "" @@ -122,7 +123,7 @@ class User < ActiveRecord::Base when 10..24 message = "Volunteer" icon = :heart - rank = 100 + rank = 101 when 25..49 message = member_level_string icon = :copper @@ -146,7 +147,7 @@ class User < ActiveRecord::Base if self.payments.last.date > (DateTime.now - 45.days) flair = "-paid" else - message = "Last Payment (#{(DateTime.now - self.payments.last.date).to_i} days ago)" + message = "Last Payment #{(DateTime.now - self.payments.last.date).to_i/30} months ago" rank = rank/10 end else @@ -155,7 +156,7 @@ class User < ActiveRecord::Base end end - return [:message => message, :icon => icon, :flair => flair, :rank => rank] + return {:message => message, :icon => icon, :flair => flair, :rank => rank} end def send_new_user_email From 9fb2507aa1ab90030acff4d37903592eb1a4f3c7 Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Tue, 27 Aug 2013 23:06:16 -0700 Subject: [PATCH 14/19] Updating coin --- public/no-coin.png | Bin 1310 -> 1271 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/public/no-coin.png b/public/no-coin.png index 5205bfddd02997723aa088ec0f43c9fcb0ee0b83..73182322d6bddf9d17f838ea58c4add5c3eceb7c 100644 GIT binary patch delta 1177 zcmV;K1ZMl53ik<+Zhv%1L_t(YiG@{LYgAVlUEjC&WKL$1I3vMgT9}qZQV_L8>qE3? zz@P{Uf?80jD0#E#A85fJA=I{{v80bJO-isxN3gUHf;U=yupJX6U=k({Oy-hvGH2iV zFc)Vsi9N8{o5Oe3I^WuB??uizyy2nj+}yA+X4D#!vPv1NwSTtO8fQ#V8#6gLG&BRi zZVIpwK{=b)zyJ82%y6dMr*)1u#_Moz&TiB=BzQ-I1-r{JaC}&zXXL` zZm$i)v|3wB<`)(aluBSBK+IrfkPu+z#yeuJl80elYkh3b(WCb^B`D-_`(R8uzqFJn zEH1(U;2K~Zn17nUn~B39K*jf0v{EA<9zXt|j>+{!P%Q2j%<1gxY$E^iB|y{;E<_cy zK061Q$$*!$=-jdeU0b&%q?GALH*fB3CCKHv9dN6d%_dg!`PM%a0_OKS(AU3$c(vRj zk^5m1a`JmbZhQwOLbR(3ojpAXCQ3h^n%dSNsC0H-R)0Z|d^JDc-o6m1+`A8#c?R=a zx&f|!`Vo<*PvMB6J~;=H63mRQ-d+ePlSax*bpi&_*it3~IM-;u4#dphJ$pa|;K^ff zxeRqXjmX`*01@=*(~vuNHtL0xNc8mqM56$R!!SHywC=Bz%Pqjox_d)IaFGbCQcx2U z;9qY71b>)AhtP5G6Ju1){MS02n4fN`KGWP@4L@Ix_?J_g}RR2(T9Vm+6hq zNDV`J9>A;_L?RlCwdr3U;gcs&)6-S(x37Tr>(;2Q~F`tMi%^2#Q63sL0m2@j5|^6PS_z`Z`c3 z0)GT_oH~W*zyNx7?SegW1R%gZe*sczLk9+h0*GjmrIdF%Ja0ChOtuuP{`m*=gNFbC zB9%ho>}yLV&zyzsAAlGh2FGG;hqF>H!)QJC!TtL)48Rf5c%rWlLdr&AX==KE|BHi5(9UX1byqcef3W9tz2qpkP)^62hFptC%iQ7i0gsxPu<@|Z*XV1_(I=boE zSf3HA`87A11KymVr6M*#fIU?;D6!6 zjlgM(%peH%JEd-!Fih$&Y{cSb$rhAK$S*8Ft*zy?QX}slJoxa9nOx1ETz99GmyOnA z)|h@c2O~7cYwisKrrAUb(X$6Xj ziPjQcG)8?969m#HBmNKiWTMdrUc@wzRDI$>Oi4hSh|xdaLknn3a!m2*VH%0Te)_uV}3gvcGaTJwJc{ ze*?@W5`&7=lxdmCR5}gEiGW$a!VqRKOXVw-HAG;T2DH{W4ZW*vX6D?Q0seH|A#Ivd z$@XL-kxBvp8%inMxqKO$zn-Xp^F#3J*;2`eC>|qbZgZke6ML?+BADS` zn5o%$rCdhe7oS7Iu!&Ih>z90ppqmt+ZQE{?hJTxVZf@FC3t(CnkjXSjQ*V&6UxFwV zk^n}zywXyd>ig>}BS;KvZs_=bckh0ztmX0wARLeYy6l%4Y+1D;Gvi`!A82F*0N~xe zjcZr0R%+3=0i{g?EwdG7*tQKQ2?&cbk_4iv*9W*#q<{8a{ZTK@dTn{Z+=06_TT4@fUAVsPOyE*(D(0I+*{ zz>d>W)~FjrC_F(g0jE~V<%PoiLPM%&pWlL5e?I`AaPua%OitDa41a$N^Ru(Kd*TG7 zaKDi~Ga?ZOS{qm5(=(F>M1-{-&)>^^(tnY)AgE+;ZFm@IR64u&+i$>OSaVCfR6_s9 zA0hfs+KQ}|b5KC%`Yy2IgAic?0AfB81VPU8JTNm>1PGO?c>ecucw%v}$%<_ciqJvO zbZ>QJI$+sWE-3hiYuEJ-ky}JGS}ZK*{Za`OhHy{)1n`hX=5<{yZgt;{$A8e4 zNaPv3t2*5p2F^Ll8d5}4wrwY@h}GP9E!-kpIiE-+k!nxoNn4}o+1c8_X&RZ+mbDdz zaZ+oYG)w~!dF;q^oCs3sG)&9NLD6XE+}t1acrAziX{F*3*V(U0A0X1tLIDt|411+u zXbr Date: Wed, 28 Aug 2013 03:18:47 -0700 Subject: [PATCH 15/19] IPN improvements --- app/assets/stylesheets/application.css | 1 + app/controllers/ipns_controller.rb | 10 ++++++++++ app/controllers/macs_controller.rb | 12 +++++++++++- app/controllers/users_controller.rb | 4 ++-- app/models/ability.rb | 10 ++++++++-- app/models/user.rb | 4 ++-- app/views/ipns/show.html.erb | 7 ++++--- app/views/macs/scan.html.erb | 6 ++++-- app/views/users/show.html.erb | 8 +++++--- config/routes.rb | 1 + 10 files changed, 48 insertions(+), 15 deletions(-) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 43f1ef9..f8ee609 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -20,6 +20,7 @@ font-size: 13px; line-height: 18px; color: #333; text-align: center; +text-decoration: none; text-shadow: 0 1px 1px rgba(255,255,255,.75); vertical-align: middle; background-color: #f5f5f5; diff --git a/app/controllers/ipns_controller.rb b/app/controllers/ipns_controller.rb index 931556a..6451415 100644 --- a/app/controllers/ipns_controller.rb +++ b/app/controllers/ipns_controller.rb @@ -24,6 +24,16 @@ class IpnsController < ApplicationController #end end + def import + @ipn = Ipn.new_from_dynamic_params(params) + @ipn.data = params.to_json + @ipn.save + redirect_to ipn_path(@ipn) + #unless @ipn.validate! + # Rails.logger.error "Unable to validate IPN: #{@ipn.inspect}" + #end + end + def validate if @ipn.validate! redirect_to ipns_url, :notice => 'Valid!' diff --git a/app/controllers/macs_controller.rb b/app/controllers/macs_controller.rb index 245dfd7..8f8401a 100644 --- a/app/controllers/macs_controller.rb +++ b/app/controllers/macs_controller.rb @@ -1,5 +1,15 @@ class MacsController < ApplicationController -load_and_authorize_resource :mac, :except => [:index, :scan, :import] +rescue_from CanCan::AccessDenied do |exception| + today = Date.today + event = Date.new(2013,9,1) + + if today == event + redirect_to main_app.root_url, :alert => "CryptoParty today; no MAC scanning. Sorry, NSA!" + else + redirect_to main_app.root_url, :alert => "Nothing to see here!" + end +end +load_and_authorize_resource :mac load_and_authorize_resource :user, :through => :mac, :except => [:index, :show, :scan, :import] #require "active_record" diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 02cfc23..38f7e7f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -69,7 +69,7 @@ class UsersController < ApplicationController def create respond_to do |format| if @user.save - format.html { redirect_to users_url, :notice => 'User was successfully created.' } + format.html { redirect_to @user, :notice => 'User was successfully created.' } format.json { render :json => @user, :status => :created, :location => @user } else format.html { render :action => "new" } @@ -83,7 +83,7 @@ class UsersController < ApplicationController def update respond_to do |format| if @user.update_attributes(params[:user]) - format.html { redirect_to users_url, :notice => 'User was successfully updated.' } + format.html { redirect_to @user, :notice => 'User was successfully updated.' } format.json { head :no_content } else format.html { render :action => "edit" } diff --git a/app/models/ability.rb b/app/models/ability.rb index 70c3ee7..1c47f5e 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -3,14 +3,20 @@ class Ability def initialize(user) # Anonymous can read mac - can :read, Mac + today = Date.today + event = Date.new(2013,9,1) + + unless today == event + can :read, Mac + can :scan, Mac # Need anonymous so CRON can scan + end if !user.nil? # By default, users can only see their own stuff can :read, Card, :user_id => user.id can :read, Certification - can :read_details, Mac + can :read_details, Mac unless today == event can [:update], Mac, :user_id => nil can [:create,:update], Mac, :user_id => user.id can :read, User, :id => user.id #TODO: why can users update themselves? diff --git a/app/models/user.rb b/app/models/user.rb index beec17f..6e7fa48 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -86,7 +86,7 @@ class User < ActiveRecord::Base end def self.member_levels - {25 => "Associate", 50 => "Basic", 100 => "Plus"} + {25 => "Associate", 50 => "Basic", 75 => "Basic", 100 => "Plus"} end def member_status @@ -144,7 +144,7 @@ class User < ActiveRecord::Base # There are payments if self.payments.count > 0 then # They're on time - if self.payments.last.date > (DateTime.now - 45.days) + if self.payments.last.date > (DateTime.now - 60.days) flair = "-paid" else message = "Last Payment #{(DateTime.now - self.payments.last.date).to_i/30} months ago" diff --git a/app/views/ipns/show.html.erb b/app/views/ipns/show.html.erb index 8fdc7ec..146481c 100644 --- a/app/views/ipns/show.html.erb +++ b/app/views/ipns/show.html.erb @@ -8,10 +8,11 @@ <% end %>

    <% if @ipn.payment.present? %> - <%= link_to "Payment", @ipn.payment %> + <%= link_to "Linked Payment", @ipn.payment %> <% else %> - Couldn't link '<%= @ipn.payer_email %>' or payment amount '<%= @ipn.payment_gross.to_i %>' not a valid membership level. Please create payment manually. + Couldn't link automatically. Please create payment manually or adjust the user account and try again to <%= link_to "link email '#{@ipn.payer_email}' at membership level '#{@ipn.payment_gross.to_i}'", link_ipn_path(@ipn) %>. <% end %> +

    -<%= link_to "Back", ipns_path %> \ No newline at end of file +<%= link_to "Back", ipns_path %> diff --git a/app/views/macs/scan.html.erb b/app/views/macs/scan.html.erb index 5068661..74934ca 100644 --- a/app/views/macs/scan.html.erb +++ b/app/views/macs/scan.html.erb @@ -1,5 +1,7 @@ Scanning... -<% @log.each do |log| %> +<% if can? :read_details, Mac + @log.each do |log| %> <%= log.mac %> = <%= log.ip %>
    -<% end %> +<% end +end %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index eaa42c7..fe5e953 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -74,17 +74,19 @@

    <% end %>

    - Card:<%= link_to "+ Add", (new_card_path+"?user="+@user.id.to_s), :class => 'btn' if can? :create, Card %> + Card: <%= link_to "+ Add", (new_card_path+"?user="+@user.id.to_s), :class => 'btn' if can? :create, Card %> <% if current_user.admin? then %> +

      <% @user.cards.each do |c| %> - <%= link_to c.card_number, c %><%= "," unless c == @user.cards.last %> +
    • <%= link_to c.card_number, c %><%= "," unless c == @user.cards.last %>
    • <% end %> +
    <% else %> <%= unless @user.cards.blank? then raw("✓") end %> <% end %>

    -Certifications:<%= link_to "+ Add", (new_user_certification_path+"?user="+@user.id.to_s), :class => 'btn' if can? :create, UserCertification %> +Certifications: <%= link_to "+ Add", (new_user_certification_path+"?user="+@user.id.to_s), :class => 'btn' if can? :create, UserCertification %>
      <% @user.certifications.each do |certification| %>
    • <%= link_to certification.name, certification %>
    • diff --git a/config/routes.rb b/config/routes.rb index 4a4f563..4cfeb05 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,5 @@ Dooraccess::Application.routes.draw do + match 'ipns/import' => 'ipns#import', :as => :import_ipn resources :ipns match 'ipns/:id/link' => 'ipns#link', :as => :link_ipn match 'ipns/:id/validate' => 'ipns#validate', :as => :validate_ipn From 211d79cbcd1a662bdb25f6b1d31feab416c8a2ca Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Wed, 28 Aug 2013 04:25:54 -0700 Subject: [PATCH 16/19] Adding csv import --- app/controllers/paypal_csvs_controller.rb | 28 +++++++ app/models/ipn.rb | 8 ++ app/models/paypal_csv.rb | 73 +++++++++++++++++++ app/views/ipns/index.html.erb | 2 +- app/views/paypal_csvs/index.html.erb | 31 ++++++++ app/views/paypal_csvs/new.html.erb | 15 ++++ config/routes.rb | 3 + .../20130828104240_create_paypal_csvs.rb | 28 +++++++ db/schema.rb | 28 ++++++- test/fixtures/paypal_csvs.yml | 41 +++++++++++ test/unit/paypal_csv_test.rb | 7 ++ 11 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 app/controllers/paypal_csvs_controller.rb create mode 100644 app/models/paypal_csv.rb create mode 100644 app/views/paypal_csvs/index.html.erb create mode 100644 app/views/paypal_csvs/new.html.erb create mode 100644 db/migrate/20130828104240_create_paypal_csvs.rb create mode 100644 test/fixtures/paypal_csvs.yml create mode 100644 test/unit/paypal_csv_test.rb diff --git a/app/controllers/paypal_csvs_controller.rb b/app/controllers/paypal_csvs_controller.rb new file mode 100644 index 0000000..2f48d23 --- /dev/null +++ b/app/controllers/paypal_csvs_controller.rb @@ -0,0 +1,28 @@ +class PaypalCsvsController < ApplicationController + load_and_authorize_resource :paypal_csv + before_filter :authenticate_user! + + def index + end + + def show + end + + def new + end + + def create + PaypalCsv.batch_import_from_csv(params[:file].path) + redirect_to paypal_csvs_path, :notice => 'Paypal CSV batch was successfully loaded.' + end + + def link + result = @paypal_csv.link_payment + if result.first + redirect_to paypal_csvs_url, :notice => 'Payment was successfully linked.' + else + redirect_to paypal_csvs_url, :notice => result.last + end + end + +end diff --git a/app/models/ipn.rb b/app/models/ipn.rb index 2b37017..002a834 100644 --- a/app/models/ipn.rb +++ b/app/models/ipn.rb @@ -5,6 +5,14 @@ class Ipn < ActiveRecord::Base after_create :create_payment + def date_parsed + begin + Date.strptime(self.payment_date, "%H:%M:%S %b %e, %Y %Z") + rescue + Date.new + end + end + def self.new_from_dynamic_params(params) ipn = Ipn.new() diff --git a/app/models/paypal_csv.rb b/app/models/paypal_csv.rb new file mode 100644 index 0000000..1791aa6 --- /dev/null +++ b/app/models/paypal_csv.rb @@ -0,0 +1,73 @@ +require 'csv' +class PaypalCsv < ActiveRecord::Base + attr_accessible :data, :_address_status, :_counterparty_status, :_currency, :_fee, :_from_email_address, :_gross, :_item_id, :_item_title, :_name, :_net, :_status, :_time, :_time_zone, :_to_email_address, :_transaction_id, :_type, :date, :string + belongs_to :payment + + after_create :create_payment + + def date_parsed + begin + Date.strptime(self._date, "%m/%d/%Y") + rescue + Date.new + end + end + + def self.batch_import_from_csv(filename) + csv = CSV.table(filename) + csv.each do |row| + paypal_csv = PaypalCsv.new() + + paypal_csv.attributes.each do |c| + unless row[c.first.to_sym].nil? + paypal_csv[c.first.to_sym] = row[c.first.to_sym] + end + end + + paypal_csv.data = row.to_json + paypal_csv.save + end + + return true + end + + def link_payment + create_payment + end + + private + def create_payment + # find user by email, then by payee + user = User.find_by_email(self._from_email_address) + user = User.find_by_payee(self._from_email_address) if user.nil? && self._from_email_address.present? + + # Only create payments if the CSV matches a member + if user.present? + # And is a payment (not a cancellation, etc) + payment_types = ["Recurring Payment Received","Payment Received"] + if payment_types.include?(self._type) + # And a member level + if User.member_levels[self._gross.to_i].present? + payment = Payment.new + payment.date = Date.strptime(self._date, "%m/%d/%Y") #7/6/2013 for Jul 06 + payment.user_id = user.id + payment.amount = self._gross + if payment.save + self.payment_id = payment.id + self.save! + else + return [false, "Unable to link payment. Payment error: #{payment.errors.full_messages.first}"] + end + else + return [false, "Unable to link payment. Couldn't find membership level '#{self._gross.to_i}'."] + end + else + return [false, "Unable to link payment. Transaction is a '#{self._type}' instead of '#{payment_types.inspect}'."] + end + else + return [false, "Unable to link payment. Couldn't find user/payee '#{self._from_email_address}'."] + end + + return [true] + end +end diff --git a/app/views/ipns/index.html.erb b/app/views/ipns/index.html.erb index 724c670..70c7709 100644 --- a/app/views/ipns/index.html.erb +++ b/app/views/ipns/index.html.erb @@ -6,7 +6,7 @@ Item Amount - <% @ipns.reverse!.each do |ipn| %> + <% @ipns.sort_by(&:date_parsed).reverse!.each do |ipn| %> <%= ipn.payment_date %> <%= ipn.first_name %> <%= ipn.last_name %> diff --git a/app/views/paypal_csvs/index.html.erb b/app/views/paypal_csvs/index.html.erb new file mode 100644 index 0000000..305bf70 --- /dev/null +++ b/app/views/paypal_csvs/index.html.erb @@ -0,0 +1,31 @@ +<%= link_to "Upload CSV", new_paypal_csv_path %> + + + + + + + +<% @paypal_csvs.sort_by(&:date_parsed).reverse!.each do |paypal_csv| %> + + + + + + + + +<% end %> +
      DateNameItemAmount
      <%= paypal_csv.date %><%= paypal_csv._name %><%= paypal_csv._item_title %> + <% if paypal_csv._gross.blank? %> + <%= paypal_csv._type %> + <% else %> + <%= paypal_csv._gross %> + <% end %> + + <% if paypal_csv.payment.present? %> + <%= link_to "Linked Payment", paypal_csv.payment %> + <% else %> + <%= link_to "Try to link email '#{paypal_csv._from_email_address}' at membership level '#{paypal_csv._gross.to_i}'", link_paypal_csv_path(paypal_csv) %> + <% end %> + <%= link_to "Details", paypal_csv %>
      diff --git a/app/views/paypal_csvs/new.html.erb b/app/views/paypal_csvs/new.html.erb new file mode 100644 index 0000000..275f41e --- /dev/null +++ b/app/views/paypal_csvs/new.html.erb @@ -0,0 +1,15 @@ + + +<%= form_tag('/paypal_csvs', :multipart => true) do |f| %> +
      + <%= label_tag :file %> + <%= file_field_tag :file %> +
      +
      + <%= submit_tag %> +
      +<% end %> \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 4cfeb05..ba34d4d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,6 +4,9 @@ Dooraccess::Application.routes.draw do match 'ipns/:id/link' => 'ipns#link', :as => :link_ipn match 'ipns/:id/validate' => 'ipns#validate', :as => :validate_ipn + resources :paypal_csvs + match 'paypal_csvs/:id/link' => 'paypal_csvs#link', :as => :link_paypal_csv + resources :payments resources :user_certifications diff --git a/db/migrate/20130828104240_create_paypal_csvs.rb b/db/migrate/20130828104240_create_paypal_csvs.rb new file mode 100644 index 0000000..f0d682e --- /dev/null +++ b/db/migrate/20130828104240_create_paypal_csvs.rb @@ -0,0 +1,28 @@ +class CreatePaypalCsvs < ActiveRecord::Migration + def change + create_table :paypal_csvs do |t| + t.integer :payment_id + t.text :data + t.string :date + t.string :_time + t.string :_time_zone + t.string :_name + t.string :_type + t.string :_status + t.string :_currency + t.string :_gross + t.string :_fee + t.string :_net + t.string :_from_email_address + t.string :_to_email_address + t.string :_transaction_id + t.string :_counterparty_status + t.string :_address_status + t.string :_item_title + t.string :_item_id + t.string :string + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index ac3f771..25dbad2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -10,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130824072157) do +ActiveRecord::Schema.define(:version => 20130828104240) do create_table "cards", :force => true do |t| t.string "card_number" @@ -90,6 +91,31 @@ ActiveRecord::Schema.define(:version => 20130824072157) do add_index "payments", ["user_id"], :name => "index_payments_on_user_id" + create_table "paypal_csvs", :force => true do |t| + t.integer "payment_id" + t.text "data" + t.string "date" + t.string "_time" + t.string "_time_zone" + t.string "_name" + t.string "_type" + t.string "_status" + t.string "_currency" + t.string "_gross" + t.string "_fee" + t.string "_net" + t.string "_from_email_address" + t.string "_to_email_address" + t.string "_transaction_id" + t.string "_counterparty_status" + t.string "_address_status" + t.string "_item_title" + t.string "_item_id" + t.string "string" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "user_certifications", :force => true do |t| t.integer "user_id" t.integer "certification_id" diff --git a/test/fixtures/paypal_csvs.yml b/test/fixtures/paypal_csvs.yml new file mode 100644 index 0000000..c103612 --- /dev/null +++ b/test/fixtures/paypal_csvs.yml @@ -0,0 +1,41 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html + +one: + date: MyString + _time: MyString + _time_zone: MyString + _name: MyString + _type: MyString + _status: MyString + _currency: MyString + _gross: MyString + _fee: MyString + _net: MyString + _from_email_address: MyString + _to_email_address: MyString + _transaction_id: MyString + _counterparty_status: MyString + _address_status: MyString + _item_title: MyString + _item_id: MyString + string: MyString + +two: + date: MyString + _time: MyString + _time_zone: MyString + _name: MyString + _type: MyString + _status: MyString + _currency: MyString + _gross: MyString + _fee: MyString + _net: MyString + _from_email_address: MyString + _to_email_address: MyString + _transaction_id: MyString + _counterparty_status: MyString + _address_status: MyString + _item_title: MyString + _item_id: MyString + string: MyString diff --git a/test/unit/paypal_csv_test.rb b/test/unit/paypal_csv_test.rb new file mode 100644 index 0000000..c610e02 --- /dev/null +++ b/test/unit/paypal_csv_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class PaypalCsvTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From 10a1e4eb843fccae36fd8d877bf9ee7fd94585fb Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Wed, 28 Aug 2013 05:15:50 -0700 Subject: [PATCH 17/19] Switching to RVM, View tweaks --- .rvmrc | 35 ++++++++++++++++++++++++++++ Gemfile | 4 +++- Gemfile.lock | 6 +++++ app/models/payment.rb | 1 + app/views/ipns/index.html.erb | 6 +++-- app/views/payments/index.html.erb | 9 +++++-- app/views/payments/show.html.erb | 7 +++++- app/views/paypal_csvs/index.html.erb | 6 ++++- db/schema.rb | 1 - 9 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 .rvmrc diff --git a/.rvmrc b/.rvmrc new file mode 100644 index 0000000..d462736 --- /dev/null +++ b/.rvmrc @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# This is an RVM Project .rvmrc file, used to automatically load the ruby +# development environment upon cd'ing into the directory + +# First we specify our desired [@], the @gemset name is optional, +# Only full ruby name is supported here, for short names use: +# echo "rvm use 1.8.7" > .rvmrc +environment_id="ruby-1.9.3-p385@members-hsl" + +# Uncomment the following lines if you want to verify rvm version per project +# rvmrc_rvm_version="1.18.8 (stable)" # 1.10.1 seams as a safe start +# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || { +# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading." +# return 1 +# } + +# First we attempt to load the desired environment directly from the environment +# file. This is very fast and efficient compared to running through the entire +# CLI and selector. If you want feedback on which environment was used then +# insert the word 'use' after --create as this triggers verbose mode. +if [[ -d "${rvm_path:-$HOME/.rvm}/environments" + && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]] +then + \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id" + [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] && + \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true +else + # If the environment file has not yet been created, use the RVM CLI to select. + rvm --create "$environment_id" || { + echo "Failed to create RVM environment '${environment_id}'." + return 1 + } +fi + diff --git a/Gemfile b/Gemfile index 921e64e..ca2b42f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -#ruby '1.9.3' +ruby '1.9.3' gem 'rails', '3.2.3' @@ -46,3 +46,5 @@ gem 'bcrypt-ruby', '~> 3.0.0' #gem "paperclip", "~> 3.0" gem 'gravtastic' + +gem 'passenger' diff --git a/Gemfile.lock b/Gemfile.lock index 94abf55..0f56725 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,6 +39,7 @@ GEM coffee-script-source execjs coffee-script-source (1.3.3) + daemon_controller (1.1.5) devise (2.1.1) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) @@ -63,6 +64,10 @@ GEM mime-types (1.19) multi_json (1.3.6) orm_adapter (0.1.0) + passenger (4.0.14) + daemon_controller (>= 1.1.0) + rack + rake (>= 0.8.1) polyglot (0.3.3) rack (1.4.1) rack-cache (1.2) @@ -124,6 +129,7 @@ DEPENDENCIES gravtastic jquery-rails json + passenger rails (= 3.2.3) sass-rails (~> 3.2.3) sqlite3 diff --git a/app/models/payment.rb b/app/models/payment.rb index ea70430..4b10b67 100644 --- a/app/models/payment.rb +++ b/app/models/payment.rb @@ -1,6 +1,7 @@ class Payment < ActiveRecord::Base belongs_to :user has_one :ipn + has_one :paypal_csv attr_accessible :date, :user_id, :created_by, :amount validates_presence_of :user_id, :date, :amount # not created_by diff --git a/app/views/ipns/index.html.erb b/app/views/ipns/index.html.erb index 70c7709..1951302 100644 --- a/app/views/ipns/index.html.erb +++ b/app/views/ipns/index.html.erb @@ -1,4 +1,7 @@ - +

      PayPal IPN Records

      +

      + Automatically loaded from PayPal's servers +

      @@ -30,4 +33,3 @@ <% end %>
      Date
      - diff --git a/app/views/payments/index.html.erb b/app/views/payments/index.html.erb index 58bbd83..a12862a 100644 --- a/app/views/payments/index.html.erb +++ b/app/views/payments/index.html.erb @@ -1,7 +1,12 @@

      Listing payments

      -<%= link_to 'New Payment', new_payment_path %> -
      +

      +Create Payments: +<%= link_to 'Manually', new_payment_path %> | +<%= link_to 'Batched CSV', paypal_csvs_path %> | +<%= link_to 'IPN', ipns_path %> +

      + diff --git a/app/views/payments/show.html.erb b/app/views/payments/show.html.erb index 316d2ec..95cc9db 100644 --- a/app/views/payments/show.html.erb +++ b/app/views/payments/show.html.erb @@ -35,7 +35,12 @@ <% if @payment.ipn.present? %>

      - <%= link_to "Paid via PayPal", @payment.ipn %> + <%= link_to "Paid via PayPal (IPN)", @payment.ipn %> +

      +<% end %> +<% if @payment.paypal_csv.present? %> +

      + <%= link_to "Paid via PayPal (CSV)", @payment.paypal_csv %>

      <% end %> diff --git a/app/views/paypal_csvs/index.html.erb b/app/views/paypal_csvs/index.html.erb index 305bf70..2343574 100644 --- a/app/views/paypal_csvs/index.html.erb +++ b/app/views/paypal_csvs/index.html.erb @@ -1,4 +1,8 @@ -<%= link_to "Upload CSV", new_paypal_csv_path %> +

      PayPal CSV Records

      +

      + <%= link_to "Upload CSV", new_paypal_csv_path %> +

      +
      User
      diff --git a/db/schema.rb b/db/schema.rb index 25dbad2..3cb31fb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,4 +1,3 @@ -# encoding: UTF-8 # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. From e8e024c042bc02e1493a9d7ea77a8ad8ca95535d Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Wed, 28 Aug 2013 05:14:10 -0700 Subject: [PATCH 18/19] Adding CSV show --- app/views/paypal_csvs/show.html.erb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 app/views/paypal_csvs/show.html.erb diff --git a/app/views/paypal_csvs/show.html.erb b/app/views/paypal_csvs/show.html.erb new file mode 100644 index 0000000..5aa7bb2 --- /dev/null +++ b/app/views/paypal_csvs/show.html.erb @@ -0,0 +1,17 @@ +

      PayPal CSV Item Details

      +<% @paypal_csv.attributes.except("data","payment_id").each do |attr| %> +

      + <%= attr.first.to_s %>: + <%= @paypal_csv.attributes[attr.first] %> +

      +<% end %> + +

      + <% if @paypal_csv.payment.present? %> + <%= link_to "Linked Payment", @paypal_csv.payment %> + <% else %> + Couldn't link automatically. Please create payment manually or adjust the user account and try again to <%= link_to "link email '#{@paypal_csv._from_email_address}' at membership level '#{@paypal_csv._gross.to_i}'", link_paypal_csv_path(@paypal_csv) %>. + <% end %> + +

      +<%= link_to "Back", paypal_csvs_path %> \ No newline at end of file From 25c0d1e1cbee17d1f4e99df1431b888fdfe7b9e0 Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Wed, 28 Aug 2013 08:19:01 -0700 Subject: [PATCH 19/19] Finalized payments and CSVs and inactivity --- app/controllers/home_controller.rb | 2 + app/controllers/users_controller.rb | 4 ++ app/models/ability.rb | 4 +- app/models/ipn.rb | 5 ++- app/models/payment.rb | 1 - app/models/paypal_csv.rb | 4 +- app/models/user.rb | 42 +++++++++++++++---- app/views/home/index.html.erb | 2 + app/views/users/inactive.html.erb | 65 +++++++++++++++++++++++++++++ app/views/users/index.html.erb | 3 ++ config/routes.rb | 1 + 11 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 app/views/users/inactive.html.erb diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index da2d5ef..838f7de 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -5,6 +5,8 @@ def index @recent_certs = UserCertification.where("created_at > ?", DateTime.now - 7.days).count @num_users = User.count @recent_users = User.where("created_at > ?", DateTime.now - 7.days).count + @num_paid_users = User.all.select{|u| u.member_status >= 250 }.count + @num_delinquent_users = User.all.select{|u| !u.payment_status }.count if can? :read, User then @recent_user_names = User.where("member_level > 10").accessible_by(current_ability).order('created_at desc').limit(5) end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 38f7e7f..4b6877c 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -33,6 +33,10 @@ class UsersController < ApplicationController end end + def inactive + @users = @users.all.select{|u| u if u.payment_status == false }.sort_by{ |u| -u.delinquency } + end + # GET /users/1 # GET /users/1.json def show diff --git a/app/models/ability.rb b/app/models/ability.rb index 1c47f5e..4f5ea1b 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -35,9 +35,11 @@ class Ability can :read, UserCertification end - # Accountants can manage all + # Accountants can manage payments if user.accountant? can :manage, Payment + can :manage, Ipn + can :manage, PaypalCsv end # Admins can manage all diff --git a/app/models/ipn.rb b/app/models/ipn.rb index 002a834..417884a 100644 --- a/app/models/ipn.rb +++ b/app/models/ipn.rb @@ -45,6 +45,7 @@ class Ipn < ActiveRecord::Base end unless response == "VERIFIED" Rails.logger.error "Invalid IPN: #{response}" + Rails.logger.error "Data: #{self.data}" return false end @@ -58,8 +59,8 @@ class Ipn < ActiveRecord::Base private def create_payment # find user by email, then by payee - user = User.find_by_email(self.payer_email) - user = User.find_by_payee(self.payer_email) if user.nil? && self.payer_email.present? + user = User.where("lower(email) = ?", self._from_email_address.downcase).first + user = User.where("lower(payee) = ?", self._from_email_address.downcase).first if user.nil? && self._from_email_address.present? # Only create payments if the IPN matches a member if user.present? diff --git a/app/models/payment.rb b/app/models/payment.rb index 4b10b67..8c4ab5b 100644 --- a/app/models/payment.rb +++ b/app/models/payment.rb @@ -7,7 +7,6 @@ class Payment < ActiveRecord::Base validates_presence_of :user_id, :date, :amount # not created_by validates_uniqueness_of :date, :scope => :user_id, :message => ' of payment already exists for this user.' - def human_date if date.year < DateTime.now.year date.strftime("%b %e, %y") diff --git a/app/models/paypal_csv.rb b/app/models/paypal_csv.rb index 1791aa6..d150e8e 100644 --- a/app/models/paypal_csv.rb +++ b/app/models/paypal_csv.rb @@ -38,8 +38,8 @@ class PaypalCsv < ActiveRecord::Base private def create_payment # find user by email, then by payee - user = User.find_by_email(self._from_email_address) - user = User.find_by_payee(self._from_email_address) if user.nil? && self._from_email_address.present? + user = User.where("lower(email) = ?", self._from_email_address.downcase).first + user = User.where("lower(payee) = ?", self._from_email_address.downcase).first if user.nil? && self._from_email_address.present? # Only create payments if the CSV matches a member if user.present? diff --git a/app/models/user.rb b/app/models/user.rb index 6e7fa48..899ee2f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -89,6 +89,11 @@ class User < ActiveRecord::Base {25 => "Associate", 50 => "Basic", 75 => "Basic", 100 => "Plus"} end + def payment_status + results = payment_status_calculation + return results[:paid] + end + def member_status results = member_status_calculation return results[:rank] @@ -99,6 +104,15 @@ class User < ActiveRecord::Base return "" end + def delinquency + if self.payments.count > 0 + paydate = self.payments.maximum(:date) + (Date.today - paydate).to_i + else + (Date.today - self.created_at.to_date).to_i + end + end + private def member_status_calculation @@ -112,7 +126,7 @@ class User < ActiveRecord::Base case self.member_level.to_i when 0..9 if self.payments.count > 0 then - message = "Former Member (#{(DateTime.now - self.payments.last.date).to_i} days ago)" + message = "Former Member (#{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago)" icon = :timeout rank = 1 else @@ -138,27 +152,41 @@ class User < ActiveRecord::Base rank = 1000 end + payment_results = payment_status_calculation + flair = payment_results[:flair] + rank = rank/10 unless payment_results[:paid] + message = payment_results[:message] unless payment_results[:message].blank? + + return {:message => message, :icon => icon, :flair => flair, :rank => rank} + end + + def payment_status_calculation + flair = "" + message = "" + paid = true + # Second status item is payment status case self.member_level.to_i when 25..999 # There are payments if self.payments.count > 0 then # They're on time - if self.payments.last.date > (DateTime.now - 60.days) + if self.payments.maximum(:date) > (DateTime.now - 60.days) flair = "-paid" + paid = true else - message = "Last Payment #{(DateTime.now - self.payments.last.date).to_i/30} months ago" - rank = rank/10 + message = "Last Payment #{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago" + paid = false end else message = "No Payments Recorded" - rank = rank/10 + paid = false end end - - return {:message => message, :icon => icon, :flair => flair, :rank => rank} + return {:message => message, :paid => paid, :flair => flair} end + def send_new_user_email Rails.logger.info UserMailer.new_user_email(self).deliver end diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index 9d47d6d..6be51a1 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -27,6 +27,8 @@
      <%= @num_users %> (<%= @recent_users %> new in the last 7 days)
      # of People Certified:
      <%= @num_certs %> (<%= @recent_certs %> new in the last 7 days)
      +
      # of Current Paying Members:
      +
      <%= @num_paid_users %> (<%= @num_delinquent_users %> not-current)
      # of Door Accesses Granted:
      <%= @num_door_opens %> (<%= @today_door_opens %> today, <%= @recent_door_opens %> in the last 7 days)
      # of Door Accesses Denied:
      diff --git a/app/views/users/inactive.html.erb b/app/views/users/inactive.html.erb new file mode 100644 index 0000000..35c7911 --- /dev/null +++ b/app/views/users/inactive.html.erb @@ -0,0 +1,65 @@ +

      Inactive Users

      + +
      Date
      + + + <% if current_user.admin? then %><% end %> + + <% if current_user.admin? %>+ <% end %> + + + + + + + + <% if current_user.admin? then %><% end %> + + <% if current_user.admin? then %> + + <% end %> + + + + + + + + + +<% if !@users.blank? %> + <% @users.each do |user| %> + + + <% if current_user.admin? then %><% end %> + + <% if current_user.admin? then %><% end %> + + + + + + + + + <% end %> +<% end %> +
      NameEmailCertificationsOrientation?Card?Pmt MethodDesired LevelLast PaymentJoined
      <%= image_tag user.gravatar_url(:default => "http://members.heatsynclabs.org/assets/nil.png"), :class => :avatar %><%= link_to user.name, user %><%= user.email %><% user.certifications.each do |c| %> + <%= link_to c.name, c %><%= "," unless c.id == user.certifications.last.id %> + <% end %> + <%= unless user.orientation.blank? then raw("") end %> + <%= unless user.cards.blank? then raw("") end %><%= user.payment_method %><%= user.member_level %><% delinquency = user.delinquency %> + <% if delinquency == 9999 %> + No Payments + <% else %> + <%= (delinquency/30).to_s+" mo. ago" %> + <% end %> + <%= user.created_at.to_date %><%= link_to 'Edit', edit_user_path(user) if can? :update, user %><%= link_to 'Destroy', user, :confirm => 'Are you sure? WARNING: THIS DOES NOT REMOVE THE USER FROM THE DOOR SYSTEM! DISABLE THEM FIRST.', :method => :delete if can? :destroy, user %>
      + +<% if current_user.orientation.blank? then %> +

      There's a lot more to see here, but our records show you haven't completed the new member orientation yet. If that's incorrect, please contact a volunteer.

      +<% end %> + +
      diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 19a48e2..bcbff58 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -6,6 +6,9 @@ <% if can? :manage, User %> | <%= link_to 'Merge Users', users_merge_path %> <% end %> +<% if current_user.admin? %> + | <%= link_to 'Inactive Users', users_inactive_path %> +<% end %> diff --git a/config/routes.rb b/config/routes.rb index ba34d4d..46ceb98 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -28,6 +28,7 @@ Dooraccess::Application.routes.draw do match 'user_summary/:id' => 'users#user_summary' # User summary view match 'users/merge' => 'users#merge_view', :via => :get # Merge view match 'users/merge' => 'users#merge_action', :via => :post # Merge action + match 'users/inactive' => 'users#inactive' # Inactive users report resources :users match 'users/create' => 'users#create', :via => :post # Use POST users/create instead of POST users to avoid devise conflict