Merge pull request #2 from zyphlar/master

Updating hsl repo
This commit is contained in:
Will Bradley 2014-07-06 17:01:02 -07:00
commit 3cb1d4e730
269 changed files with 10851 additions and 362 deletions

2
.gitignore vendored
View File

@ -21,5 +21,7 @@ tmp/
# Ignore config and database files (passwords) # Ignore config and database files (passwords)
/config/config.yml /config/config.yml
/config/s3.yml
/config/database.yml /config/database.yml
/config/initializers/secret_token.rb /config/initializers/secret_token.rb
.env

1
.ruby-gemset Normal file
View File

@ -0,0 +1 @@
members-hsl

1
.ruby-version Normal file
View File

@ -0,0 +1 @@
ruby-1.9.3-p385

35
.rvmrc
View File

@ -1,35 +0,0 @@
#!/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

View File

@ -3,6 +3,7 @@ source 'https://rubygems.org'
ruby '1.9.3' ruby '1.9.3'
gem 'rails', '3.2.8' gem 'rails', '3.2.8'
gem 'dotenv-rails'
# 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'
@ -46,7 +47,8 @@ gem 'bcrypt-ruby', '~> 3.0.0'
# To use debugger # To use debugger
#gem 'debugger' #gem 'debugger'
#gem "paperclip", "~> 3.0" gem "paperclip", "~> 3.0"
gem "aws-sdk"
gem 'gravtastic' gem 'gravtastic'
gem 'passenger' gem 'passenger'

View File

@ -29,9 +29,17 @@ GEM
i18n (~> 0.6) i18n (~> 0.6)
multi_json (~> 1.0) multi_json (~> 1.0)
arel (3.0.2) arel (3.0.2)
aws-sdk (1.33.0)
json (~> 1.4)
nokogiri (>= 1.4.4)
uuidtools (~> 2.1)
bcrypt-ruby (3.0.1) bcrypt-ruby (3.0.1)
builder (3.0.4) builder (3.0.4)
cancan (1.6.10) cancan (1.6.10)
climate_control (0.0.3)
activesupport (>= 3.0)
cocaine (0.5.3)
climate_control (>= 0.0.3, < 1.0)
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)
@ -45,6 +53,9 @@ GEM
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (~> 3.1) railties (~> 3.1)
warden (~> 1.2.1) warden (~> 1.2.1)
dotenv (0.10.0)
dotenv-rails (0.10.0)
dotenv (= 0.10.0)
erubis (2.7.0) erubis (2.7.0)
execjs (2.0.2) execjs (2.0.2)
gravtastic (3.2.6) gravtastic (3.2.6)
@ -61,8 +72,16 @@ GEM
mime-types (~> 1.16) mime-types (~> 1.16)
treetop (~> 1.4.8) treetop (~> 1.4.8)
mime-types (1.25) mime-types (1.25)
mini_portile (0.5.2)
multi_json (1.8.2) multi_json (1.8.2)
nokogiri (1.6.1)
mini_portile (~> 0.5.0)
orm_adapter (0.4.0) orm_adapter (0.4.0)
paperclip (3.5.4)
activemodel (>= 3.0.0)
activesupport (>= 3.0.0)
cocaine (~> 0.5.3)
mime-types
passenger (4.0.19) passenger (4.0.19)
daemon_controller (>= 1.1.0) daemon_controller (>= 1.1.0)
rack rack
@ -129,6 +148,7 @@ GEM
uglifier (2.2.1) uglifier (2.2.1)
execjs (>= 0.3.0) execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2) multi_json (~> 1.0, >= 1.0.2)
uuidtools (2.1.4)
warden (1.2.3) warden (1.2.3)
rack (>= 1.0) rack (>= 1.0)
@ -136,13 +156,16 @@ PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
aws-sdk
bcrypt-ruby (~> 3.0.0) bcrypt-ruby (~> 3.0.0)
cancan cancan
coffee-rails (~> 3.2.1) coffee-rails (~> 3.2.1)
devise devise
dotenv-rails
gravtastic gravtastic
jquery-rails jquery-rails
json json
paperclip (~> 3.0)
passenger passenger
pg pg
rails (= 3.2.8) rails (= 3.2.8)

View File

