Merge pull request #1 from zyphlar/master

Getting the HSL copy up to date
This commit is contained in:
Will Bradley 2013-12-02 01:26:33 -08:00
commit d807aad668
276 changed files with 29538 additions and 477 deletions

6
.gitignore vendored
View File

@ -9,13 +9,17 @@
# Ignore the default SQLite database. # Ignore the default SQLite database.
/db/*.sqlite3 /db/*.sqlite3
/db/*.sqlite3.*
# Ignore all logfiles and tempfiles. # Ignore all logfiles and tempfiles.
/log/*.log /log/*.log
/tmp /tmp
tmp/
# Ignore compiled assets # Ignore compiled assets
/public/assets /public/assets
# Ignore config file # Ignore config and database files (passwords)
/config/config.yml /config/config.yml
/config/database.yml
/config/initializers/secret_token.rb

35
.rvmrc Normal file
View File

@ -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 <ruby>[@<gemset>], 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

12
Gemfile
View File

@ -1,11 +1,15 @@
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'rails', '3.2.3' ruby '1.9.3'
gem 'rails', '3.2.8'
# Bundle edge Rails instead: # Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git' # gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'sqlite3' gem 'sqlite3'
gem 'pg'
gem 'taps'
gem 'json' gem 'json'
@ -40,7 +44,11 @@ gem 'bcrypt-ruby', '~> 3.0.0'
# gem 'capistrano' # gem 'capistrano'
# To use debugger # To use debugger
# gem 'ruby-debug' #gem 'debugger'
#gem "paperclip", "~> 3.0" #gem "paperclip", "~> 3.0"
gem 'gravtastic' gem 'gravtastic'
gem 'passenger'
gem "rails-settings-cached", "0.2.4"

View File

@ -1,116 +1,135 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actionmailer (3.2.3) actionmailer (3.2.8)
actionpack (= 3.2.3) actionpack (= 3.2.8)
mail (~> 2.4.4) mail (~> 2.4.4)
actionpack (3.2.3) actionpack (3.2.8)
activemodel (= 3.2.3) activemodel (= 3.2.8)
activesupport (= 3.2.3) activesupport (= 3.2.8)
builder (~> 3.0.0) builder (~> 3.0.0)
erubis (~> 2.7.0) erubis (~> 2.7.0)
journey (~> 1.0.1) journey (~> 1.0.4)
rack (~> 1.4.0) rack (~> 1.4.0)
rack-cache (~> 1.2) rack-cache (~> 1.2)
rack-test (~> 0.6.1) rack-test (~> 0.6.1)
sprockets (~> 2.1.2) sprockets (~> 2.1.3)
activemodel (3.2.3) activemodel (3.2.8)
activesupport (= 3.2.3) activesupport (= 3.2.8)
builder (~> 3.0.0) builder (~> 3.0.0)
activerecord (3.2.3) activerecord (3.2.8)
activemodel (= 3.2.3) activemodel (= 3.2.8)
activesupport (= 3.2.3) activesupport (= 3.2.8)
arel (~> 3.0.2) arel (~> 3.0.2)
tzinfo (~> 0.3.29) tzinfo (~> 0.3.29)
activeresource (3.2.3) activeresource (3.2.8)
activemodel (= 3.2.3) activemodel (= 3.2.8)
activesupport (= 3.2.3) activesupport (= 3.2.8)
activesupport (3.2.3) activesupport (3.2.8)
i18n (~> 0.6) i18n (~> 0.6)
multi_json (~> 1.0) multi_json (~> 1.0)
arel (3.0.2) arel (3.0.2)
bcrypt-ruby (3.0.1) bcrypt-ruby (3.0.1)
builder (3.0.0) builder (3.0.4)
cancan (1.6.8) cancan (1.6.10)
coffee-rails (3.2.2) coffee-rails (3.2.2)
coffee-script (>= 2.2.0) coffee-script (>= 2.2.0)
railties (~> 3.2.0) railties (~> 3.2.0)
coffee-script (2.2.0) coffee-script (2.2.0)
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.3.3) coffee-script-source (1.6.3)
devise (2.1.1) daemon_controller (1.1.5)
devise (2.2.7)
bcrypt-ruby (~> 3.0) bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (~> 3.1) railties (~> 3.1)
warden (~> 1.2.1) warden (~> 1.2.1)
erubis (2.7.0) erubis (2.7.0)
execjs (1.4.0) execjs (2.0.2)
multi_json (~> 1.0)
gravtastic (3.2.6) gravtastic (3.2.6)
hike (1.2.1) hike (1.2.3)
i18n (0.6.0) i18n (0.6.5)
journey (1.0.4) journey (1.0.4)
jquery-rails (2.1.1) jquery-rails (3.0.4)
railties (>= 3.1.0, < 5.0) railties (>= 3.0, < 5.0)
thor (~> 0.14) thor (>= 0.14, < 2.0)
json (1.7.5) json (1.8.1)
libv8 (3.3.10.4) libv8 (3.16.14.3)
mail (2.4.4) mail (2.4.4)
i18n (>= 0.4.0) i18n (>= 0.4.0)
mime-types (~> 1.16) mime-types (~> 1.16)
treetop (~> 1.4.8) treetop (~> 1.4.8)
mime-types (1.19) mime-types (1.25)
multi_json (1.3.6) multi_json (1.8.2)
orm_adapter (0.1.0) orm_adapter (0.4.0)
passenger (4.0.19)
daemon_controller (>= 1.1.0)
rack
rake (>= 0.8.1)
pg (0.17.0)
polyglot (0.3.3) polyglot (0.3.3)
rack (1.4.1) rack (1.4.5)
rack-cache (1.2) rack-cache (1.2)
rack (>= 0.4) rack (>= 0.4)
rack-ssl (1.3.2) rack-ssl (1.3.3)
rack rack
rack-test (0.6.1) rack-test (0.6.2)
rack (>= 1.0) rack (>= 1.0)
rails (3.2.3) rails (3.2.8)
actionmailer (= 3.2.3) actionmailer (= 3.2.8)
actionpack (= 3.2.3) actionpack (= 3.2.8)
activerecord (= 3.2.3) activerecord (= 3.2.8)
activeresource (= 3.2.3) activeresource (= 3.2.8)
activesupport (= 3.2.3) activesupport (= 3.2.8)
bundler (~> 1.0) bundler (~> 1.0)
railties (= 3.2.3) railties (= 3.2.8)
railties (3.2.3) rails-settings-cached (0.2.4)
actionpack (= 3.2.3) rails (>= 3.0.0)
activesupport (= 3.2.3) railties (3.2.8)
actionpack (= 3.2.8)
activesupport (= 3.2.8)
rack-ssl (~> 1.3.2) rack-ssl (~> 1.3.2)
rake (>= 0.8.7) rake (>= 0.8.7)
rdoc (~> 3.4) rdoc (~> 3.4)
thor (~> 0.14.6) thor (>= 0.14.6, < 2.0)
rake (0.9.2.2) rake (10.1.0)
rdoc (3.12) rdoc (3.12.2)
json (~> 1.4) json (~> 1.4)
sass (3.2.1) ref (1.0.5)
sass-rails (3.2.5) rest-client (1.6.7)
mime-types (>= 1.16)
sass (3.2.11)
sass-rails (3.2.6)
railties (~> 3.2.0) railties (~> 3.2.0)
sass (>= 3.1.10) sass (>= 3.1.10)
tilt (~> 1.3) tilt (~> 1.3)
sequel (3.20.0)
sinatra (1.0)
rack (>= 1.0)
sprockets (2.1.3) sprockets (2.1.3)
hike (~> 1.2) hike (~> 1.2)
rack (~> 1.0) rack (~> 1.0)
tilt (~> 1.1, != 1.3.0) tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.6) sqlite3 (1.3.8)
therubyracer (0.10.1) taps (0.3.24)
libv8 (~> 3.3.10) rack (>= 1.0.1)
thor (0.14.6) rest-client (>= 1.4.0, < 1.7.0)
tilt (1.3.3) sequel (~> 3.20.0)
treetop (1.4.10) sinatra (~> 1.0.0)
therubyracer (0.12.0)
libv8 (~> 3.16.14.0)
ref
thor (0.18.1)
tilt (1.4.1)
treetop (1.4.15)
polyglot polyglot
polyglot (>= 0.3.1) polyglot (>= 0.3.1)
tzinfo (0.3.33) tzinfo (0.3.38)
uglifier (1.2.7) uglifier (2.2.1)
execjs (>= 0.3.0) execjs (>= 0.3.0)
multi_json (~> 1.3) multi_json (~> 1.0, >= 1.0.2)
warden (1.2.1) warden (1.2.3)
rack (>= 1.0) rack (>= 1.0)
PLATFORMS PLATFORMS
@ -124,8 +143,12 @@ DEPENDENCIES
gravtastic gravtastic
jquery-rails jquery-rails
json json
rails (= 3.2.3) passenger
pg
rails (= 3.2.8)
rails-settings-cached (= 0.2.4)
sass-rails (~> 3.2.3) sass-rails (~> 3.2.3)
sqlite3 sqlite3
taps
therubyracer therubyracer
uglifier (>= 1.0.3) uglifier (>= 1.0.3)

View File

@ -10,6 +10,12 @@ Distributed under a Creative Commons Attribution 3.0 license http://creativecomm
To use: To use:
* Load into a Rails 3 environment * Load into a Rails 3 environment
* Rename config/config.yml.example to config/config.yml and edit appropriately * Copy config/config.yml.example to config/config.yml and edit appropriately
* Use the Rails console to create a new User and set user.admin = true * Copy config/database.yml.example to config/database.yml and edit appropriately
* Copy config/initializers/secret_token.rb.example to config/config/initializers/secret_token.rb and edit appropriately
* Run bundle install, rake db:migrate, etc. * Run bundle install, rake db:migrate, etc.
* Use the Rails console to create a new User with admin rights
* user = User.new
* user.password = "foobar"
* user.admin = true
* user.save

View File

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

View File

@ -12,3 +12,46 @@
*= require_tree . *= require_tree .
*/ */
.caption { display: inline-block; background-color: #eee; border: 1px solid #333; border-radius: 5px; margin-bottom: 1em; } .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-decoration: none;
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);
}
.member-status-symbol, .social-icon {
vertical-align: middle;
}
a.social-link:hover {
background: transparent;
}
.lined-table td {
border-bottom: 1px dashed black;
}

View File

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

View File

@ -6,8 +6,16 @@
.hoverinfo { cursor: progress; } .hoverinfo { cursor: progress; }
.payment_links { background-color: #ddd; padding: 1em; border-radius: 1em; .payment_links { background-color: #ddd; padding: 1em; border-radius: 1em;
display: inline-block; float: right; } display: inline-block; float: right; max-width: 30%; min-width: 10em;}
.payment-highlighted {
background-color: orange !important;
}
.avatar { height: 2em; width: 2em; } .avatar { height: 2em; width: 2em; }
.avatar-large {
vertical-align: top;
}
textarea { height: 10em; } textarea { height: 10em; }

View File

@ -7,9 +7,18 @@ class ApplicationController < ActionController::Base
else else
flash[:alert] = "Nothing to see here!" flash[:alert] = "Nothing to see here!"
end end
Rails.logger.warn "----------\r\nWARNING: AccessDenied Exception: #{exception.inspect} User: #{current_user.inspect}\r\n----------"
redirect_to root_url redirect_to root_url
end end
@payment_methods = [[nil],["PayPal"],["Dwolla"],["Bill Pay"],["Check"],["Cash"],["Other"]] @payment_methods = [[nil],["PayPal"],["Dwolla"],["Bill Pay"],["Check"],["Cash"],["Other"]]
@payment_instructions = {nil => nil, :paypal => "Set up a monthly recurring payment to hslfinances@gmail.com", :dwolla => "Set up a monthly recurring payment to hslfinances@gmail.com", :billpay => "Have your bank send a monthly check to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201", :check => "Mail to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201 OR put in the drop safe at the Lab with a deposit slip firmly attached each month.", :cash => "Put in the drop safe at the Lab with a deposit slip firmly attached each month.", :other => "Hmm... talk to a Treasurer!"} @payment_instructions = {nil => nil, :paypal => "Set up a monthly recurring payment to hslfinances@gmail.com", :dwolla => "Set up a monthly recurring payment to hslfinances@gmail.com", :billpay => "Have your bank send a monthly check to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201", :check => "Mail to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201 OR put in the drop safe at the Lab with a deposit slip firmly attached each month.", :cash => "Put in the drop safe at the Lab with a deposit slip firmly attached each month.", :other => "Hmm... talk to a Treasurer!"}
end end
# Add a "fit" function to sanitize inputs for mac history
class Fixnum
def fit(range)
self > range.max ? range.max : (self < range.min ? range.min : self)
end
end

View File

@ -11,15 +11,16 @@ class CardsController < ApplicationController
if can? :read, DoorLog then if can? :read, DoorLog then
most_active_count = 0 most_active_count = 0
runner_up_count = 0
@most_active_card = nil @most_active_card = nil
@runner_up_card = nil
@cards.each do |card| @cards.each do |card|
card_num_R = card.card_number.to_i(16)%32767 card_num_R = card.card_number.to_i(16)%32767
card[:accesses_this_week] = DoorLog.where('key = "R" AND data =? AND created_at > ?', card_num_R, DateTime.now - 7.days).order("created_at DESC").count card[:accesses_this_week] = DoorLog.where("key = ? AND data = ? AND created_at > ?", 'G', card_num_R, DateTime.now - 1.month).order("created_at DESC").group_by { |d| d.created_at.beginning_of_day }.count
if(card[:accesses_this_week] > most_active_count) then
most_active_count = card[:accesses_this_week]
@most_active_card = card
end
end end
@most_active_cards = @cards.sort{|a,b| b[:accesses_this_week] <=> a[:accesses_this_week]}
@most_active_card = @most_active_cards[0]
@runner_up_card = @most_active_cards[1]
end end
respond_to do |format| respond_to do |format|
@ -33,7 +34,7 @@ class CardsController < ApplicationController
def show def show
if can? :read, DoorLog then if can? :read, DoorLog then
card_num_R = @card.card_number.to_i(16)%32767 card_num_R = @card.card_number.to_i(16)%32767
@door_logs = DoorLog.where('key = "R" AND data =?', card_num_R).order("created_at DESC") @door_logs = DoorLog.where('key = ? AND data = ?', "G", card_num_R).order("created_at DESC")
end end
respond_to do |format| respond_to do |format|
format.html # show.html.erb format.html # show.html.erb

View File

@ -1,6 +1,6 @@
class CertificationsController < ApplicationController class CertificationsController < ApplicationController
load_and_authorize_resource :certification load_and_authorize_resource :certification
load_and_authorize_resource :user, :through => :certification #load_and_authorize_resource :user, :through => :certification
before_filter :authenticate_user! before_filter :authenticate_user!
# GET /certifications # GET /certifications

View File

@ -26,6 +26,7 @@ class DoorLogsController < ApplicationController
# GET /door_logs/auto_download # GET /door_logs/auto_download
def auto_download def auto_download
@results = DoorLog.download_from_door @results = DoorLog.download_from_door
@status = DoorLog.download_status # for space_api
respond_to do |format| respond_to do |format|
format.html # show.html.erb format.html # show.html.erb

View File

@ -5,14 +5,22 @@ def index
@recent_certs = UserCertification.where("created_at > ?", DateTime.now - 7.days).count @recent_certs = UserCertification.where("created_at > ?", DateTime.now - 7.days).count
@num_users = User.count @num_users = User.count
@recent_users = User.where("created_at > ?", DateTime.now - 7.days).count @recent_users = User.where("created_at > ?", DateTime.now - 7.days).count
# Payments: member levels are multipled by 10 to indicate current payment; 25 x 10 = 250
@num_paid_users = User.all.select{|u| u.member_status >= 250 }.count
@num_plus_users = User.all.select{|u| u.member_status == 1000 }.count
@num_basic_users = User.all.select{|u| u.member_status == 500 }.count
@num_associate_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 if can? :read, User then
@recent_user_names = User.where("member_level > 10").accessible_by(current_ability).order('created_at desc').limit(5) @recent_user_names = User.where("member_level > 10").accessible_by(current_ability).order('created_at desc').limit(5)
end end
@num_door_opens = DoorLog.where("key = 'G'").count @num_door_opens = DoorLog.where("key = 'G'").count
@today_door_opens = DoorLog.where("key = 'G' AND created_at > ?", DateTime.now - 1.day).count @today_door_opens = DoorLog.where("key = 'G' AND created_at > ?", DateTime.now - 1.day).count
@recent_door_opens = DoorLog.where("key = 'G' AND created_at > ?", DateTime.now - 7.days).count @recent_door_opens = DoorLog.where("key = 'G' AND created_at > ?", DateTime.now - 7.days).count
@num_door_denieds = DoorLog.where("key = 'f'").count @num_door_denieds = DoorLog.where("key = 'D'").count
@recent_door_denieds = DoorLog.where("key = 'f' AND created_at > ?", DateTime.now - 7.days).count @recent_door_denieds = DoorLog.where("key = 'D' AND created_at > ?", DateTime.now - 1.month).count
@num_logins = User.sum('sign_in_count')
@recent_logins = User.where('current_sign_in_at > ?',Date.today - 7.days).count
@num_macs = Mac.count @num_macs = Mac.count
@recent_macs = Mac.where("since > ?", DateTime.now - 1.day).count @recent_macs = Mac.where("since > ?", DateTime.now - 1.day).count
@ -21,4 +29,10 @@ def index
end end
end end
def more_info
respond_to do |format|
format.html # more_info.html.erb
end
end
end end

View File

@ -0,0 +1,54 @@
class IpnsController < ApplicationController
load_and_authorize_resource :ipn, :except => [:new, :create]
before_filter :authenticate_user!, :except => [:new, :create]
protect_from_forgery :except => [:create]
def index
@ipns = Ipn.all
end
def show
end
def new
end
def create
@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 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!'
else
redirect_to ipns_url, :notice => 'INVALID'
end
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

View File

