Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 04d9b0bf68 | |||
| 6957ab7ac1 | |||
| 96a748ba0b | |||
| 2cd1966250 | |||
| f19e822d88 | |||
| 6e13ec34ef | |||
|
|
63a9b1b2f0 | ||
| 5a8375581f | |||
| 3097803d05 | |||
| 40102775ea | |||
| d522d0a43b | |||
| c976a6c216 | |||
| d1c5dba667 | |||
| 98b9db5fe1 | |||
| cd84e33e56 | |||
| a6f6e2e141 | |||
| 6334928779 | |||
| 85ec806b22 | |||
| b42236d228 | |||
| e0e43ccdbd | |||
| 3ce0d3c45a | |||
| 3cb1d4e730 | |||
| 1d6699ae9f | |||
| 3a028ca7fc | |||
| b23de1c1a2 | |||
| 409227f2d5 | |||
| 6198ad419c | |||
| 57a5a00352 | |||
| 2f4872218e | |||
| daeb7713e0 | |||
| 0cd63b085b | |||
| a95c1220bf | |||
| 7e8c8e1bb6 | |||
| 11f837cf7e | |||
| 18a52b4fca | |||
| b3279299cf | |||
| cb0f203f82 | |||
| bedd80d555 | |||
| 8f069cee4d | |||
| 16c75b2d0f | |||
| 13e72fef23 | |||
| 83442c4bff | |||
| 74d3fc45dd | |||
| d4286e4ddf | |||
| 3cd6c59df0 | |||
| d8311fa7cc | |||
| 0cfa83a9f7 | |||
| 691089dd1c | |||
| e8ef430733 | |||
| 20594d651c | |||
| cac59e9de5 | |||
| c74da562bc | |||
| 095b6d3965 | |||
| c5556a0d50 | |||
| 03a0588073 | |||
| 17aa2b971c | |||
| d943adc458 | |||
| 8425aaecd2 | |||
| 8a31655fd3 | |||
| 3d97e92eb7 | |||
| 3f5a7012bd | |||
| fb47188af2 | |||
| bff2be7cc9 | |||
| ee7e79a433 | |||
| 2c4cd4a020 | |||
| a2cbcac798 | |||
| 23dcb0715c | |||
| 74e60d4ec9 | |||
| e61895d20f | |||
| 58926cf3ab | |||
| e16e35e098 | |||
| 1d0e8721e1 | |||
| 56450cf319 | |||
| 92c3a6d2be | |||
| 73833dd2bf | |||
| c6bb164f73 | |||
| aa29f30f30 | |||
| a2a9d082d8 | |||
| 8060f76cca | |||
| f7d26ab69e | |||
| 6157280655 | |||
| cbadcc5c73 | |||
| ef590793f2 | |||
| d807aad668 | |||
| 41a8431be5 | |||
| d2434be109 | |||
| 9dc8645c32 | |||
| 02b997b838 | |||
| d88abe7d32 | |||
| 0d1894d6e8 | |||
| 008ceaed9e | |||
| 15836b4f4e | |||
| 57e6ac1fc5 | |||
| 04bab84293 | |||
| d61289abaf | |||
| 75da7ffb6a | |||
| 0eeaddabd7 | |||
| 370c82e075 | |||
| 7f7cd6e9ff | |||
| f11d5ebe1e | |||
| 4da2ec9463 | |||
| 7cd33ff030 | |||
| 7bfd45a163 | |||
| 42679aa410 | |||
| 9e08a0d036 | |||
| 2c204a8f9d | |||
| f03ffcd356 | |||
| b267da7b37 | |||
| ec84d8f5e0 | |||
| e002907da7 | |||
| 5acb3cd272 | |||
| 16e5e8d584 | |||
| b5383eaafb | |||
| d1985dae12 | |||
| 4631489eba | |||
| e61ac5d32e | |||
| 3ddfb3442b | |||
| 530f4834ca | |||
| 3c02724488 | |||
| ec139a97a8 | |||
| c5e02a51be | |||
| cad46e45c5 | |||
| 377170d47c | |||
| df0e91c4ee | |||
| 84525ca65f | |||
| faaeb2b4a2 | |||
| c61b8ac8d4 | |||
| 8820dc4986 | |||
| 37aba522cf | |||
| d1ba469767 | |||
| f4868a32ae | |||
| 880488c73e | |||
| 2231d124a6 | |||
| accec4edb1 | |||
| 25c0d1e1cb | |||
| e8e024c042 | |||
| 10a1e4eb84 | |||
| 211d79cbcd | |||
| a1ef8a5fd5 |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -9,13 +9,19 @@
|
|||||||
|
|
||||||
# Ignore the default SQLite database.
|
# Ignore the default SQLite database.
|
||||||
/db/*.sqlite3
|
/db/*.sqlite3
|
||||||
|
/db/*.sqlite3.*
|
||||||
|
|
||||||
# Ignore all logfiles and tempfiles.
|
# Ignore all logfiles and tempfiles.
|
||||||
/log/*.log
|
/log/*.log
|
||||||
/tmp
|
/tmp
|
||||||
|
tmp/
|
||||||
|
|
||||||
# Ignore compiled assets
|
# Ignore compiled assets
|
||||||
/public/assets
|
/public/assets
|
||||||
|
|
||||||
# Ignore config file
|
# Ignore config and database files (passwords)
|
||||||
/config/config.yml
|
/config/config.yml
|
||||||
|
/config/s3.yml
|
||||||
|
/config/database.yml
|
||||||
|
/config/initializers/secret_token.rb
|
||||||
|
.env
|
||||||
1
.ruby-gemset
Normal file
1
.ruby-gemset
Normal file
@@ -0,0 +1 @@
|
|||||||
|
members-hsl
|
||||||
1
.ruby-version
Normal file
1
.ruby-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ruby-1.9.3-p385
|
||||||
14
Gemfile
14
Gemfile
@@ -1,13 +1,16 @@
|
|||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
#ruby '1.9.3'
|
ruby '1.9.3'
|
||||||
|
|
||||||
gem 'rails', '3.2.3'
|
gem 'rails', '3.2.8'
|
||||||
|
gem 'dotenv-rails'
|
||||||
|
|
||||||
# Bundle edge Rails instead:
|
# Bundle edge Rails instead:
|
||||||
# gem 'rails', :git => 'git://github.com/rails/rails.git'
|
# gem 'rails', :git => 'git://github.com/rails/rails.git'
|
||||||
|
|
||||||
gem 'sqlite3'
|
gem 'sqlite3'
|
||||||
|
gem 'pg'
|
||||||
|
gem 'taps'
|
||||||
|
|
||||||
gem 'json'
|
gem 'json'
|
||||||
|
|
||||||
@@ -44,5 +47,10 @@ gem 'bcrypt-ruby', '~> 3.0.0'
|
|||||||
# To use debugger
|
# To use debugger
|
||||||
#gem 'debugger'
|
#gem 'debugger'
|
||||||
|
|
||||||
#gem "paperclip", "~> 3.0"
|
gem "paperclip", "~> 3.0"
|
||||||
|
gem "aws-sdk"
|
||||||
gem 'gravtastic'
|
gem 'gravtastic'
|
||||||
|
|
||||||
|
gem 'passenger'
|
||||||
|
|
||||||
|
gem "rails-settings-cached", "0.2.4"
|
||||||
|
|||||||
168
Gemfile.lock
168
Gemfile.lock
@@ -1,131 +1,177 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actionmailer (3.2.3)
|
actionmailer (3.2.8)
|
||||||
actionpack (= 3.2.3)
|
actionpack (= 3.2.8)
|
||||||
mail (~> 2.4.4)
|
mail (~> 2.4.4)
|
||||||
actionpack (3.2.3)
|
actionpack (3.2.8)
|
||||||
activemodel (= 3.2.3)
|
activemodel (= 3.2.8)
|
||||||
activesupport (= 3.2.3)
|
activesupport (= 3.2.8)
|
||||||
builder (~> 3.0.0)
|
builder (~> 3.0.0)
|
||||||
erubis (~> 2.7.0)
|
erubis (~> 2.7.0)
|
||||||
journey (~> 1.0.1)
|
journey (~> 1.0.4)
|
||||||
rack (~> 1.4.0)
|
rack (~> 1.4.0)
|
||||||
rack-cache (~> 1.2)
|
rack-cache (~> 1.2)
|
||||||
rack-test (~> 0.6.1)
|
rack-test (~> 0.6.1)
|
||||||
sprockets (~> 2.1.2)
|
sprockets (~> 2.1.3)
|
||||||
activemodel (3.2.3)
|
activemodel (3.2.8)
|
||||||
activesupport (= 3.2.3)
|
activesupport (= 3.2.8)
|
||||||
builder (~> 3.0.0)
|
builder (~> 3.0.0)
|
||||||
activerecord (3.2.3)
|
activerecord (3.2.8)
|
||||||
activemodel (= 3.2.3)
|
activemodel (= 3.2.8)
|
||||||
activesupport (= 3.2.3)
|
activesupport (= 3.2.8)
|
||||||
arel (~> 3.0.2)
|
arel (~> 3.0.2)
|
||||||
tzinfo (~> 0.3.29)
|
tzinfo (~> 0.3.29)
|
||||||
activeresource (3.2.3)
|
activeresource (3.2.8)
|
||||||
activemodel (= 3.2.3)
|
activemodel (= 3.2.8)
|
||||||
activesupport (= 3.2.3)
|
activesupport (= 3.2.8)
|
||||||
activesupport (3.2.3)
|
activesupport (3.2.8)
|
||||||
i18n (~> 0.6)
|
i18n (~> 0.6)
|
||||||
multi_json (~> 1.0)
|
multi_json (~> 1.0)
|
||||||
arel (3.0.2)
|
arel (3.0.2)
|
||||||
|
aws-sdk (1.33.0)
|
||||||
|
json (~> 1.4)
|
||||||
|
nokogiri (>= 1.4.4)
|
||||||
|
uuidtools (~> 2.1)
|
||||||
bcrypt-ruby (3.0.1)
|
bcrypt-ruby (3.0.1)
|
||||||
builder (3.0.0)
|
builder (3.0.4)
|
||||||
cancan (1.6.8)
|
cancan (1.6.10)
|
||||||
|
climate_control (0.0.3)
|
||||||
|
activesupport (>= 3.0)
|
||||||
|
cocaine (0.5.3)
|
||||||
|
climate_control (>= 0.0.3, < 1.0)
|
||||||
coffee-rails (3.2.2)
|
coffee-rails (3.2.2)
|
||||||
coffee-script (>= 2.2.0)
|
coffee-script (>= 2.2.0)
|
||||||
railties (~> 3.2.0)
|
railties (~> 3.2.0)
|
||||||
coffee-script (2.2.0)
|
coffee-script (2.2.0)
|
||||||
coffee-script-source
|
coffee-script-source
|
||||||
execjs
|
execjs
|
||||||
coffee-script-source (1.3.3)
|
coffee-script-source (1.6.3)
|
||||||
devise (2.1.1)
|
daemon_controller (1.1.5)
|
||||||
|
devise (2.2.7)
|
||||||
bcrypt-ruby (~> 3.0)
|
bcrypt-ruby (~> 3.0)
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
railties (~> 3.1)
|
railties (~> 3.1)
|
||||||
warden (~> 1.2.1)
|
warden (~> 1.2.1)
|
||||||
|
dotenv (0.10.0)
|
||||||
|
dotenv-rails (0.10.0)
|
||||||
|
dotenv (= 0.10.0)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
execjs (1.4.0)
|
execjs (2.0.2)
|
||||||
multi_json (~> 1.0)
|
|
||||||
gravtastic (3.2.6)
|
gravtastic (3.2.6)
|
||||||
hike (1.2.1)
|
hike (1.2.3)
|
||||||
i18n (0.6.0)
|
i18n (0.6.5)
|
||||||
journey (1.0.4)
|
journey (1.0.4)
|
||||||
jquery-rails (2.1.1)
|
jquery-rails (3.0.4)
|
||||||
railties (>= 3.1.0, < 5.0)
|
railties (>= 3.0, < 5.0)
|
||||||
thor (~> 0.14)
|
thor (>= 0.14, < 2.0)
|
||||||
json (1.7.5)
|
json (1.8.1)
|
||||||
libv8 (3.3.10.4)
|
libv8 (3.16.14.3)
|
||||||
mail (2.4.4)
|
mail (2.4.4)
|
||||||
i18n (>= 0.4.0)
|
i18n (>= 0.4.0)
|
||||||
mime-types (~> 1.16)
|
mime-types (~> 1.16)
|
||||||
treetop (~> 1.4.8)
|
treetop (~> 1.4.8)
|
||||||
mime-types (1.19)
|
mime-types (1.25)
|
||||||
multi_json (1.3.6)
|
mini_portile (0.5.2)
|
||||||
orm_adapter (0.1.0)
|
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)
|
||||||
polyglot (0.3.3)
|
polyglot (0.3.3)
|
||||||
rack (1.4.1)
|
rack (1.4.5)
|
||||||
rack-cache (1.2)
|
rack-cache (1.2)
|
||||||
rack (>= 0.4)
|
rack (>= 0.4)
|
||||||
rack-ssl (1.3.2)
|
rack-ssl (1.3.3)
|
||||||
rack
|
rack
|
||||||
rack-test (0.6.1)
|
rack-test (0.6.2)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rails (3.2.3)
|
rails (3.2.8)
|
||||||
actionmailer (= 3.2.3)
|
actionmailer (= 3.2.8)
|
||||||
actionpack (= 3.2.3)
|
actionpack (= 3.2.8)
|
||||||
activerecord (= 3.2.3)
|
activerecord (= 3.2.8)
|
||||||
activeresource (= 3.2.3)
|
activeresource (= 3.2.8)
|
||||||
activesupport (= 3.2.3)
|
activesupport (= 3.2.8)
|
||||||
bundler (~> 1.0)
|
bundler (~> 1.0)
|
||||||
railties (= 3.2.3)
|
railties (= 3.2.8)
|
||||||
railties (3.2.3)
|
rails-settings-cached (0.2.4)
|
||||||
actionpack (= 3.2.3)
|
rails (>= 3.0.0)
|
||||||
activesupport (= 3.2.3)
|
railties (3.2.8)
|
||||||
|
actionpack (= 3.2.8)
|
||||||
|
activesupport (= 3.2.8)
|
||||||
rack-ssl (~> 1.3.2)
|
rack-ssl (~> 1.3.2)
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
rdoc (~> 3.4)
|
rdoc (~> 3.4)
|
||||||
thor (~> 0.14.6)
|
thor (>= 0.14.6, < 2.0)
|
||||||
rake (0.9.2.2)
|
rake (10.1.0)
|
||||||
rdoc (3.12)
|
rdoc (3.12.2)
|
||||||
json (~> 1.4)
|
json (~> 1.4)
|
||||||
sass (3.2.1)
|
ref (1.0.5)
|
||||||
sass-rails (3.2.5)
|
rest-client (1.6.7)
|
||||||
|
mime-types (>= 1.16)
|
||||||
|
sass (3.2.11)
|
||||||
|
sass-rails (3.2.6)
|
||||||
railties (~> 3.2.0)
|
railties (~> 3.2.0)
|
||||||
sass (>= 3.1.10)
|
sass (>= 3.1.10)
|
||||||
tilt (~> 1.3)
|
tilt (~> 1.3)
|
||||||
|
sequel (3.20.0)
|
||||||
|
sinatra (1.0)
|
||||||
|
rack (>= 1.0)
|
||||||
sprockets (2.1.3)
|
sprockets (2.1.3)
|
||||||
hike (~> 1.2)
|
hike (~> 1.2)
|
||||||
rack (~> 1.0)
|
rack (~> 1.0)
|
||||||
tilt (~> 1.1, != 1.3.0)
|
tilt (~> 1.1, != 1.3.0)
|
||||||
sqlite3 (1.3.6)
|
sqlite3 (1.3.8)
|
||||||
therubyracer (0.10.1)
|
taps (0.3.24)
|
||||||
libv8 (~> 3.3.10)
|
rack (>= 1.0.1)
|
||||||
thor (0.14.6)
|
rest-client (>= 1.4.0, < 1.7.0)
|
||||||
tilt (1.3.3)
|
sequel (~> 3.20.0)
|
||||||
treetop (1.4.10)
|
sinatra (~> 1.0.0)
|
||||||
|
therubyracer (0.12.0)
|
||||||
|
libv8 (~> 3.16.14.0)
|
||||||
|
ref
|
||||||
|
thor (0.18.1)
|
||||||
|
tilt (1.4.1)
|
||||||
|
treetop (1.4.15)
|
||||||
polyglot
|
polyglot
|
||||||
polyglot (>= 0.3.1)
|
polyglot (>= 0.3.1)
|
||||||
tzinfo (0.3.33)
|
tzinfo (0.3.38)
|
||||||
uglifier (1.2.7)
|
uglifier (2.2.1)
|
||||||
execjs (>= 0.3.0)
|
execjs (>= 0.3.0)
|
||||||
multi_json (~> 1.3)
|
multi_json (~> 1.0, >= 1.0.2)
|
||||||
warden (1.2.1)
|
uuidtools (2.1.4)
|
||||||
|
warden (1.2.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
aws-sdk
|
||||||
bcrypt-ruby (~> 3.0.0)
|
bcrypt-ruby (~> 3.0.0)
|
||||||
cancan
|
cancan
|
||||||
coffee-rails (~> 3.2.1)
|
coffee-rails (~> 3.2.1)
|
||||||
devise
|
devise
|
||||||
|
dotenv-rails
|
||||||
gravtastic
|
gravtastic
|
||||||
jquery-rails
|
jquery-rails
|
||||||
json
|
json
|
||||||
rails (= 3.2.3)
|
paperclip (~> 3.0)
|
||||||
|
passenger
|
||||||
|
pg
|
||||||
|
rails (= 3.2.8)
|
||||||
|
rails-settings-cached (= 0.2.4)
|
||||||
sass-rails (~> 3.2.3)
|
sass-rails (~> 3.2.3)
|
||||||
sqlite3
|
sqlite3
|
||||||
|
taps
|
||||||
therubyracer
|
therubyracer
|
||||||
uglifier (>= 1.0.3)
|
uglifier (>= 1.0.3)
|
||||||
|
|||||||
25
README.md
Normal file
25
README.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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/
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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
15
README.rdoc
@@ -1,15 +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-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
Normal file → Executable file
0
app/assets/images/logo.png
Normal file → Executable file
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
0
app/assets/images/nil.png
Normal file → Executable file
0
app/assets/images/nil.png
Normal file → Executable file
|
Before Width: | Height: | Size: 95 B After Width: | Height: | Size: 95 B |
0
app/assets/javascripts/application.js
Normal file → Executable file
0
app/assets/javascripts/application.js
Normal file → Executable file
0
app/assets/javascripts/certifications.js.coffee
Normal file → Executable file
0
app/assets/javascripts/certifications.js.coffee
Normal file → Executable file
0
app/assets/javascripts/door_logs.js.coffee
Normal file → Executable file
0
app/assets/javascripts/door_logs.js.coffee
Normal file → Executable file
0
app/assets/javascripts/home.js.coffee
Normal file → Executable file
0
app/assets/javascripts/home.js.coffee
Normal file → Executable file
0
app/assets/javascripts/ipn.js.coffee
Normal file → Executable file
0
app/assets/javascripts/ipn.js.coffee
Normal file → Executable file
0
app/assets/javascripts/mac_logs.js.coffee
Normal file → Executable file
0
app/assets/javascripts/mac_logs.js.coffee
Normal file → Executable file
0
app/assets/javascripts/pamela.js.coffee
Normal file → Executable file
0
app/assets/javascripts/pamela.js.coffee
Normal file → Executable file
0
app/assets/javascripts/payments.js.coffee
Normal file → Executable file
0
app/assets/javascripts/payments.js.coffee
Normal file → Executable file
0
app/assets/javascripts/user_certifications.js.coffee
Normal file → Executable file
0
app/assets/javascripts/user_certifications.js.coffee
Normal file → Executable file
0
app/assets/javascripts/users.js.coffee
Normal file → Executable file
0
app/assets/javascripts/users.js.coffee
Normal file → Executable file
9
app/assets/stylesheets/application.css
Normal file → Executable file
9
app/assets/stylesheets/application.css
Normal file → Executable file
@@ -46,3 +46,12 @@ box-shadow: inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
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
Normal file → Executable file
0
app/assets/stylesheets/certifications.css.scss
Normal file → Executable file
0
app/assets/stylesheets/door_logs.css.scss
Normal file → Executable file
0
app/assets/stylesheets/door_logs.css.scss
Normal file → Executable file
0
app/assets/stylesheets/home.css.scss
Normal file → Executable file
0
app/assets/stylesheets/home.css.scss
Normal file → Executable file
0
app/assets/stylesheets/ipn.css.scss
Normal file → Executable file
0
app/assets/stylesheets/ipn.css.scss
Normal file → Executable file
0
app/assets/stylesheets/mac_logs.css.scss
Normal file → Executable file
0
app/assets/stylesheets/mac_logs.css.scss
Normal file → Executable file
0
app/assets/stylesheets/macs.css.scss
Normal file → Executable file
0
app/assets/stylesheets/macs.css.scss
Normal file → Executable file
0
app/assets/stylesheets/payments.css.scss
Normal file → Executable file
0
app/assets/stylesheets/payments.css.scss
Normal file → Executable file
11
app/assets/stylesheets/scaffolds.css.scss
Normal file → Executable file
11
app/assets/stylesheets/scaffolds.css.scss
Normal file → Executable file
@@ -19,17 +19,6 @@ pre {
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
color: #000;
|
|
||||||
&:visited {
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
div {
|
||||||
&.field, &.actions {
|
&.field, &.actions {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|||||||
0
app/assets/stylesheets/user_certifications.css.scss
Normal file → Executable file
0
app/assets/stylesheets/user_certifications.css.scss
Normal file → Executable file
10
app/assets/stylesheets/users.css.scss
Normal file → Executable file
10
app/assets/stylesheets/users.css.scss
Normal file → Executable file
@@ -6,8 +6,16 @@
|
|||||||
.hoverinfo { cursor: progress; }
|
.hoverinfo { cursor: progress; }
|
||||||
|
|
||||||
.payment_links { background-color: #ddd; padding: 1em; border-radius: 1em;
|
.payment_links { background-color: #ddd; padding: 1em; border-radius: 1em;
|
||||||
display: inline-block; float: right; }
|
display: inline-block; float: right; max-width: 30%; min-width: 10em;}
|
||||||
|
|
||||||
|
.payment-highlighted {
|
||||||
|
background-color: orange !important;
|
||||||
|
}
|
||||||
|
|
||||||
.avatar { height: 2em; width: 2em; }
|
.avatar { height: 2em; width: 2em; }
|
||||||
|
|
||||||
|
.avatar-large {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
textarea { height: 10em; }
|
textarea { height: 10em; }
|
||||||
|
|||||||
34
app/controllers/application_controller.rb
Normal file → Executable file
34
app/controllers/application_controller.rb
Normal file → Executable file
@@ -1,15 +1,49 @@
|
|||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
protect_from_forgery
|
protect_from_forgery
|
||||||
|
|
||||||
|
force_ssl if: :ssl_forced?
|
||||||
|
|
||||||
|
def ssl_forced?
|
||||||
|
# Non-production environments and read-only stuff like the space API and MACs should not require SSL. (APIs hate following 301s).
|
||||||
|
if Rails.env.development? || Rails.env.test? || ["space_api","macs"].include?(params[:controller])
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
rescue_from CanCan::AccessDenied do |exception|
|
rescue_from CanCan::AccessDenied do |exception|
|
||||||
if !current_user.nil? && current_user.orientation.blank? then
|
if !current_user.nil? && current_user.orientation.blank? then
|
||||||
flash[:alert] = "Sorry, you probably need to complete New Member Orientation before having access to this page. <br/>Please check your email and schedule a New Member Orientation with a volunteer."
|
flash[:alert] = "Sorry, you probably need to complete New Member Orientation before having access to this page. <br/>Please check your email and schedule a New Member Orientation with a volunteer."
|
||||||
else
|
else
|
||||||
flash[:alert] = "Nothing to see here!"
|
flash[:alert] = "Nothing to see here!"
|
||||||
end
|
end
|
||||||
|
Rails.logger.warn "----------\r\nWARNING: AccessDenied Exception: #{exception.inspect} User: #{current_user.inspect}\r\n----------"
|
||||||
redirect_to root_url
|
redirect_to root_url
|
||||||
end
|
end
|
||||||
|
|
||||||
@payment_methods = [[nil],["PayPal"],["Dwolla"],["Bill Pay"],["Check"],["Cash"],["Other"]]
|
@payment_methods = [[nil],["PayPal"],["Dwolla"],["Bill Pay"],["Check"],["Cash"],["Other"]]
|
||||||
@payment_instructions = {nil => nil, :paypal => "Set up a monthly recurring payment to hslfinances@gmail.com", :dwolla => "Set up a monthly recurring payment to hslfinances@gmail.com", :billpay => "Have your bank send a monthly check to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201", :check => "Mail to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201 OR put in the drop safe at the Lab with a deposit slip firmly attached each month.", :cash => "Put in the drop safe at the Lab with a deposit slip firmly attached each month.", :other => "Hmm... talk to a Treasurer!"}
|
@payment_instructions = {nil => nil, :paypal => "Set up a monthly recurring payment to hslfinances@gmail.com", :dwolla => "Set up a monthly recurring payment to hslfinances@gmail.com", :billpay => "Have your bank send a monthly check to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201", :check => "Mail to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201 OR put in the drop safe at the Lab with a deposit slip firmly attached each month.", :cash => "Put in the drop safe at the Lab with a deposit slip firmly attached each month.", :other => "Hmm... talk to a Treasurer!"}
|
||||||
|
|
||||||
|
# Check authorization of a user / sign them in manually
|
||||||
|
def check_auth(email,password)
|
||||||
|
resource = User.find_by_email(email)
|
||||||
|
if resource && resource.valid_password?(password)
|
||||||
|
resource.remember_me = true
|
||||||
|
sign_in :user, resource
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Add a "fit" function to sanitize inputs for mac history
|
||||||
|
class Fixnum
|
||||||
|
def fit(range)
|
||||||
|
self > range.max ? range.max : (self < range.min ? range.min : self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|||||||
56
app/controllers/cards_controller.rb
Normal file → Executable file
56
app/controllers/cards_controller.rb
Normal file → Executable file
@@ -1,6 +1,6 @@
|
|||||||
class CardsController < ApplicationController
|
class CardsController < ApplicationController
|
||||||
load_and_authorize_resource
|
load_and_authorize_resource except: :authorize
|
||||||
before_filter :authenticate_user!
|
before_filter :authenticate_user!, except: :authorize
|
||||||
|
|
||||||
# GET /cards
|
# GET /cards
|
||||||
# GET /cards.json
|
# GET /cards.json
|
||||||
@@ -10,20 +10,17 @@ class CardsController < ApplicationController
|
|||||||
@cards = @cards.sort_by{|e| e[:id]}
|
@cards = @cards.sort_by{|e| e[:id]}
|
||||||
|
|
||||||
if can? :read, DoorLog then
|
if can? :read, DoorLog then
|
||||||
Rails.logger.info "CARD STATS:"
|
|
||||||
most_active_count = 0
|
most_active_count = 0
|
||||||
|
runner_up_count = 0
|
||||||
@most_active_card = nil
|
@most_active_card = nil
|
||||||
|
@runner_up_card = nil
|
||||||
@cards.each do |card|
|
@cards.each do |card|
|
||||||
card_num_R = card.card_number.to_i(16)%32767
|
card_num_R = card.card_number.to_i(16)%32767
|
||||||
Rails.logger.info card_num_R
|
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
|
||||||
card[:accesses_this_week] = DoorLog.where('key = "G" AND data =? AND created_at > ?', card_num_R, DateTime.now - 7.days).order("created_at DESC").count
|
|
||||||
Rails.logger.info card[:accesses_this_week]
|
|
||||||
if(card[:accesses_this_week] > most_active_count) then
|
|
||||||
Rails.logger.info "ACTIVE"
|
|
||||||
most_active_count = card[:accesses_this_week]
|
|
||||||
@most_active_card = card
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@most_active_cards = @cards.sort{|a,b| b[:accesses_this_week] <=> a[:accesses_this_week]}
|
||||||
|
@most_active_card = @most_active_cards[0]
|
||||||
|
@runner_up_card = @most_active_cards[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
@@ -37,7 +34,7 @@ class CardsController < ApplicationController
|
|||||||
def show
|
def show
|
||||||
if can? :read, DoorLog then
|
if can? :read, DoorLog then
|
||||||
card_num_R = @card.card_number.to_i(16)%32767
|
card_num_R = @card.card_number.to_i(16)%32767
|
||||||
@door_logs = DoorLog.where('key = "R" AND data =?', card_num_R).order("created_at DESC")
|
@door_logs = DoorLog.where('key = ? AND data = ?', "G", card_num_R).order("created_at DESC")
|
||||||
end
|
end
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # show.html.erb
|
format.html # show.html.erb
|
||||||
@@ -114,6 +111,41 @@ class CardsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def authorize
|
||||||
|
|
||||||
|
# Stop unless signed in already, OR if the supplied user/pass params are good.
|
||||||
|
unless current_user || check_auth(params['user'],params['pass'])
|
||||||
|
@auth = "bad_user_or_pass"
|
||||||
|
else
|
||||||
|
# Stop unless the user can access the door system
|
||||||
|
unless can? :authorize, Card
|
||||||
|
@auth = "bad_user_permissions"
|
||||||
|
Rails.logger.warn "----------\r\nWARNING: CARD AUTH ATTEMPT DENIED. USER #{current_user.inspect}\r\n----------"
|
||||||
|
else
|
||||||
|
|
||||||
|
begin
|
||||||
|
@card = Card.find(:first, :conditions => ["lower(card_number) = ?", params[:id].downcase])
|
||||||
|
@auth = @card.inspect
|
||||||
|
if @card && @card.user
|
||||||
|
@auth = @card.user.has_certification?(params[:device])
|
||||||
|
else
|
||||||
|
@auth = false
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
@auth = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if @card && @card.user
|
||||||
|
username = @card.user.name
|
||||||
|
else
|
||||||
|
username = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: [@auth, username]
|
||||||
|
end
|
||||||
|
|
||||||
# DELETE /cards/1
|
# DELETE /cards/1
|
||||||
# DELETE /cards/1.json
|
# DELETE /cards/1.json
|
||||||
def destroy
|
def destroy
|
||||||
|
|||||||
2
app/controllers/certifications_controller.rb
Normal file → Executable file
2
app/controllers/certifications_controller.rb
Normal file → Executable file
@@ -1,6 +1,6 @@
|
|||||||
class CertificationsController < ApplicationController
|
class CertificationsController < ApplicationController
|
||||||
load_and_authorize_resource :certification
|
load_and_authorize_resource :certification
|
||||||
load_and_authorize_resource :user, :through => :certification
|
#load_and_authorize_resource :user, :through => :certification
|
||||||
before_filter :authenticate_user!
|
before_filter :authenticate_user!
|
||||||
|
|
||||||
# GET /certifications
|
# GET /certifications
|
||||||
|
|||||||
87
app/controllers/contracts_controller.rb
Normal file
87
app/controllers/contracts_controller.rb
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
class ContractsController < ApplicationController
|
||||||
|
load_and_authorize_resource :contract
|
||||||
|
before_filter :authenticate_user!, :load_users
|
||||||
|
layout 'resources'
|
||||||
|
|
||||||
|
def index
|
||||||
|
if params[:user_id].present?
|
||||||
|
@contracts = Contract.where(user_id: params[:user_id])
|
||||||
|
end
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.json { render :json => @contracts }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
# if @contract.first_name.blank? && @contract.last_name.blank? && @contract.cosigner.blank? # assume autodetect of filename
|
||||||
|
# begin
|
||||||
|
# name_split = params[:contract][:document].original_filename.sub(".jpg","").split
|
||||||
|
# if name_split.count == 4 # we have one name
|
||||||
|
# @contract.first_name = name_split[0]
|
||||||
|
# @contract.last_name = name_split[1]
|
||||||
|
# # 2 is the hyphen
|
||||||
|
# @contract.signed_at = Date.parse(name_split[3])
|
||||||
|
# elsif name_split.count == 7 && name_split[2] == "by" # we have two names
|
||||||
|
# @contract.first_name = name_split[0]
|
||||||
|
# @contract.last_name = name_split[1]
|
||||||
|
# # 2 is "by"
|
||||||
|
# @contract.cosigner = "#{name_split[3]} #{name_split[4]}"
|
||||||
|
# # 5 is the hyphen
|
||||||
|
# @contract.signed_at = Date.parse(name_split[6])
|
||||||
|
# else
|
||||||
|
# Rails.logger.info "Couldn't determine name from filename array: #{name_split.inspect}"
|
||||||
|
# end
|
||||||
|
# rescue Exception => e
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
@contract.created_by = current_user
|
||||||
|
respond_to do |format|
|
||||||
|
if @contract.save
|
||||||
|
format.html { redirect_to @contract, :notice => 'Contract was successfully created.' }
|
||||||
|
format.json { render :json => @contract, :status => :created, :location => @contract }
|
||||||
|
else
|
||||||
|
format.html { render :action => "new" }
|
||||||
|
format.json { render :json => @contract.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_for_user
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
respond_to do |format|
|
||||||
|
if @contract.update_attributes(params[:contract])
|
||||||
|
format.html { redirect_to @contract, :notice => 'Contract was successfully updated.' }
|
||||||
|
format.json { head :no_content }
|
||||||
|
else
|
||||||
|
format.html { render :action => "edit" }
|
||||||
|
format.json { render :json => @contract.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@contract.destroy
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to contracts_url }
|
||||||
|
format.json { head :no_content }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_users
|
||||||
|
@users = User.accessible_by(current_ability).sort_by(&:name)
|
||||||
|
end
|
||||||
|
end
|
||||||
32
app/controllers/door_logs_controller.rb
Normal file → Executable file
32
app/controllers/door_logs_controller.rb
Normal file → Executable file
@@ -5,7 +5,36 @@ class DoorLogsController < ApplicationController
|
|||||||
# GET /door_logs
|
# GET /door_logs
|
||||||
# GET /door_logs.json
|
# GET /door_logs.json
|
||||||
def index
|
def index
|
||||||
@door_logs = DoorLog.find(:all, :order => "created_at DESC", :limit => 500)
|
# @door_logs = DoorLog.find(:all, :order => "created_at DESC", :limit => 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # index.html.erb
|
format.html # index.html.erb
|
||||||
@@ -26,6 +55,7 @@ class DoorLogsController < ApplicationController
|
|||||||
# GET /door_logs/auto_download
|
# GET /door_logs/auto_download
|
||||||
def auto_download
|
def auto_download
|
||||||
@results = DoorLog.download_from_door
|
@results = DoorLog.download_from_door
|
||||||
|
@status = DoorLog.download_status # for space_api
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # show.html.erb
|
format.html # show.html.erb
|
||||||
|
|||||||
68
app/controllers/home_controller.rb
Normal file → Executable file
68
app/controllers/home_controller.rb
Normal file → Executable file
@@ -1,24 +1,54 @@
|
|||||||
class HomeController < ApplicationController
|
class HomeController < ApplicationController
|
||||||
|
layout 'resources'
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@num_certs = UserCertification.count
|
# Alerts
|
||||||
@recent_certs = UserCertification.where("created_at > ?", DateTime.now - 7.days).count
|
if user_signed_in? && current_user.orientation.blank? then
|
||||||
@num_users = User.count
|
flash[:alert] = "There's a lot more to see here, but our records show you haven't completed the new member orientation yet. If that's incorrect, please contact a volunteer."
|
||||||
@recent_users = User.where("created_at > ?", DateTime.now - 7.days).count
|
|
||||||
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 = 'f'").count
|
|
||||||
@recent_door_denieds = DoorLog.where("key = 'f' AND created_at > ?", DateTime.now - 7.days).count
|
|
||||||
@num_macs = Mac.count
|
|
||||||
@recent_macs = Mac.where("since > ?", DateTime.now - 1.day).count
|
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.html # index.html.erb
|
|
||||||
end
|
end
|
||||||
end
|
# 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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
0
app/controllers/ipns_controller.rb
Normal file → Executable file
0
app/controllers/ipns_controller.rb
Normal file → Executable file
4
app/controllers/mac_logs_controller.rb
Normal file → Executable file
4
app/controllers/mac_logs_controller.rb
Normal file → Executable file
@@ -4,6 +4,10 @@ before_filter :authenticate_user!
|
|||||||
|
|
||||||
def index
|
def index
|
||||||
@mac_logs = MacLog.desc.limit(1000)
|
@mac_logs = MacLog.desc.limit(1000)
|
||||||
|
@macs = {}
|
||||||
|
@mac_logs.each do |log|
|
||||||
|
@macs.merge!({log.mac => Mac.find_by_mac(log.mac)})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
339
app/controllers/macs_controller.rb
Normal file → Executable file
339
app/controllers/macs_controller.rb
Normal file → Executable file
@@ -1,22 +1,66 @@
|
|||||||
class MacsController < ApplicationController
|
class MacsController < ApplicationController
|
||||||
rescue_from CanCan::AccessDenied do |exception|
|
load_and_authorize_resource :mac, :except => [:index, :create, :history]
|
||||||
today = Date.today
|
#load_and_authorize_resource :user, :through => :mac, :except => [:index, :show, :scan, :import]
|
||||||
event = Date.new(2013,9,1)
|
|
||||||
|
|
||||||
if today == event
|
before_filter :arp_lookup, :only => :new
|
||||||
redirect_to main_app.root_url, :alert => "CryptoParty today; no MAC scanning. Sorry, NSA!"
|
|
||||||
else
|
|
||||||
redirect_to main_app.root_url, :alert => "Nothing to see here!"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
load_and_authorize_resource :mac
|
|
||||||
load_and_authorize_resource :user, :through => :mac, :except => [:index, :show, :scan, :import]
|
|
||||||
|
|
||||||
#require "active_record"
|
#require "active_record"
|
||||||
require "optparse"
|
require "optparse"
|
||||||
#require "rubygems"
|
#require "rubygems"
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
recent_mac_logs_ungrouped = MacLog.last(1000)
|
||||||
|
if recent_mac_logs_ungrouped.present?
|
||||||
|
@mac_time_start_date = recent_mac_logs_ungrouped.first.created_at
|
||||||
|
recent_mac_logs = recent_mac_logs_ungrouped.group_by(&:mac)
|
||||||
|
@mac_times = {}
|
||||||
|
# Go thru each mac
|
||||||
|
recent_mac_logs.each do |mac_log|
|
||||||
|
last_active = nil
|
||||||
|
# And the entries for each mac (mac_log.first is the string, mac_log.last is the array)
|
||||||
|
mac_log.last.each do |entry|
|
||||||
|
# Find an activate followed immediately by a deactivate
|
||||||
|
if entry.action == "activate"
|
||||||
|
last_active = entry
|
||||||
|
else
|
||||||
|
if last_active && entry.action == "deactivate"
|
||||||
|
# Calculate the time difference between the two and append to this mac's total time
|
||||||
|
this_entry = @mac_times[entry.mac]
|
||||||
|
if this_entry
|
||||||
|
this_time = this_entry[:time]
|
||||||
|
else
|
||||||
|
this_time = 0
|
||||||
|
end
|
||||||
|
@mac_times[entry.mac] = {:mac => entry, :time => (entry.created_at - last_active.created_at) + this_time}
|
||||||
|
else
|
||||||
|
# No pair found; discard.
|
||||||
|
last_active = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@mac_times_sorted = @mac_times.sort{|a,b| b.last[:time] <=> a.last[:time] }
|
||||||
|
@most_active_mac = nil
|
||||||
|
@runner_up_mac = nil
|
||||||
|
@mac_times_sorted.each do |mac_time|
|
||||||
|
unless @most_active_mac
|
||||||
|
this_mac = Mac.find_by_mac(mac_time.first)
|
||||||
|
unless this_mac.hidden
|
||||||
|
@most_active_mac = this_mac
|
||||||
|
@most_active = mac_time
|
||||||
|
end
|
||||||
|
else
|
||||||
|
unless @runner_up_mac
|
||||||
|
this_mac = Mac.find_by_mac(mac_time.first)
|
||||||
|
unless this_mac.hidden
|
||||||
|
@runner_up_mac = this_mac
|
||||||
|
@runner_up = mac_time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
#@active_macs = Mac.where(:active => true, :hidden => false)
|
#@active_macs = Mac.where(:active => true, :hidden => false)
|
||||||
#@active_macs += Mac.where(:active => true, :hidden => nil)
|
#@active_macs += Mac.where(:active => true, :hidden => nil)
|
||||||
|
|
||||||
@@ -26,7 +70,7 @@ def index
|
|||||||
elsif user_signed_in? then
|
elsif user_signed_in? then
|
||||||
@active_macs = Mac.where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).includes(:user).order("users.name ASC").group("users.name")
|
@active_macs = Mac.where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).includes(:user).order("users.name ASC").group("users.name")
|
||||||
else
|
else
|
||||||
@active_macs = Mac.select("mac, note, user_id").where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).joins(:user).order("users.name ASC").group("users.name")
|
@active_macs = Mac.select("mac, note, user_id").where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).joins(:user).order("users.name ASC").group("users.name, mac, note, user_id")
|
||||||
end
|
end
|
||||||
|
|
||||||
@hidden_macs = Mac.where("macs.active = ? AND macs.hidden = ?", true, true).order("note ASC")
|
@hidden_macs = Mac.where("macs.active = ? AND macs.hidden = ?", true, true).order("note ASC")
|
||||||
@@ -42,10 +86,52 @@ def index
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def history
|
||||||
|
authorize! :read_details, Mac
|
||||||
|
begin
|
||||||
|
@start_date = DateTime.parse(params[:start])
|
||||||
|
@end_date = DateTime.parse(params[:end])
|
||||||
|
rescue
|
||||||
|
@start_date = DateTime.now - 2.weeks
|
||||||
|
@end_date = DateTime.now
|
||||||
|
end
|
||||||
|
|
||||||
|
@mac_logs_by_hour = MacLog.where("created_at > ? AND created_at < ?", @start_date, @end_date).group_by{|m| m.created_at.beginning_of_hour}
|
||||||
|
@mac_log_graph = []
|
||||||
|
mac_running_balance = 0
|
||||||
|
lowest_balance = 0
|
||||||
|
@mac_logs_by_hour.each do |time, mac_log|
|
||||||
|
mac_log.each do |entry|
|
||||||
|
# Add one computer for activate, subtract one for deactivate
|
||||||
|
if entry.action == "activate"
|
||||||
|
mac_running_balance += 1
|
||||||
|
elsif entry.action == "deactivate"
|
||||||
|
mac_running_balance -= 1
|
||||||
|
end
|
||||||
|
# Keep track of the lowest number in the graph
|
||||||
|
if mac_running_balance < lowest_balance
|
||||||
|
lowest_balance = mac_running_balance
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@mac_log_graph << [time.to_time.to_i*1000,mac_running_balance]
|
||||||
|
end
|
||||||
|
|
||||||
|
if lowest_balance != 0
|
||||||
|
# Subtract a negative balance to raise everything
|
||||||
|
@mac_log_graph = @mac_log_graph.map{ |time,balance| [time, balance - lowest_balance] }
|
||||||
|
end
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.json { render :json => @mac_log_graph }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# GET /macs/1
|
# GET /macs/1
|
||||||
# GET /macs/1.json
|
# GET /macs/1.json
|
||||||
def show
|
def show
|
||||||
@mac = Mac.find(params[:id])
|
@mac = Mac.find(params[:id])
|
||||||
|
@mac_logs = MacLog.where(:mac => @mac.mac)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # show.html.erb
|
format.html # show.html.erb
|
||||||
@@ -80,26 +166,34 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
# POST /macs
|
# POST /macs
|
||||||
# POST /user
|
|
||||||
def create
|
def create
|
||||||
@mac = Mac.new(params[:mac])
|
@mac = Mac.new(params[:mac])
|
||||||
authorize! :update, @mac
|
@existing_mac = Mac.find_by_mac(@mac.mac)
|
||||||
|
if can? :manage, Mac then
|
||||||
|
@users = User.accessible_by(current_ability).sort_by(&:name)
|
||||||
|
else
|
||||||
|
@users = [current_user]
|
||||||
|
end
|
||||||
|
|
||||||
if can? :manage, Mac then
|
if @existing_mac.present?
|
||||||
@users = User.accessible_by(current_ability).sort_by(&:name)
|
if @existing_mac.user_id.nil?
|
||||||
else
|
redirect_to edit_mac_path(@existing_mac), :notice => 'This MAC already exists, edit it here:'
|
||||||
@users = [current_user]
|
else
|
||||||
end
|
@mac.errors.add(:user,"for this MAC is already set to #{@existing_mac.user.name} -- please contact them or an admin if this is incorrect.")
|
||||||
|
render :action => "new"
|
||||||
respond_to do |format|
|
end
|
||||||
if @mac.save
|
|
||||||
format.html { redirect_to macs_path, :notice => 'Mac was successfully created.' }
|
|
||||||
format.json { render :json => @mac, :status => :created, :location => @mac }
|
|
||||||
else
|
else
|
||||||
format.html { render :action => "new" }
|
|
||||||
format.json { render :json => @mac.errors, :status => :unprocessable_entity }
|
respond_to do |format|
|
||||||
|
if @mac.save
|
||||||
|
format.html { redirect_to macs_path, :notice => 'MAC was successfully created.' }
|
||||||
|
format.json { render :json => @mac, :status => :created, :location => @mac }
|
||||||
|
else
|
||||||
|
format.html { render :action => "new" }
|
||||||
|
format.json { render :json => @mac.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# PUT /macs/1
|
# PUT /macs/1
|
||||||
@@ -119,7 +213,7 @@ end
|
|||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @mac.save
|
if @mac.save
|
||||||
format.html { redirect_to macs_path, :notice => 'Mac was successfully updated.' }
|
format.html { redirect_to macs_path, :notice => 'MAC was successfully updated.' }
|
||||||
format.json { head :no_content }
|
format.json { head :no_content }
|
||||||
else
|
else
|
||||||
format.html { render :action => "edit" }
|
format.html { render :action => "edit" }
|
||||||
@@ -128,115 +222,122 @@ end
|
|||||||
end
|
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
|
def scan
|
||||||
Rails.logger.info "starting scan..."
|
Rails.logger.info "starting scan..."
|
||||||
# Command line arguments
|
# Command line arguments
|
||||||
options = {};
|
options = {};
|
||||||
OptionParser.new { |opts|
|
OptionParser.new { |opts|
|
||||||
opts.banner = "Usage: pamela-scanner.rb --interface=en0"
|
opts.banner = "Usage: pamela-scanner.rb --interface=en0"
|
||||||
|
|
||||||
options[:verbose] = true
|
options[:verbose] = true
|
||||||
opts.on("v", "--verbose", "Run verbosely") { |verbose|
|
opts.on("v", "--verbose", "Run verbosely") { |verbose|
|
||||||
options[:verbose] = verbose
|
options[:verbose] = verbose
|
||||||
}
|
}
|
||||||
|
|
||||||
options[:interface] = "eth0"
|
options[:interface] = "eth0"
|
||||||
opts.on("i", "--interface=interface", "Network Interface") { |interface|
|
opts.on("i", "--interface=interface", "Network Interface") { |interface|
|
||||||
options[:interface] = interface
|
options[:interface] = interface
|
||||||
}
|
}
|
||||||
|
|
||||||
options[:max_age] = 20
|
options[:max_age] = 20
|
||||||
opts.on("a", "--max-age=minutes", "Minutes to keep expired macs active") { |max_age|
|
opts.on("a", "--max-age=minutes", "Minutes to keep expired macs active") { |max_age|
|
||||||
options[:max_age] = max_age.to_i
|
options[:max_age] = max_age.to_i
|
||||||
}
|
}
|
||||||
|
|
||||||
options[:db_host] = "configure_me"
|
options[:db_host] = "configure_me"
|
||||||
opts.on("r", "--db-host=host", "Database Host") { |host|
|
opts.on("r", "--db-host=host", "Database Host") { |host|
|
||||||
options[:db_host] = host
|
options[:db_host] = host
|
||||||
}
|
}
|
||||||
|
|
||||||
options[:db_name] = "configure_me"
|
options[:db_name] = "configure_me"
|
||||||
opts.on("n", "--db-name=name", "Database Name") { |name|
|
opts.on("n", "--db-name=name", "Database Name") { |name|
|
||||||
options[:db_name] = name
|
options[:db_name] = name
|
||||||
}
|
}
|
||||||
|
|
||||||
options[:db_user] = "configure_me"
|
options[:db_user] = "configure_me"
|
||||||
opts.on("u", "--db-user=user", "Database User") { |user|
|
opts.on("u", "--db-user=user", "Database User") { |user|
|
||||||
options[:db_user] = user
|
options[:db_user] = user
|
||||||
}
|
}
|
||||||
|
|
||||||
options[:db_password] = "configure_me"
|
options[:db_password] = "configure_me"
|
||||||
opts.on("p", "--db-password=password", "Database Password") { |password|
|
opts.on("p", "--db-password=password", "Database Password") { |password|
|
||||||
options[:db_password] = password
|
options[:db_password] = password
|
||||||
}
|
}
|
||||||
|
|
||||||
}.parse!
|
}.parse!
|
||||||
|
|
||||||
# Open the database
|
# Open the database
|
||||||
#ActiveRecord::Base::establish_connection(
|
#ActiveRecord::Base::establish_connection(
|
||||||
# :adapter => "mysql2",
|
# :adapter => "mysql2",
|
||||||
# :host => options[:db_host],
|
# :host => options[:db_host],
|
||||||
# :database => options[:db_name],
|
# :database => options[:db_name],
|
||||||
# :username => options[:db_user],
|
# :username => options[:db_user],
|
||||||
# :password => options[:db_password])
|
# :password => options[:db_password])
|
||||||
|
|
||||||
#class Mac < ActiveRecord::Base
|
#class Mac < ActiveRecord::Base
|
||||||
#end
|
#end
|
||||||
|
|
||||||
#class MacLog < ActiveRecord::Base
|
#class MacLog < ActiveRecord::Base
|
||||||
#end
|
#end
|
||||||
|
|
||||||
# Scan the network for mac addresses
|
# Scan the network for mac addresses
|
||||||
macs = {};
|
macs = {};
|
||||||
command = sprintf("arp-scan -R --interface=%s --localnet", options[:interface])
|
command = sprintf("arp-scan -R --interface=%s --localnet", options[:interface])
|
||||||
if options[:verbose]
|
if options[:verbose]
|
||||||
Rails.logger.info "Running [#{command}]"
|
Rails.logger.info "Running [#{command}]"
|
||||||
end
|
end
|
||||||
IO.popen(command) { |stdin|
|
IO.popen(command) { |stdin|
|
||||||
Rails.logger.info "Reading stdin: "+stdin.inspect
|
result = stdin.read()
|
||||||
stdin.each { |line|
|
result.lines.each { |line|
|
||||||
next if line !~ /^([\d\.]+)\s+([[:xdigit:]:]+)\s/;
|
Rails.logger.info "Reading stdin: "+line.inspect
|
||||||
macs[$2] = $1;
|
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
|
# Scan the existing macs and update each record as necessary
|
||||||
Mac.find(:all).each { |entry|
|
Mac.find(:all).each { |entry|
|
||||||
mac = entry.mac.downcase
|
mac = entry.mac.downcase
|
||||||
ip = entry.ip
|
ip = entry.ip
|
||||||
if macs.has_key?(mac)
|
if macs.has_key?(mac) # if our scan shows this mac
|
||||||
if ! entry.active || ! entry.since
|
if ! entry.active || ! entry.since
|
||||||
Rails.logger.info "Activating #{mac} at #{ip}" if options[:verbose]
|
Rails.logger.info "Activating #{mac} at #{ip}" if options[:verbose]
|
||||||
entry.since = Time.now
|
entry.since = Time.now
|
||||||
MacLog.new(:mac => mac, :ip => ip, :action => "activate").save
|
MacLog.new(:mac => mac, :ip => ip, :action => "activate").save
|
||||||
end
|
end
|
||||||
entry.active = 1
|
entry.active = 1
|
||||||
entry.ip = ip
|
entry.ip = ip
|
||||||
entry.refreshed = Time.now
|
entry.refreshed = Time.now
|
||||||
entry.save
|
entry.save
|
||||||
macs.delete(mac)
|
macs.delete(mac)
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
# Entry is no longer current
|
# Entry is no longer current
|
||||||
if entry.active
|
if entry.active
|
||||||
ageMinutes = ((Time.now - entry.refreshed)/60).to_i
|
ageMinutes = ((Time.now - entry.refreshed)/60).to_i
|
||||||
next if ageMinutes < options[:max_age]
|
next if ageMinutes < options[:max_age]
|
||||||
Rails.logger.info "Deactivating #{mac}, #{ageMinutes} minutes old" if options[:verbose]
|
Rails.logger.info "Deactivating #{mac}, #{ageMinutes} minutes old" if options[:verbose]
|
||||||
entry.active = 0
|
entry.active = 0
|
||||||
entry.save
|
entry.save
|
||||||
MacLog.new(:mac => mac, :ip => ip, :action => "deactivate").save
|
MacLog.new(:mac => mac, :ip => ip, :action => "deactivate").save
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add entries for any macs not already in the db
|
# Add entries for any macs not already in the db
|
||||||
macs.each { |mac, ip|
|
macs.each { |mac, ip|
|
||||||
Rails.logger.info "Activating new entry #{mac} at #{ip}" if options[:verbose]
|
Rails.logger.info "Activating new entry #{mac} at #{ip}" if options[:verbose]
|
||||||
Mac.new(:mac => mac, :ip => ip, :active => 1, :since => Time.now, :refreshed => Time.now).save
|
Mac.new(:mac => mac, :ip => ip, :active => 1, :since => Time.now, :refreshed => Time.now).save
|
||||||
Rails.logger.info MacLog.new(:mac => mac, :ip => ip, :action => "activate").save
|
Rails.logger.info MacLog.new(:mac => mac, :ip => ip, :action => "activate").save
|
||||||
}
|
}
|
||||||
|
|
||||||
@log = MacLog.all
|
@log = MacLog.all
|
||||||
|
|
||||||
|
|||||||
57
app/controllers/payments_controller.rb
Normal file → Executable file
57
app/controllers/payments_controller.rb
Normal file → Executable file
@@ -1,6 +1,6 @@
|
|||||||
class PaymentsController < ApplicationController
|
class PaymentsController < ApplicationController
|
||||||
load_and_authorize_resource :payment
|
load_and_authorize_resource :payment
|
||||||
load_and_authorize_resource :user, :through => :payment
|
#load_and_authorize_resource :user, :through => :payment
|
||||||
before_filter :authenticate_user!
|
before_filter :authenticate_user!
|
||||||
|
|
||||||
# Load users and certs based on current ability
|
# Load users and certs based on current ability
|
||||||
@@ -16,6 +16,10 @@ class PaymentsController < ApplicationController
|
|||||||
# GET /payments.json
|
# GET /payments.json
|
||||||
def index
|
def index
|
||||||
@payments = @payments.order("date DESC")
|
@payments = @payments.order("date DESC")
|
||||||
|
@graph = { :members => chart("members"),
|
||||||
|
:total => chart("total"),
|
||||||
|
:basic => chart("basic"),
|
||||||
|
:associate => chart("associate")}
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # index.html.erb
|
format.html # index.html.erb
|
||||||
@@ -23,6 +27,57 @@ class PaymentsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Private method for index charts
|
||||||
|
def chart name
|
||||||
|
chart_name = name || "total"
|
||||||
|
if chart_name == "total"
|
||||||
|
chart_type = [25, 50, 100]
|
||||||
|
elsif chart_name == "members"
|
||||||
|
chart_type = [25, 50, 100]
|
||||||
|
elsif chart_name == "basic"
|
||||||
|
chart_type = [50]
|
||||||
|
elsif chart_name == "associate"
|
||||||
|
chart_type = [25]
|
||||||
|
else
|
||||||
|
chart_type = []
|
||||||
|
end
|
||||||
|
|
||||||
|
payment_months = @payments.sort_by(&:date).group_by{ |p| p.date.beginning_of_month }
|
||||||
|
@payments_by_month = []
|
||||||
|
payment_months.each do |month|
|
||||||
|
# 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
|
||||||
# GET /payments/1.json
|
# GET /payments/1.json
|
||||||
def show
|
def show
|
||||||
|
|||||||
28
app/controllers/paypal_csvs_controller.rb
Executable file
28
app/controllers/paypal_csvs_controller.rb
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
class PaypalCsvsController < ApplicationController
|
||||||
|
load_and_authorize_resource :paypal_csv
|
||||||
|
before_filter :authenticate_user!
|
||||||
|
|
||||||
|
def index
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
PaypalCsv.batch_import_from_csv(params[:file].path)
|
||||||
|
redirect_to paypal_csvs_path, :notice => 'Paypal CSV batch was successfully loaded.'
|
||||||
|
end
|
||||||
|
|
||||||
|
def link
|
||||||
|
result = @paypal_csv.link_payment
|
||||||
|
if result.first
|
||||||
|
redirect_to paypal_csvs_url, :notice => 'Payment was successfully linked.'
|
||||||
|
else
|
||||||
|
redirect_to paypal_csvs_url, :notice => result.last
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
0
app/controllers/registrations_controller.rb
Normal file → Executable file
0
app/controllers/registrations_controller.rb
Normal file → Executable file
46
app/controllers/resource_categories_controller.rb
Executable file
46
app/controllers/resource_categories_controller.rb
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
class ResourceCategoriesController < ApplicationController
|
||||||
|
load_and_authorize_resource
|
||||||
|
layout 'resources'
|
||||||
|
|
||||||
|
def create
|
||||||
|
authorize! :create, @resource_category
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @resource_category.save
|
||||||
|
format.html { redirect_to resource_categories_path, :notice => "Category was successfully created." }
|
||||||
|
format.json { head :no_content }
|
||||||
|
else
|
||||||
|
format.html { render :action => "new" }
|
||||||
|
format.json { render :json => @resource_category.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
authorize! :update, @resource_category
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @resource_category.update_attributes(params[:resource_category])
|
||||||
|
format.html { redirect_to resource_categories_path, :notice => "Category was successfully updated." }
|
||||||
|
format.json { head :no_content }
|
||||||
|
else
|
||||||
|
format.html { render :action => "edit" }
|
||||||
|
format.json { render :json => @resource_category.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @resource_category.destroy
|
||||||
|
format.html { redirect_to resource_categories_path, :notice => "Category was deleted." }
|
||||||
|
format.json { head :ok }
|
||||||
|
else
|
||||||
|
format.html { redirect_to resource_categories_path, :notice => "Category could not be deleted. #{@resource_category.errors.full_messages.first}." }
|
||||||
|
format.json { render :json => @resource_category.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
63
app/controllers/resources_controller.rb
Executable file
63
app/controllers/resources_controller.rb
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
class ResourcesController < ApplicationController
|
||||||
|
load_and_authorize_resource
|
||||||
|
before_filter :load_users
|
||||||
|
layout 'resources'
|
||||||
|
|
||||||
|
def index
|
||||||
|
@featured_resource = @resources.where("picture_file_name IS NOT NULL").sample
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
# don't get too excited... for some reason this gets set to the current_user
|
||||||
|
@resource.user_id = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@resource.modified_by = current_user.id # log who modified this last
|
||||||
|
authorize! :create, @resource
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @resource.save
|
||||||
|
format.html { redirect_to resource_path(@resource), :notice => "Resource was successfully created." }
|
||||||
|
format.json { head :no_content }
|
||||||
|
else
|
||||||
|
format.html { render :action => "new" }
|
||||||
|
format.json { render :json => @resource.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@resource.modified_by = current_user.id # log who modified this last
|
||||||
|
@resource.assign_attributes(params[:resource])
|
||||||
|
authorize! :update, @resource
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @resource.update_attributes(params[:resource])
|
||||||
|
format.html { redirect_to resource_path(@resource), :notice => "Resource was successfully updated." }
|
||||||
|
format.json { head :no_content }
|
||||||
|
else
|
||||||
|
format.html { render :action => "edit" }
|
||||||
|
format.json { render :json => @resource.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@resource.destroy
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to resources_path, :notice => "Resource was deleted." }
|
||||||
|
format.json { head :ok }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_users
|
||||||
|
if can? :assign_user, Resource then
|
||||||
|
@users = User.accessible_by(current_ability).sort_by(&:name)
|
||||||
|
else
|
||||||
|
@users = [current_user]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
32
app/controllers/settings_controller.rb
Executable file
32
app/controllers/settings_controller.rb
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
class SettingsController < ApplicationController
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
def index
|
||||||
|
@settings = Setting.all
|
||||||
|
@@default_settings.each do |key, value|
|
||||||
|
if @settings[key].blank?
|
||||||
|
@settings[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
value = Setting[params[:id].to_sym]
|
||||||
|
if value.present?
|
||||||
|
@setting = {}
|
||||||
|
@setting[:var] = params[:id]
|
||||||
|
@setting[:value] = value
|
||||||
|
elsif @@default_settings[params[:id].to_sym].present?
|
||||||
|
@setting = {}
|
||||||
|
@setting[:var] = params[:id]
|
||||||
|
@setting[:value] = @@default_settings[params[:id].to_sym]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
Setting[params[:id]] = params[:value]
|
||||||
|
|
||||||
|
redirect_to settings_path
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
131
app/controllers/space_api_controller.rb
Executable file
131
app/controllers/space_api_controller.rb
Executable file
@@ -0,0 +1,131 @@
|
|||||||
|
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
|
||||||
68
app/controllers/statistics_controller.rb
Executable file
68
app/controllers/statistics_controller.rb
Executable file
@@ -0,0 +1,68 @@
|
|||||||
|
class StatisticsController < ApplicationController
|
||||||
|
before_filter :load_and_authorize_user
|
||||||
|
|
||||||
|
def index
|
||||||
|
end
|
||||||
|
|
||||||
|
def door_log
|
||||||
|
# Get own user's door data
|
||||||
|
cards = @user.cards
|
||||||
|
card_hash = {}
|
||||||
|
cards.each{|c| card_hash[c.card_number.to_i(16)%32767] = c.card_number}
|
||||||
|
card_num_Rs = cards.map{|c| c.card_number.to_i(16)%32767}
|
||||||
|
@door_logs = DoorLog.where("data = ?", card_num_Rs).order("created_at ASC")
|
||||||
|
@door_logs.map{|l|
|
||||||
|
l.data = card_hash[l.data.to_i].to_i(16)
|
||||||
|
l.key = DoorLog.key_legend[l.key]
|
||||||
|
}
|
||||||
|
|
||||||
|
@door_log_graph = []
|
||||||
|
@door_logs.where("key = 'G'").group_by{|l| l.created_at.beginning_of_day}.each{|l| @door_log_graph << [l.first.to_time.to_i*1000,l.last.size]}
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.json { render :json => @door_logs }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def mac_log
|
||||||
|
macs = @user.macs.where(:hidden => false).map{|m| m.mac}
|
||||||
|
@mac_logs = MacLog.where(:mac => macs)
|
||||||
|
@mac_log_graph = {}
|
||||||
|
macs.each do |mac|
|
||||||
|
mac_log = MacLog.where(:mac => mac)
|
||||||
|
|
||||||
|
mac_times = []
|
||||||
|
last_active = nil
|
||||||
|
mac_log.each do |entry|
|
||||||
|
# Find an activate followed immediately by a deactivate
|
||||||
|
if entry.action == "activate"
|
||||||
|
last_active = entry
|
||||||
|
else
|
||||||
|
if last_active && entry.action == "deactivate"
|
||||||
|
# Calculate the time difference between the two and append to this mac's total time
|
||||||
|
mac_times << [entry.created_at, ((entry.created_at - last_active.created_at)/60/60)]
|
||||||
|
else
|
||||||
|
# No pair found; discard.
|
||||||
|
last_active = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mac_log_graph = []
|
||||||
|
mac_times.group_by{|m| m.first.beginning_of_day}.each{|m| mac_log_graph << [m.first.to_time.to_i*1000,m.last.map{|n| n.last}.sum.round(2)]}
|
||||||
|
# Store each mac in the hash with its graph
|
||||||
|
@mac_log_graph[mac] = mac_log_graph
|
||||||
|
end
|
||||||
|
#@mac_log_graph = mac_log_grouped.map{|g| [g.first.to_time.to_i*1000, g.last.size] }
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.json { render :json => @mac_logs }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_and_authorize_user
|
||||||
|
@user = current_user
|
||||||
|
authorize! :read, @user
|
||||||
|
end
|
||||||
|
end
|
||||||
4
app/controllers/user_certifications_controller.rb
Normal file → Executable file
4
app/controllers/user_certifications_controller.rb
Normal file → Executable file
@@ -1,7 +1,7 @@
|
|||||||
class UserCertificationsController < ApplicationController
|
class UserCertificationsController < ApplicationController
|
||||||
load_and_authorize_resource :user_certification
|
load_and_authorize_resource :user_certification
|
||||||
load_and_authorize_resource :user, :through => :user_certification
|
#load_and_authorize_resource :user, :through => :user_certification
|
||||||
load_and_authorize_resource :certification, :through => :user_certification
|
#load_and_authorize_resource :certification, :through => :user_certification
|
||||||
before_filter :authenticate_user!
|
before_filter :authenticate_user!
|
||||||
|
|
||||||
# Load users and certs based on current ability
|
# Load users and certs based on current ability
|
||||||
|
|||||||
112
app/controllers/users_controller.rb
Normal file → Executable file
112
app/controllers/users_controller.rb
Normal file → Executable file
@@ -1,38 +1,81 @@
|
|||||||
class UsersController < ApplicationController
|
class UsersController < ApplicationController
|
||||||
load_and_authorize_resource
|
load_and_authorize_resource
|
||||||
before_filter :authenticate_user!
|
before_filter :authenticate_user!
|
||||||
|
layout 'resources'
|
||||||
|
|
||||||
|
def sort_by_cert(certs,id)
|
||||||
|
result = 0
|
||||||
|
certs.each do |c|
|
||||||
|
if c.id == id
|
||||||
|
result = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
# GET /users
|
# GET /users
|
||||||
# GET /users.json
|
# GET /users.json
|
||||||
def index
|
def index
|
||||||
case params[:sort]
|
unless params[:full] # by default, show summary
|
||||||
when "name"
|
|
||||||
@users = @users.sort_by(&:name)
|
@users = @users.active.sort_by{|u| [-u.member_level, u.name.downcase] }
|
||||||
when "certifications"
|
#@users.paying + @users.volunteer
|
||||||
@users = @users.sort_by{ |u| [-u.certifications.count,u.name] }
|
#.joins(:payments).where("payments.date > ? OR ", (DateTime.now - 60.days)).uniq
|
||||||
when "orientation"
|
|
||||||
@users = @users.sort_by{ |u| [-u.orientation.to_i,u.name] }
|
respond_to do |format|
|
||||||
when "waiver"
|
format.html { render 'summary', layout: 'resources' }
|
||||||
@users = @users.sort_by{ |u| [-u.waiver.to_i,u.name] }
|
format.json { render :json => @users }
|
||||||
when "member"
|
end
|
||||||
@users = @users.sort_by{ |u| [-u.member_status.to_i,u.name] }
|
|
||||||
when "card"
|
else # show full
|
||||||
@users = @users.sort_by{ |u| [-u.cards.count,u.name] }
|
|
||||||
when "instructor"
|
case params[:sort]
|
||||||
@users = @users.sort{ |a,b| [b.instructor.to_s,a.name] <=> [a.instructor.to_s,b.name] }
|
when "name"
|
||||||
when "admin"
|
@users = @users.sort_by{ |u| u.name.downcase }
|
||||||
@users = @users.sort{ |a,b| [b.admin.to_s,a.name] <=> [a.admin.to_s,b.name] }
|
when "cert"
|
||||||
else
|
@users = @users.sort_by{ |u| [-sort_by_cert(u.certifications,params[:cert].to_i),u.name.downcase] }
|
||||||
@users = @users.sort_by(&:name)
|
when "orientation"
|
||||||
end
|
@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
|
||||||
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # index.html.erb
|
format.html # index.html.erb
|
||||||
format.json { render :json => @users }
|
format.json { render :json => @users }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# 'Active' users who haven't paid recently
|
||||||
|
def inactive
|
||||||
|
@users = @users.all.select{|u| u if u.payment_status == false }.sort_by{ |u| -u.delinquency }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Recent user activity
|
||||||
|
def activity
|
||||||
|
@zombie_members = User.where('sign_in_count = 0').where('member_level > 1')
|
||||||
|
@user_logins = User.where(:current_sign_in_at => 2.months.ago..Time.now).where('sign_in_count > 1')
|
||||||
|
@new_users = User.where(:created_at => 3.months.ago..Date.today)
|
||||||
|
@cardless_users = User.includes('cards').where(['users.member_level >= ?','50']).where('cards.id IS NULL')
|
||||||
|
end
|
||||||
|
|
||||||
|
# New members (for emailing out)
|
||||||
|
def new_member_report
|
||||||
|
@new_users = User.where(:created_at => 3.months.ago..Date.today).where(:hidden => false).where(['member_level >= ?','1'])
|
||||||
|
end
|
||||||
|
|
||||||
# GET /users/1
|
# GET /users/1
|
||||||
# GET /users/1.json
|
# GET /users/1.json
|
||||||
def show
|
def show
|
||||||
@@ -43,6 +86,22 @@ class UsersController < ApplicationController
|
|||||||
end
|
end
|
||||||
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
|
# GET /user_summary/1
|
||||||
def user_summary
|
def user_summary
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
@@ -67,6 +126,9 @@ class UsersController < ApplicationController
|
|||||||
# POST /users
|
# POST /users
|
||||||
# POST /users.json
|
# POST /users.json
|
||||||
def create
|
def create
|
||||||
|
# update oriented_by only if orientation has been set
|
||||||
|
@user.oriented_by_id = current_user.id unless @user.orientation.blank?
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @user.save
|
if @user.save
|
||||||
format.html { redirect_to @user, :notice => 'User was successfully created.' }
|
format.html { redirect_to @user, :notice => 'User was successfully created.' }
|
||||||
@@ -81,6 +143,10 @@ class UsersController < ApplicationController
|
|||||||
# PUT /users/1
|
# PUT /users/1
|
||||||
# PUT /users/1.json
|
# PUT /users/1.json
|
||||||
def update
|
def update
|
||||||
|
# update oriented_by only if it's blank but the (new) orientation isn't blank
|
||||||
|
# gotta test the params because they don't get applied til below.
|
||||||
|
@user.oriented_by_id = current_user.id if @user.oriented_by.blank? && (!params[:user]["orientation(1i)"].blank?)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @user.update_attributes(params[:user])
|
if @user.update_attributes(params[:user])
|
||||||
format.html { redirect_to @user, :notice => 'User was successfully updated.' }
|
format.html { redirect_to @user, :notice => 'User was successfully updated.' }
|
||||||
|
|||||||
27
app/helpers/application_helper.rb
Normal file → Executable file
27
app/helpers/application_helper.rb
Normal file → Executable file
@@ -1,4 +1,31 @@
|
|||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
@payment_methods = [[nil],["PayPal"],["Dwolla"],["Bill Pay"],["Check"],["Cash"],["Other"]]
|
@payment_methods = [[nil],["PayPal"],["Dwolla"],["Bill Pay"],["Check"],["Cash"],["Other"]]
|
||||||
@payment_instructions = {nil => nil, :paypal => "Set up a monthly recurring payment to hslfinances@gmail.com", :dwolla => "Set up a monthly recurring payment to hslfinances@gmail.com", :billpay => "Have your bank send a monthly check to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201", :check => "Mail to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201 OR put in the drop safe at the Lab with a deposit slip firmly attached each month.", :cash => "Put in the drop safe at the Lab with a deposit slip firmly attached each month.", :other => "Hmm... talk to a Treasurer!"}
|
@payment_instructions = {nil => nil, :paypal => "Set up a monthly recurring payment to hslfinances@gmail.com", :dwolla => "Set up a monthly recurring payment to hslfinances@gmail.com", :billpay => "Have your bank send a monthly check to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201", :check => "Mail to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201 OR put in the drop safe at the Lab with a deposit slip firmly attached each month.", :cash => "Put in the drop safe at the Lab with a deposit slip firmly attached each month.", :other => "Hmm... talk to a Treasurer!"}
|
||||||
|
|
||||||
|
def sort_link(title, column, options = {})
|
||||||
|
condition = options[:unless] if options.has_key?(:unless)
|
||||||
|
sort_dir = params[:dir] == 'up' ? 'down' : 'up'
|
||||||
|
link_to_unless condition, title, request.parameters.merge( {:sort => column, :dir => sort_dir} )
|
||||||
|
end
|
||||||
|
|
||||||
|
def li_link_to(name = nil, options = nil, html_options = nil, &block)
|
||||||
|
html_options, options, name = options, name, block if block_given?
|
||||||
|
options ||= {}
|
||||||
|
|
||||||
|
html_options = convert_options_to_data_attributes(options, html_options)
|
||||||
|
|
||||||
|
url = url_for(options)
|
||||||
|
html_options['href'] ||= url
|
||||||
|
|
||||||
|
if current_page?(url)
|
||||||
|
content_tag(:li, class: "active") do
|
||||||
|
content_tag(:a, name || url, html_options, &block)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
content_tag(:li) do
|
||||||
|
content_tag(:a, name || url, html_options, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
0
app/helpers/certifications_helper.rb
Normal file → Executable file
0
app/helpers/certifications_helper.rb
Normal file → Executable file
0
app/helpers/door_logs_helper.rb
Normal file → Executable file
0
app/helpers/door_logs_helper.rb
Normal file → Executable file
0
app/helpers/home_helper.rb
Normal file → Executable file
0
app/helpers/home_helper.rb
Normal file → Executable file
0
app/helpers/ipn_helper.rb
Normal file → Executable file
0
app/helpers/ipn_helper.rb
Normal file → Executable file
0
app/helpers/mac_logs_helper.rb
Normal file → Executable file
0
app/helpers/mac_logs_helper.rb
Normal file → Executable file
0
app/helpers/pamela_helper.rb
Normal file → Executable file
0
app/helpers/pamela_helper.rb
Normal file → Executable file
0
app/helpers/payments_helper.rb
Normal file → Executable file
0
app/helpers/payments_helper.rb
Normal file → Executable file
0
app/helpers/user_certifications_helper.rb
Normal file → Executable file
0
app/helpers/user_certifications_helper.rb
Normal file → Executable file
0
app/helpers/users_helper.rb
Normal file → Executable file
0
app/helpers/users_helper.rb
Normal file → Executable file
0
app/mailers/.gitkeep
Normal file → Executable file
0
app/mailers/.gitkeep
Normal file → Executable file
12
app/mailers/door_mailer.rb
Executable file
12
app/mailers/door_mailer.rb
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
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
|
||||||
17
app/mailers/user_mailer.rb
Normal file → Executable file
17
app/mailers/user_mailer.rb
Normal file → Executable file
@@ -1,13 +1,20 @@
|
|||||||
class UserMailer < ActionMailer::Base
|
class UserMailer < ActionMailer::Base
|
||||||
default :from => "wiki@heatsynclabs.org"
|
default :from => "no-reply@heatsynclabs.org"
|
||||||
|
|
||||||
def new_user_email(user)
|
def new_user_email(user)
|
||||||
@user = user
|
@user = user
|
||||||
@url = "http://members.heatsynclabs.org"
|
@url = "http://members.heatsynclabs.org"
|
||||||
|
|
||||||
#@admins = User.where(:name => "Will Bradley")
|
mail(:to => 'member-notifications@heatsynclabs.org',
|
||||||
#@admins.each do |admin|
|
:subject => "New HSL Member: "+user.name)
|
||||||
mail(:to => 'info@heatsynclabs.org', :subject => "New HSL Member: "+user.name)
|
end
|
||||||
#end
|
|
||||||
|
def email(to_user,from_user,subject,body)
|
||||||
|
@url = "http://members.heatsynclabs.org"
|
||||||
|
@body = body
|
||||||
|
@from_user = from_user
|
||||||
|
|
||||||
|
mail(:to => to_user.email,
|
||||||
|
:subject => "HSL Message: "+subject)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
0
app/models/.gitkeep
Normal file → Executable file
0
app/models/.gitkeep
Normal file → Executable file
35
app/models/ability.rb
Normal file → Executable file
35
app/models/ability.rb
Normal file → Executable file
@@ -2,25 +2,30 @@ class Ability
|
|||||||
include CanCan::Ability
|
include CanCan::Ability
|
||||||
|
|
||||||
def initialize(user)
|
def initialize(user)
|
||||||
# Anonymous can read mac
|
can :read, Mac # Anonymous can read mac
|
||||||
today = Date.today
|
can :scan, Mac # Need anonymous so CRON can scan
|
||||||
event = Date.new(2013,9,1)
|
can :read, Resource
|
||||||
|
can :read, ResourceCategory
|
||||||
unless today == event
|
|
||||||
can :read, Mac
|
|
||||||
can :scan, Mac # Need anonymous so CRON can scan
|
|
||||||
end
|
|
||||||
|
|
||||||
if !user.nil?
|
if !user.nil?
|
||||||
|
|
||||||
# By default, users can only see their own stuff
|
# By default, users can only see their own stuff
|
||||||
can :read, Card, :user_id => user.id
|
can :read, Card, :user_id => user.id
|
||||||
can :read, Certification
|
can :read, Certification
|
||||||
can :read_details, Mac unless today == event
|
can :read_details, Mac
|
||||||
can [:update], Mac, :user_id => nil
|
can [:update], Mac, :user_id => nil
|
||||||
can [:create,:update], Mac, :user_id => user.id
|
can [:create,:update], Mac, :user_id => user.id
|
||||||
can :read, User, :id => user.id #TODO: why can users update themselves?
|
can [:create,:update,:destroy], Resource, :user_id => user.id
|
||||||
|
can :read, Payment, :user_id => user.id
|
||||||
can :read, UserCertification, :user_id => user.id
|
can :read, UserCertification, :user_id => user.id
|
||||||
|
can :read, User, :id => user.id #TODO: why can users update themselves? Maybe because Devise doesn't check users/edit?
|
||||||
|
can :compose_email, User
|
||||||
|
can :send_email, User
|
||||||
|
|
||||||
|
if user.card_access_enabled
|
||||||
|
can :access_doors_remotely, :door_access
|
||||||
|
can :authorize, Card # used for interlock card/certification auth
|
||||||
|
end
|
||||||
|
|
||||||
# Instructors can manage certs and see users
|
# Instructors can manage certs and see users
|
||||||
if user.instructor?
|
if user.instructor?
|
||||||
@@ -31,13 +36,17 @@ class Ability
|
|||||||
end
|
end
|
||||||
# Users can see others' stuff if they've been oriented
|
# Users can see others' stuff if they've been oriented
|
||||||
unless user.orientation.blank?
|
unless user.orientation.blank?
|
||||||
can :read, User, :hidden => [nil,false]
|
can [:read,:new_member_report,:activity], User, :hidden => [nil,false]
|
||||||
can :read, UserCertification
|
can :read, UserCertification
|
||||||
|
can [:create,:update], Resource, :user_id => [nil,user.id]
|
||||||
|
can [:create,:update,:destroy], ResourceCategory
|
||||||
end
|
end
|
||||||
|
|
||||||
# Accountants can manage all
|
# Accountants can manage payments
|
||||||
if user.accountant?
|
if user.accountant?
|
||||||
can :manage, Payment
|
can :manage, Payment
|
||||||
|
can :manage, Ipn
|
||||||
|
can :manage, PaypalCsv
|
||||||
end
|
end
|
||||||
|
|
||||||
# Admins can manage all
|
# Admins can manage all
|
||||||
@@ -45,7 +54,7 @@ class Ability
|
|||||||
can :manage, :all
|
can :manage, :all
|
||||||
end
|
end
|
||||||
|
|
||||||
# Prevent all destruction for now
|
# Prevent most destruction for now
|
||||||
#cannot :destroy, User
|
#cannot :destroy, User
|
||||||
#cannot :destroy, Card
|
#cannot :destroy, Card
|
||||||
cannot :destroy, Certification
|
cannot :destroy, Certification
|
||||||
|
|||||||
50
app/models/card.rb
Normal file → Executable file
50
app/models/card.rb
Normal file → Executable file
@@ -11,21 +11,14 @@ class Card < ActiveRecord::Base
|
|||||||
door_access_url = APP_CONFIG['door_access_url']
|
door_access_url = APP_CONFIG['door_access_url']
|
||||||
door_access_password = APP_CONFIG['door_access_password']
|
door_access_password = APP_CONFIG['door_access_password']
|
||||||
|
|
||||||
# connect to door access system
|
cardid = self.id.to_s.rjust(3, '0')
|
||||||
source = open("#{door_access_url}?e=#{door_access_password}").read
|
|
||||||
results = source.scan(/ok/)
|
|
||||||
if(results.size > 0) then
|
|
||||||
#only continue if we've got an OK login
|
|
||||||
cardid = self.id.to_s.rjust(3, '0') #TODO: provide ability for
|
|
||||||
cardperm = self.card_permissions.to_s.rjust(3, '0')
|
cardperm = self.card_permissions.to_s.rjust(3, '0')
|
||||||
cardnum = self.card_number.rjust(8, '0')
|
cardnum = self.card_number.rjust(8, '0')
|
||||||
|
|
||||||
source = open("#{door_access_url}?m#{cardid}&p#{cardperm}&t#{cardnum}").read
|
# login and send the command all in one go (auto-logout is a feature of the arduino when used this way)
|
||||||
|
source = open("#{door_access_url}?m#{cardid}&p#{cardperm}&t#{cardnum}&e=#{door_access_password}").read
|
||||||
results = source.scan(/cur/)
|
results = source.scan(/cur/)
|
||||||
|
|
||||||
#logout
|
|
||||||
open("#{door_access_url}?e=0000")
|
|
||||||
|
|
||||||
if(results.size > 0) then
|
if(results.size > 0) then
|
||||||
#only return true if we got some kind of decent response
|
#only return true if we got some kind of decent response
|
||||||
return true
|
return true
|
||||||
@@ -33,10 +26,6 @@ class Card < ActiveRecord::Base
|
|||||||
# We didn't get a decent response.
|
# We didn't get a decent response.
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
else
|
|
||||||
# We didn't get an OK login.
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.upload_all_to_door
|
def self.upload_all_to_door
|
||||||
@@ -47,30 +36,21 @@ class Card < ActiveRecord::Base
|
|||||||
door_access_url = APP_CONFIG['door_access_url']
|
door_access_url = APP_CONFIG['door_access_url']
|
||||||
door_access_password = APP_CONFIG['door_access_password']
|
door_access_password = APP_CONFIG['door_access_password']
|
||||||
|
|
||||||
source = open("#{door_access_url}?e=#{door_access_password}").read
|
@cards.each do |u|
|
||||||
results = source.scan(/ok/)
|
cardid = u.id.to_s.rjust(3, '0')
|
||||||
if(results.size > 0) then
|
cardperm = u.card_permissions.to_s.rjust(3, '0')
|
||||||
@cards.each do |u|
|
cardnum = u.card_number.rjust(8, '0')
|
||||||
#only continue if we've got an OK login
|
|
||||||
cardid = u.id.to_s.rjust(3, '0')
|
|
||||||
cardperm = u.card_permissions.to_s.rjust(3, '0')
|
|
||||||
cardnum = u.card_number.rjust(8, '0')
|
|
||||||
|
|
||||||
source = open("#{door_access_url}?m#{cardid}&p#{cardperm}&t#{cardnum}").read
|
# login and send the command all in one go (auto-logout is a feature of the arduino when used this way)
|
||||||
results = source.scan(/cur/)
|
source = open("#{door_access_url}?m#{cardid}&p#{cardperm}&t#{cardnum}&e=#{door_access_password}").read
|
||||||
|
results = source.scan(/cur/)
|
||||||
|
|
||||||
if(results.size > 0) then
|
if(results.size > 0) then
|
||||||
#only return true if we got some kind of decent response
|
#only return true if we got some kind of decent response
|
||||||
@end_results.push([cardid,"OK"])
|
@end_results.push([cardid,"OK"])
|
||||||
else
|
else
|
||||||
@end_results.push([cardid,"FAIL"])
|
@end_results.push([cardid,"FAIL"])
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#logout
|
|
||||||
open("#{door_access_url}?e=0000")
|
|
||||||
else
|
|
||||||
@end_results.push([cardid,"FAIL"])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return @end_results
|
return @end_results
|
||||||
|
|||||||
4
app/models/certification.rb
Normal file → Executable file
4
app/models/certification.rb
Normal file → Executable file
@@ -1,5 +1,7 @@
|
|||||||
class Certification < ActiveRecord::Base
|
class Certification < ActiveRecord::Base
|
||||||
attr_accessible :description, :name
|
attr_accessible :description, :name, :slug
|
||||||
has_many :user_certifications
|
has_many :user_certifications
|
||||||
has_many :users, :through => :user_certifications
|
has_many :users, :through => :user_certifications
|
||||||
|
|
||||||
|
validates_presence_of :name, :slug
|
||||||
end
|
end
|
||||||
|
|||||||
24
app/models/contract.rb
Normal file
24
app/models/contract.rb
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
class Contract < ActiveRecord::Base
|
||||||
|
belongs_to :user
|
||||||
|
belongs_to :created_by, :foreign_key => "created_by_id", :class_name => "User"
|
||||||
|
attr_accessible :user_id, :first_name, :last_name, :cosigner,
|
||||||
|
:signed_at, :document, :document_file_name, :document_content_type,
|
||||||
|
:document_file_size, :document_updated_at
|
||||||
|
# :created_by not accessible for security purposes
|
||||||
|
|
||||||
|
validates_presence_of :first_name, :signed_at #, :last_name
|
||||||
|
|
||||||
|
has_attached_file :document,
|
||||||
|
{ :styles =>
|
||||||
|
{
|
||||||
|
:medium => "300x300>",
|
||||||
|
:large => "900x900>"
|
||||||
|
},
|
||||||
|
:storage => :s3,
|
||||||
|
:s3_protocol => :https,
|
||||||
|
:s3_credentials => { :access_key_id => ENV['S3_KEY'],
|
||||||
|
:secret_access_key => ENV['S3_SECRET'] },
|
||||||
|
:path => ":attachment/:id/:style.:extension",
|
||||||
|
:bucket => ENV['S3_BUCKET']
|
||||||
|
}
|
||||||
|
end
|
||||||
124
app/models/door_log.rb
Normal file → Executable file
124
app/models/door_log.rb
Normal file → Executable file
@@ -2,6 +2,126 @@ class DoorLog < ActiveRecord::Base
|
|||||||
attr_accessible :data, :key
|
attr_accessible :data, :key
|
||||||
require 'open-uri'
|
require 'open-uri'
|
||||||
|
|
||||||
|
def self.execute_command(command)
|
||||||
|
output = ""
|
||||||
|
# load config values
|
||||||
|
door_access_url = APP_CONFIG['door_access_url']
|
||||||
|
door_access_password = APP_CONFIG['door_access_password']
|
||||||
|
|
||||||
|
#login
|
||||||
|
source = open("#{door_access_url}?e=#{door_access_password}").read
|
||||||
|
results = source.scan(/ok/)
|
||||||
|
|
||||||
|
#only continue if we've got an OK login
|
||||||
|
if(results.size > 0) then
|
||||||
|
# Parse the command and result
|
||||||
|
parsing = parse_command(command)
|
||||||
|
output += parsing[:output]
|
||||||
|
url_param = parsing[:url_param]
|
||||||
|
|
||||||
|
# Execute the command
|
||||||
|
open("#{door_access_url}?#{url_param}")
|
||||||
|
|
||||||
|
self.download_status # Update the status cache
|
||||||
|
else
|
||||||
|
output += 'Failed to connect to door system.'
|
||||||
|
end
|
||||||
|
|
||||||
|
#logout
|
||||||
|
open("#{door_access_url}?e=0000")
|
||||||
|
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_command(command)
|
||||||
|
output = ""
|
||||||
|
url_param = ""
|
||||||
|
# @commands = [
|
||||||
|
# ["Unlock all doors","unlock"],
|
||||||
|
# ["Unlock Front door","unlock-front"],
|
||||||
|
# ["Unlock Rear door","unlock-rear"],
|
||||||
|
# ["Lock all doors","lock"],
|
||||||
|
# ["Lock Front door","lock-front"],
|
||||||
|
# ["Lock Rear door","lock-rear"],
|
||||||
|
# ["Open Front door","open-front"],
|
||||||
|
# ["Open Rear door","open-rear"],
|
||||||
|
# ["Arm alarm","arm"],
|
||||||
|
# ["Disarm alarm","disarm"]
|
||||||
|
# ]
|
||||||
|
case command
|
||||||
|
when "open-front"
|
||||||
|
output += "Front door opened."
|
||||||
|
url_param = "o1"
|
||||||
|
when "open-rear"
|
||||||
|
output += "Rear door opened."
|
||||||
|
url_param = "o2"
|
||||||
|
when "u", "unlock"
|
||||||
|
output += "Doors unlocked, remember to re-lock them."
|
||||||
|
url_param = "u"
|
||||||
|
when "u1", "unlock-front"
|
||||||
|
output += "Front Door unlocked, remember to re-lock it."
|
||||||
|
url_param = "u=1"
|
||||||
|
when "u2", "unlock-rear"
|
||||||
|
output += "Rear Door unlocked, remember to re-lock it."
|
||||||
|
url_param = "u=2"
|
||||||
|
when "lock", "l"
|
||||||
|
output += "Doors locked."
|
||||||
|
url_param = "l"
|
||||||
|
when "lock-front", "l1"
|
||||||
|
output += "Front Door locked."
|
||||||
|
url_param = "l=1"
|
||||||
|
when "lock-rear", "l2"
|
||||||
|
output += "Rear Door locked."
|
||||||
|
url_param = "l=2"
|
||||||
|
when "arm"
|
||||||
|
output += "Armed."
|
||||||
|
url_param = "2"
|
||||||
|
when "disarm"
|
||||||
|
output += "Disarmed."
|
||||||
|
url_param = "1"
|
||||||
|
else
|
||||||
|
output += "Fail. Don't be a naughty user!"
|
||||||
|
url_param = "9" # Using 9 because it's just status, no harm done
|
||||||
|
end
|
||||||
|
return {:output => output, :url_param => url_param}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.show_status
|
||||||
|
door_logs = DoorLog.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
|
def self.download_from_door
|
||||||
# load config values
|
# load config values
|
||||||
door_access_url = APP_CONFIG['door_access_url']
|
door_access_url = APP_CONFIG['door_access_url']
|
||||||
@@ -41,4 +161,8 @@ class DoorLog < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.key_legend
|
||||||
|
{'G' => "Granted", "R" => "Read", "D" => "Denied",
|
||||||
|
'g' => "granted", "r" => "read", "d" => "denied"}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
13
app/models/ipn.rb
Normal file → Executable file
13
app/models/ipn.rb
Normal file → Executable file
@@ -5,6 +5,14 @@ class Ipn < ActiveRecord::Base
|
|||||||
|
|
||||||
after_create :create_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)
|
def self.new_from_dynamic_params(params)
|
||||||
ipn = Ipn.new()
|
ipn = Ipn.new()
|
||||||
|
|
||||||
@@ -37,6 +45,7 @@ class Ipn < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
unless response == "VERIFIED"
|
unless response == "VERIFIED"
|
||||||
Rails.logger.error "Invalid IPN: #{response}"
|
Rails.logger.error "Invalid IPN: #{response}"
|
||||||
|
Rails.logger.error "Data: #{self.data}"
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -50,8 +59,8 @@ class Ipn < ActiveRecord::Base
|
|||||||
private
|
private
|
||||||
def create_payment
|
def create_payment
|
||||||
# find user by email, then by payee
|
# find user by email, then by payee
|
||||||
user = User.find_by_email(self.payer_email)
|
user = User.where("lower(email) = ?", self._from_email_address.downcase).first
|
||||||
user = User.find_by_payee(self.payer_email) if user.nil? && self.payer_email.present?
|
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
|
# Only create payments if the IPN matches a member
|
||||||
if user.present?
|
if user.present?
|
||||||
|
|||||||
0
app/models/mac.rb
Normal file → Executable file
0
app/models/mac.rb
Normal file → Executable file
0
app/models/mac_log.rb
Normal file → Executable file
0
app/models/mac_log.rb
Normal file → Executable file
2
app/models/payment.rb
Normal file → Executable file
2
app/models/payment.rb
Normal file → Executable file
@@ -1,12 +1,12 @@
|
|||||||
class Payment < ActiveRecord::Base
|
class Payment < ActiveRecord::Base
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
has_one :ipn
|
has_one :ipn
|
||||||
|
has_one :paypal_csv
|
||||||
attr_accessible :date, :user_id, :created_by, :amount
|
attr_accessible :date, :user_id, :created_by, :amount
|
||||||
|
|
||||||
validates_presence_of :user_id, :date, :amount # not created_by
|
validates_presence_of :user_id, :date, :amount # not created_by
|
||||||
validates_uniqueness_of :date, :scope => :user_id, :message => ' of payment already exists for this user.'
|
validates_uniqueness_of :date, :scope => :user_id, :message => ' of payment already exists for this user.'
|
||||||
|
|
||||||
|
|
||||||
def human_date
|
def human_date
|
||||||
if date.year < DateTime.now.year
|
if date.year < DateTime.now.year
|
||||||
date.strftime("%b %e, %y")
|
date.strftime("%b %e, %y")
|
||||||
|
|||||||
80
app/models/paypal_csv.rb
Executable file
80
app/models/paypal_csv.rb
Executable file
@@ -0,0 +1,80 @@
|
|||||||
|
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
|
||||||
31
app/models/resource.rb
Executable file
31
app/models/resource.rb
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
class Resource < ActiveRecord::Base
|
||||||
|
attr_accessible :supercategory, :user_id, :resource_category_id, :name, :serial, :specs, :status, :donatable,
|
||||||
|
:picture, :picture_file_name, :picture_content_type, :picture_file_size, :picture_updated_at,
|
||||||
|
:picture2, :picture2_file_name, :picture2_content_type, :picture2_file_size, :picture2_updated_at,
|
||||||
|
:picture3, :picture3_file_name, :picture3_content_type, :picture3_file_size, :picture3_updated_at,
|
||||||
|
:picture4, :picture4_file_name, :picture4_content_type, :picture4_file_size, :picture4_updated_at,
|
||||||
|
:notes, :estimated_value, :disposed_at, :modified_by
|
||||||
|
|
||||||
|
belongs_to :owner, :class_name => "ToolshareUser" #TODO: remove owner
|
||||||
|
belongs_to :user
|
||||||
|
belongs_to :resource_category
|
||||||
|
|
||||||
|
PICTURE_OPTIONS = { :styles => { :medium => "300x300>",
|
||||||
|
:thumb => "100x100>",
|
||||||
|
:tiny => "50x50>"},
|
||||||
|
:storage => :s3,
|
||||||
|
:s3_protocol => :https,
|
||||||
|
:s3_credentials => { :access_key_id => ENV['S3_KEY'],
|
||||||
|
:secret_access_key => ENV['S3_SECRET'] },
|
||||||
|
:path => ":attachment/:id/:style.:extension",
|
||||||
|
:bucket => ENV['S3_BUCKET'] }
|
||||||
|
|
||||||
|
has_attached_file :picture, PICTURE_OPTIONS
|
||||||
|
has_attached_file :picture2, PICTURE_OPTIONS
|
||||||
|
has_attached_file :picture3, PICTURE_OPTIONS
|
||||||
|
has_attached_file :picture4, PICTURE_OPTIONS
|
||||||
|
|
||||||
|
def resource_category_name
|
||||||
|
resource_category.name if resource_category
|
||||||
|
end
|
||||||
|
end
|
||||||
13
app/models/resource_category.rb
Executable file
13
app/models/resource_category.rb
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
class ResourceCategory < ActiveRecord::Base
|
||||||
|
has_many :resources
|
||||||
|
attr_accessible :name
|
||||||
|
|
||||||
|
before_destroy :has_resources?
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def has_resources?
|
||||||
|
errors.add(:base, "Cannot delete category with associated resources") unless resources.count == 0
|
||||||
|
errors.blank? #return false, to not destroy the element, otherwise, it will delete.
|
||||||
|
end
|
||||||
|
end
|
||||||
3
app/models/setting.rb
Executable file
3
app/models/setting.rb
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
class Setting < RailsSettings::CachedSettings
|
||||||
|
attr_accessible :var
|
||||||
|
end
|
||||||
4
app/models/toolshare_user.rb
Executable file
4
app/models/toolshare_user.rb
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
class ToolshareUser < ActiveRecord::Base
|
||||||
|
has_many :resources, :foreign_key => "owner_id"
|
||||||
|
attr_accessible :name, :email
|
||||||
|
end
|
||||||
95
app/models/user.rb
Normal file → Executable file
95
app/models/user.rb
Normal file → Executable file
@@ -1,6 +1,6 @@
|
|||||||
class User < ActiveRecord::Base
|
class User < ActiveRecord::Base
|
||||||
include Gravtastic
|
include Gravtastic
|
||||||
gravtastic :size => 120, :default => ""
|
gravtastic :size => 150, :default => ""
|
||||||
|
|
||||||
# Include default devise modules. Others available are:
|
# Include default devise modules. Others available are:
|
||||||
# :token_authenticatable, :confirmable,
|
# :token_authenticatable, :confirmable,
|
||||||
@@ -9,12 +9,24 @@ class User < ActiveRecord::Base
|
|||||||
:recoverable, :rememberable, :trackable, :validatable
|
:recoverable, :rememberable, :trackable, :validatable
|
||||||
|
|
||||||
# Setup accessible (or protected) attributes for your model
|
# Setup accessible (or protected) attributes for your model
|
||||||
attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :admin, :instructor, :member, :emergency_name, :emergency_phone, :current_skills, :desired_skills, :waiver, :emergency_email, :phone, :payment_method, :orientation, :member_level, :certifications, :hidden, :marketing_source, :payee, :accountant #TODO: make admin/instructor/member/etc not accessible
|
attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :admin, :instructor, :member, :emergency_name, :emergency_phone, :current_skills, :desired_skills, :waiver, :emergency_email, :phone, :payment_method, :orientation, :member_level, :certifications, :hidden, :marketing_source, :payee, :accountant, :exit_reason, :twitter_url, :facebook_url, :github_url, :website_url, :email_visible, :phone_visible, :postal_code #TODO: make admin/instructor/member/etc not accessible
|
||||||
|
|
||||||
|
belongs_to :oriented_by, :foreign_key => "oriented_by_id", :class_name => "User"
|
||||||
has_many :cards
|
has_many :cards
|
||||||
has_many :user_certifications
|
has_many :user_certifications
|
||||||
has_many :certifications, :through => :user_certifications
|
has_many :certifications, :through => :user_certifications
|
||||||
|
has_many :contracts
|
||||||
has_many :payments
|
has_many :payments
|
||||||
|
has_many :macs
|
||||||
|
has_many :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
|
after_create :send_new_user_email
|
||||||
|
|
||||||
@@ -45,6 +57,7 @@ class User < ActiveRecord::Base
|
|||||||
user_to_absorb.payments.each {|payment|
|
user_to_absorb.payments.each {|payment|
|
||||||
Rails.logger.info "PAYMENT BEFORE: "+payment.inspect
|
Rails.logger.info "PAYMENT BEFORE: "+payment.inspect
|
||||||
payment.user_id = self.id
|
payment.user_id = self.id
|
||||||
|
payment.amount = 0 if payment.amount.nil? # Bypass validation on amount
|
||||||
payment.save!
|
payment.save!
|
||||||
Rails.logger.info "PAYMENT AFTER: "+payment.inspect
|
Rails.logger.info "PAYMENT AFTER: "+payment.inspect
|
||||||
}
|
}
|
||||||
@@ -52,6 +65,11 @@ class User < ActiveRecord::Base
|
|||||||
user_to_absorb.destroy
|
user_to_absorb.destroy
|
||||||
end
|
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
|
def name_with_email_and_visibility
|
||||||
if hidden then
|
if hidden then
|
||||||
"#{name} (#{email}) HIDDEN"
|
"#{name} (#{email}) HIDDEN"
|
||||||
@@ -89,6 +107,11 @@ class User < ActiveRecord::Base
|
|||||||
{25 => "Associate", 50 => "Basic", 75 => "Basic", 100 => "Plus"}
|
{25 => "Associate", 50 => "Basic", 75 => "Basic", 100 => "Plus"}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def payment_status
|
||||||
|
results = payment_status_calculation
|
||||||
|
return results[:paid]
|
||||||
|
end
|
||||||
|
|
||||||
def member_status
|
def member_status
|
||||||
results = member_status_calculation
|
results = member_status_calculation
|
||||||
return results[:rank]
|
return results[:rank]
|
||||||
@@ -96,10 +119,43 @@ class User < ActiveRecord::Base
|
|||||||
|
|
||||||
def member_status_symbol
|
def member_status_symbol
|
||||||
results = member_status_calculation
|
results = member_status_calculation
|
||||||
return "<img src='/#{results[:icon]}#{results[:flair]}-coin.png' title='#{results[:message]}' />"
|
return "<img src='/#{results[:icon]}#{results[:flair]}-coin.png' title='#{results[:message]}' class='member-status-symbol' />"
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
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
|
||||||
|
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
|
def member_status_calculation
|
||||||
# Begin output buffer
|
# Begin output buffer
|
||||||
@@ -112,7 +168,7 @@ class User < ActiveRecord::Base
|
|||||||
case self.member_level.to_i
|
case self.member_level.to_i
|
||||||
when 0..9
|
when 0..9
|
||||||
if self.payments.count > 0 then
|
if self.payments.count > 0 then
|
||||||
message = "Former Member (#{(DateTime.now - self.payments.last.date).to_i} days ago)"
|
message = "Former Member (#{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago)"
|
||||||
icon = :timeout
|
icon = :timeout
|
||||||
rank = 1
|
rank = 1
|
||||||
else
|
else
|
||||||
@@ -138,29 +194,38 @@ class User < ActiveRecord::Base
|
|||||||
rank = 1000
|
rank = 1000
|
||||||
end
|
end
|
||||||
|
|
||||||
|
payment_results = payment_status_calculation
|
||||||
|
flair = payment_results[:flair]
|
||||||
|
rank = rank/10 unless payment_results[:paid]
|
||||||
|
message = payment_results[:message] unless payment_results[:message].blank?
|
||||||
|
|
||||||
|
return {:message => message, :icon => icon, :flair => flair, :rank => rank}
|
||||||
|
end
|
||||||
|
|
||||||
|
def payment_status_calculation
|
||||||
|
flair = ""
|
||||||
|
message = ""
|
||||||
|
paid = true
|
||||||
|
|
||||||
# Second status item is payment status
|
# Second status item is payment status
|
||||||
case self.member_level.to_i
|
case self.member_level.to_i
|
||||||
when 25..999
|
when 25..999
|
||||||
# There are payments
|
# There are payments
|
||||||
if self.payments.count > 0 then
|
if self.payments.count > 0 then
|
||||||
# They're on time
|
# They're on time
|
||||||
if self.payments.last.date > (DateTime.now - 60.days)
|
if self.payments.maximum(:date) > (DateTime.now - 60.days)
|
||||||
flair = "-paid"
|
flair = "-paid"
|
||||||
|
paid = true
|
||||||
else
|
else
|
||||||
message = "Last Payment #{(DateTime.now - self.payments.last.date).to_i/30} months ago"
|
message = "Last Payment #{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago"
|
||||||
rank = rank/10
|
paid = false
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
message = "No Payments Recorded"
|
message = "No Payments Recorded"
|
||||||
rank = rank/10
|
paid = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return {:message => message, :paid => paid, :flair => flair}
|
||||||
return {:message => message, :icon => icon, :flair => flair, :rank => rank}
|
|
||||||
end
|
|
||||||
|
|
||||||
def send_new_user_email
|
|
||||||
Rails.logger.info UserMailer.new_user_email(self).deliver
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
6
app/models/user_certification.rb
Normal file → Executable file
6
app/models/user_certification.rb
Normal file → Executable file
@@ -8,10 +8,10 @@ class UserCertification < ActiveRecord::Base
|
|||||||
belongs_to :certification
|
belongs_to :certification
|
||||||
|
|
||||||
def user_name
|
def user_name
|
||||||
if self.user.blank?
|
if user.blank?
|
||||||
""
|
return "n/a (user ##{user_id} missing)"
|
||||||
else
|
else
|
||||||
self.user.name
|
return self.user.name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
0
app/views/cards/_form.html.erb
Normal file → Executable file
0
app/views/cards/_form.html.erb
Normal file → Executable file
0
app/views/cards/edit.html.erb
Normal file → Executable file
0
app/views/cards/edit.html.erb
Normal file → Executable file
20
app/views/cards/index.html.erb
Normal file → Executable file
20
app/views/cards/index.html.erb
Normal file → Executable file
@@ -1,13 +1,20 @@
|
|||||||
<h1>Access Cards</h1>
|
<h1>Access Cards</h1>
|
||||||
|
|
||||||
<%= link_to 'New Card', new_card_path if can? :create, Card %>
|
<%= link_to 'New Card', new_card_path, :class => "btn" if can? :create, Card %>
|
||||||
<%= link_to 'Upload all cards', upload_all_path if can? :upload_all, Card %>
|
<%= link_to 'Upload all cards', upload_all_path, :class => "btn" if can? :upload_all, Card %>
|
||||||
<%= link_to 'Door Logs', door_logs_path if can? :read, DoorLog %>
|
<%= link_to 'Door Logs', door_logs_path, :class => "btn" if can? :read, DoorLog %>
|
||||||
|
<%= link_to 'Space API', space_api_path, :class => "btn" %>
|
||||||
|
<%= link_to 'Remote Door Access', space_api_access_path, :class => "btn" if can? :access_doors_remotely, :door_access %>
|
||||||
<p>
|
<p>
|
||||||
<b>Most Active Card Last 7 Days:</b> <%= @most_active_card.name unless @most_active_card.blank? %> (<%= @most_active_card.accesses_this_week unless @most_active_card.blank? %> times)
|
<b>Most Active Card Last Month:</b> <%= @most_active_card.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>
|
</p>
|
||||||
|
|
||||||
<table>
|
<table class="lined-table">
|
||||||
<col />
|
<col />
|
||||||
<col />
|
<col />
|
||||||
<col class="col_highlight" />
|
<col class="col_highlight" />
|
||||||
@@ -19,7 +26,7 @@
|
|||||||
<th>DB ID</th>
|
<th>DB ID</th>
|
||||||
<th>Card #</th>
|
<th>Card #</th>
|
||||||
<th>Access?</th>
|
<th>Access?</th>
|
||||||
<th>Accesses Last 7 Days</th>
|
<th>Days Accessed Last Month</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
@@ -32,6 +39,7 @@
|
|||||||
<% if card.user.nil? %>
|
<% if card.user.nil? %>
|
||||||
n/a
|
n/a
|
||||||
<% else %>
|
<% else %>
|
||||||
|
<%= raw(card.user.member_status_symbol) %>
|
||||||
<%= link_to card.user.name , card %>
|
<%= link_to card.user.name , card %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
0
app/views/cards/new.html.erb
Normal file → Executable file
0
app/views/cards/new.html.erb
Normal file → Executable file
0
app/views/cards/show.html.erb
Normal file → Executable file
0
app/views/cards/show.html.erb
Normal file → Executable file
0
app/views/cards/upload.html.erb
Normal file → Executable file
0
app/views/cards/upload.html.erb
Normal file → Executable file
0
app/views/cards/upload_all.html.erb
Normal file → Executable file
0
app/views/cards/upload_all.html.erb
Normal file → Executable file
4
app/views/certifications/_form.html.erb
Normal file → Executable file
4
app/views/certifications/_form.html.erb
Normal file → Executable file
@@ -15,6 +15,10 @@
|
|||||||
<%= f.label :name %><br />
|
<%= f.label :name %><br />
|
||||||
<%= f.text_field :name %>
|
<%= f.text_field :name %>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<%= f.label :slug, "Slug (lowercase, single-word identifier)" %><br />
|
||||||
|
<%= f.text_field :slug %>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<%= f.label :description %><br />
|
<%= f.label :description %><br />
|
||||||
<%= f.text_area :description %>
|
<%= f.text_area :description %>
|
||||||
|
|||||||
0
app/views/certifications/edit.html.erb
Normal file → Executable file
0
app/views/certifications/edit.html.erb
Normal file → Executable file
1
app/views/certifications/index.html.erb
Normal file → Executable file
1
app/views/certifications/index.html.erb
Normal file → Executable file
@@ -5,6 +5,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<% @certifications.each do |certification| %>
|
<% @certifications.each do |certification| %>
|
||||||
<li><%= link_to certification.name, certification %>
|
<li><%= link_to certification.name, certification %>
|
||||||
|
(<%= certification.slug %>)
|
||||||
<% if can? :update, certification %> | <%= link_to 'Edit', edit_certification_path(certification) %><% end %>
|
<% if can? :update, certification %> | <%= link_to 'Edit', edit_certification_path(certification) %><% end %>
|
||||||
<% if can? :destroy, certification %> | <%= link_to 'Destroy', certification, :confirm => 'Are you sure?', :method => :delete %><% end %>
|
<% if can? :destroy, certification %> | <%= link_to 'Destroy', certification, :confirm => 'Are you sure?', :method => :delete %><% end %>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
0
app/views/certifications/new.html.erb
Normal file → Executable file
0
app/views/certifications/new.html.erb
Normal file → Executable file
5
app/views/certifications/show.html.erb
Normal file → Executable file
5
app/views/certifications/show.html.erb
Normal file → Executable file
@@ -3,6 +3,11 @@
|
|||||||
<%= @certification.name %>
|
<%= @certification.name %>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b>Slug (lowercase, single-word identifier):</b>
|
||||||
|
<%= @certification.slug %>
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<b>Description:</b>
|
<b>Description:</b>
|
||||||
<%= simple_format @certification.description %>
|
<%= simple_format @certification.description %>
|
||||||
|
|||||||
51
app/views/contracts/_form.html.erb
Normal file
51
app/views/contracts/_form.html.erb
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<%= form_for(@contract, html: {class: "col-sm-6"}) do |f| %>
|
||||||
|
<% if @contract.errors.any? %>
|
||||||
|
<div id="error_explanation">
|
||||||
|
<h2><%= pluralize(@contract.errors.count, "error") %> prohibited this contract from being saved:</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<% @contract.errors.full_messages.each do |msg| %>
|
||||||
|
<li><%= msg %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<%= f.label :first_name %><br />
|
||||||
|
<%= f.text_field :first_name, class: "form-control" %>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<%= f.label :last_name %><br />
|
||||||
|
<%= f.text_field :last_name, class: "form-control" %>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<%= f.label :cosigner %><br />
|
||||||
|
<%= f.text_field :cosigner, class: "form-control" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<%= f.label :user_id, "User" %><br />
|
||||||
|
<%= collection_select(:contract, :user_id, @users, :id, :name, :include_blank => true) %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<%= f.label :signed_at %><br />
|
||||||
|
<%= f.date_select :signed_at %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<%= f.label :document %><br />
|
||||||
|
<%= link_to "View Existing Document", @contract.document.url, class: "btn btn-default" unless @contract.document.blank? %>
|
||||||
|
<p>
|
||||||
|
<%= link_to "Upload New Document", "#", class: "btn btn-default", onclick: "$('#document_upload').removeClass('hidden'); $(this).addClass('hidden')" unless @contract.document.blank? %>
|
||||||
|
<div id="document_upload" class="<%= "hidden" unless @contract.document.blank? %>">
|
||||||
|
<%= f.file_field :document, class: "form-control", style: "width: 100%; height: 100px; background-color: #f9f9f9;" %>
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<%= f.submit nil, class: "btn btn-primary" %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
4
app/views/contracts/edit.html.erb
Normal file
4
app/views/contracts/edit.html.erb
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<h1>Edit Contract
|
||||||
|
<%= link_to 'Back', contracts_path, class: "btn btn-default" %>
|
||||||
|
</h1>
|
||||||
|
<%= render 'form' %>
|
||||||
42
app/views/contracts/index.html.erb
Normal file
42
app/views/contracts/index.html.erb
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<div class="row">
|
||||||
|
<h1 class="col-md-8">Contracts
|
||||||
|
<%= link_to 'Add Contract', new_contract_path, :class => "btn btn-success" if can? :create, Contract %>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<th>Scan?</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>User</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<% @contracts.sort_by{|r| (r.last_name) || "" }.each do |contract| %>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<% unless contract.document.blank? %>
|
||||||
|
<span class="glyphicon glyphicon-ok"></span>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= contract.last_name %>,
|
||||||
|
<%= contract.first_name %>
|
||||||
|
<%= "and #{contract.cosigner}" unless contract.cosigner.blank? %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= link_to contract.user.name, contract.user if contract.user %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= contract.signed_at.to_date.to_s(:long) %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= link_to "View", contract, class: "btn btn-primary" %>
|
||||||
|
<%= link_to "Edit", edit_contract_path(contract), class: "btn btn-default" %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
<br />
|
||||||
|
|
||||||
80
app/views/contracts/new.html.erb
Normal file
80
app/views/contracts/new.html.erb
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<h1>New Contract
|
||||||
|
<%= link_to 'Back', contracts_path, class: "btn btn-default" %>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<%= render 'form' %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<div class="alert alert-info">Note: if you name your scans in one of the below formats, this form will try to fill itself out automatically when you select the file. (The spaces, hyphen, and jpg or pdf extensions are mandatory for this to work.) <em>Double-check the names, users, and dates, though!!</em><br/>
|
||||||
|
<strong>first last - date.[jpg|pdf]</strong><br/>
|
||||||
|
<strong>first last by cosigner name - date.[jpg|pdf]</strong><br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function capitalizeIfIsntCapitalized(str){
|
||||||
|
if(str[0].toLowerCase() == str[0] ){
|
||||||
|
return str[0].toUpperCase() + str.slice(1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#contract_document").change(function (){
|
||||||
|
doc = document.getElementById("contract_document")
|
||||||
|
if(doc.files && doc.files.length > 0) {
|
||||||
|
name_split = doc.files[0].name.replace(".jpg","").replace(".pdf","").split(" ");
|
||||||
|
|
||||||
|
$("#contract_user_alert").remove(); // clear any existing alerts
|
||||||
|
$("#contract_user_spinner").remove(); // clear spinner
|
||||||
|
|
||||||
|
if(name_split.length == 4){ // we have one name
|
||||||
|
$("#contract_first_name").val(capitalizeIfIsntCapitalized(name_split[0]));
|
||||||
|
$("#contract_last_name").val(capitalizeIfIsntCapitalized(name_split[1]));
|
||||||
|
// 2 is the hyphen
|
||||||
|
signed_at = new Date(name_split[3]);
|
||||||
|
$("#contract_signed_at_1i").val(signed_at.getUTCFullYear()).change();
|
||||||
|
$("#contract_signed_at_2i").val(signed_at.getUTCMonth()+1).change();
|
||||||
|
$("#contract_signed_at_3i").val(signed_at.getUTCDate()).change();
|
||||||
|
}
|
||||||
|
else if(name_split.length == 7 && name_split[2] == "by"){ // we have two names
|
||||||
|
$("#contract_first_name").val(capitalizeIfIsntCapitalized(name_split[0]));
|
||||||
|
$("#contract_last_name").val(capitalizeIfIsntCapitalized(name_split[1]));
|
||||||
|
// 2 is "by"
|
||||||
|
$("#contract_cosigner").val(capitalizeIfIsntCapitalized(name_split[3])+" "+capitalizeIfIsntCapitalized(name_split[4]));
|
||||||
|
// 5 is the hyphen
|
||||||
|
signed_at = new Date(name_split[6]);
|
||||||
|
$("#contract_signed_at_1i").val(signed_at.getUTCFullYear());
|
||||||
|
$("#contract_signed_at_2i").val(signed_at.getUTCMonth()+1);
|
||||||
|
$("#contract_signed_at_3i").val(signed_at.getUTCDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try and select the relevant user if exists
|
||||||
|
if( $("#contract_first_name").val().length > 0 && $("#contract_last_name").val().length > 0 ) {
|
||||||
|
user_id = $('#contract_user_id option').filter(function(){
|
||||||
|
return $(this).text() == $("#contract_first_name").val() + " " + $("#contract_last_name").val();
|
||||||
|
}).prop("selected", "true").val();
|
||||||
|
|
||||||
|
// Start a spinner before AJAX request
|
||||||
|
$("#contract_user_id").after("<i id='contract_user_spinner' class='icon-spin icon-refresh'></i>");
|
||||||
|
// If we found a user, check how many contracts that user already has
|
||||||
|
$.get("/users/"+user_id+"/contracts.json",function(data){
|
||||||
|
$("#contract_user_spinner").remove(); // clear spinner
|
||||||
|
if(data.length > 0){
|
||||||
|
other_contract_links = $.map(data,function(item){
|
||||||
|
output = "<a href='/contracts/"+item["id"]+"'>"+item["id"]+"</a> ";
|
||||||
|
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>
|
||||||
41
app/views/contracts/show.html.erb
Normal file
41
app/views/contracts/show.html.erb
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<div class="row">
|
||||||
|
<h1 class="col-md-8">
|
||||||
|
Contract
|
||||||
|
<%= link_to 'Back', contracts_path, :class => "btn btn-default" %>
|
||||||
|
<%= link_to 'New', new_contract_path, :class => "btn btn-success" %>
|
||||||
|
<%= link_to 'Edit', edit_contract_path(@contract), :class => "btn btn-primary" %>
|
||||||
|
<%= link_to 'Delete', contract_path(@contract), {:confirm => 'Are you sure you want to delete this forever?', :method => :delete, :class => "btn btn-danger"} if can? :destroy, @contract %>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>
|
||||||
|
<%= @contract.first_name %>
|
||||||
|
<%= @contract.last_name %>
|
||||||
|
<%= "and #{@contract.cosigner}" unless @contract.cosigner.blank? %>
|
||||||
|
<%= link_to "(#{@contract.user.name})", @contract.user if @contract.user %>
|
||||||
|
<small>
|
||||||
|
signed
|
||||||
|
<%= @contract.signed_at.to_date.to_s(:long) %>
|
||||||
|
</small>
|
||||||
|
</h2>
|
||||||
|
<% unless @contract.created_by.blank? %>
|
||||||
|
<p>
|
||||||
|
<em>Created by <%= @contract.created_by.name %></em>
|
||||||
|
</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if @contract.document.blank? %>
|
||||||
|
<p>No document uploaded</p>
|
||||||
|
<% else %>
|
||||||
|
<p><%= link_to "Download Contract", @contract.document.url %>
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<% contract_url = (@contract.document.exists?(:large) ? @contract.document.url(:large) : @contract.document.url) %>
|
||||||
|
<iframe src="<%= contract_url %>" width="100%" height="600"></iframe>
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
0
app/views/devise/confirmations/new.html.erb
Normal file → Executable file
0
app/views/devise/confirmations/new.html.erb
Normal file → Executable file
0
app/views/devise/mailer/confirmation_instructions.html.erb
Normal file → Executable file
0
app/views/devise/mailer/confirmation_instructions.html.erb
Normal file → Executable file
0
app/views/devise/mailer/reset_password_instructions.html.erb
Normal file → Executable file
0
app/views/devise/mailer/reset_password_instructions.html.erb
Normal file → Executable file
0
app/views/devise/mailer/unlock_instructions.html.erb
Normal file → Executable file
0
app/views/devise/mailer/unlock_instructions.html.erb
Normal file → Executable file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user