@ -5,17 +5,18 @@ via Ethernet ( see: https://github.com/zyphlar/Open_Access_Control_Ethernet )
https://github.com/zyphlar/Open-Source-Access-Control-Web-Interface https://github.com/zyphlar/Open-Source-Access-Control-Web-Interface
Copyright Will Bradley, 2012-2013 Copyright Will Bradley, 2012-2014
Distributed under a Creative Commons Attribution 3.0 license http://creativecommons.org/licenses/by/3.0/ Distributed under a Creative Commons Attribution 3.0 license http://creativecommons.org/licenses/by/3.0/
Contributions welcome! Simply send a pull request via Github.
To use: To use:
* Install Imagemagick (for Paperclip / image uploads)
* Install arp-scan (for LAN Mac address scanning)
* Load into a Rails 3 environment * Load into a Rails 3 environment
* Copy config/config.yml.example to config/config.yml and edit appropriately * Copy config/config.yml.example to config/config.yml and edit appropriately
* Copy config/database.yml.example to config/database.yml and edit appropriately * Copy config/database.yml.example to config/database.yml and edit appropriately
* Copy env.example to .env and edit appropriately for your Amazon S3 account OR adjust the resource.rb and contract.rb model settings to use different storage for picture attachments (via Paperclip)
* Copy config/initializers/secret_token.rb.example to config/config/initializers/secret_token.rb 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. * See/edit db/seeds.rb for the initial admin account info.
* Use the Rails console to create a new User with admin rights * Run bundle install, rake db:migrate, rake db:seed, etc.
* user = User.new
* user.password = "foobar"
* user.admin = true
* user.save

0
app/assets/images/logo.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

0
app/assets/images/nil.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 95 B

After

Width:  |  Height:  |  Size: 95 B

0
app/assets/javascripts/application.js Normal file → Executable file
View File

0
app/assets/javascripts/certifications.js.coffee Normal file → Executable file
View File

0
app/assets/javascripts/door_logs.js.coffee Normal file → Executable file
View File

0
app/assets/javascripts/home.js.coffee Normal file → Executable file
View File

0
app/assets/javascripts/ipn.js.coffee Normal file → Executable file
View File

0
app/assets/javascripts/mac_logs.js.coffee Normal file → Executable file
View File

0
app/assets/javascripts/pamela.js.coffee Normal file → Executable file
View File

0
app/assets/javascripts/payments.js.coffee Normal file → Executable file
View File

0
app/assets/javascripts/user_certifications.js.coffee Normal file → Executable file
View File

0
app/assets/javascripts/users.js.coffee Normal file → Executable file
View File

0
app/assets/stylesheets/application.css Normal file → Executable file
View File

0
app/assets/stylesheets/certifications.css.scss Normal file → Executable file
View File

0
app/assets/stylesheets/door_logs.css.scss Normal file → Executable file
View File

0
app/assets/stylesheets/home.css.scss Normal file → Executable file
View File

0
app/assets/stylesheets/ipn.css.scss Normal file → Executable file
View File

0
app/assets/stylesheets/mac_logs.css.scss Normal file → Executable file
View File

0
app/assets/stylesheets/macs.css.scss Normal file → Executable file
View File

0
app/assets/stylesheets/payments.css.scss Normal file → Executable file
View File

11
app/assets/stylesheets/scaffolds.css.scss Normal file → Executable file
View File

@ -19,17 +19,6 @@ pre {
font-size: 11px; font-size: 11px;
} }
a {
color: #000;
&:visited {
color: #666;
}
&:hover {
color: #fff;
background-color: #000;
}
}
div { div {
&.field, &.actions { &.field, &.actions {
margin-bottom: 10px; margin-bottom: 10px;

0
app/assets/stylesheets/user_certifications.css.scss Normal file → Executable file
View File

0
app/assets/stylesheets/users.css.scss Normal file → Executable file
View File

25
app/controllers/application_controller.rb Normal file → Executable file
View File

@ -1,6 +1,17 @@
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
protect_from_forgery protect_from_forgery
force_ssl if: :ssl_forced?
def ssl_forced?
# Non-production environments and read-only stuff like the space API and MACs should not require SSL. (APIs hate following 301s).
if Rails.env.development? || Rails.env.test? || ["space_api","macs"].include?(params[:controller])
return false
else
return true
end
end
rescue_from CanCan::AccessDenied do |exception| rescue_from CanCan::AccessDenied do |exception|
if !current_user.nil? && current_user.orientation.blank? then if !current_user.nil? && current_user.orientation.blank? then
flash[:alert] = "Sorry, you probably need to complete New Member Orientation before having access to this page. <br/>Please check your email and schedule a New Member Orientation with a volunteer." flash[:alert] = "Sorry, you probably need to complete New Member Orientation before having access to this page. <br/>Please check your email and schedule a New Member Orientation with a volunteer."
@ -13,6 +24,20 @@ class ApplicationController < ActionController::Base
@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!"}
# Check authorization of a user / sign them in manually
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 end
# Add a "fit" function to sanitize inputs for mac history # Add a "fit" function to sanitize inputs for mac history

39
app/controllers/cards_controller.rb Normal file → Executable file
View File

@ -1,6 +1,6 @@
class CardsController < ApplicationController class CardsController < ApplicationController
load_and_authorize_resource load_and_authorize_resource except: :authorize
before_filter :authenticate_user! before_filter :authenticate_user!, except: :authorize
# GET /cards # GET /cards
# GET /cards.json # GET /cards.json
@ -111,6 +111,41 @@ class CardsController < ApplicationController
end end
end end
def authorize
# Stop unless signed in already, OR if the supplied user/pass params are good.
unless current_user || check_auth(params['user'],params['pass'])
@auth = "bad_user_or_pass"
else
# Stop unless the user can access the door system
unless can? :authorize, Card
@auth = "bad_user_permissions"
Rails.logger.warn "----------\r\nWARNING: CARD AUTH ATTEMPT DENIED. USER #{current_user.inspect}\r\n----------"
else
begin
@card = Card.find(:first, :conditions => ["lower(card_number) = ?", params[:id].downcase])
@auth = @card.inspect
if @card && @card.user
@auth = @card.user.has_certification?(params[:device])
else
@auth = false
end
rescue
@auth = false
end
end
end
if @card && @card.user
username = @card.user.name
else
username = nil
end
render json: [@auth, username]
end
# DELETE /cards/1 # DELETE /cards/1
# DELETE /cards/1.json # DELETE /cards/1.json
def destroy def destroy

0
app/controllers/certifications_controller.rb Normal file → Executable file
View File

View File

@ -0,0 +1,87 @@
class ContractsController < ApplicationController
load_and_authorize_resource :contract
before_filter :authenticate_user!, :load_users
layout 'resources'
def index
if params[:user_id].present?
@contracts = Contract.where(user_id: params[:user_id])
end
respond_to do |format|
format.html
format.json { render :json => @contracts }
end
end
def show
end
def new
end
def edit
end
def create
# if @contract.first_name.blank? && @contract.last_name.blank? && @contract.cosigner.blank? # assume autodetect of filename
# begin
# name_split = params[:contract][:document].original_filename.sub(".jpg","").split
# if name_split.count == 4 # we have one name
# @contract.first_name = name_split[0]
# @contract.last_name = name_split[1]
# # 2 is the hyphen
# @contract.signed_at = Date.parse(name_split[3])
# elsif name_split.count == 7 && name_split[2] == "by" # we have two names
# @contract.first_name = name_split[0]
# @contract.last_name = name_split[1]
# # 2 is "by"
# @contract.cosigner = "#{name_split[3]} #{name_split[4]}"
# # 5 is the hyphen
# @contract.signed_at = Date.parse(name_split[6])
# else
# Rails.logger.info "Couldn't determine name from filename array: #{name_split.inspect}"
# end
# rescue Exception => e
# end
# end
@contract.created_by = current_user
respond_to do |format|
if @contract.save
format.html { redirect_to @contract, :notice => 'Contract was successfully created.' }
format.json { render :json => @contract, :status => :created, :location => @contract }
else
format.html { render :action => "new" }
format.json { render :json => @contract.errors, :status => :unprocessable_entity }
end
end
end
def find_for_user
end
def update
respond_to do |format|
if @contract.update_attributes(params[:contract])
format.html { redirect_to @contract, :notice => 'Contract was successfully updated.' }
format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => @contract.errors, :status => :unprocessable_entity }
end
end
end
def destroy
@contract.destroy
respond_to do |format|
format.html { redirect_to contracts_url }
format.json { head :no_content }
end
end
def load_users
@users = User.accessible_by(current_ability).sort_by(&:name)
end
end

30
app/controllers/door_logs_controller.rb Normal file → Executable file
View File

@ -5,7 +5,35 @@ class DoorLogsController < ApplicationController
# GET /door_logs # GET /door_logs
# GET /door_logs.json # GET /door_logs.json
def index def index
@door_logs = DoorLog.find(:all, :order => "created_at DESC", :limit => 500) @door_logs = DoorLog.find(:all, :order => "created_at DESC", :limit => 100)
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
@door_logs_by_hour = DoorLog.where("created_at > ? AND created_at < ? AND (key = ? OR key = ?)", @start_date, @end_date,"door_1_locked","door_2_locked").order("created_at ASC").group_by(&:key)
@door_log_graph = [
@door_logs_by_hour["door_1_locked"].map{|d| [d.created_at.to_time.to_i*1000, 1^d.data.to_i]}, # use XOR to invert 1 into 0 and vice versa
@door_logs_by_hour["door_2_locked"].map{|d| [d.created_at.to_time.to_i*1000, 1^d.data.to_i]}
]
# @door_logs_by_hour.each do |door_log|
# # Add one computer for activate, subtract one for deactivate
# if door_log.data == 1
#
# elsif door_log.data == 0
# mac_running_balance -= 1
# end
# @door_log_graph << [time.to_time.to_i*1000,mac_running_balance]
# end
respond_to do |format| respond_to do |format|
format.html # index.html.erb format.html # index.html.erb

78
app/controllers/home_controller.rb Normal file → Executable file
View File

@ -1,38 +1,54 @@
class HomeController < ApplicationController class HomeController < ApplicationController
layout 'resources'
def index def index
@num_certs = UserCertification.count # Alerts
@recent_certs = UserCertification.where("created_at > ?", DateTime.now - 7.days).count if user_signed_in? && current_user.orientation.blank? then
@num_users = User.count flash[: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."
@recent_users = User.where("created_at > ?", DateTime.now - 7.days).count end
# Payments: member levels are multipled by 10 to indicate current payment; 25 x 10 = 250 if user_signed_in? && current_user.member_status.between?(2,100) then
@num_paid_users = User.all.select{|u| u.member_status >= 250 }.count flash[:alert] = "<!--
@num_plus_users = User.all.select{|u| u.member_status == 1000 }.count Member: <%= current_user.member.inspect
@num_basic_users = User.all.select{|u| u.member_status == 500 }.count Level: <%= current_user.member_level.inspect
@num_associate_users = User.all.select{|u| u.member_status == 250 }.count -->
@num_delinquent_users = User.all.select{|u| !u.payment_status }.count Looks like we haven't acknowledged a recent payment for you yet. This could be because we're slow, but if in doubt please see your profile for payment instructions, consider updating your membership level to something accurate, or contact us.<br/>Thanks for supporting us!"
if can? :read, User then
@recent_user_names = User.where("member_level > 10").accessible_by(current_ability).order('created_at desc').limit(5)
end
@num_door_opens = DoorLog.where("key = 'G'").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
@num_door_denieds = DoorLog.where("key = 'D'").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
@recent_macs = Mac.where("since > ?", DateTime.now - 1.day).count
respond_to do |format|
format.html # index.html.erb
end end
end
def more_info # Fun Stats
respond_to do |format| @featured_resource = Resource.where("picture_file_name IS NOT NULL").sample
format.html # more_info.html.erb
@num_certs = UserCertification.count
@recent_certs = UserCertification.where("created_at > ?", DateTime.now - 7.days).count
@num_users = User.count
@recent_users = User.where("created_at > ?", DateTime.now - 7.days).count
# 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
@recent_user_names = User.where("member_level > 10").accessible_by(current_ability).order('created_at desc').limit(5)
end
@num_door_opens = DoorLog.where("key = 'G'").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
@num_door_denieds = DoorLog.where("key = 'D'").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
@recent_macs = Mac.where("since > ?", DateTime.now - 1.day).count
respond_to do |format|
format.html # index.html.erb
end
end
def more_info
respond_to do |format|
format.html # more_info.html.erb
end
end end
end
end end

0
app/controllers/ipns_controller.rb Normal file → Executable file
View File

4
app/controllers/mac_logs_controller.rb Normal file → Executable file
View File

@ -4,6 +4,10 @@ before_filter :authenticate_user!
def index def index
@mac_logs = MacLog.desc.limit(1000) @mac_logs = MacLog.desc.limit(1000)
@macs = {}
@mac_logs.each do |log|
@macs.merge!({log.mac => Mac.find_by_mac(log.mac)})
end
end end
end end

12
app/controllers/macs_controller.rb Normal file → Executable file
View File

@ -224,7 +224,7 @@ end
def arp_lookup def arp_lookup
@ip = request.env['REMOTE_ADDR'] @ip = request.env['REMOTE_ADDR']
@arp = %x(/usr/sbin/arp -a | grep #{@ip}) @arp = /([0-9A-F]{2}[:-]){5}([0-9A-F]{2})/i.match(%x(arp -a | grep #{@ip}))
end end
def scan def scan
@ -292,18 +292,22 @@ Rails.logger.info "starting scan..."
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 result = stdin.read()
stdin.each { |line| result.lines.each { |line|
Rails.logger.info "Reading stdin: "+line.inspect
next if line !~ /^([\d\.]+)\s+([[:xdigit:]:]+)\s/; next if line !~ /^([\d\.]+)\s+([[:xdigit:]:]+)\s/;
macs[($2).downcase] = ($1).downcase; macs[($2).downcase] = ($1).downcase;
} }
Rails.logger.info "STDIN:"+result.lines.count.inspect
@macs = macs.dup # make a copy for output in the view
Rails.logger.info "MACS:"+@macs.inspect
} }
# 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 our scan shows this 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

25
app/controllers/payments_controller.rb Normal file → Executable file
View File

@ -45,22 +45,19 @@ class PaymentsController < ApplicationController
payment_months = @payments.sort_by(&:date).group_by{ |p| p.date.beginning_of_month } payment_months = @payments.sort_by(&:date).group_by{ |p| p.date.beginning_of_month }
@payments_by_month = [] @payments_by_month = []
payment_months.each do |month| payment_months.each do |month|
# Only grab the last year from today # Calculate sum of amounts for each month and store at end of month array
if month.first > (Date.today - 1.year) && month.first < Date.today @payments_by_month << [month.first.to_time.to_i*1000, month.last.sum{|p|
# Calculate sum of amounts for each month and store at end of month array amount = amount_or_level(p)
@payments_by_month << [month.first.to_time.to_i*1000, month.last.sum{|p| if chart_type.include?(amount)
amount = amount_or_level(p) if chart_name == "members"
if chart_type.include?(amount) 1 # Output 1 to count members
if chart_name == "members"
1 # Output 1 to count members
else
amount # Output dollars to count amount
end
else else
0 amount # Output dollars to count amount
end end
}] else
end 0
end
}]
end end
return @payments_by_month return @payments_by_month

0
app/controllers/paypal_csvs_controller.rb Normal file → Executable file
View File

0
app/controllers/registrations_controller.rb Normal file → Executable file
View File

View File

@ -0,0 +1,46 @@
class ResourceCategoriesController < ApplicationController
load_and_authorize_resource
layout 'resources'
def create
authorize! :create, @resource_category
respond_to do |format|
if @resource_category.save
format.html { redirect_to resource_categories_path, :notice => "Category was successfully created." }
format.json { head :no_content }
else
format.html { render :action => "new" }
format.json { render :json => @resource_category.errors, :status => :unprocessable_entity }
end
end
end
def update
authorize! :update, @resource_category
respond_to do |format|
if @resource_category.update_attributes(params[:resource_category])
format.html { redirect_to resource_categories_path, :notice => "Category was successfully updated." }
format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => @resource_category.errors, :status => :unprocessable_entity }
end
end
end
def destroy
respond_to do |format|
if @resource_category.destroy
format.html { redirect_to resource_categories_path, :notice => "Category was deleted." }
format.json { head :ok }
else
format.html { redirect_to resource_categories_path, :notice => "Category could not be deleted. #{@resource_category.errors.full_messages.first}." }
format.json { render :json => @resource_category.errors, :status => :unprocessable_entity }
end
end
end
end

View File

@ -0,0 +1,63 @@
class ResourcesController < ApplicationController
load_and_authorize_resource
before_filter :load_users
layout 'resources'
def index
@featured_resource = @resources.where("picture_file_name IS NOT NULL").sample
end
def new
# don't get too excited... for some reason this gets set to the current_user
@resource.user_id = nil
end
def create
@resource.modified_by = current_user.id # log who modified this last
authorize! :create, @resource
respond_to do |format|
if @resource.save
format.html { redirect_to resource_path(@resource), :notice => "Resource was successfully created." }
format.json { head :no_content }
else
format.html { render :action => "new" }
format.json { render :json => @resource.errors, :status => :unprocessable_entity }
end
end
end
def update
@resource.modified_by = current_user.id # log who modified this last
@resource.assign_attributes(params[:resource])
authorize! :update, @resource
respond_to do |format|
if @resource.update_attributes(params[:resource])
format.html { redirect_to resource_path(@resource), :notice => "Resource was successfully updated." }
format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => @resource.errors, :status => :unprocessable_entity }
end
end
end
def destroy
@resource.destroy
respond_to do |format|
format.html { redirect_to resources_path, :notice => "Resource was deleted." }
format.json { head :ok }
end
end
def load_users
if can? :assign_user, Resource then
@users = User.accessible_by(current_ability).sort_by(&:name)
else
@users = [current_user]
end
end
end

0
app/controllers/settings_controller.rb Normal file → Executable file
View File

33
app/controllers/space_api_controller.rb Normal file → Executable file
View File

@ -38,8 +38,19 @@ class SpaceApiController < ApplicationController
else else
unless can? :access_doors_remotely, :door_access unless can? :access_doors_remotely, :door_access
@output = "Sorry, your account isn't able to control doors remotely." @output = "Sorry, your account isn't able to control doors remotely."
else
@output = "Ready to control doors. Send POST params to this URL as per the HTML form."
end end
end end
# Render the form again (or result)
respond_to do |format|
format.html
format.json {
response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
render :json => @output
}
end
end end
def access_post def access_post
@ -78,19 +89,17 @@ class SpaceApiController < ApplicationController
end end
end end
# Render the form again # Render the form again (or result)
render :access respond_to do |format|
end format.html {
render :access
def check_auth(email,password) }
resource = User.find_by_email(email) format.json {
if resource && resource.valid_password?(password) response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
resource.remember_me = true render :json => @output
sign_in :user, resource }
return true
else
return false
end end
end end
end end

0
app/controllers/statistics_controller.rb Normal file → Executable file
View File

0
app/controllers/user_certifications_controller.rb Normal file → Executable file
View File

69
app/controllers/users_controller.rb Normal file → Executable file
View File

@ -1,6 +1,7 @@
class UsersController < ApplicationController class UsersController < ApplicationController
load_and_authorize_resource load_and_authorize_resource
before_filter :authenticate_user! before_filter :authenticate_user!
layout 'resources'
def sort_by_cert(certs,id) def sort_by_cert(certs,id)
result = 0 result = 0
@ -15,31 +16,44 @@ class UsersController < ApplicationController
# GET /users # GET /users
# GET /users.json # GET /users.json
def index def index
case params[:sort] unless params[:full] # by default, show summary
when "name"
@users = @users.sort_by(&:name) @users = @users.paying + @users.volunteer
when "cert" #.joins(:payments).where("payments.date > ? OR ", (DateTime.now - 60.days)).uniq
@users = @users.sort_by{ |u| [-sort_by_cert(u.certifications,params[:cert].to_i),u.name] }
when "orientation" respond_to do |format|
@users = @users.sort_by{ |u| [-u.orientation.to_i,u.name] } format.html { render 'summary', layout: 'resources' }
when "waiver" format.json { render :json => @users }
@users = @users.sort_by{ |u| [-u.waiver.to_i,u.name] } end
when "member"
@users = @users.sort_by{ |u| [-u.member_status.to_i,u.name] } else # show full
when "card"
@users = @users.sort_by{ |u| [-u.cards.count,u.name] } case params[:sort]
when "instructor" when "name"
@users = @users.sort{ |a,b| [b.instructor.to_s,a.name] <=> [a.instructor.to_s,b.name] } @users = @users.sort_by(&:name)
when "admin" when "cert"
@users = @users.sort{ |a,b| [b.admin.to_s,a.name] <=> [a.admin.to_s,b.name] } @users = @users.sort_by{ |u| [-sort_by_cert(u.certifications,params[:cert].to_i),u.name] }
else when "orientation"
@users = @users.sort_by(&:name) @users = @users.sort_by{ |u| [-u.orientation.to_i,u.name] }
end when "waiver"
@users = @users.sort_by{ |u| [-u.contract_date.to_i,u.name] }
when "member"
@users = @users.sort_by{ |u| [-u.member_status.to_i,u.name] }
when "card"
@users = @users.sort_by{ |u| [-u.cards.count,u.name] }
when "instructor"
@users = @users.sort{ |a,b| [b.instructor.to_s,a.name] <=> [a.instructor.to_s,b.name] }
when "admin"
@users = @users.sort{ |a,b| [b.admin.to_s,a.name] <=> [a.admin.to_s,b.name] }
else
@users = @users.sort_by(&:name)
end
respond_to do |format| respond_to do |format|
format.html # index.html.erb format.html # index.html.erb
format.json { render :json => @users } format.json { render :json => @users }
end
end end
end end
@ -73,12 +87,10 @@ class UsersController < ApplicationController
def compose_email def compose_email
@user = User.find(params[:user_id]) @user = User.find(params[:user_id])
authorize! :read, @user
end end
def send_email def send_email
@user = User.find(params[:user_id]) @user = User.find(params[:user_id])
authorize! :read, @user
@subject = params[:subject] @subject = params[:subject]
@body = params[:body] @body = params[:body]
if @user.send_email(current_user,@subject,@body) if @user.send_email(current_user,@subject,@body)
@ -113,6 +125,9 @@ class UsersController < ApplicationController
# POST /users # POST /users
# POST /users.json # POST /users.json
def create def create
# update oriented_by only if orientation has been set
@user.oriented_by_id = current_user.id unless @user.orientation.blank?
respond_to do |format| respond_to do |format|
if @user.save if @user.save
format.html { redirect_to @user, :notice => 'User was successfully created.' } format.html { redirect_to @user, :notice => 'User was successfully created.' }
@ -127,6 +142,10 @@ class UsersController < ApplicationController
# PUT /users/1 # PUT /users/1
# PUT /users/1.json # PUT /users/1.json
def update def update
# update oriented_by only if it's blank but the (new) orientation isn't blank
# gotta test the params because they don't get applied til below.
@user.oriented_by_id = current_user.id if @user.oriented_by.blank? && (!params[:user]["orientation(1i)"].blank?)
respond_to do |format| respond_to do |format|
if @user.update_attributes(params[:user]) if @user.update_attributes(params[:user])
format.html { redirect_to @user, :notice => 'User was successfully updated.' } format.html { redirect_to @user, :notice => 'User was successfully updated.' }

27
app/helpers/application_helper.rb Normal file → Executable file
View File

@ -1,4 +1,31 @@
module ApplicationHelper module ApplicationHelper
@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!"}
def sort_link(title, column, options = {})
condition = options[:unless] if options.has_key?(:unless)
sort_dir = params[:dir] == 'up' ? 'down' : 'up'
link_to_unless condition, title, request.parameters.merge( {:sort => column, :dir => sort_dir} )
end
def li_link_to(name = nil, options = nil, html_options = nil, &block)
html_options, options, name = options, name, block if block_given?
options ||= {}
html_options = convert_options_to_data_attributes(options, html_options)
url = url_for(options)
html_options['href'] ||= url
if current_page?(url)
content_tag(:li, class: "active") do
content_tag(:a, name || url, html_options, &block)
end
else
content_tag(:li) do
content_tag(:a, name || url, html_options, &block)
end
end
end
end end

0
app/helpers/certifications_helper.rb Normal file → Executable file
View File

0
app/helpers/door_logs_helper.rb Normal file → Executable file
View File

0
app/helpers/home_helper.rb Normal file → Executable file
View File

0
app/helpers/ipn_helper.rb Normal file → Executable file
View File

0
app/helpers/mac_logs_helper.rb Normal file → Executable file
View File

0
app/helpers/pamela_helper.rb Normal file → Executable file
View File

0
app/helpers/payments_helper.rb Normal file → Executable file
View File

0
app/helpers/user_certifications_helper.rb Normal file → Executable file
View File

0
app/helpers/users_helper.rb Normal file → Executable file
View File

0
app/mailers/.gitkeep Normal file → Executable file
View File

0
app/mailers/user_mailer.rb Normal file → Executable file
View File

0
app/models/.gitkeep Normal file → Executable file
View File

14
app/models/ability.rb Normal file → Executable file
View File

@ -4,6 +4,8 @@ class Ability
def initialize(user) def initialize(user)
can :read, Mac # Anonymous can read mac can :read, Mac # Anonymous can read mac
can :scan, Mac # Need anonymous so CRON can scan can :scan, Mac # Need anonymous so CRON can scan
can :read, Resource
can :read, ResourceCategory
if !user.nil? if !user.nil?
@ -13,12 +15,16 @@ 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 [:create,:update,:destroy], Resource, :user_id => user.id
can :read, Payment, :user_id => user.id 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
can :read, User, :id => user.id #TODO: why can users update themselves? Maybe because Devise doesn't check users/edit?
can :compose_email, User
can :send_email, User
if user.card_access_enabled if user.card_access_enabled
can :access_doors_remotely, :door_access can :access_doors_remotely, :door_access
can :authorize, Card # used for interlock card/certification auth
end end
# Instructors can manage certs and see users # Instructors can manage certs and see users
@ -30,8 +36,10 @@ class Ability
end end
# Users can see others' stuff if they've been oriented # Users can see others' stuff if they've been oriented
unless user.orientation.blank? unless user.orientation.blank?
can :read, User, :hidden => [nil,false] can [:read,:new_member_report,:activity], User, :hidden => [nil,false]
can :read, UserCertification can :read, UserCertification
can [:create,:update], Resource, :user_id => [nil,user.id]
can [:create,:update,:destroy], ResourceCategory
end end
# Accountants can manage payments # Accountants can manage payments
@ -46,7 +54,7 @@ class Ability
can :manage, :all can :manage, :all
end end
# Prevent all destruction for now # Prevent most destruction for now
#cannot :destroy, User #cannot :destroy, User
#cannot :destroy, Card #cannot :destroy, Card
cannot :destroy, Certification cannot :destroy, Certification

50
app/models/card.rb Normal file → Executable file
View File

@ -11,21 +11,14 @@ class Card < ActiveRecord::Base
door_access_url = APP_CONFIG['door_access_url'] door_access_url = APP_CONFIG['door_access_url']
door_access_password = APP_CONFIG['door_access_password'] door_access_password = APP_CONFIG['door_access_password']
# connect to door access system cardid = self.id.to_s.rjust(3, '0')
source = open("#{door_access_url}?e=#{door_access_password}").read
results = source.scan(/ok/)
if(results.size > 0) then
#only continue if we've got an OK login
cardid = self.id.to_s.rjust(3, '0') #TODO: provide ability for
cardperm = self.card_permissions.to_s.rjust(3, '0') cardperm = self.card_permissions.to_s.rjust(3, '0')
cardnum = self.card_number.rjust(8, '0') cardnum = self.card_number.rjust(8, '0')
source = open("#{door_access_url}?m#{cardid}&p#{cardperm}&t#{cardnum}").read # login and send the command all in one go (auto-logout is a feature of the arduino when used this way)
source = open("#{door_access_url}?m#{cardid}&p#{cardperm}&t#{cardnum}&e=#{door_access_password}").read
results = source.scan(/cur/) results = source.scan(/cur/)
#logout
open("#{door_access_url}?e=0000")
if(results.size > 0) then if(results.size > 0) then
#only return true if we got some kind of decent response #only return true if we got some kind of decent response
return true return true
@ -33,10 +26,6 @@ class Card < ActiveRecord::Base
# We didn't get a decent response. # We didn't get a decent response.
return false return false
end end
else
# We didn't get an OK login.
return false
end
end end
def self.upload_all_to_door def self.upload_all_to_door
@ -47,30 +36,21 @@ class Card < ActiveRecord::Base
door_access_url = APP_CONFIG['door_access_url'] door_access_url = APP_CONFIG['door_access_url']
door_access_password = APP_CONFIG['door_access_password'] door_access_password = APP_CONFIG['door_access_password']
source = open("#{door_access_url}?e=#{door_access_password}").read @cards.each do |u|
results = source.scan(/ok/) cardid = u.id.to_s.rjust(3, '0')
if(results.size > 0) then cardperm = u.card_permissions.to_s.rjust(3, '0')
@cards.each do |u| cardnum = u.card_number.rjust(8, '0')
#only continue if we've got an OK login
cardid = u.id.to_s.rjust(3, '0')
cardperm = u.card_permissions.to_s.rjust(3, '0')
cardnum = u.card_number.rjust(8, '0')
source = open("#{door_access_url}?m#{cardid}&p#{cardperm}&t#{cardnum}").read # login and send the command all in one go (auto-logout is a feature of the arduino when used this way)
results = source.scan(/cur/) source = open("#{door_access_url}?m#{cardid}&p#{cardperm}&t#{cardnum}&e=#{door_access_password}").read
results = source.scan(/cur/)
if(results.size > 0) then if(results.size > 0) then
#only return true if we got some kind of decent response #only return true if we got some kind of decent response
@end_results.push([cardid,"OK"]) @end_results.push([cardid,"OK"])
else else
@end_results.push([cardid,"FAIL"]) @end_results.push([cardid,"FAIL"])
end
end end
#logout
open("#{door_access_url}?e=0000")
else
@end_results.push([cardid,"FAIL"])
end end
return @end_results return @end_results

4
app/models/certification.rb Normal file → Executable file
View File

@ -1,5 +1,7 @@
class Certification < ActiveRecord::Base class Certification < ActiveRecord::Base
attr_accessible :description, :name attr_accessible :description, :name, :slug
has_many :user_certifications has_many :user_certifications
has_many :users, :through => :user_certifications has_many :users, :through => :user_certifications
validates_presence_of :name, :slug
end end

24
app/models/contract.rb Normal file
View File

@ -0,0 +1,24 @@
class Contract < ActiveRecord::Base
belongs_to :user
belongs_to :created_by, :foreign_key => "created_by_id", :class_name => "User"
attr_accessible :user_id, :first_name, :last_name, :cosigner,
:signed_at, :document, :document_file_name, :document_content_type,
:document_file_size, :document_updated_at
# :created_by not accessible for security purposes
validates_presence_of :first_name, :signed_at #, :last_name
has_attached_file :document,
{ :styles =>
{
:medium => "300x300>",
:large => "900x900>"
},
:storage => :s3,
:s3_protocol => :https,
:s3_credentials => { :access_key_id => ENV['S3_KEY'],
:secret_access_key => ENV['S3_SECRET'] },
:path => ":attachment/:id/:style.:extension",
:bucket => ENV['S3_BUCKET']
}
end

0
app/models/door_log.rb Normal file → Executable file
View File

0
app/models/ipn.rb Normal file → Executable file
View File

0
app/models/mac.rb Normal file → Executable file
View File

0
app/models/mac_log.rb Normal file → Executable file
View File

0
app/models/payment.rb Normal file → Executable file
View File

0
app/models/paypal_csv.rb Normal file → Executable file
View File

31
app/models/resource.rb Executable file
View File

@ -0,0 +1,31 @@
class Resource < ActiveRecord::Base
attr_accessible :supercategory, :user_id, :resource_category_id, :name, :serial, :specs, :status, :donatable,
:picture, :picture_file_name, :picture_content_type, :picture_file_size, :picture_updated_at,
:picture2, :picture2_file_name, :picture2_content_type, :picture2_file_size, :picture2_updated_at,
:picture3, :picture3_file_name, :picture3_content_type, :picture3_file_size, :picture3_updated_at,
:picture4, :picture4_file_name, :picture4_content_type, :picture4_file_size, :picture4_updated_at,
:notes, :estimated_value, :disposed_at, :modified_by
belongs_to :owner, :class_name => "ToolshareUser" #TODO: remove owner
belongs_to :user
belongs_to :resource_category
PICTURE_OPTIONS = { :styles => { :medium => "300x300>",
:thumb => "100x100>",
:tiny => "50x50>"},
:storage => :s3,
:s3_protocol => :https,
:s3_credentials => { :access_key_id => ENV['S3_KEY'],
:secret_access_key => ENV['S3_SECRET'] },
:path => ":attachment/:id/:style.:extension",
:bucket => ENV['S3_BUCKET'] }
has_attached_file :picture, PICTURE_OPTIONS
has_attached_file :picture2, PICTURE_OPTIONS
has_attached_file :picture3, PICTURE_OPTIONS
has_attached_file :picture4, PICTURE_OPTIONS
def resource_category_name
resource_category.name if resource_category
end
end

13
app/models/resource_category.rb Executable file
View File

@ -0,0 +1,13 @@
class ResourceCategory < ActiveRecord::Base
has_many :resources
attr_accessible :name
before_destroy :has_resources?
private
def has_resources?
errors.add(:base, "Cannot delete category with associated resources") unless resources.count == 0
errors.blank? #return false, to not destroy the element, otherwise, it will delete.
end
end

0
app/models/setting.rb Normal file → Executable file
View File

4
app/models/toolshare_user.rb Executable file
View File

@ -0,0 +1,4 @@
class ToolshareUser < ActiveRecord::Base
has_many :resources, :foreign_key => "owner_id"
attr_accessible :name, :email
end

26
app/models/user.rb Normal file → Executable file
View File

@ -1,6 +1,6 @@
class User < ActiveRecord::Base class User < ActiveRecord::Base
include Gravtastic include Gravtastic
gravtastic :size => 120, :default => "" gravtastic :size => 150, :default => ""
# Include default devise modules. Others available are: # Include default devise modules. Others available are:
# :token_authenticatable, :confirmable, # :token_authenticatable, :confirmable,
@ -9,16 +9,24 @@ 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, :exit_reason, :twitter_url, :facebook_url, :github_url, :website_url, :email_visible, :phone_visible #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, :postal_code #TODO: make admin/instructor/member/etc not accessible
belongs_to :oriented_by, :foreign_key => "oriented_by_id", :class_name => "User"
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 :contracts
has_many :payments has_many :payments
has_many :macs has_many :macs
has_many :resources
scope :volunteer, -> { where('member_level >= 10 AND member_level < 25') }
scope :paying, -> { joins(:payments).where("payments.date > ?", (DateTime.now - 90.days)).uniq }
validates_format_of [:twitter_url, :facebook_url, :github_url, :website_url], :with => URI::regexp(%w(http https)), :allow_blank => true validates_format_of [:twitter_url, :facebook_url, :github_url, :website_url], :with => URI::regexp(%w(http https)), :allow_blank => true
# disable # validates_presence_of :postal_code
after_create :send_new_user_email after_create :send_new_user_email
def absorb_user(user_to_absorb) def absorb_user(user_to_absorb)
@ -130,7 +138,19 @@ class User < ActiveRecord::Base
Rails.logger.info UserMailer.email(self,from_user,subject,body).deliver Rails.logger.info UserMailer.email(self,from_user,subject,body).deliver
end end
private def has_certification?(cert_slug)
if self.certifications.find_by_slug(cert_slug)
true
else
false
end
end
def contract_date
self.contracts.first.signed_at unless self.contracts.blank?
end
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

0
app/models/user_certification.rb Normal file → Executable file
View File

0
app/views/cards/_form.html.erb Normal file → Executable file
View File

0
app/views/cards/edit.html.erb Normal file → Executable file
View File

6
app/views/cards/index.html.erb Normal file → Executable file
View File

@ -6,11 +6,11 @@
<%= link_to 'Space API', space_api_path, :class => "btn" %> <%= 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 %> <%= 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 Month:</b> <%= @most_active_card.name unless @most_active_card.blank? %> (<%= @most_active_card.accesses_this_week unless @most_active_card.blank? %> days) <b>Most Active Card Last Month:</b> <%= @most_active_card.user.name unless @most_active_card.user.blank? %> (<%= @most_active_card.accesses_this_week unless @most_active_card.blank? %> days)
</p> </p>
<p> <p>
<% unless @runner_up_card.blank? %> <% unless @runner_up_card.blank? || @runner_up_card.user.blank? %>
<b>Runner Up:</b> <%= @runner_up_card.name %> (<%= @runner_up_card.accesses_this_week %> days) <b>Runner Up:</b> <%= @runner_up_card.user.name %> (<%= @runner_up_card.accesses_this_week %> days)
<% end %> <% end %>
</p> </p>

0
app/views/cards/new.html.erb Normal file → Executable file
View File

0
app/views/cards/show.html.erb Normal file → Executable file
View File

0
app/views/cards/upload.html.erb Normal file → Executable file
View File

0
app/views/cards/upload_all.html.erb Normal file → Executable file
View File

4
app/views/certifications/_form.html.erb Normal file → Executable file
View File

@ -15,6 +15,10 @@
<%= f.label :name %><br /> <%= f.label :name %><br />
<%= f.text_field :name %> <%= f.text_field :name %>
</div> </div>
<div class="field">
<%= f.label :slug, "Slug (lowercase, single-word identifier)" %><br />
<%= f.text_field :slug %>
</div>
<div class="field"> <div class="field">
<%= f.label :description %><br /> <%= f.label :description %><br />
<%= f.text_area :description %> <%= f.text_area :description %>

0
app/views/certifications/edit.html.erb Normal file → Executable file
View File

1
app/views/certifications/index.html.erb Normal file → Executable file
View File

@ -5,6 +5,7 @@
<ul> <ul>
<% @certifications.each do |certification| %> <% @certifications.each do |certification| %>
<li><%= link_to certification.name, certification %> <li><%= link_to certification.name, certification %>
(<%= certification.slug %>)
<% if can? :update, certification %> | <%= link_to 'Edit', edit_certification_path(certification) %><% end %> <% if can? :update, certification %> | <%= link_to 'Edit', edit_certification_path(certification) %><% end %>
<% if can? :destroy, certification %> | <%= link_to 'Destroy', certification, :confirm => 'Are you sure?', :method => :delete %><% end %> <% if can? :destroy, certification %> | <%= link_to 'Destroy', certification, :confirm => 'Are you sure?', :method => :delete %><% end %>
</li> </li>

0
app/views/certifications/new.html.erb Normal file → Executable file
View File

5
app/views/certifications/show.html.erb Normal file → Executable file
View File

@ -3,6 +3,11 @@
<%= @certification.name %> <%= @certification.name %>
</p> </p>
<p>
<b>Slug (lowercase, single-word identifier):</b>
<%= @certification.slug %>
</p>
<p> <p>
<b>Description:</b> <b>Description:</b>
<%= simple_format @certification.description %> <%= simple_format @certification.description %>

View File

@ -0,0 +1,51 @@
<%= form_for(@contract, html: {class: "col-sm-6"}) do |f| %>
<% if @contract.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@contract.errors.count, "error") %> prohibited this contract from being saved:</h2>
<ul>
<% @contract.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group">
<%= f.label :first_name %><br />
<%= f.text_field :first_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :last_name %><br />
<%= f.text_field :last_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :cosigner %><br />
<%= f.text_field :cosigner, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :user_id, "User" %><br />
<%= collection_select(:contract, :user_id, @users, :id, :name, :include_blank => true) %>
</div>
<div class="form-group">
<%= f.label :signed_at %><br />
<%= f.date_select :signed_at %>
</div>
<div class="form-group">
<%= f.label :document %><br />
<%= link_to "View Existing Document", @contract.document.url, class: "btn btn-default" unless @contract.document.blank? %>
<p>
<%= link_to "Upload New Document", "#", class: "btn btn-default", onclick: "$('#document_upload').removeClass('hidden'); $(this).addClass('hidden')" unless @contract.document.blank? %>
<div id="document_upload" class="<%= "hidden" unless @contract.document.blank? %>">
<%= f.file_field :document, class: "form-control", style: "width: 100%; height: 100px; background-color: #f9f9f9;" %>
</div>
</p>
</div>
<div class="form-group">
<%= f.submit nil, class: "btn btn-primary" %>
</div>
<% end %>