@ -1,12 +1,66 @@
class MacsController < ApplicationController class MacsController < ApplicationController
load_and_authorize_resource :mac, :except => [:index, :scan, :import] load_and_authorize_resource :mac, :except => [:create, :history]
load_and_authorize_resource :user, :through => :mac, :except => [:index, :show, :scan, :import] #load_and_authorize_resource :user, :through => :mac, :except => [:index, :show, :scan, :import]
before_filter :arp_lookup, :only => :new
#require "active_record" #require "active_record"
require "optparse" require "optparse"
#require "rubygems" #require "rubygems"
def index def index
recent_mac_logs_ungrouped = MacLog.last(1000)
if recent_mac_logs_ungrouped.present?
@mac_time_start_date = recent_mac_logs_ungrouped.first.created_at
recent_mac_logs = recent_mac_logs_ungrouped.group_by(&:mac)
@mac_times = {}
# Go thru each mac
recent_mac_logs.each do |mac_log|
last_active = nil
# And the entries for each mac (mac_log.first is the string, mac_log.last is the array)
mac_log.last.each do |entry|
# Find an activate followed immediately by a deactivate
if entry.action == "activate"
last_active = entry
else
if last_active && entry.action == "deactivate"
# Calculate the time difference between the two and append to this mac's total time
this_entry = @mac_times[entry.mac]
if this_entry
this_time = this_entry[:time]
else
this_time = 0
end
@mac_times[entry.mac] = {:mac => entry, :time => (entry.created_at - last_active.created_at) + this_time}
else
# No pair found; discard.
last_active = nil
end
end
end
end
@mac_times_sorted = @mac_times.sort{|a,b| b.last[:time] <=> a.last[:time] }
@most_active_mac = nil
@runner_up_mac = nil
@mac_times_sorted.each do |mac_time|
unless @most_active_mac
this_mac = Mac.find_by_mac(mac_time.first)
unless this_mac.hidden
@most_active_mac = this_mac
@most_active = mac_time
end
else
unless @runner_up_mac
this_mac = Mac.find_by_mac(mac_time.first)
unless this_mac.hidden
@runner_up_mac = this_mac
@runner_up = mac_time
end
end
end
end
end
#@active_macs = Mac.where(:active => true, :hidden => false) #@active_macs = Mac.where(:active => true, :hidden => false)
#@active_macs += Mac.where(:active => true, :hidden => nil) #@active_macs += Mac.where(:active => true, :hidden => nil)
@ -16,7 +70,7 @@ def index
elsif user_signed_in? then elsif user_signed_in? then
@active_macs = Mac.where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).includes(:user).order("users.name ASC").group("users.name") @active_macs = Mac.where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).includes(:user).order("users.name ASC").group("users.name")
else else
@active_macs = Mac.select("mac, note, user_id").where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).joins(:user).order("users.name ASC").group("users.name") @active_macs = Mac.select("mac, note, user_id").where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).joins(:user).order("users.name ASC").group("users.name, mac, note, user_id")
end end
@hidden_macs = Mac.where("macs.active = ? AND macs.hidden = ?", true, true).order("note ASC") @hidden_macs = Mac.where("macs.active = ? AND macs.hidden = ?", true, true).order("note ASC")
@ -32,10 +86,52 @@ def index
end end
end end
def history
authorize! :read_details, Mac
begin
@start_date = DateTime.parse(params[:start])
@end_date = DateTime.parse(params[:end])
rescue
@start_date = DateTime.now - 2.weeks
@end_date = DateTime.now
end
@mac_logs_by_hour = MacLog.where("created_at > ? AND created_at < ?", @start_date, @end_date).group_by{|m| m.created_at.beginning_of_hour}
@mac_log_graph = []
mac_running_balance = 0
lowest_balance = 0
@mac_logs_by_hour.each do |time, mac_log|
mac_log.each do |entry|
# Add one computer for activate, subtract one for deactivate
if entry.action == "activate"
mac_running_balance += 1
elsif entry.action == "deactivate"
mac_running_balance -= 1
end
# Keep track of the lowest number in the graph
if mac_running_balance < lowest_balance
lowest_balance = mac_running_balance
end
end
@mac_log_graph << [time.to_time.to_i*1000,mac_running_balance]
end
if lowest_balance != 0
# Subtract a negative balance to raise everything
@mac_log_graph = @mac_log_graph.map{ |time,balance| [time, balance - lowest_balance] }
end
respond_to do |format|
format.html
format.json { render :json => @mac_log_graph }
end
end
# GET /macs/1 # GET /macs/1
# GET /macs/1.json # GET /macs/1.json
def show def show
@mac = Mac.find(params[:id]) @mac = Mac.find(params[:id])
@mac_logs = MacLog.where(:mac => @mac.mac)
respond_to do |format| respond_to do |format|
format.html # show.html.erb format.html # show.html.erb
@ -70,26 +166,34 @@ end
end end
# POST /macs # POST /macs
# POST /user
def create def create
@mac = Mac.new(params[:mac]) @mac = Mac.new(params[:mac])
authorize! :update, @mac @existing_mac = Mac.find_by_mac(@mac.mac)
if can? :manage, Mac then
@users = User.accessible_by(current_ability).sort_by(&:name)
else
@users = [current_user]
end
if can? :manage, Mac then if @existing_mac.present?
@users = User.accessible_by(current_ability).sort_by(&:name) if @existing_mac.user_id.nil?
else redirect_to edit_mac_path(@existing_mac), :notice => 'This MAC already exists, edit it here:'
@users = [current_user] else
end @mac.errors.add(:user,"for this MAC is already set to #{@existing_mac.user.name} -- please contact them or an admin if this is incorrect.")
render :action => "new"
respond_to do |format| end
if @mac.save
format.html { redirect_to macs_path, :notice => 'Mac was successfully created.' }
format.json { render :json => @mac, :status => :created, :location => @mac }
else else
format.html { render :action => "new" }
format.json { render :json => @mac.errors, :status => :unprocessable_entity } respond_to do |format|
if @mac.save
format.html { redirect_to macs_path, :notice => 'MAC was successfully created.' }
format.json { render :json => @mac, :status => :created, :location => @mac }
else
format.html { render :action => "new" }
format.json { render :json => @mac.errors, :status => :unprocessable_entity }
end
end
end end
end
end end
# PUT /macs/1 # PUT /macs/1
@ -109,7 +213,7 @@ end
respond_to do |format| respond_to do |format|
if @mac.save if @mac.save
format.html { redirect_to macs_path, :notice => 'Mac was successfully updated.' } format.html { redirect_to macs_path, :notice => 'MAC was successfully updated.' }
format.json { head :no_content } format.json { head :no_content }
else else
format.html { render :action => "edit" } format.html { render :action => "edit" }
@ -118,115 +222,118 @@ end
end end
end end
def arp_lookup
@ip = request.env['REMOTE_ADDR']
@arp = %x(/usr/sbin/arp -a | grep #{@ip})
end
def scan def scan
Rails.logger.info "starting scan..." Rails.logger.info "starting scan..."
# Command line arguments # Command line arguments
options = {}; options = {};
OptionParser.new { |opts| OptionParser.new { |opts|
opts.banner = "Usage: pamela-scanner.rb --interface=en0" opts.banner = "Usage: pamela-scanner.rb --interface=en0"
options[:verbose] = true options[:verbose] = true
opts.on("v", "--verbose", "Run verbosely") { |verbose| opts.on("v", "--verbose", "Run verbosely") { |verbose|
options[:verbose] = verbose options[:verbose] = verbose
} }
options[:interface] = "eth0" options[:interface] = "eth0"
opts.on("i", "--interface=interface", "Network Interface") { |interface| opts.on("i", "--interface=interface", "Network Interface") { |interface|
options[:interface] = interface options[:interface] = interface
} }
options[:max_age] = 20 options[:max_age] = 20
opts.on("a", "--max-age=minutes", "Minutes to keep expired macs active") { |max_age| opts.on("a", "--max-age=minutes", "Minutes to keep expired macs active") { |max_age|
options[:max_age] = max_age.to_i options[:max_age] = max_age.to_i
} }
options[:db_host] = "configure_me" options[:db_host] = "configure_me"
opts.on("r", "--db-host=host", "Database Host") { |host| opts.on("r", "--db-host=host", "Database Host") { |host|
options[:db_host] = host options[:db_host] = host
} }
options[:db_name] = "configure_me" options[:db_name] = "configure_me"
opts.on("n", "--db-name=name", "Database Name") { |name| opts.on("n", "--db-name=name", "Database Name") { |name|
options[:db_name] = name options[:db_name] = name
} }
options[:db_user] = "configure_me" options[:db_user] = "configure_me"
opts.on("u", "--db-user=user", "Database User") { |user| opts.on("u", "--db-user=user", "Database User") { |user|
options[:db_user] = user options[:db_user] = user
} }
options[:db_password] = "configure_me" options[:db_password] = "configure_me"
opts.on("p", "--db-password=password", "Database Password") { |password| opts.on("p", "--db-password=password", "Database Password") { |password|
options[:db_password] = password options[:db_password] = password
} }
}.parse! }.parse!
# Open the database # Open the database
#ActiveRecord::Base::establish_connection( #ActiveRecord::Base::establish_connection(
# :adapter => "mysql2", # :adapter => "mysql2",
# :host => options[:db_host], # :host => options[:db_host],
# :database => options[:db_name], # :database => options[:db_name],
# :username => options[:db_user], # :username => options[:db_user],
# :password => options[:db_password]) # :password => options[:db_password])
#class Mac < ActiveRecord::Base #class Mac < ActiveRecord::Base
#end #end
#class MacLog < ActiveRecord::Base #class MacLog < ActiveRecord::Base
#end #end
# Scan the network for mac addresses # Scan the network for mac addresses
macs = {}; macs = {};
command = sprintf("arp-scan -R --interface=%s --localnet", options[:interface]) command = sprintf("arp-scan -R --interface=%s --localnet", options[:interface])
if options[:verbose] if options[:verbose]
Rails.logger.info "Running [#{command}]" Rails.logger.info "Running [#{command}]"
end end
IO.popen(command) { |stdin| IO.popen(command) { |stdin|
Rails.logger.info "Reading stdin: "+stdin.inspect Rails.logger.info "Reading stdin: "+stdin.inspect
stdin.each { |line| stdin.each { |line|
next if line !~ /^([\d\.]+)\s+([[:xdigit:]:]+)\s/; next if line !~ /^([\d\.]+)\s+([[:xdigit:]:]+)\s/;
macs[$2] = $1; macs[($2).downcase] = ($1).downcase;
} }
} }
# Scan the existing macs and update each record as necessary # Scan the existing macs and update each record as necessary
Mac.find(:all).each { |entry| Mac.find(:all).each { |entry|
mac = entry.mac.downcase mac = entry.mac.downcase
ip = entry.ip ip = entry.ip
if macs.has_key?(mac) if macs.has_key?(mac)
if ! entry.active || ! entry.since if ! entry.active || ! entry.since
Rails.logger.info "Activating #{mac} at #{ip}" if options[:verbose] Rails.logger.info "Activating #{mac} at #{ip}" if options[:verbose]
entry.since = Time.now entry.since = Time.now
MacLog.new(:mac => mac, :ip => ip, :action => "activate").save MacLog.new(:mac => mac, :ip => ip, :action => "activate").save
end end
entry.active = 1 entry.active = 1
entry.ip = ip entry.ip = ip
entry.refreshed = Time.now entry.refreshed = Time.now
entry.save entry.save
macs.delete(mac) macs.delete(mac)
next next
end end
# Entry is no longer current # Entry is no longer current
if entry.active if entry.active
ageMinutes = ((Time.now - entry.refreshed)/60).to_i ageMinutes = ((Time.now - entry.refreshed)/60).to_i
next if ageMinutes < options[:max_age] next if ageMinutes < options[:max_age]
Rails.logger.info "Deactivating #{mac}, #{ageMinutes} minutes old" if options[:verbose] Rails.logger.info "Deactivating #{mac}, #{ageMinutes} minutes old" if options[:verbose]
entry.active = 0 entry.active = 0
entry.save entry.save
MacLog.new(:mac => mac, :ip => ip, :action => "deactivate").save MacLog.new(:mac => mac, :ip => ip, :action => "deactivate").save
end end
} }
# Add entries for any macs not already in the db # Add entries for any macs not already in the db
macs.each { |mac, ip| macs.each { |mac, ip|
Rails.logger.info "Activating new entry #{mac} at #{ip}" if options[:verbose] Rails.logger.info "Activating new entry #{mac} at #{ip}" if options[:verbose]
Mac.new(:mac => mac, :ip => ip, :active => 1, :since => Time.now, :refreshed => Time.now).save Mac.new(:mac => mac, :ip => ip, :active => 1, :since => Time.now, :refreshed => Time.now).save
Rails.logger.info MacLog.new(:mac => mac, :ip => ip, :action => "activate").save Rails.logger.info MacLog.new(:mac => mac, :ip => ip, :action => "activate").save
} }
@log = MacLog.all @log = MacLog.all

View File

@ -1,6 +1,6 @@
class PaymentsController < ApplicationController class PaymentsController < ApplicationController
load_and_authorize_resource :payment load_and_authorize_resource :payment
load_and_authorize_resource :user, :through => :payment #load_and_authorize_resource :user, :through => :payment
before_filter :authenticate_user! before_filter :authenticate_user!
# Load users and certs based on current ability # Load users and certs based on current ability
@ -16,6 +16,10 @@ class PaymentsController < ApplicationController
# GET /payments.json # GET /payments.json
def index def index
@payments = @payments.order("date DESC") @payments = @payments.order("date DESC")
@graph = { :members => chart("members"),
:total => chart("total"),
:basic => chart("basic"),
:associate => chart("associate")}
respond_to do |format| respond_to do |format|
format.html # index.html.erb format.html # index.html.erb
@ -23,6 +27,60 @@ class PaymentsController < ApplicationController
end end
end end
# Private method for index charts
def chart name
chart_name = name || "total"
if chart_name == "total"
chart_type = [25, 50, 100]
elsif chart_name == "members"
chart_type = [25, 50, 100]
elsif chart_name == "basic"
chart_type = [50]
elsif chart_name == "associate"
chart_type = [25]
else
chart_type = []
end
payment_months = @payments.sort_by(&:date).group_by{ |p| p.date.beginning_of_month }
@payments_by_month = []
payment_months.each do |month|
# Only grab the last year from today
if month.first > (Date.today - 1.year) && month.first < Date.today
# Calculate sum of amounts for each month and store at end of month array
@payments_by_month << [month.first.to_time.to_i*1000, month.last.sum{|p|
amount = amount_or_level(p)
if chart_type.include?(amount)
if chart_name == "members"
1 # Output 1 to count members
else
amount # Output dollars to count amount
end
else
0
end
}]
end
end
return @payments_by_month
end
def amount_or_level p
if p.amount
return p.amount.to_i
else
if p.user
Rails.logger.info p.user.member_level
return p.user.member_level.to_i
else
Rails.logger.info p.inspect
Rails.logger.info p.user.inspect
return 0
end
end
end
# GET /payments/1 # GET /payments/1
# GET /payments/1.json # GET /payments/1.json
def show def show

View File

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

View File

@ -0,0 +1,32 @@
class SettingsController < ApplicationController
authorize_resource
def index
@settings = Setting.all
@@default_settings.each do |key, value|
if @settings[key].blank?
@settings[key] = value
end
end
end
def edit
value = Setting[params[:id].to_sym]
if value.present?
@setting = {}
@setting[:var] = params[:id]
@setting[:value] = value
elsif @@default_settings[params[:id].to_sym].present?
@setting = {}
@setting[:var] = params[:id]
@setting[:value] = @@default_settings[params[:id].to_sym]
end
end
def update
Setting[params[:id]] = params[:value]
redirect_to settings_path
end
end

View File

@ -0,0 +1,96 @@
class SpaceApiController < ApplicationController
# Individually remove authorizing stuff since there is no SpaceApi model
authorize_resource :except => [:index, :access, :access_post]
# User auth here happens via params, instead of form.
before_filter :authenticate_user!, :except => [:index, :access, :access_post]
def index
@json = JSON.parse(Setting.space_api_json_template)
door_status = DoorLog.show_status # Expect {:unlocked => boolean, :door_1_locked => boolean, :door_2_locked => boolean}
@json["open"] = door_status[:unlocked]
if( door_status[:unlocked] )
@json["status"] = "doors_open=both"
elsif( !door_status[:door_1_locked] )
@json["status"] = "doors_open=door1"
elsif( !door_status[:door_2_locked] )
@json["status"] = "doors_open=door2"
else
@json["status"] = "doors_open=none"
end
respond_to do |format|
format.html
format.json {
response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
render :json => @json
}
end
end
def access
@status = DoorLog.show_status
# Nothing, just render form
unless user_signed_in?
@output = "Invalid email or password. Please login with your Members DB email and password below."
else
unless can? :access_doors_remotely, :door_access
@output = "Sorry, your account isn't able to control doors remotely."
end
end
end
def access_post
@output = ""
#if params['cmd'] == "check-login" then
# if users[params['user']] && users[params['user']]['pass'].to_s == (Digest::SHA2.new(bitlen=512) << params['pass']).to_s then
# @output += '{ "login": "okay" }'
# else
# @output += '{ "login": "fail" }'
# end
#
# exit
#end
# Stop unless signed in already, OR if the supplied user/pass params are good.
unless current_user || check_auth(params['user'],params['pass'])
@output += "Invalid email or password."
else
# Stop unless the user can access the door system
unless can? :access_doors_remotely, :door_access
@output += "Sorry, your account isn't able to control doors remotely. Ask an admin if this is incorrect."
Rails.logger.warn "----------\r\nWARNING: DOOR ACCESS ATTEMPT DENIED. USER #{current_user.inspect}\r\n----------"
else
# Stop unless we've got a command to run
unless params['cmd']
@output += "No command specified."
else
# Log the access
Rails.logger.info "Door access: user #{current_user.inspect}"
DoorLog.create!({:key => "rem_"+DoorLog.parse_command(params['cmd'])[:url_param], :data => current_user.id})
# Execute the access
@output += DoorLog.execute_command(params['cmd'])
end
end
end
# Render the form again
render :access
end
def check_auth(email,password)
resource = User.find_by_email(email)
if resource && resource.valid_password?(password)
resource.remember_me = true
sign_in :user, resource
return true
else
return false
end
end
end

View File

@ -0,0 +1,68 @@
class StatisticsController < ApplicationController
before_filter :load_and_authorize_user
def index
end
def door_log
# Get own user's door data
cards = @user.cards
card_hash = {}
cards.each{|c| card_hash[c.card_number.to_i(16)%32767] = c.card_number}
card_num_Rs = cards.map{|c| c.card_number.to_i(16)%32767}
@door_logs = DoorLog.where("data = ?", card_num_Rs).order("created_at ASC")
@door_logs.map{|l|
l.data = card_hash[l.data.to_i].to_i(16)
l.key = DoorLog.key_legend[l.key]
}
@door_log_graph = []
@door_logs.where("key = 'G'").group_by{|l| l.created_at.beginning_of_day}.each{|l| @door_log_graph << [l.first.to_time.to_i*1000,l.last.size]}
respond_to do |format|
format.html
format.json { render :json => @door_logs }
end
end
def mac_log
macs = @user.macs.where(:hidden => false).map{|m| m.mac}
@mac_logs = MacLog.where(:mac => macs)
@mac_log_graph = {}
macs.each do |mac|
mac_log = MacLog.where(:mac => mac)
mac_times = []
last_active = nil
mac_log.each do |entry|
# Find an activate followed immediately by a deactivate
if entry.action == "activate"
last_active = entry
else
if last_active && entry.action == "deactivate"
# Calculate the time difference between the two and append to this mac's total time
mac_times << [entry.created_at, ((entry.created_at - last_active.created_at)/60/60)]
else
# No pair found; discard.
last_active = nil
end
end
end
mac_log_graph = []
mac_times.group_by{|m| m.first.beginning_of_day}.each{|m| mac_log_graph << [m.first.to_time.to_i*1000,m.last.map{|n| n.last}.sum.round(2)]}
# Store each mac in the hash with its graph
@mac_log_graph[mac] = mac_log_graph
end
#@mac_log_graph = mac_log_grouped.map{|g| [g.first.to_time.to_i*1000, g.last.size] }
respond_to do |format|
format.html
format.json { render :json => @mac_logs }
end
end
def load_and_authorize_user
@user = current_user
authorize! :read, @user
end
end

View File

@ -1,12 +1,12 @@
class UserCertificationsController < ApplicationController class UserCertificationsController < ApplicationController
load_and_authorize_resource :user_certification load_and_authorize_resource :user_certification
load_and_authorize_resource :user, :through => :user_certification #load_and_authorize_resource :user, :through => :user_certification
load_and_authorize_resource :certification, :through => :user_certification #load_and_authorize_resource :certification, :through => :user_certification
before_filter :authenticate_user! before_filter :authenticate_user!
# Load users and certs based on current ability # Load users and certs based on current ability
before_filter :only => [:new, :edit, :create, :update] do 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) @certifications = Certification.accessible_by(current_ability).sort_by(&:name)
end end

