Compare commits
No commits in common. "master" and "members" have entirely different histories.
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
members-hsl
|
|
@ -1 +0,0 @@
|
|||
ruby-1.9.3-p385
|
16
Gemfile
16
Gemfile
|
@ -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"
|
||||
|
|
168
Gemfile.lock
168
Gemfile.lock
|
@ -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)
|
||||
|
|
25
README.md
25
README.md
|
@ -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
15
README.rdoc
Normal 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
0
app/assets/images/logo.png
Executable file → Normal file
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
0
app/assets/images/nil.png
Executable file → Normal file
0
app/assets/images/nil.png
Executable file → Normal file
Before Width: | Height: | Size: 95 B After Width: | Height: | Size: 95 B |
0
app/assets/javascripts/application.js
Executable file → Normal file
0
app/assets/javascripts/application.js
Executable file → Normal file
0
app/assets/javascripts/certifications.js.coffee
Executable file → Normal file
0
app/assets/javascripts/certifications.js.coffee
Executable file → Normal file
0
app/assets/javascripts/door_logs.js.coffee
Executable file → Normal file
0
app/assets/javascripts/door_logs.js.coffee
Executable file → Normal file
0
app/assets/javascripts/home.js.coffee
Executable file → Normal file
0
app/assets/javascripts/home.js.coffee
Executable file → Normal 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/
|
|
@ -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/
|
|
@ -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/
|
|
@ -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
0
app/assets/javascripts/user_certifications.js.coffee
Executable file → Normal file
0
app/assets/javascripts/users.js.coffee
Executable file → Normal file
0
app/assets/javascripts/users.js.coffee
Executable file → Normal file
44
app/assets/stylesheets/application.css
Executable file → Normal file
44
app/assets/stylesheets/application.css
Executable file → Normal 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
0
app/assets/stylesheets/certifications.css.scss
Executable file → Normal file
0
app/assets/stylesheets/door_logs.css.scss
Executable file → Normal file
0
app/assets/stylesheets/door_logs.css.scss
Executable file → Normal file
1
app/assets/stylesheets/home.css.scss
Executable file → Normal file
1
app/assets/stylesheets/home.css.scss
Executable file → Normal 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 }
|
||||
|
|
|
@ -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/
|
|
@ -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/
|
|
@ -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; }
|
|
@ -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
11
app/assets/stylesheets/scaffolds.css.scss
Executable file → Normal 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
0
app/assets/stylesheets/user_certifications.css.scss
Executable file → Normal file
10
app/assets/stylesheets/users.css.scss
Executable file → Normal file
10
app/assets/stylesheets/users.css.scss
Executable file → Normal 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
34
app/controllers/application_controller.rb
Executable file → Normal 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
59
app/controllers/cards_controller.rb
Executable file → Normal 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
2
app/controllers/certifications_controller.rb
Executable file → Normal 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
|
||||
|
|
|
@ -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
32
app/controllers/door_logs_controller.rb
Executable file → Normal 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
58
app/controllers/home_controller.rb
Executable file → Normal 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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
0
app/controllers/registrations_controller.rb
Executable file → Normal 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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
6
app/controllers/user_certifications_controller.rb
Executable file → Normal 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
156
app/controllers/users_controller.rb
Executable file → Normal 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
27
app/helpers/application_helper.rb
Executable file → Normal 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
0
app/helpers/certifications_helper.rb
Executable file → Normal file
0
app/helpers/door_logs_helper.rb
Executable file → Normal file
0
app/helpers/door_logs_helper.rb
Executable file → Normal file
0
app/helpers/home_helper.rb
Executable file → Normal file
0
app/helpers/home_helper.rb
Executable file → Normal file
|
@ -1,2 +0,0 @@
|
|||
module IpnHelper
|
||||
end
|
|
@ -1,2 +0,0 @@
|
|||
module MacLogsHelper
|
||||
end
|
|
@ -1,2 +0,0 @@
|
|||
module PamelaHelper
|
||||
end
|
|
@ -1,2 +0,0 @@
|
|||
module PaymentsHelper
|
||||
end
|
0
app/helpers/user_certifications_helper.rb
Executable file → Normal file
0
app/helpers/user_certifications_helper.rb
Executable file → Normal file
0
app/helpers/users_helper.rb
Executable file → Normal file
0
app/helpers/users_helper.rb
Executable file → Normal file
0
app/mailers/.gitkeep
Executable file → Normal file
0
app/mailers/.gitkeep
Executable file → Normal 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
|
|
@ -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
0
app/models/.gitkeep
Executable file → Normal file
63
app/models/ability.rb
Executable file → Normal file
63
app/models/ability.rb
Executable file → Normal 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
53
app/models/card.rb
Executable file → Normal 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
4
app/models/certification.rb
Executable file → Normal 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
|
||||
|
|
|
@ -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
126
app/models/door_log.rb
Executable file → Normal 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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
class MacLog < ActiveRecord::Base
|
||||
attr_accessible :action, :ip, :mac
|
||||
|
||||
scope :desc, order("mac_logs.created_at DESC")
|
||||
end
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||
class Setting < RailsSettings::CachedSettings
|
||||
attr_accessible :var
|
||||
end
|
|
@ -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
231
app/models/user.rb
Executable file → Normal 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'>◌</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'>◔</span>"
|
||||
# 25 or higher is paying, show a check
|
||||
end
|
||||
if self.member >= 25 then
|
||||
output = "<span class='hoverinfo' title='25'>◑</span>"
|
||||
end
|
||||
if self.member >= 50 then
|
||||
output = "<span class='hoverinfo' title='50'>◕</span>"
|
||||
end
|
||||
if self.member >= 100 then
|
||||
output = "<span class='hoverinfo' title='100'>●</span>"
|
||||
end
|
||||
|
||||
if self.member < self.member_level.to_i then
|
||||
output = "<span class='hoverinfo' title='Lapsed'>✗</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
9
app/models/user_certification.rb
Executable file → Normal 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
3
app/views/cards/_form.html.erb
Executable file → Normal 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
0
app/views/cards/edit.html.erb
Executable file → Normal file
29
app/views/cards/index.html.erb
Executable file → Normal file
29
app/views/cards/index.html.erb
Executable file → Normal 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
0
app/views/cards/new.html.erb
Executable file → Normal file
15
app/views/cards/show.html.erb
Executable file → Normal file
15
app/views/cards/show.html.erb
Executable file → Normal 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
0
app/views/cards/upload.html.erb
Executable file → Normal file
0
app/views/cards/upload_all.html.erb
Executable file → Normal file
0
app/views/cards/upload_all.html.erb
Executable file → Normal file
4
app/views/certifications/_form.html.erb
Executable file → Normal file
4
app/views/certifications/_form.html.erb
Executable file → Normal 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
0
app/views/certifications/edit.html.erb
Executable file → Normal file
1
app/views/certifications/index.html.erb
Executable file → Normal file
1
app/views/certifications/index.html.erb
Executable file → Normal 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
0
app/views/certifications/new.html.erb
Executable file → Normal file
16
app/views/certifications/show.html.erb
Executable file → Normal file
16
app/views/certifications/show.html.erb
Executable file → Normal 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 %>
|
||||
|
|
|
@ -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 %>
|
|
@ -1,4 +0,0 @@
|
|||
<h1>Edit Contract
|
||||
<%= link_to 'Back', contracts_path, class: "btn btn-default" %>
|
||||
</h1>
|
||||
<%= render 'form' %>
|
|
@ -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 />
|
||||
|
|
@ -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> ";
|
||||
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>
|
|
@ -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
0
app/views/devise/confirmations/new.html.erb
Executable file → Normal file
0
app/views/devise/mailer/confirmation_instructions.html.erb
Executable file → Normal file
0
app/views/devise/mailer/confirmation_instructions.html.erb
Executable file → Normal file
0
app/views/devise/mailer/reset_password_instructions.html.erb
Executable file → Normal file
0
app/views/devise/mailer/reset_password_instructions.html.erb
Executable file → Normal file
0
app/views/devise/mailer/unlock_instructions.html.erb
Executable file → Normal file
0
app/views/devise/mailer/unlock_instructions.html.erb
Executable file → Normal file
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user