View File

@ -0,0 +1,4 @@
<h1>Edit Contract
<%= link_to 'Back', contracts_path, class: "btn btn-default" %>
</h1>
<%= render 'form' %>

View File

@ -0,0 +1,42 @@
<div class="row">
<h1 class="col-md-8">Contracts
<%= link_to 'Add Contract', new_contract_path, :class => "btn btn-success" if can? :create, Contract %>
</h1>
</div>
<table class="table">
<tr>
<th>Scan?</th>
<th>Name</th>
<th>User</th>
<th>Date</th>
<th></th>
</tr>
<% @contracts.sort_by{|r| (r.last_name) || "" }.each do |contract| %>
<tr>
<td>
<% unless contract.document.blank? %>
<span class="glyphicon glyphicon-ok"></span>
<% end %>
</td>
<td>
<%= contract.last_name %>,
<%= contract.first_name %>
<%= "and #{contract.cosigner}" unless contract.cosigner.blank? %>
</td>
<td>
<%= link_to contract.user.name, contract.user if contract.user %>
</td>
<td>
<%= contract.signed_at.to_date.to_s(:long) %>
</td>
<td>
<%= link_to "View", contract, class: "btn btn-primary" %>
<%= link_to "Edit", edit_contract_path(contract), class: "btn btn-default" %>
</td>
</tr>
<% end %>
</table>
<br />