View File

@ -2,14 +2,24 @@ class UsersController < ApplicationController
load_and_authorize_resource load_and_authorize_resource
before_filter :authenticate_user! before_filter :authenticate_user!
def sort_by_cert(certs,id)
result = 0
certs.each do |c|
if c.id == id
result = 1
end
end
return result
end
# GET /users # GET /users
# GET /users.json # GET /users.json
def index def index
case params[:sort] case params[:sort]
when "name" when "name"
@users = @users.sort_by(&:name) @users = @users.sort_by(&:name)
when "certifications" when "cert"
@users = @users.sort_by{ |u| [-u.certifications.count,u.name] } @users = @users.sort_by{ |u| [-sort_by_cert(u.certifications,params[:cert].to_i),u.name] }
when "orientation" when "orientation"
@users = @users.sort_by{ |u| [-u.orientation.to_i,u.name] } @users = @users.sort_by{ |u| [-u.orientation.to_i,u.name] }
when "waiver" when "waiver"
@ -33,6 +43,24 @@ class UsersController < ApplicationController
end end
end end
# 'Active' users who haven't paid recently
def inactive
@users = @users.all.select{|u| u if u.payment_status == false }.sort_by{ |u| -u.delinquency }
end
# Recent user activity
def activity
@zombie_members = User.where('sign_in_count = 0').where('member_level > 1')
@user_logins = User.where(:current_sign_in_at => 2.months.ago..Time.now).where('sign_in_count > 1')
@new_users = User.where(:created_at => 3.months.ago..Date.today)
@cardless_users = User.includes('cards').where(['users.member_level >= ?','50']).where('cards.id IS NULL')
end
# New members (for emailing out)
def new_member_report
@new_users = User.where(:created_at => 3.months.ago..Date.today).where(:hidden => false).where(['member_level >= ?','1'])
end
# GET /users/1 # GET /users/1
# GET /users/1.json # GET /users/1.json
def show def show
@ -43,6 +71,32 @@ class UsersController < ApplicationController
end end
end end
def compose_email
@user = User.find(params[:user_id])
authorize! :read, @user
end
def send_email
@user = User.find(params[:user_id])
authorize! :read, @user
@subject = params[:subject]
@body = params[:body]
if @user.send_email(current_user,@subject,@body)
redirect_to user_path(@user), :notice => "Email sent successfully."
else
flash[:alert] = "Error sending email."
render :compose_email
end
end
# GET /user_summary/1
def user_summary
respond_to do |format|
format.html { render :partial => "user_summary" } # show.html.erb
format.json { render :json => @user }
end
end
# GET /users/new # GET /users/new
# GET /users/new.json # GET /users/new.json
def new def new
@ -61,7 +115,7 @@ class UsersController < ApplicationController
def create def create
respond_to do |format| respond_to do |format|
if @user.save 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 } format.json { render :json => @user, :status => :created, :location => @user }
else else
format.html { render :action => "new" } format.html { render :action => "new" }
@ -75,7 +129,7 @@ class UsersController < ApplicationController
def update def update
respond_to do |format| respond_to do |format|
if @user.update_attributes(params[:user]) 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 } format.json { head :no_content }
else else
format.html { render :action => "edit" } format.html { render :action => "edit" }
@ -84,6 +138,37 @@ class UsersController < ApplicationController
end end
end end
# GET /users/merge
def merge_view
@users = @users.sort_by(&:name)
respond_to do |format|
format.html # merge_view.html.erb
end
end
# POST /users/merge
def merge_action
@user_to_keep = User.find(params[:user][:to_keep])
Rails.logger.info "USER TO KEEP:"
Rails.logger.info @user_to_keep.inspect
@user_to_merge = User.find(params[:user][:to_merge])
Rails.logger.info "USER TO MERGE:"
Rails.logger.info @user_to_merge.inspect
@user_to_keep.absorb_user(@user_to_merge)
Rails.logger.info "RESULT:"
Rails.logger.info @user_to_keep.inspect
Rails.logger.info @user_to_keep.cards.inspect
Rails.logger.info @user_to_keep.user_certifications.inspect
Rails.logger.info @user_to_keep.payments.inspect
respond_to do |format|
format.html { redirect_to @user_to_keep, :notice => 'Users successfully merged.' }
end
end
# DELETE /users/1 # DELETE /users/1
# DELETE /users/1.json # DELETE /users/1.json
def destroy def destroy

View File

@ -0,0 +1,2 @@
module IpnHelper
end

View File

@ -1,13 +1,20 @@
class UserMailer < ActionMailer::Base class UserMailer < ActionMailer::Base
default :from => "wiki@heatsynclabs.org" default :from => "no-reply@heatsynclabs.org"
def new_user_email(user) def new_user_email(user)
@user = user @user = user
@url = "http://members.heatsynclabs.org" @url = "http://members.heatsynclabs.org"
#@admins = User.where(:name => "Will Bradley") mail(:to => 'member-notifications@heatsynclabs.org',
#@admins.each do |admin| :subject => "New HSL Member: "+user.name)
mail(:to => 'info@heatsynclabs.org', :subject => "New HSL Member: "+user.name) end
#end
def email(to_user,from_user,subject,body)
@url = "http://members.heatsynclabs.org"
@body = body
@from_user = from_user
mail(:to => to_user.email,
:subject => "HSL Message: "+subject)
end end
end end

View File

@ -2,8 +2,8 @@ class Ability
include CanCan::Ability include CanCan::Ability
def initialize(user) def initialize(user)
# Anonymous can read mac can :read, Mac # Anonymous can read mac
can :read, Mac can :scan, Mac # Need anonymous so CRON can scan
if !user.nil? if !user.nil?
@ -13,9 +13,14 @@ class Ability
can :read_details, Mac can :read_details, Mac
can [:update], Mac, :user_id => nil can [:update], Mac, :user_id => nil
can [:create,:update], Mac, :user_id => user.id can [:create,:update], Mac, :user_id => user.id
can :read, User, :id => user.id #TODO: why can users update themselves? can :read, Payment, :user_id => user.id
can [:read,:new_member_report], User, :id => user.id #TODO: why can users update themselves? Maybe because Devise doesn't check users/edit?
can :read, UserCertification, :user_id => user.id can :read, UserCertification, :user_id => user.id
if user.card_access_enabled
can :access_doors_remotely, :door_access
end
# Instructors can manage certs and see users # Instructors can manage certs and see users
if user.instructor? if user.instructor?
can :manage, Certification can :manage, Certification
@ -29,9 +34,11 @@ class Ability
can :read, UserCertification can :read, UserCertification
end end
# Accountants can manage all # Accountants can manage payments
if user.accountant? if user.accountant?
can :manage, Payment can :manage, Payment
can :manage, Ipn
can :manage, PaypalCsv
end end
# Admins can manage all # Admins can manage all
@ -40,8 +47,8 @@ class Ability
end end
# Prevent all destruction for now # Prevent all destruction for now
cannot :destroy, User #cannot :destroy, User
cannot :destroy, Card #cannot :destroy, Card
cannot :destroy, Certification cannot :destroy, Certification
cannot :destroy, Mac cannot :destroy, Mac
cannot :destroy, MacLog cannot :destroy, MacLog

View File

@ -2,7 +2,8 @@ class Card < ActiveRecord::Base
require 'open-uri' require 'open-uri'
attr_accessible :id, :user_id, :name, :card_number, :card_permissions 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 belongs_to :user
def upload_to_door def upload_to_door

View File

@ -2,6 +2,126 @@ class DoorLog < ActiveRecord::Base
attr_accessible :data, :key attr_accessible :data, :key
require 'open-uri' require 'open-uri'
def self.execute_command(command)
output = ""
# load config values
door_access_url = APP_CONFIG['door_access_url']
door_access_password = APP_CONFIG['door_access_password']
#login
source = open("#{door_access_url}?e=#{door_access_password}").read
results = source.scan(/ok/)
#only continue if we've got an OK login
if(results.size > 0) then
# Parse the command and result
parsing = parse_command(command)
output += parsing[:output]
url_param = parsing[:url_param]
# Execute the command
open("#{door_access_url}?#{url_param}")
self.download_status # Update the status cache
else
output += 'Failed to connect to door system.'
end
#logout
open("#{door_access_url}?e=0000")
return output
end
def self.parse_command(command)
output = ""
url_param = ""
# @commands = [
# ["Unlock all doors","unlock"],
# ["Unlock Front door","unlock-front"],
# ["Unlock Rear door","unlock-rear"],
# ["Lock all doors","lock"],
# ["Lock Front door","lock-front"],
# ["Lock Rear door","lock-rear"],
# ["Open Front door","open-front"],
# ["Open Rear door","open-rear"],
# ["Arm alarm","arm"],
# ["Disarm alarm","disarm"]
# ]
case command
when "open-front"
output += "Front door opened."
url_param = "o1"
when "open-rear"
output += "Rear door opened."
url_param = "o2"
when "u", "unlock"
output += "Doors unlocked, remember to re-lock them."
url_param = "u"
when "u1", "unlock-front"
output += "Front Door unlocked, remember to re-lock it."
url_param = "u=1"
when "u2", "unlock-rear"
output += "Rear Door unlocked, remember to re-lock it."
url_param = "u=2"
when "lock", "l"
output += "Doors locked."
url_param = "l"
when "lock-front", "l1"
output += "Front Door locked."
url_param = "l=1"
when "lock-rear", "l2"
output += "Rear Door locked."
url_param = "l=2"
when "arm"
output += "Armed."
url_param = "2"
when "disarm"
output += "Disarmed."
url_param = "1"
else
output += "Fail. Don't be a naughty user!"
url_param = "9" # Using 9 because it's just status, no harm done
end
return {:output => output, :url_param => url_param}
end
def self.show_status
door_logs = DoorLog.where(key: ["door_1_locked","door_2_locked"]).order('created_at DESC').take(2)
door_1_locked = parse_locked_status(door_logs, "door_1_locked")
door_2_locked = parse_locked_status(door_logs, "door_2_locked")
# Doors are unlocked if 1 AND 2 are NOT locked
status = {:unlocked => (!door_1_locked && !door_2_locked), :door_1_locked => door_1_locked, :door_2_locked => door_2_locked }
end
def self.parse_locked_status(door_logs, door_key)
door_logs_selected = door_logs.select{|s| s.key == door_key }
if door_logs_selected.present?
door_data = door_logs_selected.first.data
if door_data == 0 # 0 = unlocked
return false
else
return true # 1 = locked
end
end
end
def self.download_status
# load config values
door_access_url = APP_CONFIG['door_access_url']
door_access_password = APP_CONFIG['door_access_password']
# query for status
source = open("#{door_access_url}?9").read
# expect {"armed"=>255, "activated"=>255, "alarm_3"=>1, "alarm_2"=>1, "door_1_locked"=>1, "door_2_locked"=>1}
# See https://github.com/heatsynclabs/Open_Access_Control_Ethernet for more info
@status = JSON.parse(source)
@status.each do |key,value|
DoorLog.create!({:key => key, :data => value})
end
end
def self.download_from_door def self.download_from_door
# load config values # load config values
door_access_url = APP_CONFIG['door_access_url'] door_access_url = APP_CONFIG['door_access_url']
@ -41,4 +161,8 @@ class DoorLog < ActiveRecord::Base
end end
end end
def self.key_legend
{'G' => "Granted", "R" => "Read", "D" => "Denied",
'g' => "granted", "r" => "read", "d" => "denied"}
end
end end

95
app/models/ipn.rb Normal file
View File

@ -0,0 +1,95 @@
require 'net/http'
class Ipn < ActiveRecord::Base
attr_accessible :data
belongs_to :payment
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()
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
# 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}"
Rails.logger.error "Data: #{self.data}"
return false
end
return true
end
def link_payment
create_payment
end
private
def create_payment
# find user by email, then by payee
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?
# 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. Couldn't find membership level '#{self.payment_gross.to_i}'."]
end
else
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

View File

@ -1,8 +1,10 @@
class Payment < ActiveRecord::Base class Payment < ActiveRecord::Base
belongs_to :user belongs_to :user
attr_accessible :date, :user_id, :created_by has_one :ipn
has_one :paypal_csv
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.' validates_uniqueness_of :date, :scope => :user_id, :message => ' of payment already exists for this user.'
def human_date def human_date

73
app/models/paypal_csv.rb Normal file
View File

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

3
app/models/setting.rb Normal file
View File

@ -0,0 +1,3 @@
class Setting < RailsSettings::CachedSettings
attr_accessible :var
end

View File

@ -9,15 +9,66 @@ class User < ActiveRecord::Base
:recoverable, :rememberable, :trackable, :validatable :recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model # Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :admin, :instructor, :member, :emergency_name, :emergency_phone, :current_skills, :desired_skills, :waiver, :emergency_email, :phone, :payment_method, :orientation, :member_level, :certifications, :hidden, :marketing_source, :payee, :accountant #TODO: make admin/instructor/member/etc not accessible attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :admin, :instructor, :member, :emergency_name, :emergency_phone, :current_skills, :desired_skills, :waiver, :emergency_email, :phone, :payment_method, :orientation, :member_level, :certifications, :hidden, :marketing_source, :payee, :accountant, :exit_reason, :twitter_url, :facebook_url, :github_url, :website_url, :email_visible, :phone_visible #TODO: make admin/instructor/member/etc not accessible
has_many :cards has_many :cards
has_many :user_certifications has_many :user_certifications
has_many :certifications, :through => :user_certifications has_many :certifications, :through => :user_certifications
has_many :payments has_many :payments
has_many :macs
validates_format_of [:twitter_url, :facebook_url, :github_url, :website_url], :with => URI::regexp(%w(http https)), :allow_blank => true
after_create :send_new_user_email after_create :send_new_user_email
def absorb_user(user_to_absorb)
# copy all attributes except email, password, name, and anything that isn't blank on the destination
user_to_absorb.attributes.each_pair {|k,v|
unless (v.nil? || k == :id || k == :email || k == :password || k == :name || k == :password_confirmation || k == :hidden || k == 'hidden' || k == :encrypted_password || !self.attributes[k].blank? )
Rails.logger.info "Updating "+k.to_s+" from "+self[k].to_s
self[k] = v
Rails.logger.info "Updated "+k.to_s+" to "+self[k].to_s
end
}
self.save!
user_to_absorb.cards.each {|card|
Rails.logger.info "CARD BEFORE: "+card.inspect
card.user_id = self.id
card.save!
Rails.logger.info "CARD AFTER: "+card.inspect
}
user_to_absorb.user_certifications.each {|user_cert|
Rails.logger.info "CERT BEFORE: "+user_cert.inspect
user_cert.user_id = self.id
user_cert.save!
Rails.logger.info "CERT AFTER: "+user_cert.inspect
}
user_to_absorb.payments.each {|payment|
Rails.logger.info "PAYMENT BEFORE: "+payment.inspect
payment.user_id = self.id
payment.amount = 0 if payment.amount.nil? # Bypass validation on amount
payment.save!
Rails.logger.info "PAYMENT AFTER: "+payment.inspect
}
user_to_absorb.destroy
end
def card_access_enabled
# If the user has at least one card with permission level 1, they have access
self.cards.where(:card_permissions => 1).count > 0
end
def name_with_email_and_visibility
if hidden then
"#{name} (#{email}) HIDDEN"
else
"#{name} (#{email})"
end
end
def name_with_payee_and_member_level def name_with_payee_and_member_level
if payee.blank? then if payee.blank? then
"#{name} - #{member_level_string}" "#{name} - #{member_level_string}"
@ -43,74 +94,117 @@ class User < ActiveRecord::Base
end end
end end
def self.member_levels
{25 => "Associate", 50 => "Basic", 75 => "Basic", 100 => "Plus"}
end
def payment_status
results = payment_status_calculation
return results[:paid]
end
def member_status def member_status
case self.member_level.to_i results = member_status_calculation
when 0 return results[:rank]
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
end end
def member_status_symbol def member_status_symbol
case self.member_level.to_i results = member_status_calculation
when 0 return "<img src='/#{results[:icon]}#{results[:flair]}-coin.png' title='#{results[:message]}' class='member-status-symbol' />"
if self.payments.count > 0 then end
"<span class='hoverinfo' title='Former Member (#{(DateTime.now - self.payments.last.date).to_i} days ago)'>:(</span>"
else def last_payment_date
"<!-- Not a member -->" self.payments.maximum(:date)
end end
when 1
"Unable" def delinquency
when 10..24 if self.payments.count > 0
"<span class='hoverinfo' title='Volunteer'>&#9684;</span>" paydate = self.payments.maximum(:date)
when 25..999 (Date.today - paydate).to_i
if self.payments.count > 0 then else
if self.payments.last.date < (DateTime.now - 45.days) (Date.today - self.created_at.to_date).to_i
"<span class='hoverinfo' title='Recently Lapsed (#{(DateTime.now - self.payments.last.date).to_i} days ago)'>&#9676;</span>"
else
case self.member_level.to_i
when 25..49
"<span class='hoverinfo' title='#{member_level_string}'>&#9681;</span>"
when 50..99
"<span class='hoverinfo' title='#{member_level_string}'>&#9685;</span>"
when 100..999
"<span class='hoverinfo' title='#{member_level_string}'>&#9679;</span>"
end
end
else
"<span class='hoverinfo' title='No Payments'>?</span>"
end
end end
end end
def send_email(from_user,subject,body)
Rails.logger.info UserMailer.email(self,from_user,subject,body).deliver
end
private private
def send_new_user_email def send_new_user_email
Rails.logger.info UserMailer.new_user_email(self).deliver Rails.logger.info UserMailer.new_user_email(self).deliver
end end
def member_status_calculation
# Begin output buffer
message = ""
icon = ""
flair = ""
rank = 0
# First status item is level
case self.member_level.to_i
when 0..9
if self.payments.count > 0 then
message = "Former Member (#{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago)"
icon = :timeout
rank = 1
else
message = "Not a Member"
icon = :no
rank = 0
end
when 10..24
message = "Volunteer"
icon = :heart
rank = 101
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
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.maximum(:date) > (DateTime.now - 60.days)
flair = "-paid"
paid = true
else
message = "Last Payment #{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago"
paid = false
end
else
message = "No Payments Recorded"
paid = false
end
end
return {:message => message, :paid => paid, :flair => flair}
end
end end

