Compare commits

..

No commits in common. "master" and "members" have entirely different histories.

464 changed files with 432 additions and 41236 deletions

8
.gitignore vendored
View File

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

View File

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

View File

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

16
Gemfile
View File

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

View File

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

View File

@ -1,25 +0,0 @@
Open Access Control Web Interface
==
Web software for managing a database of members in a collaborative grassroots workshop, and also controlling Arclight of 23b Hackerspace's Arduino access control system via Ethernet ( see: https://github.com/zyphlar/Open_Access_Control_Ethernet )
https://github.com/zyphlar/Open-Source-Access-Control-Web-Interface
Copyright Will Bradley, 2012-2014
Distributed under a Creative Commons Attribution 3.0 license http://creativecommons.org/licenses/by/3.0/
![screenshot](https://cloud.githubusercontent.com/assets/48434/8439253/a9a810e6-1f1f-11e5-8b5c-3f0b22f14a9f.png)
Contributions welcome! Simply send a pull request via Github.
To use:
* Install Imagemagick (for Paperclip / image uploads)
* Install arp-scan (for LAN Mac address scanning)
* Load into a Rails 3 environment
* 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 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
* See/edit db/seeds.rb for the initial admin account info.
* Run bundle install, rake db:migrate, rake db:seed, etc.

15
README.rdoc Normal file
View File

@ -0,0 +1,15 @@
== Open Access Control Web Interface
Web software for managing a database of members in a collaborative grassroots workshop,
and also controlling Arclight of 23b Hackerspace's Arduino access control system
via Ethernet ( see: https://github.com/zyphlar/Open_Access_Control_Ethernet )
https://github.com/zyphlar/Open-Source-Access-Control-Web-Interface
Copyright Will Bradley, 2012-2013
Distributed under a Creative Commons Attribution 3.0 license http://creativecommons.org/licenses/by/3.0/
To use:
* Load into a Rails 3 environment
* Rename 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
* Run bundle install, rake db:migrate, etc.

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

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

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

Before

Width:  |  Height:  |  Size: 95 B

After

Width:  |  Height:  |  Size: 95 B

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

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

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

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

View File

@ -1,3 +0,0 @@
# 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

@ -1,3 +0,0 @@
# 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

@ -1,3 +0,0 @@
# 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

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

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

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

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

@ -11,47 +11,3 @@
*= require_self
*= require_tree .
*/
.caption { display: inline-block; background-color: #eee; border: 1px solid #333; border-radius: 5px; margin-bottom: 1em; }
.btn {
display: inline-block;
padding: 4px 10px 4px;
margin-bottom: 0;
font-size: 13px;
line-height: 18px;
color: #333;
text-align: center;
text-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;
}

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

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

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

@ -1,4 +1,3 @@
// Place all the styles related to the home controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
table, td { vertical-align: top }

View File

@ -1,3 +0,0 @@
// 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

@ -1,3 +0,0 @@
// Place all the styles related to the MacLogs controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -1,4 +0,0 @@
// Place all the styles related to the pamela controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
.hidden { color: #ccc; }

View File

@ -1,3 +0,0 @@
// Place all the styles related to the Payments controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

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

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

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

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

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

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

@ -1,49 +1,15 @@
class ApplicationController < ActionController::Base
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|
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."
else
flash[:alert] = "Nothing to see here!"
end
Rails.logger.warn "----------\r\nWARNING: AccessDenied Exception: #{exception.inspect} User: #{current_user.inspect}\r\n----------"
redirect_to root_url
end
@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!"}
# 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
# 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

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

@ -1,6 +1,6 @@
class CardsController < ApplicationController
load_and_authorize_resource except: :authorize
before_filter :authenticate_user!, except: :authorize
load_and_authorize_resource
before_filter :authenticate_user!
# GET /cards
# GET /cards.json
@ -9,20 +9,6 @@ class CardsController < ApplicationController
#authorize! :read, @cards
@cards = @cards.sort_by{|e| e[:id]}
if can? :read, DoorLog then
most_active_count = 0
runner_up_count = 0
@most_active_card = nil
@runner_up_card = nil
@cards.each do |card|
card_num_R = card.card_number.to_i(16)%32767
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
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
respond_to do |format|
format.html # index.html.erb
format.json { render :json => @cards }
@ -32,10 +18,8 @@ class CardsController < ApplicationController
# GET /cards/1
# GET /cards/1.json
def show
if can? :read, DoorLog then
card_num_R = @card.card_number.to_i(16)%32767
@door_logs = DoorLog.where('key = ? AND data = ?', "G", card_num_R).order("created_at DESC")
end
#@card = Card.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => @card }
@ -111,41 +95,6 @@ class CardsController < ApplicationController
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.json
def destroy

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

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

View File

@ -1,87 +0,0 @@
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

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

@ -5,36 +5,7 @@ class DoorLogsController < ApplicationController
# GET /door_logs
# GET /door_logs.json
def index
# @door_logs = DoorLog.find(:all, :order => "created_at DESC", :limit => 1000)
@door_logs = DoorLog.where("key NOT LIKE 'alarm%' AND key != 'armed' AND key != 'activated'").order("created_at DESC").limit(1000)
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
@door_logs = DoorLog.find(:all, :order => "created_at DESC", :limit => 500)
respond_to do |format|
format.html # index.html.erb
@ -55,7 +26,6 @@ class DoorLogsController < ApplicationController
# GET /door_logs/auto_download
def auto_download
@results = DoorLog.download_from_door
@status = DoorLog.download_status # for space_api
respond_to do |format|
format.html # show.html.erb

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

@ -1,54 +1,18 @@
class HomeController < ApplicationController
layout 'resources'
def index
# Alerts
if user_signed_in? && current_user.orientation.blank? then
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."
end
# if user_signed_in? && current_user.member_status.between?(2,100) then
# flash[:alert] = "<!--
# Member: <%= current_user.member.inspect
# Level: <%= current_user.member_level.inspect
# -->
# 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!"
# end
def index
@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
@num_door_opens = DoorLog.where("key = 'G'").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 - 7.days).count
# Fun Stats
@featured_resource = Resource.where("picture_file_name IS NOT NULL").sample
@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
format.html # index.html.erb
end
end
end
end

View File

@ -1,54 +0,0 @@
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,13 +0,0 @@
class MacLogsController < ApplicationController
load_and_authorize_resource :mac_log
before_filter :authenticate_user!
def index
@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

View File

@ -1,362 +0,0 @@
class MacsController < ApplicationController
load_and_authorize_resource :mac, :except => [:index, :create, :history]
#load_and_authorize_resource :user, :through => :mac, :except => [:index, :show, :scan, :import]
before_filter :arp_lookup, :only => :new
#require "active_record"
require "optparse"
#require "rubygems"
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 => nil)
# De-dupe users for the public
if can? :update, Mac then
@active_macs = Mac.where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).includes(:user).order("users.name ASC")
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")
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, mac, note, user_id")
end
@hidden_macs = Mac.where("macs.active = ? AND macs.hidden = ?", true, true).order("note ASC")
@all_macs = Mac.find(:all, :order => "LOWER(mac)")
respond_to do |format|
format.html
format.json {
@filtered_macs = Mac.select("macs.mac, users.name, macs.note").where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).joins(:user)
render :json => @filtered_macs
}
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.json
def show
@mac = Mac.find(params[:id])
@mac_logs = MacLog.where(:mac => @mac.mac)
respond_to do |format|
format.html # show.html.erb
format.json { render :json => @macs }
end
end
# GET /macs/new
# GET /macs/new.json
def new
@mac = Mac.new
if can? :manage, Mac then
@users = User.accessible_by(current_ability).sort_by(&:name)
else
@users = [current_user]
end
respond_to do |format|
format.html # new.html.erb
format.json { render :json => @mac }
end
end
# GET /macs/1/edit
def edit
@mac = Mac.find(params[:id])
if can? :manage, Mac then
@users = User.accessible_by(current_ability).sort_by(&:name)
else
@users = [current_user]
end
end
# POST /macs
def create
@mac = Mac.new(params[: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 @existing_mac.present?
if @existing_mac.user_id.nil?
redirect_to edit_mac_path(@existing_mac), :notice => 'This MAC already exists, edit it here:'
else
@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"
end
else
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
# PUT /macs/1
# PUT /macs/1.json
def update
#Log who updated this
@mac = Mac.find(params[:id])
@mac.assign_attributes(params[:mac])
#@mac.user_id = params[:mac][:user_id]
authorize! :update, @mac
if can? :manage, Mac then
@users = User.accessible_by(current_ability).sort_by(&:name)
else
@users = [current_user]
end
respond_to do |format|
if @mac.save
format.html { redirect_to macs_path, :notice => 'MAC was successfully updated.' }
format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => @mac.errors, :status => :unprocessable_entity }
end
end
end
def arp_lookup
@ip = request.env['REMOTE_ADDR']
@arp = /([0-9A-F]{2}[:-]){5}([0-9A-F]{2})/i.match(%x(arp -a | grep #{@ip}))
end
def scan
Rails.logger.info "starting scan..."
# Command line arguments
options = {};
OptionParser.new { |opts|
opts.banner = "Usage: pamela-scanner.rb --interface=en0"
options[:verbose] = true
opts.on("v", "--verbose", "Run verbosely") { |verbose|
options[:verbose] = verbose
}
options[:interface] = "eth0"
opts.on("i", "--interface=interface", "Network Interface") { |interface|
options[:interface] = interface
}
options[:max_age] = 20
opts.on("a", "--max-age=minutes", "Minutes to keep expired macs active") { |max_age|
options[:max_age] = max_age.to_i
}
options[:db_host] = "configure_me"
opts.on("r", "--db-host=host", "Database Host") { |host|
options[:db_host] = host
}
options[:db_name] = "configure_me"
opts.on("n", "--db-name=name", "Database Name") { |name|
options[:db_name] = name
}
options[:db_user] = "configure_me"
opts.on("u", "--db-user=user", "Database User") { |user|
options[:db_user] = user
}
options[:db_password] = "configure_me"
opts.on("p", "--db-password=password", "Database Password") { |password|
options[:db_password] = password
}
}.parse!
# Open the database
#ActiveRecord::Base::establish_connection(
# :adapter => "mysql2",
# :host => options[:db_host],
# :database => options[:db_name],
# :username => options[:db_user],
# :password => options[:db_password])
#class Mac < ActiveRecord::Base
#end
#class MacLog < ActiveRecord::Base
#end
# Scan the network for mac addresses
macs = {};
command = sprintf("arp-scan -R --interface=%s --localnet", options[:interface])
if options[:verbose]
Rails.logger.info "Running [#{command}]"
end
IO.popen(command) { |stdin|
result = stdin.read()
result.lines.each { |line|
Rails.logger.info "Reading stdin: "+line.inspect
next if line !~ /^([\d\.]+)\s+([[:xdigit:]:]+)\s/;
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
Mac.find(:all).each { |entry|
mac = entry.mac.downcase
ip = entry.ip
if macs.has_key?(mac) # if our scan shows this mac
if ! entry.active || ! entry.since
Rails.logger.info "Activating #{mac} at #{ip}" if options[:verbose]
entry.since = Time.now
MacLog.new(:mac => mac, :ip => ip, :action => "activate").save
end
entry.active = 1
entry.ip = ip
entry.refreshed = Time.now
entry.save
macs.delete(mac)
next
end
# Entry is no longer current
if entry.active
ageMinutes = ((Time.now - entry.refreshed)/60).to_i
next if ageMinutes < options[:max_age]
Rails.logger.info "Deactivating #{mac}, #{ageMinutes} minutes old" if options[:verbose]
entry.active = 0
entry.save
MacLog.new(:mac => mac, :ip => ip, :action => "deactivate").save
end
}
# Add entries for any macs not already in the db
macs.each { |mac, ip|
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
Rails.logger.info MacLog.new(:mac => mac, :ip => ip, :action => "activate").save
}
@log = MacLog.all
end #def scan
def import
require 'csv'
csv_text = File.read('mac_log.csv')
csv = CSV.parse(csv_text)
@output = []
csv.each do |row|
@output += [row[1], Mac.create({:mac => row[0], :note => row[1], :hidden => row[2]}) ]
end
end
end

View File

@ -1,143 +0,0 @@
class PaymentsController < ApplicationController
load_and_authorize_resource :payment
#load_and_authorize_resource :user, :through => :payment
before_filter :authenticate_user!
# Load users and certs based on current ability
before_filter do
@users = User.where(:hidden => false).where("member_level > 10").accessible_by(current_ability).sort_by(&:name_with_payee_and_member_level)
end
before_filter :only => [:create, :update] do
@payment.created_by = current_user.id
end
# GET /payments
# GET /payments.json
def index
@payments = @payments.order("date DESC")
@graph = { :members => chart("members"),
:total => chart("total"),
:basic => chart("basic"),
:associate => chart("associate")}
respond_to do |format|
format.html # index.html.erb
format.json { render :json => @payments }
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|
# 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
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.json
def show
respond_to do |format|
format.html # show.html.erb
format.json { render :json => @payment }
end
end
# GET /payments/new
# GET /payments/new.json
def new
respond_to do |format|
format.html # new.html.erb
format.json { render :json => @payment }
end
end
# GET /payments/1/edit
def edit
end
# POST /payments
# POST /payments.json
def create
Rails.logger.warn "payment:"
Rails.logger.warn @payment.inspect
respond_to do |format|
if @payment.save
format.html { redirect_to payments_url, :notice => 'Payment was successfully created.' }
format.json { render :json => @payment, :status => :created, :location => @payment }
else
format.html { render :action => "new" }
format.json { render :json => @payment.errors, :status => :unprocessable_entity }
end
end
end
# PUT /payments/1
# PUT /payments/1.json
def update
respond_to do |format|
if @payment.update_attributes(params[:payment])
format.html { redirect_to payments_url, :notice => 'Payment was successfully updated.' }
format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => @payment.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /payments/1
# DELETE /payments/1.json
def destroy
@payment.destroy
respond_to do |format|
format.html { redirect_to payments_url }
format.json { head :no_content }
end
end
end

View File

@ -1,28 +0,0 @@
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

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

View File

@ -1,46 +0,0 @@
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

@ -1,63 +0,0 @@
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

View File

@ -1,32 +0,0 @@
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

@ -1,131 +0,0 @@
class SpaceApiController < ApplicationController
# Individually remove authorizing stuff since there is no SpaceApi model
authorize_resource :except => [:index, :simple, :access, :access_post, :alert_if_not_status]
# User auth here happens via params, instead of form.
before_filter :authenticate_user!, :except => [:index, :simple, :access, :access_post, :alert_if_not_status]
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 simple
door_status = DoorLog.show_status # Expect {:unlocked => boolean, :door_1_locked => boolean, :door_2_locked => boolean}
render :json => door_status
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."
else
@output = "Ready to control doors. Send POST params to this URL as per the HTML form."
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
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 (or result)
respond_to do |format|
format.html {
render :access
}
format.json {
response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
render :json => @output
}
end
end
# Expect status to be "open" or "closed"
def alert_if_not_status
@expected_status = params['status']
@status = DoorLog.show_status
if !["open","closed"].include?(@expected_status)
@output = "USAGE: Specify an expected status (/alert_if_not/open or /alert_if_not/closed). Alert emails will be sent if status doesn't match."
elsif @expected_status.to_s == "open" && @status[:unlocked] == true
@output = "Unlocked Status is OK."
elsif @expected_status.to_s == "closed" && @status[:unlocked] == false
@output = "Unlocked Status is OK."
else
@output = "Unlocked Status is NOT OK. Alerting."
@output += " - Mail result: "
@output += DoorMailer.alert(@status).deliver.inspect
end
response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
render :json => {response: @output, status: @status}
end
end

View File

@ -1,68 +0,0 @@
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

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

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

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

@ -1,115 +1,47 @@
class UsersController < ApplicationController
load_and_authorize_resource
before_filter :authenticate_user!
layout 'resources'
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.json
def index
unless params[:full] # by default, show summary
@users = @users.active.sort_by{|u| [-u.member_level, u.name.downcase] }
#@users.paying + @users.volunteer
#.joins(:payments).where("payments.date > ? OR ", (DateTime.now - 60.days)).uniq
respond_to do |format|
format.html { render 'summary', layout: 'resources' }
format.json { render :json => @users }
end
else # show full
case params[:sort]
when "name"
@users = @users.sort_by{ |u| u.name.downcase }
when "cert"
@users = @users.sort_by{ |u| [-sort_by_cert(u.certifications,params[:cert].to_i),u.name.downcase] }
when "orientation"
@users = @users.sort_by{ |u| [-u.orientation.to_i,u.name.downcase] }
when "waiver"
@users = @users.sort_by{ |u| [-u.contract_date.to_i,u.name.downcase] }
when "member"
@users = @users.sort_by{ |u| [-u.member_status.to_i,u.name.downcase] }
when "card"
@users = @users.sort_by{ |u| [-u.cards.count,u.name.downcase] }
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{ |u| u.name.downcase }
end
case params[:sort]
when "name"
@users = @users.sort_by(&:name)
when "certifications"
@users = @users.sort_by{ |u| [-u.certifications.count,u.name] }
when "orientation"
@users = @users.sort_by{ |u| [-u.orientation.to_i,u.name] }
when "waiver"
@users = @users.sort_by{ |u| [-u.waiver.to_i,u.name] }
when "member"
@users = @users.sort_by{ |u| [-u.member.to_i,-u.member_level.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|
format.html # index.html.erb
format.json { render :json => @users }
end
respond_to do |format|
format.html # index.html.erb
format.json { render :json => @users }
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.json
def show
@payments = Payment.where(:user_id => @user.id).order('date desc').limit(10)
respond_to do |format|
format.html # show.html.erb
format.json { render :json => @user }
end
end
def compose_email
@user = User.find(params[:user_id])
end
def send_email
@user = User.find(params[:user_id])
@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.json
def new
@ -126,12 +58,9 @@ class UsersController < ApplicationController
# POST /users
# POST /users.json
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|
if @user.save
format.html { redirect_to @user, :notice => 'User was successfully created.' }
format.html { redirect_to users_url, :notice => 'User was successfully created.' }
format.json { render :json => @user, :status => :created, :location => @user }
else
format.html { render :action => "new" }
@ -143,13 +72,9 @@ class UsersController < ApplicationController
# PUT /users/1
# PUT /users/1.json
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|
if @user.update_attributes(params[:user])
format.html { redirect_to @user, :notice => 'User was successfully updated.' }
format.html { redirect_to users_url, :notice => 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render :action => "edit" }
@ -158,37 +83,6 @@ class UsersController < ApplicationController
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.json
def destroy

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

@ -1,31 +1,4 @@
module ApplicationHelper
@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!"}
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

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

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

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

View File

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

View File

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

View File

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

View File

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

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

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

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

View File

@ -1,12 +0,0 @@
class DoorMailer < ActionMailer::Base
default :from => "no-reply@heatsynclabs.org"
def alert(status)
@url = "http://members.heatsynclabs.org"
@status = status
mail(:to => 'heatsynclabs@googlegroups.com',
:subject => "HSL Doors")
end
end

View File

@ -1,20 +0,0 @@
class UserMailer < ActionMailer::Base
default :from => "no-reply@heatsynclabs.org"
def new_user_email(user)
@user = user
@url = "http://members.heatsynclabs.org"
mail(:to => 'member-notifications@heatsynclabs.org',
:subject => "New HSL Member: "+user.name)
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

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

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

@ -2,67 +2,36 @@ class Ability
include CanCan::Ability
def initialize(user)
can :read, Mac # Anonymous can read mac
can :scan, Mac # Need anonymous so CRON can scan
can :read, Resource
can :read, ResourceCategory
if !user.nil?
# By default, users can only see their own stuff
can :read, Card, :user_id => user.id
can :read, Certification
can :read_details, Mac
can [:update], Mac, :user_id => nil
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, User, :id => user.id #TODO: why can users update themselves?
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
can :access_doors_remotely, :door_access
can :authorize, Card # used for interlock card/certification auth
end
# Instructors can manage certs and see users
if user.instructor?
can :manage, Certification
can [:create,:read], User, :hidden => [nil,false]
can [:create,:read], UserCertification
can [:update,:destroy], UserCertification, :created_by => user.id
end
# Users can see others' stuff if they've been oriented
unless user.orientation.blank?
can [:read,:new_member_report,:activity], User, :hidden => [nil,false]
can :read, UserCertification
can [:create,:update], Resource, :user_id => [nil,user.id]
can [:create,:update,:destroy], ResourceCategory
end
# Accountants can manage payments
if user.accountant?
can :manage, Payment
can :manage, Ipn
can :manage, PaypalCsv
end
# Admins can manage all
if user.admin?
can :manage, :all
end
# Instructors can manage certs and see users
if user.instructor?
can :manage, Certification
can [:create,:read], User, :hidden => [nil,false]
can :manage, UserCertification
end
# Users can see others' stuff if they've been oriented
unless user.orientation.blank?
can :read, User, :hidden => [nil,false]
can :read, UserCertification
end
# Prevent most destruction for now
#cannot :destroy, User
#cannot :destroy, Card
# Prevent all destruction for now
cannot :destroy, User
cannot :destroy, Card
cannot :destroy, Certification
cannot :destroy, Mac
cannot :destroy, MacLog
#cannot :destroy, UserCertification
cannot :destroy, UserCertification
cannot :destroy, DoorLog
# no exception for destroying payments
end
# Define abilities for the passed in user here. For example:
#

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

@ -2,8 +2,7 @@ class Card < ActiveRecord::Base
require 'open-uri'
attr_accessible :id, :user_id, :name, :card_number, :card_permissions
validates_presence_of :user_id, :card_number, :card_permissions
validates_uniqueness_of :id, :card_number
validates_uniqueness_of :id,:card_number
belongs_to :user
def upload_to_door
@ -11,14 +10,21 @@ class Card < ActiveRecord::Base
door_access_url = APP_CONFIG['door_access_url']
door_access_password = APP_CONFIG['door_access_password']
cardid = self.id.to_s.rjust(3, '0')
# connect to door access system
source = open("#{door_access_url}?e=#{door_access_password}").read
results = source.scan(/authok/)
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')
cardnum = self.card_number.rjust(8, '0')
# 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
source = open("#{door_access_url}?m#{cardid}&p#{cardperm}&t#{cardnum}").read
results = source.scan(/cur/)
#logout
open("#{door_access_url}?e=0000")
if(results.size > 0) then
#only return true if we got some kind of decent response
return true
@ -26,6 +32,10 @@ class Card < ActiveRecord::Base
# We didn't get a decent response.
return false
end
else
# We didn't get an OK login.
return false
end
end
def self.upload_all_to_door
@ -36,21 +46,30 @@ class Card < ActiveRecord::Base
door_access_url = APP_CONFIG['door_access_url']
door_access_password = APP_CONFIG['door_access_password']
@cards.each do |u|
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}?e=#{door_access_password}").read
results = source.scan(/authok/)
if(results.size > 0) then
@cards.each do |u|
#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')
# 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/)
source = open("#{door_access_url}?m#{cardid}&p#{cardperm}&t#{cardnum}").read
results = source.scan(/cur/)
if(results.size > 0) then
#only return true if we got some kind of decent response
@end_results.push([cardid,"OK"])
else
@end_results.push([cardid,"FAIL"])
if(results.size > 0) then
#only return true if we got some kind of decent response
@end_results.push([cardid,"OK"])
else
@end_results.push([cardid,"FAIL"])
end
end
#logout
open("#{door_access_url}?e=0000")
else
@end_results.push([cardid,"FAIL"])
end
return @end_results

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

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

View File

@ -1,24 +0,0 @@
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

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

@ -2,126 +2,6 @@ class DoorLog < ActiveRecord::Base
attr_accessible :data, :key
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.order('created_at DESC').where(key: ["door_1_locked","door_2_locked"]).limit(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 OR 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
# load config values
door_access_url = APP_CONFIG['door_access_url']
@ -129,7 +9,7 @@ class DoorLog < ActiveRecord::Base
# connect to door access system
source = open("#{door_access_url}?e=#{door_access_password}").read
results = source.scan(/ok/)
results = source.scan(/authok/)
if(results.size > 0) then
@end_results = Array.new
@ -161,8 +41,4 @@ class DoorLog < ActiveRecord::Base
end
end
def self.key_legend
{'G' => "Granted", "R" => "Read", "D" => "Denied",
'g' => "granted", "r" => "read", "d" => "denied"}
end
end

View File

@ -1,95 +0,0 @@
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,6 +0,0 @@
class Mac < ActiveRecord::Base
belongs_to :user
attr_accessible :active, :ip, :mac, :refreshed, :since, :hidden, :note, :user_id
validates_uniqueness_of :mac, :case_sensitive => false
end

View File

@ -1,5 +0,0 @@
class MacLog < ActiveRecord::Base
attr_accessible :action, :ip, :mac
scope :desc, order("mac_logs.created_at DESC")
end

View File

@ -1,17 +0,0 @@
class Payment < ActiveRecord::Base
belongs_to :user
has_one :ipn
has_one :paypal_csv
attr_accessible :date, :user_id, :created_by, :amount
validates_presence_of :user_id, :date, :amount # not created_by
validates_uniqueness_of :date, :scope => :user_id, :message => ' of payment already exists for this user.'
def human_date
if date.year < DateTime.now.year
date.strftime("%b %e, %y")
else
date.strftime("%b %e")
end
end
end

View File

@ -1,80 +0,0 @@
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()
logger.fatal row.inspect
paypal_csv.attributes.each do |c|
# Try finding the column without a prepended _ first (compatibility with new CSV format)
unless row[c.first[1..-1].to_sym].nil?
paypal_csv[c.first.to_sym] = row[c.first[1..-1].to_sym]
end
# If there's an exact match, it takes precedence
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
logger.fatal self.inspect
# 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 = ["Subscription Payment Processed","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

View File

@ -1,31 +0,0 @@
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

View File

@ -1,13 +0,0 @@
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

View File

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

View File

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

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

@ -1,6 +1,6 @@
class User < ActiveRecord::Base
include Gravtastic
gravtastic :size => 150, :default => ""
gravtastic :size => 120, :default => ""
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
@ -9,223 +9,40 @@ class User < ActiveRecord::Base
:recoverable, :rememberable, :trackable, :validatable
# 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, :postal_code #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 #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 :user_certifications
has_many :certifications, :through => :user_certifications
has_many :contracts
has_many :payments
has_many :macs
has_many :resources
scope :volunteer, -> { where('member_level >= 10 AND member_level < 25') }
scope :active, -> { where('member_level >= 10') }
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
# disable # validates_presence_of :postal_code
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
if payee.blank? then
"#{name} - #{member_level_string}"
else
"#{payee} for #{name} - #{member_level_string}"
end
end
def member_level_string
case self.member_level.to_i
when 0
"None"
when 1
"Unable"
when 10..24
"Volunteer"
when 25..49
"Associate ($25)"
when 50..99
"Basic ($50)"
when 100..999
"Plus ($100)"
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
results = member_status_calculation
return results[:rank]
end
output = ""
def member_status_symbol
results = member_status_calculation
return "<img src='/#{results[:icon]}#{results[:flair]}-coin.png' title='#{results[:message]}' class='member-status-symbol' />"
end
def last_payment_date
self.payments.maximum(:date)
end
def delinquency
if self.payments.count > 0
paydate = self.payments.maximum(:date)
(Date.today - paydate).to_i
else
(Date.today - self.created_at.to_date).to_i
if self.member_level.to_i >= 1 then
output = "<span class='hoverinfo' title='Inactive'>&#9676;</span>"
end
end
def send_email(from_user,subject,body)
Rails.logger.info UserMailer.email(self,from_user,subject,body).deliver
end
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
Rails.logger.info UserMailer.new_user_email(self).deliver
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
unless self.member.nil? then
# 1 = inactive, show an X
if self.member >= 10 then
output = "<span class='hoverinfo' title='Volunteer'>&#9684;</span>"
# 25 or higher is paying, show a check
end
if self.member >= 25 then
output = "<span class='hoverinfo' title='25'>&#9681;</span>"
end
if self.member >= 50 then
output = "<span class='hoverinfo' title='50'>&#9685;</span>"
end
if self.member >= 100 then
output = "<span class='hoverinfo' title='100'>&#9679;</span>"
end
if self.member < self.member_level.to_i then
output = "<span class='hoverinfo' title='Lapsed'>&#x2717;</span>"
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}
return output
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

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

@ -1,17 +1,8 @@
class UserCertification < ActiveRecord::Base
attr_accessible :certification_id, :user_id
validates_presence_of :certification_id, :user_id
validates_uniqueness_of :certification_id, :scope => :user_id, :message => 'already exists for this user.' # Makes sure users don't get certified twice
belongs_to :user
belongs_to :certification
def user_name
if user.blank?
return "n/a (user ##{user_id} missing)"
else
return self.user.name
end
end
end

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

@ -11,10 +11,9 @@
</div>
<% end %>
<% @card.user_id = params[:user] if params[:user].present? %>
<div class="field">
<%= f.label :user %><br />
<%= collection_select(:card, :user_id, User.all.sort_by(&:name), :id, :name, :include_blank => true) %>
<%= collection_select(:card, :user_id, User.all.sort_by(&:name), :id, :name) %>
</div>
<div class="field">
<%= f.label :name, "Card Note" %><br />

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

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

@ -1,20 +1,8 @@
<h1>Access Cards</h1>
<%= link_to 'New Card', new_card_path, :class => "btn" if can? :create, Card %>
<%= link_to 'Upload all cards', upload_all_path, :class => "btn" if can? :upload_all, Card %>
<%= 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>
<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>
<% unless @runner_up_card.blank? || @runner_up_card.user.blank? %>
<b>Runner Up:</b> <%= @runner_up_card.user.name %> (<%= @runner_up_card.accesses_this_week %> days)
<% end %>
</p>
<table class="lined-table">
<%= link_to 'New Card', new_card_path if can? :create, Card %>
<%= link_to 'Upload all cards', upload_all_path if can? :upload_all, Card %>
<table>
<col />
<col />
<col class="col_highlight" />
@ -26,7 +14,6 @@
<th>DB ID</th>
<th>Card #</th>
<th>Access?</th>
<th>Days Accessed Last Month</th>
<th></th>
<th></th>
<th></th>
@ -35,19 +22,11 @@
<% if !@cards.blank? %>
<% @cards.each do |card| %>
<tr>
<td>
<% if card.user.nil? %>
n/a
<% else %>
<%= raw(card.user.member_status_symbol) %>
<%= link_to card.user.name , card %>
<% end %>
</td>
<td><%= card.user.name %></td>
<td><%= card.name %></td>
<td><%= card.id %></td>
<td><%= card.card_number %></td>
<td><%= if card.card_permissions == 1 then "Access" end %></td>
<td><%= card.accesses_this_week unless card.accesses_this_week < 1 %></td>
<td><%= link_to 'Upload', upload_path(card) if can? :upload, card %></td>
<td><%= link_to 'Edit', edit_card_path(card) if can? :update, card %></td>
<td><%= link_to 'Destroy', card, :confirm => 'Are you sure? WARNING: THIS DOES NOT REMOVE THE CARD FROM THE DOOR SYSTEM! DISABLE AND UPLOAD IT FIRST.', :method => :delete if can? :destroy, card %></td>

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

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

@ -1,6 +1,6 @@
<p>
<b>User:</b>
<%= link_to @card.user.name, @card.user unless @card.user.blank? %>
<%= @card.user.name unless @card.user.blank? %>
</p>
<p>
@ -20,20 +20,9 @@
<p>
<b>Card Permissions:</b>
<%= if @card.card_permissions == 1 then "Enabled" else "Disabled" end %>
<%= @card.card_permissions %>
</p>
<% if can? :read, DoorLog %>
<p>
<b>Access attempts:</b>
<ul>
<% @door_logs.each do |log| %>
<li><%= log.created_at %></li>
<% end %>
</ul>
</p>
<% end %>
<%= link_to 'Upload to Door', upload_path(@card) if can? :upload, @card %>
<% if can? :update, @card then %><%= link_to 'Edit', edit_card_path(@card) %> |<% end %>
<%= link_to 'Back', cards_path %>

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

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

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

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

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

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

@ -5,7 +5,6 @@
<ul>
<% @certifications.each do |certification| %>
<li><%= link_to certification.name, certification %>
(<%= certification.slug %>)
<% 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 %>
</li>

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

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

@ -3,20 +3,18 @@
<%= @certification.name %>
</p>
<p>
<b>Slug (lowercase, single-word identifier):</b>
<%= @certification.slug %>
</p>
<p>
<b>Description:</b>
<%= simple_format @certification.description %>
</p>
<p>
<b>Certified Users:</b>
<%= link_to "Click Here", user_certifications_path %>
</p>
<b>Certified Users:</b>
<ul>
<% @certification_users.each do |user| %>
<li><%= link_to user.name, user %></li>
<% end %>
<% if @certification_users.blank? then %><li>n/a</li><% end %>
</ul>
<% if can? :update, @certification %><%= link_to 'Edit', edit_certification_path(@certification) %> |<% end %>
<%= link_to 'Back', certifications_path %>

View File

@ -1,51 +0,0 @@
<%= 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

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

View File

@ -1,42 +0,0 @@
<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

@ -1,80 +0,0 @@
<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

@ -1,41 +0,0 @@
<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 Executable file → Normal file
View File

View File

View File

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

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