View File

@ -0,0 +1,80 @@
<h1>New Contract
<%= link_to 'Back', contracts_path, class: "btn btn-default" %>
</h1>
<div class="col-xs-12">
<%= render 'form' %>
</div>
<div class="col-xs-12">
<div class="alert alert-info">Note: if you name your scans in one of the below formats, this form will try to fill itself out automatically when you select the file. (The spaces, hyphen, and jpg or pdf extensions are mandatory for this to work.) <em>Double-check the names, users, and dates, though!!</em><br/>
<strong>first last - date.[jpg|pdf]</strong><br/>
<strong>first last by cosigner name - date.[jpg|pdf]</strong><br/>
</div>
</div>
<script type="text/javascript">
function capitalizeIfIsntCapitalized(str){
if(str[0].toLowerCase() == str[0] ){
return str[0].toUpperCase() + str.slice(1);
}
else{
return str;
}
}
$(function() {
$("#contract_document").change(function (){
doc = document.getElementById("contract_document")
if(doc.files && doc.files.length > 0) {
name_split = doc.files[0].name.replace(".jpg","").replace(".pdf","").split(" ");
$("#contract_user_alert").remove(); // clear any existing alerts
$("#contract_user_spinner").remove(); // clear spinner
if(name_split.length == 4){ // we have one name
$("#contract_first_name").val(capitalizeIfIsntCapitalized(name_split[0]));
$("#contract_last_name").val(capitalizeIfIsntCapitalized(name_split[1]));
// 2 is the hyphen
signed_at = new Date(name_split[3]);
$("#contract_signed_at_1i").val(signed_at.getUTCFullYear()).change();
$("#contract_signed_at_2i").val(signed_at.getUTCMonth()+1).change();
$("#contract_signed_at_3i").val(signed_at.getUTCDate()).change();
}
else if(name_split.length == 7 && name_split[2] == "by"){ // we have two names
$("#contract_first_name").val(capitalizeIfIsntCapitalized(name_split[0]));
$("#contract_last_name").val(capitalizeIfIsntCapitalized(name_split[1]));
// 2 is "by"
$("#contract_cosigner").val(capitalizeIfIsntCapitalized(name_split[3])+" "+capitalizeIfIsntCapitalized(name_split[4]));
// 5 is the hyphen
signed_at = new Date(name_split[6]);
$("#contract_signed_at_1i").val(signed_at.getUTCFullYear());
$("#contract_signed_at_2i").val(signed_at.getUTCMonth()+1);
$("#contract_signed_at_3i").val(signed_at.getUTCDate());
}
// Try and select the relevant user if exists
if( $("#contract_first_name").val().length > 0 && $("#contract_last_name").val().length > 0 ) {
user_id = $('#contract_user_id option').filter(function(){
return $(this).text() == $("#contract_first_name").val() + " " + $("#contract_last_name").val();
}).prop("selected", "true").val();
// Start a spinner before AJAX request
$("#contract_user_id").after("<i id='contract_user_spinner' class='icon-spin icon-refresh'></i>");
// If we found a user, check how many contracts that user already has
$.get("/users/"+user_id+"/contracts.json",function(data){
$("#contract_user_spinner").remove(); // clear spinner
if(data.length > 0){
other_contract_links = $.map(data,function(item){
output = "<a href='/contracts/"+item["id"]+"'>"+item["id"]+"</a> &nbsp;&nbsp; ";
return output;
});
$("#contract_user_id").after("<span id='contract_user_alert' class='label label-danger'>Warning: this user already has "+data.length+" other contract(s). Check the existing one(s) to avoid duplication: "+other_contract_links+"</span>");
}
});
}
}
});
});
</script>