View File

@ -1,8 +1,17 @@
class UserCertification < ActiveRecord::Base class UserCertification < ActiveRecord::Base
attr_accessible :certification_id, :user_id 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 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 belongs_to :user
belongs_to :certification belongs_to :certification
def user_name
if user.blank?
return "n/a (user ##{user_id} missing)"
else
return self.user.name
end
end
end end

View File

@ -11,9 +11,10 @@
</div> </div>
<% end %> <% end %>
<% @card.user_id = params[:user] if params[:user].present? %>
<div class="field"> <div class="field">
<%= f.label :user %><br /> <%= f.label :user %><br />
<%= 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) %>
</div> </div>
<div class="field"> <div class="field">
<%= f.label :name, "Card Note" %><br /> <%= f.label :name, "Card Note" %><br />

View File

@ -1,13 +1,20 @@
<h1>Access Cards</h1> <h1>Access Cards</h1>
<%= link_to 'New Card', new_card_path if can? :create, Card %> <%= link_to 'New Card', new_card_path, :class => "btn" if can? :create, Card %>
<%= link_to 'Upload all cards', upload_all_path if can? :upload_all, Card %> <%= link_to 'Upload all cards', upload_all_path, :class => "btn" if can? :upload_all, Card %>
<%= link_to 'Door Logs', door_logs_path if can? :read, DoorLog %> <%= link_to 'Door Logs', door_logs_path, :class => "btn" if can? :read, DoorLog %>
<%= link_to 'Space API', space_api_path, :class => "btn" %>
<%= link_to 'Remote Door Access', space_api_access_path, :class => "btn" if can? :access_doors_remotely, :door_access %>
<p> <p>
<b>Most Active Card Last 7 Days:</b> <%= @most_active_card.name unless @most_active_card.blank? %> (<%= @most_active_card.accesses_this_week unless @most_active_card.blank? %> times) <b>Most Active Card Last Month:</b> <%= @most_active_card.name unless @most_active_card.blank? %> (<%= @most_active_card.accesses_this_week unless @most_active_card.blank? %> days)
</p>
<p>
<% unless @runner_up_card.blank? %>
<b>Runner Up:</b> <%= @runner_up_card.name %> (<%= @runner_up_card.accesses_this_week %> days)
<% end %>
</p> </p>
<table> <table class="lined-table">
<col /> <col />
<col /> <col />
<col class="col_highlight" /> <col class="col_highlight" />
@ -19,7 +26,7 @@
<th>DB ID</th> <th>DB ID</th>
<th>Card #</th> <th>Card #</th>
<th>Access?</th> <th>Access?</th>
<th>Accesses Last 7 Days</th> <th>Days Accessed Last Month</th>
<th></th> <th></th>
<th></th> <th></th>
<th></th> <th></th>
@ -28,7 +35,14 @@
<% if !@cards.blank? %> <% if !@cards.blank? %>
<% @cards.each do |card| %> <% @cards.each do |card| %>
<tr> <tr>
<td><%= link_to card.user.name, card %></td> <td>
<% if card.user.nil? %>
n/a
<% else %>
<%= raw(card.user.member_status_symbol) %>
<%= link_to card.user.name , card %>
<% end %>
</td>
<td><%= card.name %></td> <td><%= card.name %></td>
<td><%= card.id %></td> <td><%= card.id %></td>
<td><%= card.card_number %></td> <td><%= card.card_number %></td>

View File

@ -8,13 +8,10 @@
<%= simple_format @certification.description %> <%= simple_format @certification.description %>
</p> </p>
<b>Certified Users:</b> <p>
<ul> <b>Certified Users:</b>
<% @certification_users.each do |user| %> <%= link_to "Click Here", user_certifications_path %>
<li><%= link_to user.name, user %></li> </p>
<% end %>
<% if @certification_users.blank? then %><li>n/a</li><% end %>
</ul>
<% if can? :update, @certification %><%= link_to 'Edit', edit_certification_path(@certification) %> |<% end %> <% if can? :update, @certification %><%= link_to 'Edit', edit_certification_path(@certification) %> |<% end %>
<%= link_to 'Back', certifications_path %> <%= link_to 'Back', certifications_path %>

View File

@ -1,12 +1,21 @@
<% if params[:flash] == "welcome_msg" then %> <% if params[:flash] == "welcome_msg" then %>
<p class="notice"><strong>Thank for you choosing to become a HeatSync Labs member!</strong> As we foster this community of learning, science, and the arts every member is important. <br/><br/> <p class="notice"><strong>Thank for you choosing to become a HeatSync Labs member!</strong> As we foster this community of learning, science, and the arts every member is important. <br/><br/>
You can get your payments started by following the instructions on this page. <strong>Please note electronic recurring payments are -highly- encouraged</strong>-- we do not have staff. If you must pay via cash/check, please consider prepaying for 3, 6 or 12 months up front.<br/><br/> You can get your payments started by clicking the payment button on this page or contacting a HeatSync member. <strong>Please note electronic recurring payments are -highly- encouraged</strong>-- we do not have paid staff. If you must pay via cash/check, please consider prepaying for 3, 6 or 12 months up front to reduce volunteer workload.<br/><br/>
<strong>To claim member benefits</strong> such as storage, grab a volunteer during your next stop into HeatSync or schedule a time to meet up in advance. Someone should also be contacting you shortly via the email address you provided.<br/><br/> <strong>To claim member benefits</strong> such as storage, grab a volunteer during your next stop into HeatSync or schedule a time to meet up in advance. Someone should also be contacting you shortly via the email address you provided to schedule a New Member Orientation as well. After the orientation, you'll be equipped to be an awesome member of our community, and this website will get a lot more useful.<br/><br/>
Please also note that certain privileges like 24/7 card access require community approval.<br/><br/> Please also note that certain privileges like 24/7 card access require community approval.<br/><br/>
Thanks again, and happy hacking!</p> Thanks again, and happy hacking!</p>
<% end %> <% end %>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => html) do |f| %> <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => html) do |f| %>
<div class="field">
<%= f.label :member_level, "Membership Level" %><br />
<%= f.select :member_level, [[nil],["None",0],["Unable",1],["Volunteer",10],["Associate ($25)",25],["Basic ($50)",50],["Plus ($100)",100]] %>
</div>
<div class="field">
<%= render :partial => "/users/payment_methods", :locals => { :g => f } %>
</div>
<div class="field"> <div class="field">
<%= f.label :name, "Full Name" %><br /> <%= f.label :name, "Full Name" %><br />
<%= f.text_field :name %> <%= f.text_field :name %>
@ -15,12 +24,28 @@ Thanks again, and happy hacking!</p>
<div class="field"> <div class="field">
<%= f.label :email %><br /> <%= f.label :email %><br />
<%= f.email_field :email %> <%= f.email_field :email %>
<%= f.check_box :email_visible %>
<%= f.label :email_visible, "Show Email to All Members?" %>
</div> </div>
<div class="field"> <div class="field">
<%= f.label :phone %><br /> <%= f.label :phone %><br />
<%= f.text_field :phone %> <%= f.text_field :phone %>
<%= f.check_box :phone_visible %>
<%= f.label :phone_visible, "Show Phone to All Members?" %>
</div> </div>
<div class="field">
<%= f.label :twitter_url %>
<%= f.text_field :twitter_url, :placeholder => "https://twitter.com/heatsynclabs" %><br/>
<%= f.label :facebook_url %>
<%= f.text_field :facebook_url, :placeholder => "https://www.facebook.com/HeatSyncLabs" %><br/>
<%= f.label :github_url %>
<%= f.text_field :github_url, :placeholder => "https://github.com/heatsynclabs" %><br/>
<%= f.label :website_url %>
<%= f.text_field :website_url, :placeholder => "http://www.heatsynclabs.org" %>
</div>
<div class="field"> <div class="field">
<%= f.label :emergency_name, "Emergency Contact Name" %><br /> <%= f.label :emergency_name, "Emergency Contact Name" %><br />
<%= f.text_field :emergency_name %> <%= f.text_field :emergency_name %>
@ -33,13 +58,6 @@ Thanks again, and happy hacking!</p>
<%= f.label :emergency_email %><br /> <%= f.label :emergency_email %><br />
<%= f.text_field :emergency_email %> <%= f.text_field :emergency_email %>
</div> </div>
<div class="field">
<%= f.label :member_level, "Membership Level" %><br />
<%= f.select :member_level, [[nil],["None",0],["Unable",1],["Volunteer",10],["Associate ($25)",25],["Basic ($50)",50],["Plus ($100)",100]] %>
</div>
<div class="field">
<%= render :partial => "/users/payment_methods", :locals => { :g => f } %>
</div>
<div class="field"> <div class="field">
<%= f.label :current_skills, "What skills, knowledge and experience do you bring to the community?" %><br /> <%= f.label :current_skills, "What skills, knowledge and experience do you bring to the community?" %><br />
<%= f.text_area :current_skills %> <%= f.text_area :current_skills %>

View File

@ -2,12 +2,13 @@
<%= devise_error_messages! %> <%= devise_error_messages! %>
<div class="payment_links"> <div class="payment_links <%= "payment-highlighted" if params[:flash] == "welcome_msg" %>">
<% if can? :read, resource.payments then %> <%= render '/statistics/index' %>
<% if can? :read, resource.payments.first then %>
<h3>Recorded Payments:</h3> <h3>Recorded Payments:</h3>
<ul> <ul>
<% resource.payments.each do |payment| %> <% resource.payments.sort_by(&:date).reverse!.each do |payment| %>
<li><%= payment.date %></li> <li><%= payment.date %></li>
<% end %> <% end %>
</ul> </ul>
@ -16,22 +17,22 @@
<% if resource.payment_method == "Dwolla" %> <% if resource.payment_method == "Dwolla" %>
<h3>Dwolla Payment Link</h3> <h3>Dwolla Payment Link</h3>
<% if resource.member_level == "25" %> <% if resource.member_level == 25 %>
<strong>Associate Membership ($25/mo):</strong> <strong>Associate Membership ($25/mo):</strong>
<a href="http://members.heatsynclabs.org/users/edit" class="dwolla_button" data-name="Associate Membership" data-desc="$25/month membership" data-amount="25" data-shipping="0" data-tax="0" data-key="8nMi2WmI7F8uXVlSNNDmX2o2Cgh9Af8dfJGIrd7ZrgyjNfLIxj">Subscribe</a> <a href="http://members.heatsynclabs.org/users/edit" class="dwolla_button" data-name="Associate Membership" data-desc="$25/month membership" data-amount=25 data-shipping="0" data-tax="0" data-key="8nMi2WmI7F8uXVlSNNDmX2o2Cgh9Af8dfJGIrd7ZrgyjNfLIxj">Subscribe</a>
<% elsif resource.member_level == "50" %> <% elsif resource.member_level == 50 %>
<strong>Basic Membership ($50/mo):</strong> <strong>Basic Membership ($50/mo):</strong>
<a href="http://members.heatsynclabs.org/users/edit" class="dwolla_button" data-name="Basic Membership" data-desc="$50/month membership" data-amount="50" data-shipping="0" data-tax="0" data-key="8nMi2WmI7F8uXVlSNNDmX2o2Cgh9Af8dfJGIrd7ZrgyjNfLIxj">Subscribe</a> <a href="http://members.heatsynclabs.org/users/edit" class="dwolla_button" data-name="Basic Membership" data-desc="$50/month membership" data-amount=50 data-shipping="0" data-tax="0" data-key="8nMi2WmI7F8uXVlSNNDmX2o2Cgh9Af8dfJGIrd7ZrgyjNfLIxj">Subscribe</a>
<% elsif resource.member_level == "100" %> <% elsif resource.member_level == 100 %>
<strong>Plus Membership ($100/mo):</strong> <strong>Plus Membership ($100/mo):</strong>
<a href="http://members.heatsynclabs.org/users/edit" class="dwolla_button" data-name="Plus Membership" data-desc="$100/month membership" data-amount="100" data-shipping="0" data-tax="0" data-key="8nMi2WmI7F8uXVlSNNDmX2o2Cgh9Af8dfJGIrd7ZrgyjNfLIxj">Subscribe</a> <a href="http://members.heatsynclabs.org/users/edit" class="dwolla_button" data-name="Plus Membership" data-desc="$100/month membership" data-amount=100 data-shipping="0" data-tax="0" data-key="8nMi2WmI7F8uXVlSNNDmX2o2Cgh9Af8dfJGIrd7ZrgyjNfLIxj">Subscribe</a>
<% else %> <% else %>
Hmm, your membership level doesn't appear to be at $25/50/100... Hmm, your membership level doesn't appear to be at $25/50/100...
<% end %> <% end %>
<script type="text/javascript" src="https://www.dwolla.com/scripts/button.min.js"> </script> <script type="text/javascript" src="https://www.dwolla.com/scripts/button.min.js"> </script>
<% elsif resource.payment_method == "PayPal" %> <% elsif resource.payment_method == "PayPal" %>
<h3>PayPal Payment Link</h3> <h3>PayPal Payment Link</h3>
<% if resource.member_level == "25" %> <% if resource.member_level == 25 %>
<strong>Associate Membership ($25/mo):</strong> <strong>Associate Membership ($25/mo):</strong>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post"> <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick"> <input type="hidden" name="cmd" value="_s-xclick">
@ -39,7 +40,7 @@
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_subscribeCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!"> <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_subscribeCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1"> <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form> </form>
<% elsif resource.member_level == "50" %> <% elsif resource.member_level == 50 %>
<strong>Basic Membership ($50/mo):</strong> <strong>Basic Membership ($50/mo):</strong>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post"> <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick"> <input type="hidden" name="cmd" value="_s-xclick">
@ -47,7 +48,7 @@
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_subscribeCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!"> <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_subscribeCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1"> <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form> </form>
<% elsif resource.member_level == "100" %> <% elsif resource.member_level == 100 %>
<strong>Plus Membership ($100/mo):</strong> <strong>Plus Membership ($100/mo):</strong>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post"> <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick"> <input type="hidden" name="cmd" value="_s-xclick">

View File

@ -4,3 +4,8 @@
<%= !@results.nil? %> <%= !@results.nil? %>
</p> </p>
<p>
<b>Door status:</b>
<%= @status %>
</p>

View File

@ -8,6 +8,17 @@ Guide to Card Number storage:
cardnum = (R+(r*32767)), convert to hex cardnum = (R+(r*32767)), convert to hex
Guide to log keys and data: Guide to log keys and data:
Commands:
* rem_ = remote command issues via web UI (#=user id)
Statuses:
* armed = current status of alarm arming
* activated = current status of alarm ringing
* alarm_2 = current status of alarm sensor 2
* alarm_3 = current status of alarm sensor 3
* door_1_locked = status of door 1
* door_2_locked = status of door 2
Log Entries:
* A=alarm armed (# level) * A=alarm armed (# level)
* a=added user (# usernum) * a=added user (# usernum)
* C=keypad command (# command) * C=keypad command (# command)
@ -72,7 +83,7 @@ Guide to log keys and data:
end %> end %>
<% @cardnum = (door_log.data.to_i+(@divided_tmp*32767)).to_s(16) %> <% @cardnum = (door_log.data.to_i+(@divided_tmp*32767)).to_s(16) %>
<%= "Card: "+@cardnum %> <%= "Card: "+@cardnum %>
<% @card = Card.find(:first,:conditions=>['card_number LIKE ?', @cardnum]) %> <% @card = Card.find(:first,:conditions=>['lower(card_number) = ?', @cardnum.downcase]) %>
<%= "("+@card.user.name+")" unless @card.nil? %> <%= "("+@card.user.name+")" unless @card.nil? %>
</td> </td>
<% else <% else

View File

@ -1,23 +1,27 @@
<h1>Welcome to the HeatSync Labs Members App.</h1> <h1><%= Setting.welcome_title %></h1>
<% if !user_signed_in? then %> <% if !user_signed_in? then %>
<p>You can sign up to become a member here!</p> <%= simple_format Setting.welcome_body %>
<% end %> <% end %>
<% if user_signed_in? && current_user.orientation.blank? then %> <% if user_signed_in? && current_user.orientation.blank? then %>
<p class="alert">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.</p> <p class="alert">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.</p>
<% end %> <% end %>
<% if user_signed_in? && current_user.member.to_i < current_user.member_level.to_i then %> <% if user_signed_in? && current_user.member_status.between?(2,100) then %>
<%= current_user.member.inspect %>
<%= current_user.member_level.inspect %>
<p class="alert">Looks like we haven't acknowledged a recent payment for you yet. This could be because we're slow, or this app just got started, but if in doubt please see your profile for payment instructions, or consider updating your membership level to something accurate.<br/>Thanks for supporting HeatSync!</p> <p class="alert">Looks like we haven't acknowledged a recent payment for you yet. This could be because we're slow, or this app just got started, but if in doubt please see your profile for payment instructions, or consider updating your membership level to something accurate.<br/>Thanks for supporting HeatSync!</p>
<% end %> <% end %>
<table> <table>
<tr> <tr>
<td> <td>
<% if ((can? :read, @recent_user_names) && (@recent_user_names.count > 1)) then %> <% if ((can? :read, User) && (@recent_user_names.count > 1)) then %>
<h2>New People: <em>(say hi!)</em></h2> <h2>New People: <em>(say hi!)</em></h2>
<ul> <ul>
<% @recent_user_names.each do |user| %> <% @recent_user_names.each do |user| %>
<li><%= link_to user.name, user %> <%= raw(user.member_status_symbol) %> <em>(Signed up <%= user.created_at.strftime("%b %d") %>)</em></li> <% if can? :read, user %>
<li><%= link_to user.name, user %> <%= raw(user.member_status_symbol) %> <em>(Signed up <%= user.created_at.strftime("%b %d") %>)</em></li>
<% end %>
<% end %> <% end %>
</ul> </ul>
<% end %> <% end %>
@ -27,10 +31,14 @@
<dd><%= @num_users %> (<%= @recent_users %> new in the last 7 days)</dd> <dd><%= @num_users %> (<%= @recent_users %> new in the last 7 days)</dd>
<dt># of People Certified:</dt> <dt># of People Certified:</dt>
<dd><%= @num_certs %> (<%= @recent_certs %> new in the last 7 days)</dd> <dd><%= @num_certs %> (<%= @recent_certs %> new in the last 7 days)</dd>
<dt># of Current Paying Members:</dt>
<dd><%= @num_paid_users %> (<%= @num_plus_users %> plus, <%= @num_basic_users %> basic, <%= @num_associate_users %> associate. <%= @num_delinquent_users %> not-current)</dd>
<dt># of Door Accesses Granted:</dt> <dt># of Door Accesses Granted:</dt>
<dd><%= @num_door_opens %> (<%= @today_door_opens %> today, <%= @recent_door_opens %> in the last 7 days)</dd> <dd><%= @num_door_opens %> (<%= @today_door_opens %> today, <%= @recent_door_opens %> in the last 7 days)</dd>
<dt># of Door Accesses Denied:</dt> <dt># of Door Accesses Denied:</dt>
<dd><%= @num_door_denieds %> (<%= @recent_door_denieds %> in the last 7 days)</dd> <dd><%= @num_door_denieds %> (<%= @recent_door_denieds %> in the last month)</dd>
<dt># of Members DB Logins:</dt>
<dd><%= @num_logins %> (<%= @recent_logins %> users last 7 days)</dd>
<dt># of Computers in this DB:</dt> <dt># of Computers in this DB:</dt>
<dd><%= @num_macs %> (<%= @recent_macs %> seen today)</dd> <dd><%= @num_macs %> (<%= @recent_macs %> seen today)</dd>
</dl> </dl>
@ -39,27 +47,10 @@
<td> <td>
<h2>Member Resources</h2> <h2>Member Resources</h2>
<ul> <% if can? :access_doors_remotely, :door_access %>
<li><%= link_to "Wiki", "http://wiki.heatsynclabs.org" %></li> <ul><li><%= link_to "Remote Door Access", space_api_access_path %></li></ul>
<li><%= link_to "Discussion Group", "http://groups.google.com/group/heatsynclabs" %></li> <% end %>
<li><%= link_to "IRC", "irc://irc.freenode.net#heatsynclabs" %></li> <%= simple_format Setting.member_resources_inset %>
<li><%= link_to "Live Webcams", "http://live.heatsynclabs.org/" %></li>
<li>Lab Phone: (480) 751-1929</li>
<li>
<style type="text/css">
form input {font-family: 'Lucida Console', Monaco, monospace; }
</style>
<b>Send a Message!</b>
<form method="post" action="http://tweet.zyphon.com/signage.php">
<em>Type here and your message will show up on the LED sign in the front window!</em><br/>
<em>(Please be nice!)</em><br/>
<input type="text" name="msg" id="msg" value=" Hello" size="9" /> (max 9 chars per line)<br/>
<input type="text" name="msg2" id="msg2" value=" World" size="9" /><br/>
<input type="submit" name="submitbutton" id="submitbutton" value="Go!" />
</form>
</li>
</ul>
</td> </td>
</tr> </tr>

View File

@ -0,0 +1,2 @@
<h1>More Information</h1>
<%= simple_format Setting.more_info_page %>

View File

@ -0,0 +1,35 @@
<h2>PayPal IPN Records</h2>
<p>
<em>Automatically loaded from PayPal's servers</em>
</p>
<table>
<tr>
<th>Date</th>
<th>Name</th>
<th>Item</th>
<th>Amount</th>
</tr>
<% @ipns.sort_by(&:date_parsed).reverse!.each do |ipn| %>
<tr>
<td><%= ipn.payment_date %></td>
<td><%= ipn.first_name %> <%= ipn.last_name %></td>
<td><%= ipn.item_name %></td>
<td>
<% if ipn.payment_gross.blank? %>
<%= ipn.txn_type %>
<% else %>
<%= ipn.payment_gross %>
<% end %>
</td>
<td>
<% 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 %>
</td>
<td><%= link_to "Details", ipn %></td>
<td><%= link_to "Validate", validate_ipn_path(ipn) %></td>
</tr>
<% end %>
</table>

123
app/views/ipns/new.html.erb Normal file
View File

@ -0,0 +1,123 @@
<%= form_tag('/ipns') do |f| %>
<style type="text/css">
label {
width: 10em;
display: inline-block;}
</style>
<div class="actions">
<%= submit_tag %>
</div>
<div class="field">
<%= label_tag :first_name %>
<%= text_field_tag :first_name, "John" %>
</div>
<div class="field">
<%= label_tag :last_name %>
<%= text_field_tag :last_name, "Smith" %>
</div>
<div class="field">
<%= label_tag :payer_email %>
<%= text_field_tag :payer_email, "jsmith@example.com" %>
</div>
<div class="field">
<%= label_tag :item_name %>
<%= text_field_tag :item_name, "Associate Membership" %>
</div>
<div class="field">
<%= label_tag :payment_gross %>
<%= text_field_tag :payment_gross, "25.00" %>
</div>
<div class="field">
<%= label_tag :transaction_subject %>
<%= text_field_tag :transaction_subject, "" %>
</div>
<div class="field">
<%= label_tag :payment_date %>
<%= text_field_tag :payment_date, "20:46:54 Jun 20, 2013 PDT" %>
</div>
<div class="field">
<%= label_tag :txn_type %>
<%= text_field_tag :txn_type, "subscr_payment" %>
</div>
<div class="field">
<%= label_tag :subscr_id %>
<%= text_field_tag :subscr_id, "I-1234567890" %>
</div>
<div class="field">
<%= label_tag :residence_country %>
<%= text_field_tag :residence_country, "US" %>
</div>
<div class="field">
<%= label_tag :mc_currency %>
<%= text_field_tag :mc_currency, "USD" %>
</div>
<div class="field">
<%= label_tag :business %>
<%= text_field_tag :business, "hslfinances@gmail.com" %>
</div>
<div class="field">
<%= label_tag :payment_type %>
<%= text_field_tag :payment_type, "instant" %>
</div>
<div class="field">
<%= label_tag :protection_eligibility %>
<%= text_field_tag :protection_eligibility, "Ineligible" %>
</div>
<div class="field">
<%= label_tag :verify_sign %>
<%= text_field_tag :verify_sign, "12ru9021j9f21j90fj1290fj2910fj0219fj0" %>
</div>
<div class="field">
<%= label_tag :payer_status %>
<%= text_field_tag :payer_status, "verified" %>
</div>
<div class="field">
<%= label_tag :txn_id %>
<%= text_field_tag :txn_id, "1234567890" %>
</div>
<div class="field">
<%= label_tag :receiver_email %>
<%= text_field_tag :receiver_email, "hslfinances@gmail.com" %>
</div>
<div class="field">
<%= label_tag :payer_id %>
<%= text_field_tag :payer_id, "V812314914" %>
</div>
<div class="field">
<%= label_tag :receiver_id %>
<%= text_field_tag :receiver_id, "V90R1280182" %>
</div>
<div class="field">
<%= label_tag :payment_status %>
<%= text_field_tag :payment_status, "Completed" %>
</div>
<div class="field">
<%= label_tag :payment_fee %>
<%= text_field_tag :payment_fee, "0.85" %>
</div>
<div class="field">
<%= label_tag :mc_fee %>
<%= text_field_tag :mc_fee, "0.85" %>
</div>
<div class="field">
<%= label_tag :mc_gross %>
<%= text_field_tag :mc_gross, "25.00" %>
</div>
<div class="field">
<%= label_tag :charset %>
<%= text_field_tag :charset, "windows-1252" %>
</div>
<div class="field">
<%= label_tag :notify_version %>
<%= text_field_tag :notify_version, "3.7" %>
</div>
<div class="field">
<%= label_tag :ipn_track_id %>
<%= text_field_tag :ipn_track_id, "9d3d032d9070" %>
</div>
<div class="actions">
<%= submit_tag %>
</div>
<% end %>

View File

@ -0,0 +1,18 @@
<h2>IPN Details</h2>
<% @ipn.attributes.except("data","payment_id").each do |attr| %>
<p>
<b><%= attr.first.to_s %>:</b>
<%= @ipn.attributes[attr.first] %>
</p>
<% end %>
<p>
<% if @ipn.payment.present? %>
<%= link_to "Linked Payment", @ipn.payment %>
<% else %>
<span class="alert">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) %>.</span>
<% end %>
</p>
<%= link_to "Back", ipns_path %>

View File

@ -8,17 +8,18 @@
</head> </head>
<body> <body>
<div id="header"> <div id="header">
<a href="/" title="Home"><img src="/assets/logo.png" id="logo" /></a> <a id="home_nav_link" href="/" title="Home"><img src="/assets/logo.png" id="logo" /></a>
<%= link_to 'People', users_path if can? :read, User %> <%= link_to 'People', users_path if can? :read, User %>
<%= link_to 'Access Cards', cards_path if can? :manage, Card %> <%= 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 'Cert Classes', certifications_path if can? :read, Certification %>
<%= link_to 'User Certs', user_certifications_path if can? :create, UserCertification %> <%= link_to 'User Certs', user_certifications_path if can? :create, UserCertification %>
<% else %> <% else %>
<%= link_to 'Certifications', certifications_path if can? :read, Certification %> <%= link_to 'Certifications', certifications_path if can? :read, Certification %>
<% end %> <% end %>
<%= link_to 'Payments', payments_path if can? :read, Payment %> <%= link_to 'Payments', payments_path if can? :read, Payment %>
<%= link_to 'Computers', macs_path if user_signed_in? && (can? :read, Mac) %> <%= link_to 'Computers', macs_path if user_signed_in? || (can? :read, Mac) %>
<%= link_to 'Settings', settings_path if can? :read, Setting %>
<% if user_signed_in? then %><%= link_to 'Profile', edit_user_registration_path %><% end %> <% if user_signed_in? then %><%= link_to 'Profile', edit_user_registration_path %><% end %>
<%= link_to 'Logout', destroy_user_session_path, :method => :delete if user_signed_in? %> <%= link_to 'Logout', destroy_user_session_path, :method => :delete if user_signed_in? %>
<%= link_to 'Login', new_user_session_path unless user_signed_in? %> <%= link_to 'Login', new_user_session_path unless user_signed_in? %>
@ -29,5 +30,6 @@
<p class="alert"><%= raw(alert) %></p> <p class="alert"><%= raw(alert) %></p>
<%= yield %> <%= yield %>
</div> </div>
<%= raw Setting.analytics_code if Setting.present? %>
</body> </body>
</html> </html>

View File

@ -1,7 +1,7 @@
<%= form_for(@mac) do |f| %> <%= form_for(@mac) do |f| %>
<% if @mac.errors.any? %> <% if @mac.errors.any? %>
<div id="error_explanation"> <div id="error_explanation">
<h2><%= pluralize(@mac.errors.count, "error") %> prohibited this Mac from being saved:</h2> <h2><%= pluralize(@mac.errors.count, "error") %> prohibited this MAC from being saved:</h2>
<ul> <ul>
<% @mac.errors.full_messages.each do |msg| %> <% @mac.errors.full_messages.each do |msg| %>
@ -17,7 +17,8 @@
</div> </div>
<div class="field"> <div class="field">
<%= f.label :mac %><br /> <%= f.label :mac %><br />
<%= f.text_field :mac %> <%= f.text_field :mac, :value => (@mac.mac || @arp || nil) %>
<% if @arp.present? %><br/><em>Autodetected from your IP of <%= @ip %></em><% end %>
</div> </div>
<div class="field"> <div class="field">
<%= f.label :note %><br /> <%= f.label :note %><br />

View File

@ -1,4 +1,4 @@
<h1>Editing Mac</h1> <h1>Editing MAC</h1>
<%= render 'form' %> <%= render 'form' %>

View File

@ -0,0 +1,112 @@
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
<script src="http://code.highcharts.com/highcharts.js"></script>
<script type="text/javascript">
graph_data = <%= raw @mac_log_graph.to_json %>;
plot_band_start_hour = 16;
plot_band_end_hour = 22;
$(function() {
$('#graph').highcharts({
chart: {
type: 'column',
zoomType : 'x'
},
colors: [
'#2980B9'
],
title: {
style : { fontSize: '10px' },
text: "Click and Drag to Zoom."
},
xAxis: {
title : { text : "Days" },
type : 'datetime',
plotBands: generatePlotBands(graph_data[0][0], graph_data[graph_data.length-1][0]),
labels : { align : "left" }
},
yAxis : {
title : {
text : "# of Computers"
}
},
series: [
{
name: "Computers",
data: graph_data
},
],
plotOptions: {
spline: {
lineWidth: 4,
marker: {
enabled: false
},
pointInterval: 3600000, // one hour
}
}
});
Highcharts.setOptions({
global : {
useUTC : false
}
});
});
function generatePlotBands(start,end){
plotBands = new Array();
for (var d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
// weekends
if(d.getDay()%6==0){
// highlight the whole day
plotBands.push({
color: '#eee',
from: d.setHours(0),
to: d.setHours(23)
});
// weekend hours
plotBands.push({
color: '#94C9EC',
from: d.setHours(12),
to: d.setHours(18)
});
}
else if(d.getDay()==3){
// special wednesday hours
plotBands.push({
color: '#94C9EC',
from: d.setHours(12),
to: d.setHours(plot_band_end_hour)
});
}
else {
// open hours
plotBands.push({
color: '#82DAC9',
from: d.setHours(plot_band_start_hour),
to: d.setHours(plot_band_end_hour)
});
}
}
return plotBands;
}
</script>
<h2>Daily Computer Graph</h2>
<p><em>Note: these numbers are not absolute. They are calculated and adjusted on-the-fly and thus may vary depending on the query parameters.</em></p>
<%= link_to 'Back to Computers', macs_path, :class => 'btn' %>
<%= link_to 'Download JSON', macs_history_path+".json", :class => 'btn' %>
<div id="graph" title="MAC Presence by Day" style="height: 400px; width: 100%;"></div>
<%= form_tag(nil, :method => :get) do %>
<label>Start Date
<input id="start" name="start" type="date" value="<%= @start_date.to_date.to_s %>" />
</label>
<label>End Date
<input id="end" name="end" type="date" value="<%= @end_date.to_date.to_s %>" />
</label>
<%= submit_tag("Change Date", :name => nil, :class => 'btn') %>
<% end %>

View File

@ -1,5 +1,24 @@
<% if can? :read_details, Mac %>
<% if @mac_time_start_date.present? %>
<b>Most Active Machine Last <%= distance_of_time_in_words DateTime.now, @mac_time_start_date %>:</b>
<span title="<%= @most_active_mac.mac %><%= " - "+@most_active_mac.ip.to_s if can? :read_details, @most_active_mac %>">
<%= @most_active_mac.user.name unless @most_active_mac.user.blank? %>
<%= "("+@most_active_mac.note+")" unless @most_active_mac.note.blank? %>
- <%= (@most_active.last[:time] / 1.hour).round %> hrs
</span>
<br/>
<b>Runner Up:</b>
<span title="<%= @runner_up_mac.mac %><%= " - "+@runner_up_mac.ip.to_s if can? :read_details, @runner_up_mac %>">
<%= @runner_up_mac.user.name unless @runner_up_mac.user.blank? %>
<%= "("+@runner_up_mac.note+")" unless @runner_up_mac.note.blank? %>
- <%= (@runner_up.last[:time] / 1.hour).round %> hrs
</span>
<% end %>
<% end %>
<h2>What machines are on our network?</h2> <h2>What machines are on our network?</h2>
<%= link_to "New MAC registration", new_mac_path if can? :create, Mac %> <%= link_to "New MAC registration", new_mac_path, :class => 'btn' if can? :create, Mac %>
<%= link_to 'Activity Graph', macs_history_path, :class => 'btn' if can? :read_details, Mac %>
<ul class="mac_list"> <ul class="mac_list">
<% <%
@ -15,6 +34,10 @@ Rails.logger.info mac.inspect %>
<% end %> <% end %>
</ul> </ul>
<% unless can? :read_details, Mac %>
<p>To see more or register your own, login!</p>
<% end %>
<% if can? :read_details, Mac %> <% if can? :read_details, Mac %>
<ul class="mac_list hidden"> <ul class="mac_list hidden">
<% @hidden_macs.each do |mac| %> <% @hidden_macs.each do |mac| %>
@ -29,7 +52,7 @@ Rails.logger.info mac.inspect %>
<% end %> <% end %>
<% if can? :manage, Mac %> <% if can? :manage, Mac %>
<h3>All Macs</h3> <h3>All MACs</h3>
<table> <table>
<% @all_macs.each do |mac| %> <% @all_macs.each do |mac| %>
<tr <%= raw('class="hidden"') if mac.hidden? %>> <tr <%= raw('class="hidden"') if mac.hidden? %>>

View File

@ -1,4 +1,4 @@
<h1>New Mac</h1> <h1>New MAC</h1>
<%= render 'form' %> <%= render 'form' %>

View File

@ -1,5 +1,7 @@
Scanning... Scanning...
<% @log.each do |log| %> <% if can? :read_details, Mac
@log.each do |log| %>
<%= log.mac %> = <%= log.mac %> =
<%= log.ip %><br/> <%= log.ip %><br/>
<% end %> <% end
end %>

View File

@ -4,7 +4,7 @@
</p> </p>
<p> <p>
<b>Mac:</b> <b>MAC:</b>
<%= @mac.mac %> <%= @mac.mac %>
</p> </p>
@ -23,5 +23,16 @@
<%= @mac.ip %> <%= @mac.ip %>
</p> </p>
<% if can? :read, MacLog %>
<p>
<b>Recent Log Entries:</b>
<ul>
<% @mac_logs.each do |log| %>
<li><%= log.created_at %></li>
<% end %>
</ul>
</p>
<% end %>
<%= link_to 'Edit', edit_mac_path(@mac) %> | <%= link_to 'Edit', edit_mac_path(@mac) %> |
<%= link_to 'Back', macs_path %> <%= link_to 'Back', macs_path %>

View File

@ -19,6 +19,10 @@
<%= f.label :date, "Paid for month beginning" %><br /> <%= f.label :date, "Paid for month beginning" %><br />
<%= f.date_select :date, :default => (DateTime.now - 1.month) %> <%= f.date_select :date, :default => (DateTime.now - 1.month) %>
</div> </div>
<div class="field">
<%= f.label :amount %><br />
<%= f.number_field :amount %>
</div>
<div class="actions"> <div class="actions">
<%= f.submit %> <%= f.submit %>
</div> </div>

View File

@ -1,11 +1,121 @@
<h1>Listing payments</h1> <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
<script src="http://code.highcharts.com/highcharts.js"></script>
<script type="text/javascript">
$(function() {
$( "#graph" ).dialog({
autoOpen: false,
height: 325,
width: 525,
modal: true,
});
$( "#graph-button" ).click(function() {
$( "#graph" ).dialog( "open" );
});
$('#graph').highcharts({
chart: {
type: 'area'
},
title: {
style : { fontSize: '10px' },
text: "Numbers for Most Recent Month Greater Than They Appear"
},
xAxis: {
type: 'datetime'
},
yAxis : [{ // Primary yAxis
title : {
text: "Income"
},
labels: {
format: '${value}'
}
}, { // Secondary yAxis
title : {
text: "Members",
style: { color: '#8bbc21' }
},
labels: {
format: '{value}',
style: { color: '#8bbc21' }
},
opposite: true
}],
tooltip: {
shared: true,
xDateFormat: '%B %Y',
formatter: function() {
console.log(this);
output = '<b>'+ Highcharts.dateFormat(this.points[0].series.tooltipOptions.xDateFormat,this.x) +'</b><br/>';
total = 0;
this.points.forEach(function(e,i,a){
console.log(e);
output += '<span style="color:'+e.series.color+'">'+e.series.name +'</span>: '+ (e.series.tooltipOptions.valuePrefix||"") + Highcharts.numberFormat(e.y,0,".",",") +'<br/>';
if(i < 2){
total += e.y;
}
});
output += '<b>Total:</b> '+(this.points[0].series.tooltipOptions.valuePrefix||"")+ Highcharts.numberFormat(total,0,".",",");
return output;
}
},
plotOptions: {
area: {
stacking: 'normal',
lineColor: '#666666',
lineWidth: 1,
marker: {
lineWidth: 1,
lineColor: '#666666'
}
}
},
series: [{
name: 'Basic',
data: <%= @graph[:basic].to_json %>,
tooltip: {
valuePrefix: '$'
}
}, {
name: 'Associate',
data: <%= @graph[:associate].to_json %>,
tooltip: {
valuePrefix: '$'
}
}, {
yAxis: 1,
type: 'spline',
name: 'Members',
data: <%= @graph[:members].to_json %>
}]
});
});
</script>
<div id="graph" title="Payments by Month" style="height: 250px; width: 500px; float: right;"></div>
<h1>Listing payments <button id="graph-button" class="btn">View Graph</button></h1>
<p>
<b>Create Payments:</b>
<%= link_to 'Manually', new_payment_path %> |
<%= link_to 'Batched CSV', paypal_csvs_path %> |
<%= link_to 'IPN', ipns_path %>
</p>
<%= link_to 'New Payment', new_payment_path %>
<br />
<table> <table>
<tr> <tr>
<th>Payee</th>
<th>User</th> <th>User</th>
<th>Paid for month <br/>beginning</th> <th>Member level</th>
<th>Last Payment</th>
<th>Amount</th>
<th></th> <th></th>
<th></th> <th></th>
<th></th> <th></th>
@ -13,8 +123,11 @@
<% @payments.each do |payment| %> <% @payments.each do |payment| %>
<tr> <tr>
<td><%= link_to payment.user.name_with_payee_and_member_level, payment.user unless payment.user.blank? %></td> <td><%= payment.user.payee unless payment.user.blank? %></td>
<td><%= payment.human_date %></td> <td><%= link_to payment.user.name, payment.user unless payment.user.blank? %></td>
<td><%= payment.user.member_level_string unless payment.user.blank? %></td>
<td><%= payment.date %></td>
<td><%= payment.amount %></td>
<td><%= link_to 'Details', payment %></td> <td><%= link_to 'Details', payment %></td>
<td><%= link_to 'Edit', edit_payment_path(payment) %></td> <td><%= link_to 'Edit', edit_payment_path(payment) %></td>
</tr> </tr>

View File

@ -1,6 +1,6 @@
<p> <p>
<b>User:</b> <b>User:</b>
<%= @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? %>
</p> </p>
<p> <p>
@ -8,9 +8,19 @@
<%= @payment.date %> <%= @payment.date %>
</p> </p>
<p>
<b>Amount:</b>
<%= @payment.amount %>
</p>
<p> <p>
<b>Last Modified by:</b> <b>Last Modified by:</b>
<%= 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 %>
</p> </p>
<p> <p>
@ -23,6 +33,18 @@
<%= @payment.updated_at %> <%= @payment.updated_at %>
</p> </p>
<% if @payment.ipn.present? %>
<p>
<%= link_to "Paid via PayPal (IPN)", @payment.ipn %>
</p>
<% end %>
<% if @payment.paypal_csv.present? %>
<p>
<%= link_to "Paid via PayPal (CSV)", @payment.paypal_csv %>
</p>
<% end %>
<%= link_to 'Edit', edit_payment_path(@payment) %> | <%= 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 %> | <%= link_to 'Destroy', @payment, :confirm => 'Are you sure you want to destroy this payment?', :method => :delete if can? :destroy, @payment %> |

View File

@ -0,0 +1,35 @@
<h2>PayPal CSV Records</h2>
<p>
<%= link_to "Upload CSV", new_paypal_csv_path %>
</p>
<table>
<tr>
<th>Date</th>
<th>Name</th>
<th>Item</th>
<th>Amount</th>
</tr>
<% @paypal_csvs.sort_by(&:date_parsed).reverse!.each do |paypal_csv| %>
<tr>
<td><%= paypal_csv.date %></td>
<td><%= paypal_csv._name %></td>
<td><%= paypal_csv._item_title %></td>
<td>
<% if paypal_csv._gross.blank? %>
<%= paypal_csv._type %>
<% else %>
<%= paypal_csv._gross %>
<% end %>
</td>
<td>
<% 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 %>
</td>
<td><%= link_to "Details", paypal_csv %></td>
</tr>
<% end %>
</table>

View File

@ -0,0 +1,15 @@
<style type="text/css">
label {
width: 10em;
display: inline-block;}
</style>
<%= form_tag('/paypal_csvs', :multipart => true) do |f| %>
<div class="field">
<%= label_tag :file %>
<%= file_field_tag :file %>
</div>
<div class="actions">
<%= submit_tag %>
</div>
<% end %>

View File

@ -0,0 +1,17 @@
<h2>PayPal CSV Item Details</h2>
<% @paypal_csv.attributes.except("data","payment_id").each do |attr| %>
<p>
<b><%= attr.first.to_s %>:</b>
<%= @paypal_csv.attributes[attr.first] %>
</p>
<% end %>
<p>
<% if @paypal_csv.payment.present? %>
<%= link_to "Linked Payment", @paypal_csv.payment %>
<% else %>
<span class="alert">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) %>.</span>
<% end %>
</p>
<%= link_to "Back", paypal_csvs_path %>

View File

@ -0,0 +1,18 @@
<script src="http://code.jquery.com/jquery-migrate-1.2.1.js"></script>
<script type="text/javascript" src="/wymeditor/wymeditor/jquery.wymeditor.min.js"></script>
<script type="text/javascript">
$(function(){
$('.wysiwyg').wymeditor();
});
</script>
<%= form_tag(setting_path(@setting[:var]), :method => :put) do %>
<h3>Editing <%= @setting[:var].titleize %></h3>
<div class="field">
<%= text_area_tag :value, @setting[:value], :cols => 50, :class => "wysiwyg" %><br/>
</div>
<em>Note: there is a /more_info page you can link to for wysiwyg purposes.</em>
<div class="field">
<%= submit_tag "Save", {:class => "wymupdate"} %><br/>
<%= link_to "Back", settings_path %>
</div>
<% end %>

View File

@ -0,0 +1,16 @@
<h2>Site-Wide Settings</h2>
<table>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
<% @settings.each do |setting| %>
<tr>
<td><%= setting.first.titleize %></td>
<td>"<%= setting.last.truncate(140) %>"</td>
<% if can? :update, setting %>
<td><%= link_to 'Edit', edit_setting_path(setting.first) %></td>
<% end %>
</tr>
<% end %>
</table>

View File

@ -0,0 +1,114 @@
<h2 id="title">Door Control System</h2>
<% if @output %>
<p class="notice">
<%= raw @output %>
</p>
<hr/>
<% end %>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style type="text/css">
#status, #status span { display: inline-block; text-align: center; font-weight: bold; border-radius: 5px; }
#status { width: 400px;}
#status span { border: 1px solid black; width: 180px; padding: 5px; }
#status span input { width: 100%; margin: 0.2em 0;}
@media only screen and (max-width: 480px){
#header a:not(#home_nav_link) { display: none; }
#title {
position: absolute;
top: 0;
left: 45px;
}
#status { width: 100%; }
#status span { width: 45%; border: none; padding: 1%; margin: 1em 0; }
#status span input { font-size: 1.8em; height: 2em;}
}
.open { background-color: green; }
.closed { background-color: red; }
.open input.unlock, .closed input.lock { background: #bbb; }
</style>
<script type="text/javascript">
function updateDoorStatus(){
// grab status from space api
$.get( "<%= space_api_path(:format => :json) %>", function( data ) {
switch(data.status){
case "doors_open=both":
$( "#status" ).attr("class","open");
$( "#door_1_status" ).attr("class","open");
$( "#door_2_status" ).attr("class","open");
break;
case "doors_open=door1":
$( "#status" ).attr("class","open");
$( "#door_1_status" ).attr("class","open");
$( "#door_2_status" ).attr("class","closed");
break;
case "doors_open=door2":
$( "#status" ).attr("class","open");
$( "#door_1_status" ).attr("class","closed");
$( "#door_2_status" ).attr("class","open");
break;
case "doors_open=none":
$( "#status" ).attr("class","closed");
$( "#door_1_status" ).attr("class","closed");
$( "#door_2_status" ).attr("class","closed");
break;
}
});
}
function updateMacStatus(){
$.get( "/macs.json", function( data ) {
$("#mac_status").text(data.map(function(item){
return item.name;
}).join(", "));
});
}
// run once
updateDoorStatus();
updateMacStatus();
// and again every few seconds
setInterval(updateDoorStatus, 10000);
setInterval(updateDoorStatus, 60000);
</script>
<%= form_tag do %>
<% unless user_signed_in? %>
<p>
<%= label_tag :user, "Email Address" %><br/>
<%= text_field_tag :user %>
</p>
<p>
<%= label_tag :pass, "Password" %><br/>
<%= password_field_tag :pass %>
</p>
<% end %>
<span id="status">
<div>
<span id="overall_status">
Both<br/>
<input type="submit" name="cmd" value="unlock" class="btn unlock">
<input type="submit" name="cmd" value="lock" class="btn lock">
</span>
</div>
<span id="door_1_status">
Door 1<br/>
<input type="submit" name="cmd" value="open-front" class="btn unlock">
<input type="submit" name="cmd" value="unlock-front" class="btn unlock">
<input type="submit" name="cmd" value="lock-front" class="btn lock">
</span>
<span id="door_2_status">
Door 2<br/>
<input type="submit" name="cmd" value="open-rear" class="btn unlock">
<input type="submit" name="cmd" value="unlock-rear" class="btn unlock">
<input type="submit" name="cmd" value="lock-rear" class="btn lock">
</span>
</span>
<!--
<h3>Alarm</h3>
<p>
<input type="submit" name="cmd" value="arm" class="btn">
<input type="submit" name="cmd" value="disarm" class="btn">
</p>
-->
<% end %>
<h3>Who's here?</h3>
<p id="mac_status">
</p>

View File

@ -0,0 +1,11 @@
<h2>Space Status API
<%= link_to 'JSON', space_api_path( :format => :json ), :class => 'btn' %>
</h2>
<dl>
<% @json.each do |key,value| %>
<dt><%= key.humanize.capitalize %>:</dt>
<dd><%= value %></dd>
<% end %>
</dl>

View File

@ -0,0 +1,7 @@
<h2>Your Statistical Data</h2>
<em>Data is power, and you've got the most: this system is programmed such that nobody else can see the graphs below but you! Use the JSON dumps to make something cool!</em>
<ul>
<li><%= link_to 'MAC Address Data', mac_statistics_path %></li>
<li><%= link_to 'Card Access Data', door_statistics_path %></li>
</ul>

View File

@ -0,0 +1,52 @@
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
<script src="http://code.highcharts.com/highcharts.js"></script>
<script type="text/javascript">
$(function() {
$('#graph').highcharts({
chart: {
type: 'column',
zoomType : 'x'
},
title: {
style : { fontSize: '10px' },
text: "Click and Drag to Zoom."
},
xAxis: {
title : { text : "Days" },
type : 'datetime'
},
yAxis : {
title : {
text : "Accesses"
}
},
series: [{
name: "Accesses",
data: <%= raw @door_log_graph.to_json %>
}]
});
});
</script>
<h2>Your Card Access Data</h2>
<%= link_to 'Download JSON', door_statistics_path+".json", :class => 'btn' %>
<div id="graph" title="MAC Presence by Day" style="height: 250px; width: 500px; float: right;"></div>
<table>
<tr>
<th>Date</th>
<th>Action</th>
<th>Card # (hex)</th>
</tr>
<% @door_logs.each do |log| %>
<tr>
<td><%= log.created_at %></td>
<td><%= log.key %></td>
<td><%= log.data.to_s(16) %></td>
</tr>
<% end %>
</table>

View File

@ -0,0 +1 @@
<%= render 'index' %>

View File

@ -0,0 +1,60 @@
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
<script src="http://code.highcharts.com/highcharts.js"></script>
<script type="text/javascript">
$(function() {
$('#graph').highcharts({
chart: {
type: 'column',
zoomType : 'x'
},
title: {
style : { fontSize: '10px' },
text: "Click and Drag to Zoom, Click the Legend to Turn Off Items."
},
xAxis: {
title : { text : "Days" },
type : 'datetime'
},
yAxis : {
title : {
text : "Hours"
}
},
series: [
<% @mac_log_graph.each do |mac, graph, element| %>
{
name: "<%= raw mac %>",
data: <%= raw graph.to_json %>
},
<% end %>
],
tooltip : { valueSuffix : " hrs" }
});
});
</script>
<h2>Your Mac Address Data</h2>
<%= link_to 'Download JSON', mac_statistics_path+".json", :class => 'btn' %>
<div id="graph" title="MAC Presence by Day" style="height: 250px; width: 500px; float: right;"></div>
<table>
<tr>
<th>Date</th>
<th>Action</th>
<th>MAC</th>
<th>IP</th>
</tr>
<% @mac_logs.each do |log| %>
<tr>
<td><%= log.created_at %></td>
<td><%= log.action %></td>
<td><%= log.mac %></td>
<td><%= log.ip %></td>
</tr>
<% end %>
</table>

View File

@ -11,13 +11,14 @@
</div> </div>
<% end %> <% end %>
<% @user_certification.user_id = params[:user] if params[:user].present? %>
<div class="field"> <div class="field">
<%= f.label :user_id, "User" %><br /> <%= f.label :user_id, "User" %><br />
<%= collection_select(:user_certification, :user_id, @users, :id, :name) %> <%= collection_select(:user_certification, :user_id, @users, :id, :name, :include_blank => true) %>
</div> </div>
<div class="field"> <div class="field">
<%= f.label :certification_id, "Certification" %><br /> <%= f.label :certification_id, "Certification" %><br />
<%= collection_select(:user_certification, :certification_id, @certifications, :id, :name) %> <%= collection_select(:user_certification, :certification_id, @certifications, :id, :name, :include_blank => true) %>
</div> </div>
<div class="actions"> <div class="actions">
<%= f.submit %> <%= f.submit %>

View File

@ -1,13 +1,13 @@
<h1>User Certifications</h1> <h1>User Certifications</h1>
<%= link_to 'New User Certification', new_user_certification_path %> <%= link_to 'New User Certification', new_user_certification_path if can? :create, UserCertification %>
<% @grouped_user_certs.sort.each do |cert, user_certifications| %> <% @grouped_user_certs.sort.each do |cert, user_certifications| %>
<dl class="collapsible"> <dl class="collapsible">
<dt><%= cert.name %></dt> <dt><%= cert.name %></dt>
<% 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| %>
<dd> <dd>
<%= link_to user_certification.user.name, user_certification %> <%= link_to user_certification.user_name, user_certification %>
</dd> </dd>
<% end %> <% end %>
</dl> </dl>

View File

@ -1,6 +1,6 @@
<p> <p>
<b>User:</b> <b>User:</b>
<%= @user_certification.user.name %> <%= link_to @user_certification.user.name, @user_certification.user %>
</p> </p>
<p> <p>
@ -14,7 +14,12 @@
</p> </p>
<p> <p>
<b>Updated:</b> by <%= link_to @updated_by.name, @updated_by unless @updated_by.blank? %> <b>Updated:</b> by
<% if @updated_by.blank? %>
#<%= @user_certification.updated_by ||= "nil" %>
<% else %>
<%= link_to @updated_by.name, @updated_by %>
<% end %>
at <%= @user_certification.updated_at %> at <%= @user_certification.updated_at %>
</p> </p>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
</head>
<body>
<p><b>From:</b> <%= link_to @from_user.name, user_url(@from_user) %></p>
<p>
<%= simple_format @body %>
</p>
<p>
<i>To reply, visit <%= link_to @url, @url %></i> .
</p>
</body>
</html>

View File

@ -4,7 +4,7 @@ Please contact them at <%= @user.email %><%= " or "+@user.phone.to_s unless @use
new user orientation, waiver, welcome, payment help, etc. new user orientation, waiver, welcome, payment help, etc.
User Details: <%= link_to @url+user_path(@user), @url+user_path(@user) %> User Details: <%= link_to @url+user_path(@user), @url+user_path(@user) %>
Member Level: <%= simple_format @user.member_level %> Member Level: <%= simple_format @user.member_level_string %>
What skills, knowledge and experience do you bring to the community? What skills, knowledge and experience do you bring to the community?
<%= simple_format @user.current_skills %> <%= simple_format @user.current_skills %>

View File

@ -23,6 +23,8 @@
<div class="field"> <div class="field">
<%= f.label :email %><br /> <%= f.label :email %><br />
<%= f.email_field :email %> <%= f.email_field :email %>
<%= f.check_box :email_visible %>
<%= f.label :email_visible, "Show Email to All Members?" %>
</div> </div>
<% if @user.id.blank? || !params[:password].nil? %> <% if @user.id.blank? || !params[:password].nil? %>
<div class="field"> <div class="field">
@ -38,6 +40,22 @@
<a href="?password=edit">Change Password</a> <a href="?password=edit">Change Password</a>
</div> </div>
<% end %> <% end %>
<div class="field">
<%= f.label :phone %><br />
<%= f.text_field :phone %>
<%= f.check_box :phone_visible %>
<%= f.label :phone_visible, "Show Phone to All Members?" %>
</div>
<div class="field">
<%= f.label :twitter_url %>
<%= f.text_field :twitter_url, :placeholder => "https://twitter.com/heatsynclabs" %><br/>
<%= f.label :facebook_url %>
<%= f.text_field :facebook_url, :placeholder => "https://www.facebook.com/HeatSyncLabs" %><br/>
<%= f.label :github_url %>
<%= f.text_field :github_url, :placeholder => "https://github.com/heatsynclabs" %><br/>
<%= f.label :website_url %>
<%= f.text_field :website_url, :placeholder => "http://www.heatsynclabs.org" %>
</div>
<div class="field"> <div class="field">
<%= f.label :waiver %><br /> <%= f.label :waiver %><br />
<%= f.date_select :waiver, :include_blank => 'true', :default => 'nil' %> <%= f.date_select :waiver, :include_blank => 'true', :default => 'nil' %>
@ -69,10 +87,6 @@
<%= f.label :payee %><br /> <%= f.label :payee %><br />
<%= f.text_field :payee%> <%= f.text_field :payee%>
</div> </div>
<div class="field">
<%= f.label :phone %><br />
<%= f.text_field :phone %>
</div>
<div class="field"> <div class="field">
<%= f.label :current_skills, "What skills, knowledge and experience do you bring to the community?" %><br /> <%= f.label :current_skills, "What skills, knowledge and experience do you bring to the community?" %><br />
<%= f.text_area :current_skills %> <%= f.text_area :current_skills %>
@ -85,6 +99,10 @@
<%= f.label :marketing_source, "How'd you find out about HeatSync?" %><br /> <%= f.label :marketing_source, "How'd you find out about HeatSync?" %><br />
<%= f.text_area :marketing_source %> <%= f.text_area :marketing_source %>
</div> </div>
<div class="field">
<%= f.label :exit_reason, "Reason for Leaving" %><br />
<%= f.text_area :exit_reason %>
</div>
<div class="field"> <div class="field">
<%= f.label :instructor, "Instructor?" %><br /> <%= f.label :instructor, "Instructor?" %><br />
<%= f.check_box :instructor %> <%= f.check_box :instructor %>

View File

@ -0,0 +1,95 @@
<% user ||= @user #unless @user.blank? %>
<p>
<b>Name:</b>
<%= user.name %>
</p>
<% if current_user.admin? then %>
<p>
<b>Email:</b>
<%= user.email %>
</p>
<p>
<b>Orientation?</b>
<%= user.orientation.strftime("%B %d %Y") unless user.orientation.blank? %>
</p>
<% end %>
<p>
<b>Waiver?</b>
<%= user.waiver.strftime("%B %d %Y") unless user.waiver.blank? %>
</p>
<p>
<b>Current Member?</b>
<%= raw(user.member_status_symbol) %>
</p>
<% if current_user.admin? then %>
<p>
<b>Desired Member Level:</b>
<%= user.member_level %>
</p>
<p>
<b>Payment Method:</b>
<%= user.payment_method %>
</p>
<p>
<b>Payee:</b>
<%= user.payee %>
</p>
<p>
<b>Phone:</b>
<%= user.phone %>
</p>
<% end %>
<p>
<b>Current Skills:</b>
<%= simple_format user.current_skills %>
</p>
<p>
<b>Desired Skills:</b>
<%= simple_format user.desired_skills %>
</p>
<p>
<b>Card:</b>
<% if current_user.admin? then %>
<% user.cards.each do |c| %>
<%= link_to c.card_number, c %><%= "," unless c == user.cards.last %>
<% end %>
<% else %>
<%= unless user.cards.blank? then raw("&#x2713;") end %>
<% end %>
</p>
<b>Certifications:</b>
<ul>
<% user.certifications.each do |certification| %>
<li><%= link_to certification.name, certification %></li>
<% end %>
<% if user.certifications.blank? %><li>n/a</li><% end %>
</ul>
<% if current_user.admin? then %>
<p>
<b>Payments:</b>
<ul>
<% user.payments.each do |payment| %>
<li><%= payment.date %></li>
<% end %>
</ul>
</p>
<% end %>
<% if current_user.admin? then %>
<p>
<b>Created:</b>
<%= user.created_at %>
</p>
<p>
<b>Last signed in:</b>
<%= user.current_sign_in_at %>
</p>
<% end %>

View File

@ -0,0 +1,139 @@
<h1>Member Onboarding/Activity</h1>
<a href="#new_users">New Users</a> |
<a href="#members_without_cards">Members Without Cards</a> |
<a href="#logins">Logins</a> |
<a href="#zombies">Never-Logged-In Members</a>
<h2><a name="new_users"></a>New Users</h2>
<table>
<tr>
<th>Name</th>
<th>Member Level</th>
<th>Account Created</th>
<th>Waiver</th>
<th>Orientation</th>
<th>Payment</th>
<th></th>
</tr>
<% if !@new_users.blank? %>
<% @new_users.sort_by(&:created_at).reverse!.each do |user| %>
<tr<%= " class='hidden'" if user.hidden? %>>
<td><%= link_to user.name, user %></td>
<td><%= user.member_level_string %></td>
<td><% if user.created_at.present? %>
<%= distance_of_time_in_words(DateTime.now, user.created_at.to_date)+" ago" %>
<% else %>
<span class="alert">&times;</span>
<% end %></td>
<td><% if user.waiver.present? %>
<%= distance_of_time_in_words(DateTime.now, user.waiver.to_date)+" ago" %>
<% else %>
<span class="alert">&times;</span>
<% end %></td>
<td><% if user.orientation.present? %>
<%= distance_of_time_in_words(DateTime.now, user.orientation.to_date)+" ago" %>
<% else %>
<span class="alert">&times;</span>
<% end %></td>
<td><% if user.payments.present? %>
<%= distance_of_time_in_words(Date.today, user.payments.maximum(:date))+" ago" %>
<% else %>
<span class="alert">&times;</span>
<% end %></td>
</tr>
<% end %>
<% end %>
</table>
<h2><a name="members_without_cards"></a>Basic+ Members Without Cards</h2>
<table>
<tr>
<th>Name</th>
<th>Account Created</th>
<th>Member Level</th>
<th>Payment</th>
<th>Card</th>
<th></th>
</tr>
<% if !@cardless_users.blank? %>
<% @cardless_users.sort_by(&:created_at).reverse!.each do |user| %>
<tr<%= " class='hidden'" if user.hidden? %>>
<td><%= link_to user.name, user %></td>
<td><%= distance_of_time_in_words DateTime.now, user.created_at %> ago</td>
<td><%= user.member_level_string %></td>
<td><% if user.payments.present? %>
<% last_payment = user.payments.maximum(:date) %>
<% if Date.today-last_payment > 60 %>
<span class="alert">
<% else %>
<span class="notice">
<% end %>
<%= distance_of_time_in_words(Date.today, user.payments.maximum(:date))+" ago" %>
</span>
<% else %>
<span class="alert">&times;</span>
<% end %></td>
<td><% if user.cards.present? %>
<span class="notice">&check;</span>
<% else %>
<span class="alert">&times;</span>
<% end %></td>
</tr>
<% end %>
<% end %>
</table>
<h2><a name="logins"></a>Logins</h2>
<table>
<tr>
<th>Name</th>
<th>Current Sign In</th>
<th>Last Sign In</th>
<th>Sign In Count</th>
<th>Account Created</th>
<th></th>
</tr>
<% if !@user_logins.blank? %>
<% @user_logins.sort_by(&:current_sign_in_at).reverse!.each do |user| %>
<tr<%= " class='hidden'" if user.hidden? %>>
<td><%= link_to user.name, user %></td>
<td><%= distance_of_time_in_words DateTime.now, user.current_sign_in_at %> ago</td>
<td>
<% if user.last_sign_in_at != user.current_sign_in_at %>
<%= distance_of_time_in_words DateTime.now, user.last_sign_in_at %> ago
<% end %>
</td>
<td><%= user.sign_in_count %> times</td>
<td><%= distance_of_time_in_words DateTime.now, user.created_at %> ago</td>
</tr>
<% end %>
<% end %>
</table>
<h2><a name="zombies"></a>Never-Logged-In Members</h2>
<em>Excluding non-members</em>
<table>
<tr>
<th>Name</th>
<th>Member Level</th>
<th>Account Created</th>
<th></th>
</tr>
<% if !@zombie_members.blank? %>
<% @zombie_members.sort_by(&:created_at).reverse!.each do |user| %>
<tr<%= " class='hidden'" if user.hidden? %>>
<td><%= link_to user.name, user %></td>
<td><%= user.member_level_string %></td>
<td><%= distance_of_time_in_words DateTime.now, user.created_at %> ago</td>
</tr>
<% end %>
<% end %>
</table>

View File

@ -0,0 +1,14 @@
<h3>Send Email to <%= @user.name %></h3>
<%= form_tag do %>
<div class="field">
<%= label_tag :subject %><br/>
<%= text_field_tag :subject, @subject, :size => 52 %>
</div>
<div class="field">
<%= label_tag :body %><br/>
<%= text_area_tag :body, @body, :cols => 40 %>
</div>
<div class="field">
<%= submit_tag "Send", :class => "btn" %>
</div>
<% end %>

View File

@ -0,0 +1,65 @@
<h1>Inactive Users</h1>
<table>
<col />
<col />
<% if current_user.admin? then %><col /><% end %>
<col />
<% if current_user.admin? %><col />
<col class="col_highlight" /><% end %>
<col />
<col class="col_highlight" />
<col />
<col class="col_highlight" />
<tr>
<th></th>
<th>Name</th>
<% if current_user.admin? then %><th>Email</th><% end %>
<th>Certifications</th>
<% if current_user.admin? then %>
<th>Orientation?</th>
<% end %>
<th>Card?</th>
<th>Pmt Method</th>
<th>Desired Level</th>
<th>Last Payment</th>
<th>Joined</th>
<th></th>
<th></th>
</tr>
<% if !@users.blank? %>
<% @users.each do |user| %>
<tr<%= " class='hidden'" if user.hidden? %>>
<td><%= image_tag user.gravatar_url(:default => "http://members.heatsynclabs.org/assets/nil.png"), :class => :avatar %></td>
<td><%= link_to user.name, user %></td>
<% if current_user.admin? then %><td><%= user.email %></td><% end %>
<td><% user.certifications.each do |c| %>
<%= link_to c.name, c %><%= "," unless c.id == user.certifications.last.id %>
<% end %></td>
<% if current_user.admin? then %><td>
<%= unless user.orientation.blank? then raw("<span class='hoverinfo' title='"+user.orientation.strftime("%B %d %Y")+"'>&#x2713;</span>") end %>
</td><% end %>
<td><%= unless user.cards.blank? then raw("<span class='iconinfo'>&#x2713;</span>") end %></td>
<td><%= user.payment_method %></td>
<td><%= user.member_level %></td>
<td><% delinquency = user.delinquency %>
<% if delinquency == 9999 %>
No Payments
<% else %>
<%= (delinquency/30).to_s+" mo. ago" %>
<% end %>
</td>
<td><%= user.created_at.to_date %></td>
<td><%= link_to 'Edit', edit_user_path(user) if can? :update, user %></td>
<td><%= 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 %></td>
</tr>
<% end %>
<% end %>
</table>
<% if current_user.orientation.blank? then %>
<p class="alert">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.</p>
<% end %>
<br />

View File

@ -1,32 +1,48 @@
<h1>HeatSync People</h1> <h1>HeatSync People</h1>
<% if can? :create, User %> <% if can? :create, User %>
<%= link_to 'New User', new_user_path %> <%= link_to 'New User', new_user_path %> |
<% end %> <% end %>
<% if can? :manage, User %>
<%= link_to 'Merge Users', users_merge_path %> |
<% end %>
<% if current_user.admin? %>
<%= link_to 'Inactive Users', users_inactive_path %> |
<%= link_to 'Recent Activity', users_activity_path %> |
<% end %>
<%= link_to 'New Members Report', users_new_member_report_path %>
<% @certifications = Certification.all.sort_by(&:id) %>
<table> <table>
<col /> <col />
<col /> <col />
<% if current_user.admin? then %><col /><% end %> <% if current_user.admin? then %><col /><% end %>
<col /> <col class="col_highlight" />
<% if current_user.admin? %><col /> <% if current_user.admin? %><col />
<col class="col_highlight" /><% end %> <col class="col_highlight" /><% end %>
<col /> <col />
<col class="col_highlight" /> <col class="col_highlight" />
<col /> <col />
<col class="col_highlight" /> <% col_count = 0 %>
<% @certifications.each do |c| %>
<col <% if col_count.even? %>class="col_highlight"<% end %> />
<% col_count = col_count + 1 %>
<% end %>
<tr> <tr>
<th></th> <th></th>
<th><a href="?sort=name">Name</a></th> <th><a href="?sort=name">Name</a></th>
<th><a href="?sort=member">Member?</a></th>
<% if current_user.admin? then %><th><a href="?sort=email">Email</a></th><% end %> <% if current_user.admin? then %><th><a href="?sort=email">Email</a></th><% end %>
<th><a href="?sort=certifications">Certifications</a></th>
<% if current_user.admin? then %> <% if current_user.admin? then %>
<th><a href="?sort=orientation">Orientation?</a></th> <th><a href="?sort=orientation">Orientation?</a></th>
<% end %> <% end %>
<th><a href="?sort=waiver">Waiver?</a></th> <th><a href="?sort=waiver">Waiver?</a></th>
<th><a href="?sort=member">Member?</a></th>
<th><a href="?sort=card">Card?</a></th> <th><a href="?sort=card">Card?</a></th>
<th><a href="?sort=instructor">Instructor?</a></th> <th><a href="?sort=instructor">Instructor?</a></th>
<% if current_user.admin? then %><th><a href="?sort=admin">Admin?</a></th><% end %> <% if current_user.admin? then %><th><a href="?sort=admin">Admin?</a></th><% end %>
<% @certifications.each do |c| %>
<th><a href="?sort=cert&cert=<%= c.id %>"><%= c.name %></a></th>
<% end %>
<th></th> <th></th>
<th></th> <th></th>
</tr> </tr>
@ -36,18 +52,24 @@
<tr<%= " class='hidden'" if user.hidden? %>> <tr<%= " class='hidden'" if user.hidden? %>>
<td><%= image_tag user.gravatar_url(:default => "http://members.heatsynclabs.org/assets/nil.png"), :class => :avatar %></td> <td><%= image_tag user.gravatar_url(:default => "http://members.heatsynclabs.org/assets/nil.png"), :class => :avatar %></td>
<td><%= link_to user.name, user %></td> <td><%= link_to user.name, user %></td>
<td><%= raw(user.member_status_symbol) %></td>
<% if current_user.admin? then %><td><%= user.email %></td><% end %> <% if current_user.admin? then %><td><%= user.email %></td><% end %>
<td><% user.certifications.each do |c| %>
<%= link_to c.name, c %><%= "," unless c.id == user.certifications.last.id %>
<% end %></td>
<% if current_user.admin? then %><td> <% if current_user.admin? then %><td>
<%= unless user.orientation.blank? then raw("<span class='hoverinfo' title='"+user.orientation.strftime("%B %d %Y")+"'>&#x2713;</span>") end %> <%= unless user.orientation.blank? then raw("<span class='hoverinfo' title='"+user.orientation.strftime("%B %d %Y")+"'>&#x2713;</span>") end %>
</td><% end %> </td><% end %>
<td><%= unless user.waiver.blank? then raw("<span class='hoverinfo' title='"+user.waiver.strftime("%B %d %Y")+"'>&#x2713;</span>") end %></td> <td><%= unless user.waiver.blank? then raw("<span class='hoverinfo' title='"+user.waiver.strftime("%B %d %Y")+"'>&#x2713;</span>") end %></td>
<td><%= raw(user.member_status_symbol) %></td>
<td><%= unless user.cards.blank? then raw("<span class='iconinfo'>&#x2713;</span>") end %></td> <td><%= unless user.cards.blank? then raw("<span class='iconinfo'>&#x2713;</span>") end %></td>
<td><%= if user.instructor? then raw("<span class='iconinfo'>&#x2713;</a>") end %></td> <td><%= if user.instructor? then raw("<span class='iconinfo'>&#x2713;</a>") end %></td>
<% if current_user.admin? then %><td><%= if user.admin? then raw("<span class='iconinfo'>&#x2713;</a>") end %></td><% end %> <% if current_user.admin? then %><td><%= if user.admin? then raw("<span class='iconinfo'>&#x2713;</a>") end %></td><% end %>
<% @certifications.each do |c| %>
<td>
<% user.user_certifications.each do |u| %>
<% if u.certification_id == c.id %>
<%= link_to raw("<span class='iconinfo'>&#x2713;</span>"), u %>
<% end %>
<% end %>
</td>
<% end %>
<td><%= link_to 'Edit', edit_user_path(user) if can? :update, user %></td> <td><%= link_to 'Edit', edit_user_path(user) if can? :update, user %></td>
<td><%= 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 %></td> <td><%= 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 %></td>
</tr> </tr>

View File

@ -0,0 +1,48 @@
<style type="text/css">
#user_to_merge_details, #user_to_keep_details,
#user_to_merge_details p, #user_to_keep_details p {
font-size: 12px;
margin: 0;
}
</style>
<h1>Merging users</h1>
<%= form_tag('/users/merge', :method => :post) do %>
<b>Everything except the email, password, name, id, hidden, and blank values will be moved to the "user to keep" UNLESS there is a value already there. The "user to merge" will be deleted.</b>
<table>
<tr>
<td>
<div class="field">
<%= label_tag :user_to_keep, "User to Keep" %><br />
<%= collection_select(:user, :to_keep, @users, :id, :name_with_email_and_visibility, :include_blank => true) %>
</div>
<div id="user_to_keep_details"></div>
</td>
<td style="font-size: 16px">&#x25C0;&#x25C0;</td>
<td>
<div class="field">
<%= label_tag :user_to_merge, "User to Merge" %><br />
<%= collection_select(:user, :to_merge, @users, :id, :name_with_email_and_visibility, :include_blank => true) %>
</div>
<div id="user_to_merge_details"></div>
</td>
</tr>
</table>
<hr/>
<div class="actions">
<%= submit_tag "Merge" %>
</div>
<% end %>
<%= link_to 'Back', users_path %>
<script>
$("#user_to_keep").change(function(event,handler){
$("#user_to_keep_details").load("/user_summary/"+event.target.value);
});
$("#user_to_merge").change(function(event,handler){
$("#user_to_merge_details").load("/user_summary/"+event.target.value);
});
</script>

View File

@ -0,0 +1,24 @@
<h1>New Members</h1>
<% if !@new_users.blank? %>
<% @new_users.sort_by(&:created_at).reverse!.group_by{|u| u.created_at.beginning_of_month}.each do |g| %>
<h2><%= g.first.to_formatted_s(:month_and_year) %></h2>
<% g.last.each do |user| %>
<p><b><%= user.name %> - <%= user.member_level_string %></b></p>
<p><b>What skills, knowledge and experience do you bring to the community?</b><br/>
<%= user.current_skills %>
</p>
<p><b>
What skills, knowledge and experiences are you looking for in HeatSync?
</b><br/>
<%= user.desired_skills %>
</p>
<p><b>
How'd you find out about HeatSync?
</b><br/>
<%= user.marketing_source %>
</p>
<p>&nbsp;</p>
<% end %>
<% end %>
<% end %>

View File

@ -1,31 +1,38 @@
<%= image_tag @user.gravatar_url(:default => "http://members.heatsynclabs.org/assets/nil.png") %> <h2>
<%= image_tag @user.gravatar_url(:default => "http://members.heatsynclabs.org/assets/nil.png"), :class => 'avatar-large' %>
<p>
<b>Name:</b>
<%= @user.name %> <%= @user.name %>
</p>
<p>
<b>Waiver?</b>
<%= @user.waiver.strftime("%B %d %Y") unless @user.waiver.blank? %>
</p>
<p>
<b>Current Member?</b>
<%= raw(@user.member_status_symbol) %> <%= raw(@user.member_status_symbol) %>
</p> <%= link_to image_tag('/twitter.png', :class => 'social-icon', :title => "Twitter"), @user.twitter_url, :class => 'social-link' if @user.twitter_url.present? %>
<%= link_to image_tag('/facebook.png', :class => 'social-icon', :title => "Facebook"), @user.facebook_url, :class => 'social-link' if @user.facebook_url.present? %>
<%= link_to image_tag('/github.png', :class => 'social-icon', :title => "Github"), @user.github_url, :class => 'social-link' if @user.github_url.present? %>
<%= link_to image_tag('/website.png', :class => 'social-icon', :title => "Website"), @user.website_url, :class => 'social-link' if @user.website_url.present? %>
<%= link_to "Email User", user_compose_email_path(@user), :class => 'btn' %>
</h2>
<p>
<b>Instructor?</b> <% if current_user.admin? || @user.email_visible %>
<%= @user.instructor? %> <p>
</p> <b>Email:</b>
<%= @user.email %>
</p>
<% end %>
<% if current_user.admin? || @user.phone_visible %>
<p>
<b>Phone:</b>
<%= @user.phone %>
</p>
<% end %>
<p>
<b>Current Skills:</b>
<%= simple_format @user.current_skills %>
</p>
<p>
<b>Desired Skills:</b>
<%= simple_format @user.desired_skills %>
</p>
<% if current_user.admin? then %> <% if current_user.admin? then %>
<p>
<b>Email:</b>
<%= @user.email %>
</p>
<p> <p>
<b>Orientation?</b> <b>Orientation?</b>
<%= @user.orientation.strftime("%B %d %Y") unless @user.orientation.blank? %> <%= @user.orientation.strftime("%B %d %Y") unless @user.orientation.blank? %>
@ -44,7 +51,7 @@
</p> </p>
<p> <p>
<b>Desired Member Level:</b> <b>Desired Member Level:</b>
<%= @user.member_level %> <%= @user.member_level_string %>
</p> </p>
<p> <p>
<b>Payment Method:</b> <b>Payment Method:</b>
@ -54,54 +61,76 @@
<b>Payee:</b> <b>Payee:</b>
<%= @user.payee %> <%= @user.payee %>
</p> </p>
<p>
<b>Phone:</b>
<%= @user.phone %>
</p>
<% end %> <% end %>
<p>
<b>Current Skills:</b>
<%= simple_format @user.current_skills %>
</p>
<p>
<b>Desired Skills:</b>
<%= simple_format @user.desired_skills %>
</p>
<% if current_user.admin? then %> <% if current_user.admin? then %>
<p> <p>
<b>Found HeatSync via:</b> <b>Found HeatSync via:</b>
<%= simple_format @user.marketing_source %> <%= simple_format @user.marketing_source %>
</p> </p>
<p>
<b>Left HeatSync because:</b>
<%= simple_format @user.exit_reason %>
</p>
<% end %> <% end %>
<p>
<b>Card:</b>
<% if current_user.admin? then %>
<% @user.cards.each do |c| %>
<%= link_to c.card_number, c %><%= "," unless c == @user.cards.last %>
<% end %>
<% else %>
<%= unless @user.cards.blank? then raw("&#x2713;") end %>
<% end %>
</p>
<b>Certifications:</b> <b>Certifications: </b><%= link_to "+ Add", (new_user_certification_path+"?user="+@user.id.to_s), :class => 'btn' if can? :create, UserCertification %>
<ul> <ul>
<% @user.certifications.each do |certification| %> <% @user.certifications.each do |certification| %>
<li><%= link_to certification.name, certification %></li> <li><%= link_to certification.name, certification %></li>
<% end %> <% end %>
<% if @user.certifications.blank? %><li>n/a</li><% end %> <% if @user.certifications.blank? %><li>n/a</li><% end %>
</ul> </ul>
<% if current_user.admin? then %>
<p> <p>
<b>Payments:</b> <b>Card: </b><%= link_to "+ Add", (new_card_path+"?user="+@user.id.to_s), :class => 'btn' if can? :create, Card %>
<ul> <% if current_user.admin? then %>
<% @payments.each do |payment| %> <ul>
<li><%= payment.date %></li> <% @user.cards.each do |c| %>
<li><%= link_to c.card_number, c %><%= "," unless c == @user.cards.last %></li>
<% end %>
</ul>
<% else %>
<%= unless @user.cards.blank? then raw("&#x2713;") end %>
<% end %> <% end %>
</ul>
</p> </p>
<% end %>
<b>Computers: </b><%= link_to "+ Add", new_mac_path, :class => 'btn' if can? :create, Mac %>
<ul>
<% @user.macs.each do |mac| %>
<li><%= link_to mac.mac, mac %></li>
<% end %>
<% if @user.macs.blank? %><li>n/a</li><% end %>
</ul>
<p>
<% if current_user.admin? then %>
<b>Payments:</b>
<ul>
<% @payments.each do |payment| %>
<li><%= link_to payment.date, payment %></li>
<% end %>
</ul>
<% else %>
<b>Last Payment:</b>
<% last_payment = @user.delinquency %>
<% if last_payment < 30 %>
Less than a month ago
<% else %>
<%= last_payment/30 %> months ago
<% end %>
<% end %>
</p>
<p>
<b>Waiver?</b>
<%= @user.waiver.strftime("%B %d %Y") unless @user.waiver.blank? %>
</p>
<p>
<b>Instructor?</b>
<%= @user.instructor? %>
</p>
<% if current_user.admin? then %> <% if current_user.admin? then %>
<p> <p>

View File

@ -37,7 +37,7 @@ module Dooraccess
config.encoding = "utf-8" config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file. # Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password] config.filter_parameters += [:password, :pass]
# Use SQL instead of Active Record's schema dumper when creating the database. # Use SQL instead of Active Record's schema dumper when creating the database.
# This is necessary if your schema can't be completely dumped by the schema dumper, # This is necessary if your schema can't be completely dumped by the schema dumper,

View File

@ -1,31 +0,0 @@
# SQLite version 3.x
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem 'sqlite3'
development:
adapter: sqlite3
database: db/development.sqlite3
pool: 5
timeout: 5000
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
adapter: sqlite3
database: db/test.sqlite3
pool: 5
timeout: 5000
production:
adapter: sqlite3
database: db/production.sqlite3
pool: 5
timeout: 5000
# adapter: postgresql
# encoding: unicode
# database: members
# pool: 5
# username: postgres
# password:

View File

@ -0,0 +1,54 @@
# SQLite version 3.x
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem 'sqlite3'
# development:
# adapter: sqlite3
# database: db/development.sqlite3
# pool: 5
# timeout: 5000
development:
adapter: postgresql
encoding: unicode
database: YOUR_DATABASE_development
host: localhost
pool: 5
username: YOUR_USERNAME_HERE
password: YOUR_PASSWORD_HERE
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
# test:
# adapter: sqlite3
# database: db/test.sqlite3
# pool: 5
# timeout: 5000
test:
adapter: postgresql
encoding: unicode
database: YOUR_DATABASE_test
host: localhost
pool: 5
username: YOUR_USERNAME_HERE
password: YOUR_PASSWORD_HERE
# production:
# adapter: sqlite3
# database: db/production.sqlite3
# pool: 5
# timeout: 5000
production:
adapter: postgresql
encoding: unicode
database: YOUR_DATABASE_production
host: localhost
pool: 5
username: YOUR_USERNAME_HERE
password: YOUR_PASSWORD_HERE

View File

@ -0,0 +1,2 @@
Date::DATE_FORMATS[:month_and_year] = "%B %Y"
Time::DATE_FORMATS[:month_and_year] = "%B %Y"

View File

@ -0,0 +1,47 @@
@@default_settings = {
:welcome_title => "Welcome to the Hackerspace Members Site",
:welcome_body => "<p>We are a member-driven community workshop where you can learn, make cool stuff, meet other cool people, and make your city a better place to live!</p><p>You don't have to be a member to come visit, but if you're interested in volunteering or being a member, feel free to sign up here! For more information, <a href=\"/more_info\">Click Here</a>.</p>",
:more_info_page => "No info here yet, bug a member about filling this part out!",
:member_resources_inset => "No info here yet, bug a member about filling this part out!",
:analytics_code => "<!-- insert analytics code here -->",
:space_api_json_template => '{
"api" : "0.12",
"space" : "Your Hackerspace Name Here",
"logo" : "http://example.com/logo.png",
"lat": 0,
"lon": -0,
"icon":{
"open": "http://example.com/open.png",
"closed":"http://example.com/closed.png"
},
"url" : "http://example.com",
"address" : "123 main st, city, state, country",
"contact" : {
"phone" : "",
"irc" : "",
"twitter" : "",
"ml" : ""
},
"cam" : [""],
"feeds" : [{"name" : "",
"url" : ""},
{"name" : "",
"url" : ""}],
"apis" : {
"oac" : {
"url" : "http://this-apps-url.example.com/door_access",
"description" : "https://github.com/heatsynclabs/Open-Source-Access-Control-Web-Interface"
},
"pamela" : {
"url" : "http://this-apps-url.example.com/macs.json",
"description" : "https://github.com/heatsynclabs/Open-Source-Access-Control-Web-Interface"
}
}
}'
}
if ActiveRecord::Base.connection.tables.include?('settings') and !defined?(::Rake)
@@default_settings.each do |key, value|
Setting.save_default(key, value)
end
end