View File

@ -0,0 +1,41 @@
<div class="row">
<h1 class="col-md-8">
Contract
<%= link_to 'Back', contracts_path, :class => "btn btn-default" %>
<%= link_to 'New', new_contract_path, :class => "btn btn-success" %>
<%= link_to 'Edit', edit_contract_path(@contract), :class => "btn btn-primary" %>
<%= link_to 'Delete', contract_path(@contract), {:confirm => 'Are you sure you want to delete this forever?', :method => :delete, :class => "btn btn-danger"} if can? :destroy, @contract %>
</h1>
</div>
<h2>
<%= @contract.first_name %>
<%= @contract.last_name %>
<%= "and #{@contract.cosigner}" unless @contract.cosigner.blank? %>
<%= link_to "(#{@contract.user.name})", @contract.user if @contract.user %>
<small>
signed
<%= @contract.signed_at.to_date.to_s(:long) %>
</small>
</h2>
<% unless @contract.created_by.blank? %>
<p>
<em>Created by <%= @contract.created_by.name %></em>
</p>
<% end %>
<% if @contract.document.blank? %>
<p>No document uploaded</p>
<% else %>
<p><%= link_to "Download Contract", @contract.document.url %>
<div class="col-xs-12">
<% contract_url = (@contract.document.exists?(:large) ? @contract.document.url(:large) : @contract.document.url) %>
<iframe src="<%= contract_url %>" width="100%" height="600"></iframe>
</div>
</p>
<% end %>
<br />

0
app/views/devise/confirmations/new.html.erb Normal file → Executable file
View File

View File

View File

0
app/views/devise/mailer/unlock_instructions.html.erb Normal file → Executable file
View File

0
app/views/devise/passwords/edit.html.erb Normal file → Executable file
View File

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