View File

@ -103,7 +103,7 @@ Devise.setup do |config|
# ==> Configuration for :rememberable # ==> Configuration for :rememberable
# The time the user will be remembered without asking for credentials again. # The time the user will be remembered without asking for credentials again.
# config.remember_for = 2.weeks config.remember_for = 1.month
# If true, extends the user's remember period when remembered via cookie. # If true, extends the user's remember period when remembered via cookie.
# config.extend_remember_period = false # config.extend_remember_period = false

View File

@ -4,4 +4,4 @@
# If you change this key, all old signed cookies will become invalid! # If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random, # Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks. # no regular words or you'll be exposed to dictionary attacks.
Dooraccess::Application.config.secret_token = 'd258ec483070c67d9e7ba7bb8b6b506a30fb38d0d95ac561ef603785f4639a688747a4adf872f627acae9b57f44f822a0c4dc2f2fd3d8e6135ad0b491f72751f' Dooraccess::Application.config.secret_token = YOUR_RANDOM_STRING_HERE

View File

@ -1,7 +1,18 @@
Dooraccess::Application.routes.draw do 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
resources :paypal_csvs
match 'paypal_csvs/:id/link' => 'paypal_csvs#link', :as => :link_paypal_csv
resources :payments resources :payments
match 'statistics' => 'statistics#index', :as => :statistics
match 'statistics/mac_log' => 'statistics#mac_log', :as => :mac_statistics
match 'statistics/door_log' => 'statistics#door_log', :as => :door_statistics
resources :user_certifications resources :user_certifications
resources :certifications resources :certifications
@ -18,23 +29,39 @@ Dooraccess::Application.routes.draw do
end end
end end
resources :users match 'user_summary/:id' => 'users#user_summary' # User summary view
match 'users/activity' => 'users#activity' # User activity
match 'users/new_member_report' => 'users#new_member_report' # New member report (For emailing)
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 do
get 'email' => 'users#compose_email', :as => "compose_email"
post 'email' => 'users#send_email'
end
match 'users/create' => 'users#create', :via => :post # Use POST users/create instead of POST users to avoid devise conflict match 'users/create' => 'users#create', :via => :post # Use POST users/create instead of POST users to avoid devise conflict
match 'cards/upload_all' => 'cards#upload_all', :as => :upload_all match 'cards/upload_all' => 'cards#upload_all', :as => :upload_all
resources :cards resources :cards
match 'cards/:id/upload' => 'cards#upload', :as => :upload match 'cards/:id/upload' => 'cards#upload', :as => :upload
match 'space_api' => 'space_api#index', :as => :space_api
match 'space_api/access' => 'space_api#access', :via => :get, :as => :space_api_access
match 'space_api/access' => 'space_api#access_post', :via => :post
match 'door_logs' => 'door_logs#index', :as => :door_logs match 'door_logs' => 'door_logs#index', :as => :door_logs
match 'door_logs/download' => 'door_logs#download', :as => :download match 'door_logs/download' => 'door_logs#download', :as => :download
match 'door_logs/auto_download' => 'door_logs#auto_download', :as => :auto_download match 'door_logs/auto_download' => 'door_logs#auto_download', :as => :auto_download
match 'macs/scan' => 'macs#scan' match 'macs/scan' => 'macs#scan'
match 'macs/import' => 'macs#import' match 'macs/import' => 'macs#import'
match 'macs/history' => 'macs#history'
resources :macs resources :macs
resources :mac_logs resources :mac_logs
resources :settings, :only => [:index, :edit, :update]
match 'more_info' => 'home#more_info'
root :to => "home#index" root :to => "home#index"
# The priority is based upon order of creation: # The priority is based upon order of creation:

View File

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

View File

@ -0,0 +1,5 @@
class AddAmountToPayments < ActiveRecord::Migration
def change
add_column :payments, :amount, :decimal
end
end

View File

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

View File

@ -0,0 +1,5 @@
class AddExitReasonToUsers < ActiveRecord::Migration
def change
add_column :users, :exit_reason, :string
end
end

View File

@ -0,0 +1,10 @@
class AddSocialMediaToUsers < ActiveRecord::Migration
def change
add_column :users, :twitter_url, :string
add_column :users, :facebook_url, :string
add_column :users, :github_url, :string
add_column :users, :website_url, :string
add_column :users, :email_visible, :boolean
add_column :users, :phone_visible, :boolean
end
end

View File

@ -0,0 +1,13 @@
class ChangeSurveysToText < ActiveRecord::Migration
def up
change_column :users, :current_skills, :text, :limit => nil
change_column :users, :desired_skills, :text, :limit => nil
change_column :users, :marketing_source, :text, :limit => nil
end
def down
change_column :users, :current_skills, :string
change_column :users, :desired_skills, :string
change_column :users, :marketing_source, :string
end
end

View File

@ -0,0 +1,9 @@
class ChangeMemberLevelToInteger < ActiveRecord::Migration
def up
change_column :users, :member_level, :integer
end
def down
change_column :users, :member_level, :string
end
end

View File

@ -0,0 +1,9 @@
class ChangeUserExitReasonToText < ActiveRecord::Migration
def up
change_column :users, :exit_reason, :text, :limit => nil
end
def down
change_column :users, :exit_reason, :string
end
end

View File

@ -0,0 +1,17 @@
class CreateSettings < ActiveRecord::Migration
def self.up
create_table :settings do |t|
t.string :var, :null => false
t.text :value, :null => true
t.integer :thing_id, :null => true
t.string :thing_type, :limit => 30, :null => true
t.timestamps
end
add_index :settings, [ :thing_type, :thing_id, :var ], :unique => true
end
def self.down
drop_table :settings
end
end

View File

@ -1,3 +1,4 @@
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead # 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 # of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition. # 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. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20130212083412) do ActiveRecord::Schema.define(:version => 20130928112252) do
create_table "cards", :force => true do |t| create_table "cards", :force => true do |t|
t.string "card_number" t.string "card_number"
@ -35,6 +36,27 @@ ActiveRecord::Schema.define(:version => 20130212083412) do
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
end 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| create_table "mac_logs", :force => true do |t|
t.string "mac" t.string "mac"
t.string "ip" t.string "ip"
@ -64,10 +86,47 @@ ActiveRecord::Schema.define(:version => 20130212083412) do
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
t.integer "created_by" t.integer "created_by"
t.decimal "amount"
end end
add_index "payments", ["user_id"], :name => "index_payments_on_user_id" 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 "settings", :force => true do |t|
t.string "var", :null => false
t.text "value"
t.integer "thing_id"
t.string "thing_type", :limit => 30
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "settings", ["thing_type", "thing_id", "var"], :name => "index_settings_on_thing_type_and_thing_id_and_var", :unique => true
create_table "user_certifications", :force => true do |t| create_table "user_certifications", :force => true do |t|
t.integer "user_id" t.integer "user_id"
t.integer "certification_id" t.integer "certification_id"
@ -79,35 +138,42 @@ ActiveRecord::Schema.define(:version => 20130212083412) do
create_table "users", :force => true do |t| create_table "users", :force => true do |t|
t.string "name" t.string "name"
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
t.string "email", :default => "", :null => false t.string "email", :default => "", :null => false
t.string "encrypted_password", :default => "", :null => false t.string "encrypted_password", :default => "", :null => false
t.string "reset_password_token" t.string "reset_password_token"
t.datetime "reset_password_sent_at" t.datetime "reset_password_sent_at"
t.datetime "remember_created_at" t.datetime "remember_created_at"
t.integer "sign_in_count", :default => 0 t.integer "sign_in_count", :default => 0
t.datetime "current_sign_in_at" t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at" t.datetime "last_sign_in_at"
t.string "current_sign_in_ip" t.string "current_sign_in_ip"
t.string "last_sign_in_ip" t.string "last_sign_in_ip"
t.boolean "admin" t.boolean "admin"
t.integer "member", :limit => 255 t.integer "member"
t.datetime "waiver" t.datetime "waiver"
t.datetime "orientation" t.datetime "orientation"
t.string "emergency_name" t.string "emergency_name"
t.string "emergency_phone" t.string "emergency_phone"
t.string "emergency_email" t.string "emergency_email"
t.string "member_level" t.integer "member_level"
t.string "payment_method" t.string "payment_method"
t.string "phone" t.string "phone"
t.string "current_skills" t.text "current_skills"
t.string "desired_skills" t.text "desired_skills"
t.boolean "instructor" t.boolean "instructor"
t.boolean "hidden" t.boolean "hidden"
t.string "marketing_source" t.text "marketing_source"
t.string "payee" t.string "payee"
t.boolean "accountant" t.boolean "accountant"
t.text "exit_reason"
t.string "twitter_url"
t.string "facebook_url"
t.string "github_url"
t.string "website_url"
t.boolean "email_visible"
t.boolean "phone_visible"
end end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true add_index "users", ["email"], :name => "index_users_on_email", :unique => true

Some files were not shown because too many files have changed in this diff Show More