Compare commits
245 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 | |||
| 69a57bc63b | |||
| c662be6dc0 | |||
| ca7808a525 | |||
| 9fb2507aa1 | |||
| 0be2834a5d | |||
| ef46bf6a98 | |||
| 45bac6cae4 | |||
| 4dba2b8c3d | |||
| 18bc427eb3 | |||
| 810ff6b034 | |||
| ae031838dc | |||
| 59b6e3c838 | |||
| 98be42e9f9 | |||
| b5d9514914 | |||
| 125ad76a1c | |||
| 4067477cd4 | |||
| 9e4b79a353 | |||
| f8f11e3d7e | |||
| 805148ee40 | |||
| f111769b20 | |||
| eb782f11d5 | |||
| a1ef8a5fd5 | |||
| ec4cf4dea9 | |||
| 75e4907a9c | |||
| 20a007cac5 | |||
| 719b9447ab | |||
| 8a7fe29f6d | |||
| e630c81298 | |||
| cefd4b3878 | |||
| ed75ea0e90 | |||
| 3fb774d057 | |||
| 0ced399651 | |||
| 02920837e2 | |||
| 63913c0be3 | |||
| ac66cb0cbe | |||
| 1f63709887 | |||
| 3f3eb1ed65 | |||
| f1b752a4c4 | |||
| 2d0735e914 | |||
| d156edd683 | |||
| 1b64a6b931 | |||
| 1239d6682b | |||
| 50171effad | |||
| 43e2cdba78 | |||
| f3498ddcac | |||
| 04764af983 | |||
| 998558cd30 | |||
| 048ce52111 | |||
| a7e999614c | |||
| 6673573e36 | |||
| 75851ed88d | |||
| a022b72eb9 | |||
| 37106f65e0 | |||
| 1865123201 | |||
| 8ab6fa10ea | |||
| e9c648d36e | |||
| 7193ec832c | |||
| c547af5391 | |||
| 3d00bbed03 | |||
| 2327340b71 | |||
| 0b6975a655 | |||
| 95374a8c1f | |||
| 6d0a7b165a | |||
| bb7b2aeca5 | |||
| 7cadcabdce | |||
| 63b1b05010 | |||
| 653fcc3112 | |||
| 6e77b2bf68 | |||
| 43d949dc1d | |||
| fe283b051f | |||
| d44ae6a69f | |||
| cc3857604b | |||
| 4fc18013d9 | |||
| 31f220e121 | |||
| 257c1e5a8b | |||
|
|
0b5978d4ef | ||
| 1a9c77fbce | |||
|
|
4ad622e906 | ||
| d1ef1e7db9 | |||
| 0254f9aa2c | |||
| ccd432a67d | |||
| dc0586045f | |||
| 152d6e4e03 | |||
| 5174b4c50c | |||
| cd4f689400 | |||
| daa202131c | |||
| d73ea31625 | |||
| f3443a3a82 | |||
| ed8d284619 | |||
| 1f74d13ba7 | |||
| 921ba21a40 | |||
| 93e77b692f | |||
| 0bc5858101 | |||
| d3b8ad7222 | |||
| 03d99741e5 | |||
| 1e4dcfd9f3 | |||
| 33a9363b0d | |||
| f3a05d64c1 | |||
| c80c3bbe5c | |||
| 8c36b67cfb | |||
| 99f24fe144 | |||
| 72f5a1202a | |||
| 62fb513491 | |||
| 6df97cc3a7 | |||
| 7f623cf2ec | |||
| 8b775d9172 | |||
| b28efe046c |
12
.gitignore
vendored
12
.gitignore
vendored
@@ -9,7 +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
|
||||||
|
/public/assets
|
||||||
|
|
||||||
|
# Ignore config and database files (passwords)
|
||||||
|
/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
|
||||||
23
Gemfile
23
Gemfile
@@ -1,11 +1,16 @@
|
|||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
gem 'rails', '3.2.3'
|
ruby '1.9.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'
|
||||||
|
|
||||||
@@ -23,8 +28,12 @@ end
|
|||||||
|
|
||||||
gem 'jquery-rails'
|
gem 'jquery-rails'
|
||||||
|
|
||||||
|
gem 'devise'
|
||||||
|
|
||||||
|
gem "cancan"
|
||||||
|
|
||||||
# To use ActiveModel has_secure_password
|
# To use ActiveModel has_secure_password
|
||||||
# gem 'bcrypt-ruby', '~> 3.0.0'
|
gem 'bcrypt-ruby', '~> 3.0.0'
|
||||||
|
|
||||||
# To use Jbuilder templates for JSON
|
# To use Jbuilder templates for JSON
|
||||||
# gem 'jbuilder'
|
# gem 'jbuilder'
|
||||||
@@ -36,4 +45,12 @@ gem 'jquery-rails'
|
|||||||
# gem 'capistrano'
|
# gem 'capistrano'
|
||||||
|
|
||||||
# To use debugger
|
# To use debugger
|
||||||
# gem 'ruby-debug'
|
#gem 'debugger'
|
||||||
|
|
||||||
|
gem "paperclip", "~> 3.0"
|
||||||
|
gem "aws-sdk"
|
||||||
|
gem 'gravtastic'
|
||||||
|
|
||||||
|
gem 'passenger'
|
||||||
|
|
||||||
|
gem "rails-settings-cached", "0.2.4"
|
||||||
|
|||||||
175
Gemfile.lock
175
Gemfile.lock
@@ -1,116 +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)
|
||||||
builder (3.0.0)
|
aws-sdk (1.33.0)
|
||||||
|
json (~> 1.4)
|
||||||
|
nokogiri (>= 1.4.4)
|
||||||
|
uuidtools (~> 2.1)
|
||||||
|
bcrypt-ruby (3.0.1)
|
||||||
|
builder (3.0.4)
|
||||||
|
cancan (1.6.10)
|
||||||
|
climate_control (0.0.3)
|
||||||
|
activesupport (>= 3.0)
|
||||||
|
cocaine (0.5.3)
|
||||||
|
climate_control (>= 0.0.3, < 1.0)
|
||||||
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)
|
||||||
|
daemon_controller (1.1.5)
|
||||||
|
devise (2.2.7)
|
||||||
|
bcrypt-ruby (~> 3.0)
|
||||||
|
orm_adapter (~> 0.1)
|
||||||
|
railties (~> 3.1)
|
||||||
|
warden (~> 1.2.1)
|
||||||
|
dotenv (0.10.0)
|
||||||
|
dotenv-rails (0.10.0)
|
||||||
|
dotenv (= 0.10.0)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
execjs (1.4.0)
|
execjs (2.0.2)
|
||||||
multi_json (~> 1.0)
|
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)
|
||||||
|
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)
|
||||||
|
uuidtools (2.1.4)
|
||||||
|
warden (1.2.3)
|
||||||
|
rack (>= 1.0)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
aws-sdk
|
||||||
|
bcrypt-ruby (~> 3.0.0)
|
||||||
|
cancan
|
||||||
coffee-rails (~> 3.2.1)
|
coffee-rails (~> 3.2.1)
|
||||||
|
devise
|
||||||
|
dotenv-rails
|
||||||
|
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.
|
||||||
261
README.rdoc
261
README.rdoc
@@ -1,261 +0,0 @@
|
|||||||
== Welcome to Rails
|
|
||||||
|
|
||||||
Rails is a web-application framework that includes everything needed to create
|
|
||||||
database-backed web applications according to the Model-View-Control pattern.
|
|
||||||
|
|
||||||
This pattern splits the view (also called the presentation) into "dumb"
|
|
||||||
templates that are primarily responsible for inserting pre-built data in between
|
|
||||||
HTML tags. The model contains the "smart" domain objects (such as Account,
|
|
||||||
Product, Person, Post) that holds all the business logic and knows how to
|
|
||||||
persist themselves to a database. The controller handles the incoming requests
|
|
||||||
(such as Save New Account, Update Product, Show Post) by manipulating the model
|
|
||||||
and directing data to the view.
|
|
||||||
|
|
||||||
In Rails, the model is handled by what's called an object-relational mapping
|
|
||||||
layer entitled Active Record. This layer allows you to present the data from
|
|
||||||
database rows as objects and embellish these data objects with business logic
|
|
||||||
methods. You can read more about Active Record in
|
|
||||||
link:files/vendor/rails/activerecord/README.html.
|
|
||||||
|
|
||||||
The controller and view are handled by the Action Pack, which handles both
|
|
||||||
layers by its two parts: Action View and Action Controller. These two layers
|
|
||||||
are bundled in a single package due to their heavy interdependence. This is
|
|
||||||
unlike the relationship between the Active Record and Action Pack that is much
|
|
||||||
more separate. Each of these packages can be used independently outside of
|
|
||||||
Rails. You can read more about Action Pack in
|
|
||||||
link:files/vendor/rails/actionpack/README.html.
|
|
||||||
|
|
||||||
|
|
||||||
== Getting Started
|
|
||||||
|
|
||||||
1. At the command prompt, create a new Rails application:
|
|
||||||
<tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
|
|
||||||
|
|
||||||
2. Change directory to <tt>myapp</tt> and start the web server:
|
|
||||||
<tt>cd myapp; rails server</tt> (run with --help for options)
|
|
||||||
|
|
||||||
3. Go to http://localhost:3000/ and you'll see:
|
|
||||||
"Welcome aboard: You're riding Ruby on Rails!"
|
|
||||||
|
|
||||||
4. Follow the guidelines to start developing your application. You can find
|
|
||||||
the following resources handy:
|
|
||||||
|
|
||||||
* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
|
|
||||||
* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
|
|
||||||
|
|
||||||
|
|
||||||
== Debugging Rails
|
|
||||||
|
|
||||||
Sometimes your application goes wrong. Fortunately there are a lot of tools that
|
|
||||||
will help you debug it and get it back on the rails.
|
|
||||||
|
|
||||||
First area to check is the application log files. Have "tail -f" commands
|
|
||||||
running on the server.log and development.log. Rails will automatically display
|
|
||||||
debugging and runtime information to these files. Debugging info will also be
|
|
||||||
shown in the browser on requests from 127.0.0.1.
|
|
||||||
|
|
||||||
You can also log your own messages directly into the log file from your code
|
|
||||||
using the Ruby logger class from inside your controllers. Example:
|
|
||||||
|
|
||||||
class WeblogController < ActionController::Base
|
|
||||||
def destroy
|
|
||||||
@weblog = Weblog.find(params[:id])
|
|
||||||
@weblog.destroy
|
|
||||||
logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
The result will be a message in your log file along the lines of:
|
|
||||||
|
|
||||||
Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
|
|
||||||
|
|
||||||
More information on how to use the logger is at http://www.ruby-doc.org/core/
|
|
||||||
|
|
||||||
Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
|
|
||||||
several books available online as well:
|
|
||||||
|
|
||||||
* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
|
|
||||||
* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
|
|
||||||
|
|
||||||
These two books will bring you up to speed on the Ruby language and also on
|
|
||||||
programming in general.
|
|
||||||
|
|
||||||
|
|
||||||
== Debugger
|
|
||||||
|
|
||||||
Debugger support is available through the debugger command when you start your
|
|
||||||
Mongrel or WEBrick server with --debugger. This means that you can break out of
|
|
||||||
execution at any point in the code, investigate and change the model, and then,
|
|
||||||
resume execution! You need to install ruby-debug to run the server in debugging
|
|
||||||
mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:
|
|
||||||
|
|
||||||
class WeblogController < ActionController::Base
|
|
||||||
def index
|
|
||||||
@posts = Post.all
|
|
||||||
debugger
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
So the controller will accept the action, run the first line, then present you
|
|
||||||
with a IRB prompt in the server window. Here you can do things like:
|
|
||||||
|
|
||||||
>> @posts.inspect
|
|
||||||
=> "[#<Post:0x14a6be8
|
|
||||||
@attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>,
|
|
||||||
#<Post:0x14a6620
|
|
||||||
@attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
|
|
||||||
>> @posts.first.title = "hello from a debugger"
|
|
||||||
=> "hello from a debugger"
|
|
||||||
|
|
||||||
...and even better, you can examine how your runtime objects actually work:
|
|
||||||
|
|
||||||
>> f = @posts.first
|
|
||||||
=> #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
|
|
||||||
>> f.
|
|
||||||
Display all 152 possibilities? (y or n)
|
|
||||||
|
|
||||||
Finally, when you're ready to resume execution, you can enter "cont".
|
|
||||||
|
|
||||||
|
|
||||||
== Console
|
|
||||||
|
|
||||||
The console is a Ruby shell, which allows you to interact with your
|
|
||||||
application's domain model. Here you'll have all parts of the application
|
|
||||||
configured, just like it is when the application is running. You can inspect
|
|
||||||
domain models, change values, and save to the database. Starting the script
|
|
||||||
without arguments will launch it in the development environment.
|
|
||||||
|
|
||||||
To start the console, run <tt>rails console</tt> from the application
|
|
||||||
directory.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
|
|
||||||
* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
|
|
||||||
made to the database.
|
|
||||||
* Passing an environment name as an argument will load the corresponding
|
|
||||||
environment. Example: <tt>rails console production</tt>.
|
|
||||||
|
|
||||||
To reload your controllers and models after launching the console run
|
|
||||||
<tt>reload!</tt>
|
|
||||||
|
|
||||||
More information about irb can be found at:
|
|
||||||
link:http://www.rubycentral.org/pickaxe/irb.html
|
|
||||||
|
|
||||||
|
|
||||||
== dbconsole
|
|
||||||
|
|
||||||
You can go to the command line of your database directly through <tt>rails
|
|
||||||
dbconsole</tt>. You would be connected to the database with the credentials
|
|
||||||
defined in database.yml. Starting the script without arguments will connect you
|
|
||||||
to the development database. Passing an argument will connect you to a different
|
|
||||||
database, like <tt>rails dbconsole production</tt>. Currently works for MySQL,
|
|
||||||
PostgreSQL and SQLite 3.
|
|
||||||
|
|
||||||
== Description of Contents
|
|
||||||
|
|
||||||
The default directory structure of a generated Ruby on Rails application:
|
|
||||||
|
|
||||||
|-- app
|
|
||||||
| |-- assets
|
|
||||||
| |-- images
|
|
||||||
| |-- javascripts
|
|
||||||
| `-- stylesheets
|
|
||||||
| |-- controllers
|
|
||||||
| |-- helpers
|
|
||||||
| |-- mailers
|
|
||||||
| |-- models
|
|
||||||
| `-- views
|
|
||||||
| `-- layouts
|
|
||||||
|-- config
|
|
||||||
| |-- environments
|
|
||||||
| |-- initializers
|
|
||||||
| `-- locales
|
|
||||||
|-- db
|
|
||||||
|-- doc
|
|
||||||
|-- lib
|
|
||||||
| `-- tasks
|
|
||||||
|-- log
|
|
||||||
|-- public
|
|
||||||
|-- script
|
|
||||||
|-- test
|
|
||||||
| |-- fixtures
|
|
||||||
| |-- functional
|
|
||||||
| |-- integration
|
|
||||||
| |-- performance
|
|
||||||
| `-- unit
|
|
||||||
|-- tmp
|
|
||||||
| |-- cache
|
|
||||||
| |-- pids
|
|
||||||
| |-- sessions
|
|
||||||
| `-- sockets
|
|
||||||
`-- vendor
|
|
||||||
|-- assets
|
|
||||||
`-- stylesheets
|
|
||||||
`-- plugins
|
|
||||||
|
|
||||||
app
|
|
||||||
Holds all the code that's specific to this particular application.
|
|
||||||
|
|
||||||
app/assets
|
|
||||||
Contains subdirectories for images, stylesheets, and JavaScript files.
|
|
||||||
|
|
||||||
app/controllers
|
|
||||||
Holds controllers that should be named like weblogs_controller.rb for
|
|
||||||
automated URL mapping. All controllers should descend from
|
|
||||||
ApplicationController which itself descends from ActionController::Base.
|
|
||||||
|
|
||||||
app/models
|
|
||||||
Holds models that should be named like post.rb. Models descend from
|
|
||||||
ActiveRecord::Base by default.
|
|
||||||
|
|
||||||
app/views
|
|
||||||
Holds the template files for the view that should be named like
|
|
||||||
weblogs/index.html.erb for the WeblogsController#index action. All views use
|
|
||||||
eRuby syntax by default.
|
|
||||||
|
|
||||||
app/views/layouts
|
|
||||||
Holds the template files for layouts to be used with views. This models the
|
|
||||||
common header/footer method of wrapping views. In your views, define a layout
|
|
||||||
using the <tt>layout :default</tt> and create a file named default.html.erb.
|
|
||||||
Inside default.html.erb, call <% yield %> to render the view using this
|
|
||||||
layout.
|
|
||||||
|
|
||||||
app/helpers
|
|
||||||
Holds view helpers that should be named like weblogs_helper.rb. These are
|
|
||||||
generated for you automatically when using generators for controllers.
|
|
||||||
Helpers can be used to wrap functionality for your views into methods.
|
|
||||||
|
|
||||||
config
|
|
||||||
Configuration files for the Rails environment, the routing map, the database,
|
|
||||||
and other dependencies.
|
|
||||||
|
|
||||||
db
|
|
||||||
Contains the database schema in schema.rb. db/migrate contains all the
|
|
||||||
sequence of Migrations for your schema.
|
|
||||||
|
|
||||||
doc
|
|
||||||
This directory is where your application documentation will be stored when
|
|
||||||
generated using <tt>rake doc:app</tt>
|
|
||||||
|
|
||||||
lib
|
|
||||||
Application specific libraries. Basically, any kind of custom code that
|
|
||||||
doesn't belong under controllers, models, or helpers. This directory is in
|
|
||||||
the load path.
|
|
||||||
|
|
||||||
public
|
|
||||||
The directory available for the web server. Also contains the dispatchers and the
|
|
||||||
default HTML files. This should be set as the DOCUMENT_ROOT of your web
|
|
||||||
server.
|
|
||||||
|
|
||||||
script
|
|
||||||
Helper scripts for automation and generation.
|
|
||||||
|
|
||||||
test
|
|
||||||
Unit and functional tests along with fixtures. When using the rails generate
|
|
||||||
command, template test files will be generated for you and placed in this
|
|
||||||
directory.
|
|
||||||
|
|
||||||
vendor
|
|
||||||
External libraries that the application depends on. Also includes the plugins
|
|
||||||
subdirectory. If the app has frozen rails, those gems also go here, under
|
|
||||||
vendor/rails/. This directory is in the load path.
|
|
||||||
BIN
app/assets/images/logo.png
Executable file
BIN
app/assets/images/logo.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
app/assets/images/nil.png
Executable file
BIN
app/assets/images/nil.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 95 B |
Binary file not shown.
|
Before Width: | Height: | Size: 6.5 KiB |
0
app/assets/javascripts/application.js
Normal file → Executable file
0
app/assets/javascripts/application.js
Normal file → Executable file
3
app/assets/javascripts/certifications.js.coffee
Executable file
3
app/assets/javascripts/certifications.js.coffee
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||||
0
app/assets/javascripts/door_logs.js.coffee
Normal file → Executable file
0
app/assets/javascripts/door_logs.js.coffee
Normal file → Executable file
3
app/assets/javascripts/home.js.coffee
Executable file
3
app/assets/javascripts/home.js.coffee
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||||
3
app/assets/javascripts/ipn.js.coffee
Executable file
3
app/assets/javascripts/ipn.js.coffee
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||||
3
app/assets/javascripts/mac_logs.js.coffee
Executable file
3
app/assets/javascripts/mac_logs.js.coffee
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||||
3
app/assets/javascripts/pamela.js.coffee
Executable file
3
app/assets/javascripts/pamela.js.coffee
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||||
3
app/assets/javascripts/payments.js.coffee
Executable file
3
app/assets/javascripts/payments.js.coffee
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||||
3
app/assets/javascripts/user_certifications.js.coffee
Executable file
3
app/assets/javascripts/user_certifications.js.coffee
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
jQuery ->
|
||||||
|
$('.collapsible dt').click ->
|
||||||
|
$(this).parent().toggleClass('expanded')
|
||||||
4
app/assets/javascripts/users.js.coffee
Normal file → Executable file
4
app/assets/javascripts/users.js.coffee
Normal file → Executable file
@@ -1,3 +1,7 @@
|
|||||||
# Place all the behaviors and hooks related to the matching controller here.
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
# All this logic will automatically be available in application.js.
|
# All this logic will automatically be available in application.js.
|
||||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||||
|
jQuery ->
|
||||||
|
$('#user_payment_method').change ->
|
||||||
|
$('.payment_instructions').hide()
|
||||||
|
$('#pmt_'+$(this).val()).show()
|
||||||
|
|||||||
44
app/assets/stylesheets/application.css
Normal file → Executable file
44
app/assets/stylesheets/application.css
Normal file → Executable file
@@ -11,3 +11,47 @@
|
|||||||
*= require_self
|
*= require_self
|
||||||
*= require_tree .
|
*= require_tree .
|
||||||
*/
|
*/
|
||||||
|
.caption { display: inline-block; background-color: #eee; border: 1px solid #333; border-radius: 5px; margin-bottom: 1em; }
|
||||||
|
.btn {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 10px 4px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 18px;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 0 1px 1px rgba(255,255,255,.75);
|
||||||
|
vertical-align: middle;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
background-image: -moz-linear-gradient(top, #fff, #e6e6e6);
|
||||||
|
background-image: -ms-linear-gradient(top, #fff, #e6e6e6);
|
||||||
|
background-image: -webkit-gradient(linear,0 0,0 100%,from( #fff),to( #e6e6e6));
|
||||||
|
background-image: -webkit-linear-gradient(top, #fff, #e6e6e6);
|
||||||
|
background-image: -o-linear-gradient(top, #fff, #e6e6e6);
|
||||||
|
background-image: linear-gradient(top, #fff, #e6e6e6);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
|
||||||
|
border-color: #e6e6e6 #e6e6e6 #bfbfbf;
|
||||||
|
border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-bottom-color: #bbb;
|
||||||
|
-webkit-border-radius: 4px;
|
||||||
|
-moz-border-radius: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);
|
||||||
|
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);
|
||||||
|
cursor: pointer;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||||
|
}
|
||||||
|
.member-status-symbol, .social-icon {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
a.social-link:hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.lined-table td {
|
||||||
|
border-bottom: 1px dashed black;
|
||||||
|
}
|
||||||
|
|||||||
3
app/assets/stylesheets/certifications.css.scss
Executable file
3
app/assets/stylesheets/certifications.css.scss
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
// Place all the styles related to the Certifications controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
0
app/assets/stylesheets/door_logs.css.scss
Normal file → Executable file
0
app/assets/stylesheets/door_logs.css.scss
Normal file → Executable file
4
app/assets/stylesheets/home.css.scss
Executable file
4
app/assets/stylesheets/home.css.scss
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
// Place all the styles related to the home controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
|
table, td { vertical-align: top }
|
||||||
3
app/assets/stylesheets/ipn.css.scss
Executable file
3
app/assets/stylesheets/ipn.css.scss
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
// Place all the styles related to the Ipn controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
3
app/assets/stylesheets/mac_logs.css.scss
Executable file
3
app/assets/stylesheets/mac_logs.css.scss
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
// Place all the styles related to the MacLogs controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
4
app/assets/stylesheets/macs.css.scss
Executable file
4
app/assets/stylesheets/macs.css.scss
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
// Place all the styles related to the pamela controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
|
.hidden { color: #ccc; }
|
||||||
3
app/assets/stylesheets/payments.css.scss
Executable file
3
app/assets/stylesheets/payments.css.scss
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
// Place all the styles related to the Payments controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
40
app/assets/stylesheets/scaffolds.css.scss
Normal file → Executable file
40
app/assets/stylesheets/scaffolds.css.scss
Normal file → Executable file
@@ -4,6 +4,7 @@ body {
|
|||||||
font-family: verdana, arial, helvetica, sans-serif;
|
font-family: verdana, arial, helvetica, sans-serif;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p, ol, ul, td {
|
p, ol, ul, td {
|
||||||
@@ -18,27 +19,34 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
background-color: #eee; border: 1px solid #ddd;
|
||||||
|
border-bottom-left-radius: 1em;
|
||||||
|
border-bottom-right-radius: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header a {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
height: 2.0em;
|
||||||
|
width: 2.0em;
|
||||||
|
}
|
||||||
|
|
||||||
#notice {
|
#notice {
|
||||||
color: green;
|
color: green;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#content { margin: 1em; }
|
||||||
|
|
||||||
.field_with_errors {
|
.field_with_errors {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
background-color: red;
|
background-color: red;
|
||||||
@@ -67,3 +75,13 @@ div {
|
|||||||
list-style: square;
|
list-style: square;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table { border-spacing: 0; }
|
||||||
|
td, th { padding: 0.5em; }
|
||||||
|
.col_highlight { background-color: #ccc; }
|
||||||
|
dt { font-weight: bold; }
|
||||||
|
.notice { color: green; }
|
||||||
|
.alert { color: red; }
|
||||||
|
.hidden, .hidden a { color: #ccc; }
|
||||||
|
|
||||||
|
.payment_instructions { display: none }
|
||||||
|
|||||||
10
app/assets/stylesheets/user_certifications.css.scss
Executable file
10
app/assets/stylesheets/user_certifications.css.scss
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
// Place all the styles related to the UserCertifications controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
|
|
||||||
|
.collapsible dt { cursor: pointer; }
|
||||||
|
.collapsible dt:before { content: '\229e'; }
|
||||||
|
.collapsible dd { display: none; }
|
||||||
|
|
||||||
|
.expanded dt:before { content: '\229f'; }
|
||||||
|
.expanded dd { display: block; }
|
||||||
18
app/assets/stylesheets/users.css.scss
Normal file → Executable file
18
app/assets/stylesheets/users.css.scss
Normal file → Executable file
@@ -1,3 +1,21 @@
|
|||||||
// Place all the styles related to the users controller here.
|
// Place all the styles related to the users controller here.
|
||||||
// They will automatically be included in application.css.
|
// They will automatically be included in application.css.
|
||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
|
|
||||||
|
.iconinfo, .hoverinfo { font-size: 1.5em; }
|
||||||
|
.hoverinfo { cursor: progress; }
|
||||||
|
|
||||||
|
.payment_links { background-color: #ddd; padding: 1em; border-radius: 1em;
|
||||||
|
display: inline-block; float: right; max-width: 30%; min-width: 10em;}
|
||||||
|
|
||||||
|
.payment-highlighted {
|
||||||
|
background-color: orange !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar { height: 2em; width: 2em; }
|
||||||
|
|
||||||
|
.avatar-large {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea { height: 10em; }
|
||||||
|
|||||||
46
app/controllers/application_controller.rb
Normal file → Executable file
46
app/controllers/application_controller.rb
Normal file → Executable file
@@ -1,3 +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|
|
||||||
|
if !current_user.nil? && current_user.orientation.blank? then
|
||||||
|
flash[:alert] = "Sorry, you probably need to complete New Member Orientation before having access to this page. <br/>Please check your email and schedule a New Member Orientation with a volunteer."
|
||||||
|
else
|
||||||
|
flash[:alert] = "Nothing to see here!"
|
||||||
|
end
|
||||||
|
Rails.logger.warn "----------\r\nWARNING: AccessDenied Exception: #{exception.inspect} User: #{current_user.inspect}\r\n----------"
|
||||||
|
redirect_to root_url
|
||||||
|
end
|
||||||
|
|
||||||
|
@payment_methods = [[nil],["PayPal"],["Dwolla"],["Bill Pay"],["Check"],["Cash"],["Other"]]
|
||||||
|
@payment_instructions = {nil => nil, :paypal => "Set up a monthly recurring payment to hslfinances@gmail.com", :dwolla => "Set up a monthly recurring payment to hslfinances@gmail.com", :billpay => "Have your bank send a monthly check to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201", :check => "Mail to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201 OR put in the drop safe at the Lab with a deposit slip firmly attached each month.", :cash => "Put in the drop safe at the Lab with a deposit slip firmly attached each month.", :other => "Hmm... talk to a Treasurer!"}
|
||||||
|
|
||||||
|
# Check authorization of a user / sign them in manually
|
||||||
|
def check_auth(email,password)
|
||||||
|
resource = User.find_by_email(email)
|
||||||
|
if resource && resource.valid_password?(password)
|
||||||
|
resource.remember_me = true
|
||||||
|
sign_in :user, resource
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
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
|
||||||
|
|
||||||
|
|||||||
160
app/controllers/cards_controller.rb
Executable file
160
app/controllers/cards_controller.rb
Executable file
@@ -0,0 +1,160 @@
|
|||||||
|
class CardsController < ApplicationController
|
||||||
|
load_and_authorize_resource except: :authorize
|
||||||
|
before_filter :authenticate_user!, except: :authorize
|
||||||
|
|
||||||
|
# GET /cards
|
||||||
|
# GET /cards.json
|
||||||
|
def index
|
||||||
|
#@cards = Card.all
|
||||||
|
#authorize! :read, @cards
|
||||||
|
@cards = @cards.sort_by{|e| e[:id]}
|
||||||
|
|
||||||
|
if can? :read, DoorLog then
|
||||||
|
most_active_count = 0
|
||||||
|
runner_up_count = 0
|
||||||
|
@most_active_card = nil
|
||||||
|
@runner_up_card = nil
|
||||||
|
@cards.each do |card|
|
||||||
|
card_num_R = card.card_number.to_i(16)%32767
|
||||||
|
card[:accesses_this_week] = DoorLog.where("key = ? AND data = ? AND created_at > ?", 'G', card_num_R, DateTime.now - 1.month).order("created_at DESC").group_by { |d| d.created_at.beginning_of_day }.count
|
||||||
|
end
|
||||||
|
@most_active_cards = @cards.sort{|a,b| b[:accesses_this_week] <=> a[:accesses_this_week]}
|
||||||
|
@most_active_card = @most_active_cards[0]
|
||||||
|
@runner_up_card = @most_active_cards[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # index.html.erb
|
||||||
|
format.json { render :json => @cards }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /cards/1
|
||||||
|
# GET /cards/1.json
|
||||||
|
def show
|
||||||
|
if can? :read, DoorLog then
|
||||||
|
card_num_R = @card.card_number.to_i(16)%32767
|
||||||
|
@door_logs = DoorLog.where('key = ? AND data = ?', "G", card_num_R).order("created_at DESC")
|
||||||
|
end
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # show.html.erb
|
||||||
|
format.json { render :json => @card }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# PUT /cards/1/upload
|
||||||
|
def upload
|
||||||
|
#@card = Card.find(params[:id])
|
||||||
|
@upload_result = @card.upload_to_door
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # show.html.erb
|
||||||
|
format.json { render :json => @upload_result }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# PUT /cards/upload_all
|
||||||
|
def upload_all
|
||||||
|
@upload_result = Card.upload_all_to_door
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # show.html.erb
|
||||||
|
format.json { render :json => @upload_result }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /cards/new
|
||||||
|
# GET /cards/new.json
|
||||||
|
def new
|
||||||
|
#@card = Card.new
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # new.html.erb
|
||||||
|
format.json { render :json => @card }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /cards/1/edit
|
||||||
|
def edit
|
||||||
|
#@card = Card.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /cards
|
||||||
|
# POST /cards.json
|
||||||
|
def create
|
||||||
|
#@card = Card.new(params[:card])
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @card.save
|
||||||
|
format.html { redirect_to cards_url, :notice => 'Card was successfully created.' }
|
||||||
|
format.json { render :json => @card, :status => :created, :location => @card }
|
||||||
|
else
|
||||||
|
format.html { render :action => "new" }
|
||||||
|
format.json { render :json => @card.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# PUT /cards/1
|
||||||
|
# PUT /cards/1.json
|
||||||
|
def update
|
||||||
|
#@card = Card.find(params[:id])
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @card.update_attributes(params[:card])
|
||||||
|
format.html { redirect_to cards_url, :notice => 'Card was successfully updated.' }
|
||||||
|
format.json { head :no_content }
|
||||||
|
else
|
||||||
|
format.html { render :action => "edit" }
|
||||||
|
format.json { render :json => @card.errors, :status => :unprocessable_entity }
|
||||||
|
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.json
|
||||||
|
def destroy
|
||||||
|
#@card = Card.find(params[:id])
|
||||||
|
@card.destroy
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to cards_url, :notice => 'Card successfully deleted.' }
|
||||||
|
format.json { head :no_content }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
84
app/controllers/certifications_controller.rb
Executable file
84
app/controllers/certifications_controller.rb
Executable file
@@ -0,0 +1,84 @@
|
|||||||
|
class CertificationsController < ApplicationController
|
||||||
|
load_and_authorize_resource :certification
|
||||||
|
#load_and_authorize_resource :user, :through => :certification
|
||||||
|
before_filter :authenticate_user!
|
||||||
|
|
||||||
|
# GET /certifications
|
||||||
|
# GET /certifications.json
|
||||||
|
def index
|
||||||
|
@certifications = @certifications.sort_by(&:name)
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # index.html.erb
|
||||||
|
format.json { render :json => @certifications }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /certifications/1
|
||||||
|
# GET /certifications/1.json
|
||||||
|
def show
|
||||||
|
@certification_users = []
|
||||||
|
|
||||||
|
#TODO: make a better SQL query for this
|
||||||
|
@certification.users.sort_by(&:name).each do |user|
|
||||||
|
@certification_users.push user if can? :read, user
|
||||||
|
end
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # show.html.erb
|
||||||
|
format.json { render :json => @certification }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /certifications/new
|
||||||
|
# GET /certifications/new.json
|
||||||
|
def new
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # new.html.erb
|
||||||
|
format.json { render :json => @certification }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /certifications/1/edit
|
||||||
|
def edit
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /certifications
|
||||||
|
# POST /certifications.json
|
||||||
|
def create
|
||||||
|
respond_to do |format|
|
||||||
|
if @certification.save
|
||||||
|
format.html { redirect_to Certification, :notice => 'Certification was successfully created.' }
|
||||||
|
format.json { render :json => @certification, :status => :created, :location => @certification }
|
||||||
|
else
|
||||||
|
format.html { render :action => "new" }
|
||||||
|
format.json { render :json => @certification.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# PUT /certifications/1
|
||||||
|
# PUT /certifications/1.json
|
||||||
|
def update
|
||||||
|
respond_to do |format|
|
||||||
|
if @certification.update_attributes(params[:certification])
|
||||||
|
format.html { redirect_to Certification, :notice => 'Certification was successfully updated.' }
|
||||||
|
format.json { head :no_content }
|
||||||
|
else
|
||||||
|
format.html { render :action => "edit" }
|
||||||
|
format.json { render :json => @certification.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# DELETE /certifications/1
|
||||||
|
# DELETE /certifications/1.json
|
||||||
|
def destroy
|
||||||
|
@certification.destroy
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to certifications_url }
|
||||||
|
format.json { head :no_content }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
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
|
||||||
113
app/controllers/door_logs_controller.rb
Normal file → Executable file
113
app/controllers/door_logs_controller.rb
Normal file → Executable file
@@ -1,8 +1,40 @@
|
|||||||
class DoorLogsController < ApplicationController
|
class DoorLogsController < ApplicationController
|
||||||
|
authorize_resource :except => :auto_download
|
||||||
|
before_filter :authenticate_user!, :except => :auto_download
|
||||||
|
|
||||||
# GET /door_logs
|
# GET /door_logs
|
||||||
# GET /door_logs.json
|
# GET /door_logs.json
|
||||||
def index
|
def index
|
||||||
@door_logs = DoorLog.all
|
# @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
|
||||||
@@ -10,19 +42,7 @@ class DoorLogsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /door_logs/1
|
# GET /door_logs/download
|
||||||
# GET /door_logs/1.json
|
|
||||||
# def show
|
|
||||||
# @door_log = DoorLog.find(params[:id])
|
|
||||||
#
|
|
||||||
# respond_to do |format|
|
|
||||||
# format.html # show.html.erb
|
|
||||||
# format.json { render :json => @door_log }
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# GET /door_logs/1
|
|
||||||
# GET /door_logs/1.json
|
|
||||||
def download
|
def download
|
||||||
@results = DoorLog.download_from_door
|
@results = DoorLog.download_from_door
|
||||||
|
|
||||||
@@ -32,63 +52,16 @@ class DoorLogsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# # GET /door_logs/new
|
# GET /door_logs/auto_download
|
||||||
# # GET /door_logs/new.json
|
def auto_download
|
||||||
# def new
|
@results = DoorLog.download_from_door
|
||||||
# @door_log = DoorLog.new
|
@status = DoorLog.download_status # for space_api
|
||||||
#
|
|
||||||
# respond_to do |format|
|
|
||||||
# format.html # new.html.erb
|
|
||||||
# format.json { render :json => @door_log }
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# GET /door_logs/1/edit
|
respond_to do |format|
|
||||||
# def edit
|
format.html # show.html.erb
|
||||||
# @door_log = DoorLog.find(params[:id])
|
format.json { render :json => @results }
|
||||||
# end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# POST /door_logs
|
|
||||||
# POST /door_logs.json
|
|
||||||
# def create
|
|
||||||
# @door_log = DoorLog.new(params[:door_log])
|
|
||||||
#
|
|
||||||
# respond_to do |format|
|
|
||||||
# if @door_log.save
|
|
||||||
# format.html { redirect_to @door_log, :notice => 'Door log was successfully created.' }
|
|
||||||
# format.json { render :json => @door_log, :status => :created, :location => @door_log }
|
|
||||||
# else
|
|
||||||
# format.html { render :action => "new" }
|
|
||||||
# format.json { render :json => @door_log.errors, :status => :unprocessable_entity }
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# PUT /door_logs/1
|
|
||||||
# PUT /door_logs/1.json
|
|
||||||
# def update
|
|
||||||
# @door_log = DoorLog.find(params[:id])
|
|
||||||
#
|
|
||||||
# respond_to do |format|
|
|
||||||
# if @door_log.update_attributes(params[:door_log])
|
|
||||||
# format.html { redirect_to @door_log, :notice => 'Door log was successfully updated.' }
|
|
||||||
# format.json { head :no_content }
|
|
||||||
# else
|
|
||||||
# format.html { render :action => "edit" }
|
|
||||||
# format.json { render :json => @door_log.errors, :status => :unprocessable_entity }
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# DELETE /door_logs/1
|
|
||||||
# DELETE /door_logs/1.json
|
|
||||||
# def destroy
|
|
||||||
# @door_log = DoorLog.find(params[:id])
|
|
||||||
# @door_log.destroy
|
|
||||||
#
|
|
||||||
# respond_to do |format|
|
|
||||||
# format.html { redirect_to door_logs_url }
|
|
||||||
# format.json { head :no_content }
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
end
|
end
|
||||||
|
|||||||
54
app/controllers/home_controller.rb
Executable file
54
app/controllers/home_controller.rb
Executable file
@@ -0,0 +1,54 @@
|
|||||||
|
class HomeController < ApplicationController
|
||||||
|
layout 'resources'
|
||||||
|
|
||||||
|
def index
|
||||||
|
# Alerts
|
||||||
|
if user_signed_in? && current_user.orientation.blank? then
|
||||||
|
flash[:alert] = "There's a lot more to see here, but our records show you haven't completed the new member orientation yet. If that's incorrect, please contact a volunteer."
|
||||||
|
end
|
||||||
|
# if user_signed_in? && current_user.member_status.between?(2,100) then
|
||||||
|
# flash[:alert] = "<!--
|
||||||
|
# Member: <%= current_user.member.inspect
|
||||||
|
# Level: <%= current_user.member_level.inspect
|
||||||
|
# -->
|
||||||
|
# Looks like we haven't acknowledged a recent payment for you yet. This could be because we're slow, but if in doubt please see your profile for payment instructions, consider updating your membership level to something accurate, or contact us.<br/>Thanks for supporting us!"
|
||||||
|
# end
|
||||||
|
|
||||||
|
# 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
|
||||||
54
app/controllers/ipns_controller.rb
Executable file
54
app/controllers/ipns_controller.rb
Executable file
@@ -0,0 +1,54 @@
|
|||||||
|
class IpnsController < ApplicationController
|
||||||
|
load_and_authorize_resource :ipn, :except => [:new, :create]
|
||||||
|
before_filter :authenticate_user!, :except => [:new, :create]
|
||||||
|
|
||||||
|
protect_from_forgery :except => [:create]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@ipns = Ipn.all
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@ipn = Ipn.new_from_dynamic_params(params)
|
||||||
|
@ipn.data = params.to_json
|
||||||
|
@ipn.save
|
||||||
|
render :nothing => true
|
||||||
|
#unless @ipn.validate!
|
||||||
|
# Rails.logger.error "Unable to validate IPN: #{@ipn.inspect}"
|
||||||
|
#end
|
||||||
|
end
|
||||||
|
|
||||||
|
def import
|
||||||
|
@ipn = Ipn.new_from_dynamic_params(params)
|
||||||
|
@ipn.data = params.to_json
|
||||||
|
@ipn.save
|
||||||
|
redirect_to ipn_path(@ipn)
|
||||||
|
#unless @ipn.validate!
|
||||||
|
# Rails.logger.error "Unable to validate IPN: #{@ipn.inspect}"
|
||||||
|
#end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate
|
||||||
|
if @ipn.validate!
|
||||||
|
redirect_to ipns_url, :notice => 'Valid!'
|
||||||
|
else
|
||||||
|
redirect_to ipns_url, :notice => 'INVALID'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def link
|
||||||
|
result = @ipn.link_payment
|
||||||
|
if result.first
|
||||||
|
redirect_to ipns_url, :notice => 'Payment was successfully linked.'
|
||||||
|
else
|
||||||
|
redirect_to ipns_url, :notice => result.last
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
13
app/controllers/mac_logs_controller.rb
Executable file
13
app/controllers/mac_logs_controller.rb
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
class MacLogsController < ApplicationController
|
||||||
|
load_and_authorize_resource :mac_log
|
||||||
|
before_filter :authenticate_user!
|
||||||
|
|
||||||
|
def index
|
||||||
|
@mac_logs = MacLog.desc.limit(1000)
|
||||||
|
@macs = {}
|
||||||
|
@mac_logs.each do |log|
|
||||||
|
@macs.merge!({log.mac => Mac.find_by_mac(log.mac)})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
362
app/controllers/macs_controller.rb
Executable file
362
app/controllers/macs_controller.rb
Executable file
@@ -0,0 +1,362 @@
|
|||||||
|
class MacsController < ApplicationController
|
||||||
|
load_and_authorize_resource :mac, :except => [:index, :create, :history]
|
||||||
|
#load_and_authorize_resource :user, :through => :mac, :except => [:index, :show, :scan, :import]
|
||||||
|
|
||||||
|
before_filter :arp_lookup, :only => :new
|
||||||
|
|
||||||
|
#require "active_record"
|
||||||
|
require "optparse"
|
||||||
|
#require "rubygems"
|
||||||
|
|
||||||
|
def index
|
||||||
|
recent_mac_logs_ungrouped = MacLog.last(1000)
|
||||||
|
if recent_mac_logs_ungrouped.present?
|
||||||
|
@mac_time_start_date = recent_mac_logs_ungrouped.first.created_at
|
||||||
|
recent_mac_logs = recent_mac_logs_ungrouped.group_by(&:mac)
|
||||||
|
@mac_times = {}
|
||||||
|
# Go thru each mac
|
||||||
|
recent_mac_logs.each do |mac_log|
|
||||||
|
last_active = nil
|
||||||
|
# And the entries for each mac (mac_log.first is the string, mac_log.last is the array)
|
||||||
|
mac_log.last.each do |entry|
|
||||||
|
# Find an activate followed immediately by a deactivate
|
||||||
|
if entry.action == "activate"
|
||||||
|
last_active = entry
|
||||||
|
else
|
||||||
|
if last_active && entry.action == "deactivate"
|
||||||
|
# Calculate the time difference between the two and append to this mac's total time
|
||||||
|
this_entry = @mac_times[entry.mac]
|
||||||
|
if this_entry
|
||||||
|
this_time = this_entry[:time]
|
||||||
|
else
|
||||||
|
this_time = 0
|
||||||
|
end
|
||||||
|
@mac_times[entry.mac] = {:mac => entry, :time => (entry.created_at - last_active.created_at) + this_time}
|
||||||
|
else
|
||||||
|
# No pair found; discard.
|
||||||
|
last_active = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@mac_times_sorted = @mac_times.sort{|a,b| b.last[:time] <=> a.last[:time] }
|
||||||
|
@most_active_mac = nil
|
||||||
|
@runner_up_mac = nil
|
||||||
|
@mac_times_sorted.each do |mac_time|
|
||||||
|
unless @most_active_mac
|
||||||
|
this_mac = Mac.find_by_mac(mac_time.first)
|
||||||
|
unless this_mac.hidden
|
||||||
|
@most_active_mac = this_mac
|
||||||
|
@most_active = mac_time
|
||||||
|
end
|
||||||
|
else
|
||||||
|
unless @runner_up_mac
|
||||||
|
this_mac = Mac.find_by_mac(mac_time.first)
|
||||||
|
unless this_mac.hidden
|
||||||
|
@runner_up_mac = this_mac
|
||||||
|
@runner_up = mac_time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#@active_macs = Mac.where(:active => true, :hidden => false)
|
||||||
|
#@active_macs += Mac.where(:active => true, :hidden => nil)
|
||||||
|
|
||||||
|
# De-dupe users for the public
|
||||||
|
if can? :update, Mac then
|
||||||
|
@active_macs = Mac.where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).includes(:user).order("users.name ASC")
|
||||||
|
elsif user_signed_in? then
|
||||||
|
@active_macs = Mac.where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).includes(:user).order("users.name ASC").group("users.name")
|
||||||
|
else
|
||||||
|
@active_macs = Mac.select("mac, note, user_id").where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).joins(:user).order("users.name ASC").group("users.name, mac, note, user_id")
|
||||||
|
end
|
||||||
|
|
||||||
|
@hidden_macs = Mac.where("macs.active = ? AND macs.hidden = ?", true, true).order("note ASC")
|
||||||
|
|
||||||
|
@all_macs = Mac.find(:all, :order => "LOWER(mac)")
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.json {
|
||||||
|
@filtered_macs = Mac.select("macs.mac, users.name, macs.note").where("macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?)", true, false).joins(:user)
|
||||||
|
render :json => @filtered_macs
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def history
|
||||||
|
authorize! :read_details, Mac
|
||||||
|
begin
|
||||||
|
@start_date = DateTime.parse(params[:start])
|
||||||
|
@end_date = DateTime.parse(params[:end])
|
||||||
|
rescue
|
||||||
|
@start_date = DateTime.now - 2.weeks
|
||||||
|
@end_date = DateTime.now
|
||||||
|
end
|
||||||
|
|
||||||
|
@mac_logs_by_hour = MacLog.where("created_at > ? AND created_at < ?", @start_date, @end_date).group_by{|m| m.created_at.beginning_of_hour}
|
||||||
|
@mac_log_graph = []
|
||||||
|
mac_running_balance = 0
|
||||||
|
lowest_balance = 0
|
||||||
|
@mac_logs_by_hour.each do |time, mac_log|
|
||||||
|
mac_log.each do |entry|
|
||||||
|
# Add one computer for activate, subtract one for deactivate
|
||||||
|
if entry.action == "activate"
|
||||||
|
mac_running_balance += 1
|
||||||
|
elsif entry.action == "deactivate"
|
||||||
|
mac_running_balance -= 1
|
||||||
|
end
|
||||||
|
# Keep track of the lowest number in the graph
|
||||||
|
if mac_running_balance < lowest_balance
|
||||||
|
lowest_balance = mac_running_balance
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@mac_log_graph << [time.to_time.to_i*1000,mac_running_balance]
|
||||||
|
end
|
||||||
|
|
||||||
|
if lowest_balance != 0
|
||||||
|
# Subtract a negative balance to raise everything
|
||||||
|
@mac_log_graph = @mac_log_graph.map{ |time,balance| [time, balance - lowest_balance] }
|
||||||
|
end
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.json { render :json => @mac_log_graph }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /macs/1
|
||||||
|
# GET /macs/1.json
|
||||||
|
def show
|
||||||
|
@mac = Mac.find(params[:id])
|
||||||
|
@mac_logs = MacLog.where(:mac => @mac.mac)
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # show.html.erb
|
||||||
|
format.json { render :json => @macs }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /macs/new
|
||||||
|
# GET /macs/new.json
|
||||||
|
def new
|
||||||
|
@mac = Mac.new
|
||||||
|
if can? :manage, Mac then
|
||||||
|
@users = User.accessible_by(current_ability).sort_by(&:name)
|
||||||
|
else
|
||||||
|
@users = [current_user]
|
||||||
|
end
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # new.html.erb
|
||||||
|
format.json { render :json => @mac }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /macs/1/edit
|
||||||
|
def edit
|
||||||
|
@mac = Mac.find(params[:id])
|
||||||
|
if can? :manage, Mac then
|
||||||
|
@users = User.accessible_by(current_ability).sort_by(&:name)
|
||||||
|
else
|
||||||
|
@users = [current_user]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /macs
|
||||||
|
def create
|
||||||
|
@mac = Mac.new(params[:mac])
|
||||||
|
@existing_mac = Mac.find_by_mac(@mac.mac)
|
||||||
|
if can? :manage, Mac then
|
||||||
|
@users = User.accessible_by(current_ability).sort_by(&:name)
|
||||||
|
else
|
||||||
|
@users = [current_user]
|
||||||
|
end
|
||||||
|
|
||||||
|
if @existing_mac.present?
|
||||||
|
if @existing_mac.user_id.nil?
|
||||||
|
redirect_to edit_mac_path(@existing_mac), :notice => 'This MAC already exists, edit it here:'
|
||||||
|
else
|
||||||
|
@mac.errors.add(:user,"for this MAC is already set to #{@existing_mac.user.name} -- please contact them or an admin if this is incorrect.")
|
||||||
|
render :action => "new"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @mac.save
|
||||||
|
format.html { redirect_to macs_path, :notice => 'MAC was successfully created.' }
|
||||||
|
format.json { render :json => @mac, :status => :created, :location => @mac }
|
||||||
|
else
|
||||||
|
format.html { render :action => "new" }
|
||||||
|
format.json { render :json => @mac.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# PUT /macs/1
|
||||||
|
# PUT /macs/1.json
|
||||||
|
def update
|
||||||
|
#Log who updated this
|
||||||
|
@mac = Mac.find(params[:id])
|
||||||
|
@mac.assign_attributes(params[:mac])
|
||||||
|
#@mac.user_id = params[:mac][:user_id]
|
||||||
|
authorize! :update, @mac
|
||||||
|
|
||||||
|
if can? :manage, Mac then
|
||||||
|
@users = User.accessible_by(current_ability).sort_by(&:name)
|
||||||
|
else
|
||||||
|
@users = [current_user]
|
||||||
|
end
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @mac.save
|
||||||
|
format.html { redirect_to macs_path, :notice => 'MAC was successfully updated.' }
|
||||||
|
format.json { head :no_content }
|
||||||
|
else
|
||||||
|
format.html { render :action => "edit" }
|
||||||
|
format.json { render :json => @mac.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def arp_lookup
|
||||||
|
@ip = request.env['REMOTE_ADDR']
|
||||||
|
@arp = /([0-9A-F]{2}[:-]){5}([0-9A-F]{2})/i.match(%x(arp -a | grep #{@ip}))
|
||||||
|
end
|
||||||
|
|
||||||
|
def scan
|
||||||
|
Rails.logger.info "starting scan..."
|
||||||
|
# Command line arguments
|
||||||
|
options = {};
|
||||||
|
OptionParser.new { |opts|
|
||||||
|
opts.banner = "Usage: pamela-scanner.rb --interface=en0"
|
||||||
|
|
||||||
|
options[:verbose] = true
|
||||||
|
opts.on("v", "--verbose", "Run verbosely") { |verbose|
|
||||||
|
options[:verbose] = verbose
|
||||||
|
}
|
||||||
|
|
||||||
|
options[:interface] = "eth0"
|
||||||
|
opts.on("i", "--interface=interface", "Network Interface") { |interface|
|
||||||
|
options[:interface] = interface
|
||||||
|
}
|
||||||
|
|
||||||
|
options[:max_age] = 20
|
||||||
|
opts.on("a", "--max-age=minutes", "Minutes to keep expired macs active") { |max_age|
|
||||||
|
options[:max_age] = max_age.to_i
|
||||||
|
}
|
||||||
|
|
||||||
|
options[:db_host] = "configure_me"
|
||||||
|
opts.on("r", "--db-host=host", "Database Host") { |host|
|
||||||
|
options[:db_host] = host
|
||||||
|
}
|
||||||
|
|
||||||
|
options[:db_name] = "configure_me"
|
||||||
|
opts.on("n", "--db-name=name", "Database Name") { |name|
|
||||||
|
options[:db_name] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
options[:db_user] = "configure_me"
|
||||||
|
opts.on("u", "--db-user=user", "Database User") { |user|
|
||||||
|
options[:db_user] = user
|
||||||
|
}
|
||||||
|
|
||||||
|
options[:db_password] = "configure_me"
|
||||||
|
opts.on("p", "--db-password=password", "Database Password") { |password|
|
||||||
|
options[:db_password] = password
|
||||||
|
}
|
||||||
|
|
||||||
|
}.parse!
|
||||||
|
|
||||||
|
# Open the database
|
||||||
|
#ActiveRecord::Base::establish_connection(
|
||||||
|
# :adapter => "mysql2",
|
||||||
|
# :host => options[:db_host],
|
||||||
|
# :database => options[:db_name],
|
||||||
|
# :username => options[:db_user],
|
||||||
|
# :password => options[:db_password])
|
||||||
|
|
||||||
|
#class Mac < ActiveRecord::Base
|
||||||
|
#end
|
||||||
|
|
||||||
|
#class MacLog < ActiveRecord::Base
|
||||||
|
#end
|
||||||
|
|
||||||
|
# Scan the network for mac addresses
|
||||||
|
macs = {};
|
||||||
|
command = sprintf("arp-scan -R --interface=%s --localnet", options[:interface])
|
||||||
|
if options[:verbose]
|
||||||
|
Rails.logger.info "Running [#{command}]"
|
||||||
|
end
|
||||||
|
IO.popen(command) { |stdin|
|
||||||
|
result = stdin.read()
|
||||||
|
result.lines.each { |line|
|
||||||
|
Rails.logger.info "Reading stdin: "+line.inspect
|
||||||
|
next if line !~ /^([\d\.]+)\s+([[:xdigit:]:]+)\s/;
|
||||||
|
macs[($2).downcase] = ($1).downcase;
|
||||||
|
}
|
||||||
|
Rails.logger.info "STDIN:"+result.lines.count.inspect
|
||||||
|
@macs = macs.dup # make a copy for output in the view
|
||||||
|
Rails.logger.info "MACS:"+@macs.inspect
|
||||||
|
}
|
||||||
|
|
||||||
|
# Scan the existing macs and update each record as necessary
|
||||||
|
Mac.find(:all).each { |entry|
|
||||||
|
mac = entry.mac.downcase
|
||||||
|
ip = entry.ip
|
||||||
|
if macs.has_key?(mac) # if our scan shows this mac
|
||||||
|
if ! entry.active || ! entry.since
|
||||||
|
Rails.logger.info "Activating #{mac} at #{ip}" if options[:verbose]
|
||||||
|
entry.since = Time.now
|
||||||
|
MacLog.new(:mac => mac, :ip => ip, :action => "activate").save
|
||||||
|
end
|
||||||
|
entry.active = 1
|
||||||
|
entry.ip = ip
|
||||||
|
entry.refreshed = Time.now
|
||||||
|
entry.save
|
||||||
|
macs.delete(mac)
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
# Entry is no longer current
|
||||||
|
if entry.active
|
||||||
|
ageMinutes = ((Time.now - entry.refreshed)/60).to_i
|
||||||
|
next if ageMinutes < options[:max_age]
|
||||||
|
Rails.logger.info "Deactivating #{mac}, #{ageMinutes} minutes old" if options[:verbose]
|
||||||
|
entry.active = 0
|
||||||
|
entry.save
|
||||||
|
MacLog.new(:mac => mac, :ip => ip, :action => "deactivate").save
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add entries for any macs not already in the db
|
||||||
|
macs.each { |mac, ip|
|
||||||
|
Rails.logger.info "Activating new entry #{mac} at #{ip}" if options[:verbose]
|
||||||
|
Mac.new(:mac => mac, :ip => ip, :active => 1, :since => Time.now, :refreshed => Time.now).save
|
||||||
|
Rails.logger.info MacLog.new(:mac => mac, :ip => ip, :action => "activate").save
|
||||||
|
}
|
||||||
|
|
||||||
|
@log = MacLog.all
|
||||||
|
|
||||||
|
end #def scan
|
||||||
|
|
||||||
|
|
||||||
|
def import
|
||||||
|
|
||||||
|
require 'csv'
|
||||||
|
|
||||||
|
csv_text = File.read('mac_log.csv')
|
||||||
|
csv = CSV.parse(csv_text)
|
||||||
|
|
||||||
|
@output = []
|
||||||
|
|
||||||
|
csv.each do |row|
|
||||||
|
@output += [row[1], Mac.create({:mac => row[0], :note => row[1], :hidden => row[2]}) ]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
143
app/controllers/payments_controller.rb
Executable file
143
app/controllers/payments_controller.rb
Executable file
@@ -0,0 +1,143 @@
|
|||||||
|
class PaymentsController < ApplicationController
|
||||||
|
load_and_authorize_resource :payment
|
||||||
|
#load_and_authorize_resource :user, :through => :payment
|
||||||
|
before_filter :authenticate_user!
|
||||||
|
|
||||||
|
# Load users and certs based on current ability
|
||||||
|
before_filter do
|
||||||
|
@users = User.where(:hidden => false).where("member_level > 10").accessible_by(current_ability).sort_by(&:name_with_payee_and_member_level)
|
||||||
|
end
|
||||||
|
|
||||||
|
before_filter :only => [:create, :update] do
|
||||||
|
@payment.created_by = current_user.id
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /payments
|
||||||
|
# GET /payments.json
|
||||||
|
def index
|
||||||
|
@payments = @payments.order("date DESC")
|
||||||
|
@graph = { :members => chart("members"),
|
||||||
|
:total => chart("total"),
|
||||||
|
:basic => chart("basic"),
|
||||||
|
:associate => chart("associate")}
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # index.html.erb
|
||||||
|
format.json { render :json => @payments }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Private method for index charts
|
||||||
|
def chart name
|
||||||
|
chart_name = name || "total"
|
||||||
|
if chart_name == "total"
|
||||||
|
chart_type = [25, 50, 100]
|
||||||
|
elsif chart_name == "members"
|
||||||
|
chart_type = [25, 50, 100]
|
||||||
|
elsif chart_name == "basic"
|
||||||
|
chart_type = [50]
|
||||||
|
elsif chart_name == "associate"
|
||||||
|
chart_type = [25]
|
||||||
|
else
|
||||||
|
chart_type = []
|
||||||
|
end
|
||||||
|
|
||||||
|
payment_months = @payments.sort_by(&:date).group_by{ |p| p.date.beginning_of_month }
|
||||||
|
@payments_by_month = []
|
||||||
|
payment_months.each do |month|
|
||||||
|
# Calculate sum of amounts for each month and store at end of month array
|
||||||
|
@payments_by_month << [month.first.to_time.to_i*1000, month.last.sum{|p|
|
||||||
|
amount = amount_or_level(p)
|
||||||
|
if chart_type.include?(amount)
|
||||||
|
if chart_name == "members"
|
||||||
|
1 # Output 1 to count members
|
||||||
|
else
|
||||||
|
amount # Output dollars to count amount
|
||||||
|
end
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
}]
|
||||||
|
end
|
||||||
|
|
||||||
|
return @payments_by_month
|
||||||
|
end
|
||||||
|
|
||||||
|
def amount_or_level p
|
||||||
|
if p.amount
|
||||||
|
return p.amount.to_i
|
||||||
|
else
|
||||||
|
if p.user
|
||||||
|
Rails.logger.info p.user.member_level
|
||||||
|
return p.user.member_level.to_i
|
||||||
|
else
|
||||||
|
Rails.logger.info p.inspect
|
||||||
|
Rails.logger.info p.user.inspect
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /payments/1
|
||||||
|
# GET /payments/1.json
|
||||||
|
def show
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # show.html.erb
|
||||||
|
format.json { render :json => @payment }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /payments/new
|
||||||
|
# GET /payments/new.json
|
||||||
|
def new
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # new.html.erb
|
||||||
|
format.json { render :json => @payment }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /payments/1/edit
|
||||||
|
def edit
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /payments
|
||||||
|
# POST /payments.json
|
||||||
|
def create
|
||||||
|
Rails.logger.warn "payment:"
|
||||||
|
Rails.logger.warn @payment.inspect
|
||||||
|
respond_to do |format|
|
||||||
|
if @payment.save
|
||||||
|
format.html { redirect_to payments_url, :notice => 'Payment was successfully created.' }
|
||||||
|
format.json { render :json => @payment, :status => :created, :location => @payment }
|
||||||
|
else
|
||||||
|
format.html { render :action => "new" }
|
||||||
|
format.json { render :json => @payment.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# PUT /payments/1
|
||||||
|
# PUT /payments/1.json
|
||||||
|
def update
|
||||||
|
respond_to do |format|
|
||||||
|
if @payment.update_attributes(params[:payment])
|
||||||
|
format.html { redirect_to payments_url, :notice => 'Payment was successfully updated.' }
|
||||||
|
format.json { head :no_content }
|
||||||
|
else
|
||||||
|
format.html { render :action => "edit" }
|
||||||
|
format.json { render :json => @payment.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# DELETE /payments/1
|
||||||
|
# DELETE /payments/1.json
|
||||||
|
def destroy
|
||||||
|
@payment.destroy
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to payments_url }
|
||||||
|
format.json { head :no_content }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
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
|
||||||
14
app/controllers/registrations_controller.rb
Executable file
14
app/controllers/registrations_controller.rb
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
class RegistrationsController < Devise::RegistrationsController
|
||||||
|
protected
|
||||||
|
|
||||||
|
# After signup
|
||||||
|
def after_sign_up_path_for(resource)
|
||||||
|
'/users/edit/?flash=welcome_msg'
|
||||||
|
end
|
||||||
|
|
||||||
|
# After edit
|
||||||
|
def after_update_path_for(resource)
|
||||||
|
'/users/edit'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
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
|
||||||
94
app/controllers/user_certifications_controller.rb
Executable file
94
app/controllers/user_certifications_controller.rb
Executable file
@@ -0,0 +1,94 @@
|
|||||||
|
class UserCertificationsController < ApplicationController
|
||||||
|
load_and_authorize_resource :user_certification
|
||||||
|
#load_and_authorize_resource :user, :through => :user_certification
|
||||||
|
#load_and_authorize_resource :certification, :through => :user_certification
|
||||||
|
before_filter :authenticate_user!
|
||||||
|
|
||||||
|
# Load users and certs based on current ability
|
||||||
|
before_filter :only => [:new, :edit, :create, :update] do
|
||||||
|
@users = User.where(:hidden => [false,nil]).accessible_by(current_ability).sort_by(&:name)
|
||||||
|
@certifications = Certification.accessible_by(current_ability).sort_by(&:name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /user_certifications
|
||||||
|
# GET /user_certifications.json
|
||||||
|
def index
|
||||||
|
@grouped_user_certs = @user_certifications.group_by { |uc| uc.certification }
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # index.html.erb
|
||||||
|
format.json { render :json => @user_certifications }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /user_certifications/1
|
||||||
|
# GET /user_certifications/1.json
|
||||||
|
def show
|
||||||
|
@created_by = User.find(@user_certification.created_by) unless @user_certification.created_by.blank?
|
||||||
|
@updated_by = User.find(@user_certification.updated_by) unless @user_certification.updated_by.blank?
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # show.html.erb
|
||||||
|
format.json { render :json => @user_certification }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /user_certifications/new
|
||||||
|
# GET /user_certifications/new.json
|
||||||
|
def new
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # new.html.erb
|
||||||
|
format.json { render :json => @user_certification }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /user_certifications/1/edit
|
||||||
|
def edit
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /user_certifications
|
||||||
|
# POST /user_certifications.json
|
||||||
|
def create
|
||||||
|
#Log who created this
|
||||||
|
@user_certification.created_by = current_user.id
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @user_certification.save
|
||||||
|
format.html { redirect_to UserCertification, :notice => 'User certification was successfully created.' }
|
||||||
|
format.json { render :json => @user_certification, :status => :created, :location => @user_certification }
|
||||||
|
else
|
||||||
|
format.html { render :action => "new" }
|
||||||
|
format.json { render :json => @user_certification.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# PUT /user_certifications/1
|
||||||
|
# PUT /user_certifications/1.json
|
||||||
|
def update
|
||||||
|
#Log who updated this
|
||||||
|
@user_certification.updated_by = current_user.id
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @user_certification.update_attributes(params[:user_certification])
|
||||||
|
format.html { redirect_to UserCertification, :notice => 'User certification was successfully updated.' }
|
||||||
|
format.json { head :no_content }
|
||||||
|
else
|
||||||
|
format.html { render :action => "edit" }
|
||||||
|
format.json { render :json => @user_certification.errors, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# DELETE /user_certifications/1
|
||||||
|
# DELETE /user_certifications/1.json
|
||||||
|
def destroy
|
||||||
|
@user_certification.destroy
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to user_certifications_url }
|
||||||
|
format.json { head :no_content }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
144
app/controllers/users_controller.rb
Normal file → Executable file
144
app/controllers/users_controller.rb
Normal file → Executable file
@@ -1,52 +1,118 @@
|
|||||||
class UsersController < ApplicationController
|
class UsersController < ApplicationController
|
||||||
|
load_and_authorize_resource
|
||||||
|
before_filter :authenticate_user!
|
||||||
|
layout 'resources'
|
||||||
|
|
||||||
|
def sort_by_cert(certs,id)
|
||||||
|
result = 0
|
||||||
|
certs.each do |c|
|
||||||
|
if c.id == id
|
||||||
|
result = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
# GET /users
|
# GET /users
|
||||||
# GET /users.json
|
# GET /users.json
|
||||||
def index
|
def index
|
||||||
@users = User.all
|
unless params[:full] # by default, show summary
|
||||||
|
|
||||||
|
@users = @users.active.sort_by{|u| [-u.member_level, u.name.downcase] }
|
||||||
|
#@users.paying + @users.volunteer
|
||||||
|
#.joins(:payments).where("payments.date > ? OR ", (DateTime.now - 60.days)).uniq
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { render 'summary', layout: 'resources' }
|
||||||
|
format.json { render :json => @users }
|
||||||
|
end
|
||||||
|
|
||||||
|
else # show full
|
||||||
|
|
||||||
|
case params[:sort]
|
||||||
|
when "name"
|
||||||
|
@users = @users.sort_by{ |u| u.name.downcase }
|
||||||
|
when "cert"
|
||||||
|
@users = @users.sort_by{ |u| [-sort_by_cert(u.certifications,params[:cert].to_i),u.name.downcase] }
|
||||||
|
when "orientation"
|
||||||
|
@users = @users.sort_by{ |u| [-u.orientation.to_i,u.name.downcase] }
|
||||||
|
when "waiver"
|
||||||
|
@users = @users.sort_by{ |u| [-u.contract_date.to_i,u.name.downcase] }
|
||||||
|
when "member"
|
||||||
|
@users = @users.sort_by{ |u| [-u.member_status.to_i,u.name.downcase] }
|
||||||
|
when "card"
|
||||||
|
@users = @users.sort_by{ |u| [-u.cards.count,u.name.downcase] }
|
||||||
|
when "instructor"
|
||||||
|
@users = @users.sort{ |a,b| [b.instructor.to_s,a.name] <=> [a.instructor.to_s,b.name] }
|
||||||
|
when "admin"
|
||||||
|
@users = @users.sort{ |a,b| [b.admin.to_s,a.name] <=> [a.admin.to_s,b.name] }
|
||||||
|
else
|
||||||
|
@users = @users.sort_by{ |u| u.name.downcase }
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
@user = User.find(params[:id])
|
@payments = Payment.where(:user_id => @user.id).order('date desc').limit(10)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # show.html.erb
|
format.html # show.html.erb
|
||||||
format.json { render :json => @user }
|
format.json { render :json => @user }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# PUT /users/1/upload
|
def compose_email
|
||||||
def upload
|
@user = User.find(params[:user_id])
|
||||||
@user = User.find(params[:id])
|
end
|
||||||
@upload_result = @user.upload_to_door
|
|
||||||
|
|
||||||
respond_to do |format|
|
def send_email
|
||||||
format.html # show.html.erb
|
@user = User.find(params[:user_id])
|
||||||
format.json { render :json => @upload_result }
|
@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
|
||||||
end
|
end
|
||||||
|
|
||||||
# PUT /users/upload_all
|
# GET /user_summary/1
|
||||||
def upload_all
|
def user_summary
|
||||||
@upload_result = User.upload_all_to_door
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # show.html.erb
|
format.html { render :partial => "user_summary" } # show.html.erb
|
||||||
format.json { render :json => @upload_result }
|
format.json { render :json => @user }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /users/new
|
# GET /users/new
|
||||||
# GET /users/new.json
|
# GET /users/new.json
|
||||||
def new
|
def new
|
||||||
@user = User.new
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # new.html.erb
|
format.html # new.html.erb
|
||||||
format.json { render :json => @user }
|
format.json { render :json => @user }
|
||||||
@@ -55,13 +121,13 @@ class UsersController < ApplicationController
|
|||||||
|
|
||||||
# GET /users/1/edit
|
# GET /users/1/edit
|
||||||
def edit
|
def edit
|
||||||
@user = User.find(params[:id])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# POST /users
|
# POST /users
|
||||||
# POST /users.json
|
# POST /users.json
|
||||||
def create
|
def create
|
||||||
@user = User.new(params[:user])
|
# 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
|
||||||
@@ -77,7 +143,9 @@ class UsersController < ApplicationController
|
|||||||
# PUT /users/1
|
# PUT /users/1
|
||||||
# PUT /users/1.json
|
# PUT /users/1.json
|
||||||
def update
|
def update
|
||||||
@user = User.find(params[:id])
|
# 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])
|
||||||
@@ -90,14 +158,44 @@ class UsersController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# GET /users/merge
|
||||||
|
def merge_view
|
||||||
|
@users = @users.sort_by(&:name)
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html # merge_view.html.erb
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /users/merge
|
||||||
|
def merge_action
|
||||||
|
@user_to_keep = User.find(params[:user][:to_keep])
|
||||||
|
Rails.logger.info "USER TO KEEP:"
|
||||||
|
Rails.logger.info @user_to_keep.inspect
|
||||||
|
@user_to_merge = User.find(params[:user][:to_merge])
|
||||||
|
Rails.logger.info "USER TO MERGE:"
|
||||||
|
Rails.logger.info @user_to_merge.inspect
|
||||||
|
|
||||||
|
@user_to_keep.absorb_user(@user_to_merge)
|
||||||
|
|
||||||
|
Rails.logger.info "RESULT:"
|
||||||
|
Rails.logger.info @user_to_keep.inspect
|
||||||
|
Rails.logger.info @user_to_keep.cards.inspect
|
||||||
|
Rails.logger.info @user_to_keep.user_certifications.inspect
|
||||||
|
Rails.logger.info @user_to_keep.payments.inspect
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to @user_to_keep, :notice => 'Users successfully merged.' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# DELETE /users/1
|
# DELETE /users/1
|
||||||
# DELETE /users/1.json
|
# DELETE /users/1.json
|
||||||
def destroy
|
def destroy
|
||||||
@user = User.find(params[:id])
|
|
||||||
@user.destroy
|
@user.destroy
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to users_url }
|
format.html { redirect_to users_url, :notice => 'User successfully deleted.' }
|
||||||
format.json { head :no_content }
|
format.json { head :no_content }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
29
app/helpers/application_helper.rb
Normal file → Executable file
29
app/helpers/application_helper.rb
Normal file → Executable file
@@ -1,2 +1,31 @@
|
|||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
|
@payment_methods = [[nil],["PayPal"],["Dwolla"],["Bill Pay"],["Check"],["Cash"],["Other"]]
|
||||||
|
@payment_instructions = {nil => nil, :paypal => "Set up a monthly recurring payment to hslfinances@gmail.com", :dwolla => "Set up a monthly recurring payment to hslfinances@gmail.com", :billpay => "Have your bank send a monthly check to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201", :check => "Mail to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201 OR put in the drop safe at the Lab with a deposit slip firmly attached each month.", :cash => "Put in the drop safe at the Lab with a deposit slip firmly attached each month.", :other => "Hmm... talk to a Treasurer!"}
|
||||||
|
|
||||||
|
def sort_link(title, column, options = {})
|
||||||
|
condition = options[:unless] if options.has_key?(:unless)
|
||||||
|
sort_dir = params[:dir] == 'up' ? 'down' : 'up'
|
||||||
|
link_to_unless condition, title, request.parameters.merge( {:sort => column, :dir => sort_dir} )
|
||||||
|
end
|
||||||
|
|
||||||
|
def li_link_to(name = nil, options = nil, html_options = nil, &block)
|
||||||
|
html_options, options, name = options, name, block if block_given?
|
||||||
|
options ||= {}
|
||||||
|
|
||||||
|
html_options = convert_options_to_data_attributes(options, html_options)
|
||||||
|
|
||||||
|
url = url_for(options)
|
||||||
|
html_options['href'] ||= url
|
||||||
|
|
||||||
|
if current_page?(url)
|
||||||
|
content_tag(:li, class: "active") do
|
||||||
|
content_tag(:a, name || url, html_options, &block)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
content_tag(:li) do
|
||||||
|
content_tag(:a, name || url, html_options, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
2
app/helpers/certifications_helper.rb
Executable file
2
app/helpers/certifications_helper.rb
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
module CertificationsHelper
|
||||||
|
end
|
||||||
0
app/helpers/door_logs_helper.rb
Normal file → Executable file
0
app/helpers/door_logs_helper.rb
Normal file → Executable file
2
app/helpers/home_helper.rb
Executable file
2
app/helpers/home_helper.rb
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
module HomeHelper
|
||||||
|
end
|
||||||
2
app/helpers/ipn_helper.rb
Executable file
2
app/helpers/ipn_helper.rb
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
module IpnHelper
|
||||||
|
end
|
||||||
2
app/helpers/mac_logs_helper.rb
Executable file
2
app/helpers/mac_logs_helper.rb
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
module MacLogsHelper
|
||||||
|
end
|
||||||
2
app/helpers/pamela_helper.rb
Executable file
2
app/helpers/pamela_helper.rb
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
module PamelaHelper
|
||||||
|
end
|
||||||
2
app/helpers/payments_helper.rb
Executable file
2
app/helpers/payments_helper.rb
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
module PaymentsHelper
|
||||||
|
end
|
||||||
2
app/helpers/user_certifications_helper.rb
Executable file
2
app/helpers/user_certifications_helper.rb
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
module UserCertificationsHelper
|
||||||
|
end
|
||||||
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
|
||||||
20
app/mailers/user_mailer.rb
Executable file
20
app/mailers/user_mailer.rb
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
class UserMailer < ActionMailer::Base
|
||||||
|
default :from => "no-reply@heatsynclabs.org"
|
||||||
|
|
||||||
|
def new_user_email(user)
|
||||||
|
@user = user
|
||||||
|
@url = "http://members.heatsynclabs.org"
|
||||||
|
|
||||||
|
mail(:to => 'member-notifications@heatsynclabs.org',
|
||||||
|
:subject => "New HSL Member: "+user.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def email(to_user,from_user,subject,body)
|
||||||
|
@url = "http://members.heatsynclabs.org"
|
||||||
|
@body = body
|
||||||
|
@from_user = from_user
|
||||||
|
|
||||||
|
mail(:to => to_user.email,
|
||||||
|
:subject => "HSL Message: "+subject)
|
||||||
|
end
|
||||||
|
end
|
||||||
0
app/models/.gitkeep
Normal file → Executable file
0
app/models/.gitkeep
Normal file → Executable file
90
app/models/ability.rb
Executable file
90
app/models/ability.rb
Executable file
@@ -0,0 +1,90 @@
|
|||||||
|
class Ability
|
||||||
|
include CanCan::Ability
|
||||||
|
|
||||||
|
def initialize(user)
|
||||||
|
can :read, Mac # Anonymous can read mac
|
||||||
|
can :scan, Mac # Need anonymous so CRON can scan
|
||||||
|
can :read, Resource
|
||||||
|
can :read, ResourceCategory
|
||||||
|
|
||||||
|
if !user.nil?
|
||||||
|
|
||||||
|
# By default, users can only see their own stuff
|
||||||
|
can :read, Card, :user_id => user.id
|
||||||
|
can :read, Certification
|
||||||
|
can :read_details, Mac
|
||||||
|
can [:update], Mac, :user_id => nil
|
||||||
|
can [:create,:update], Mac, :user_id => user.id
|
||||||
|
can [:create,:update,:destroy], Resource, :user_id => user.id
|
||||||
|
can :read, Payment, :user_id => user.id
|
||||||
|
can :read, UserCertification, :user_id => user.id
|
||||||
|
can :read, User, :id => user.id #TODO: why can users update themselves? Maybe because Devise doesn't check users/edit?
|
||||||
|
can :compose_email, User
|
||||||
|
can :send_email, User
|
||||||
|
|
||||||
|
if user.card_access_enabled
|
||||||
|
can :access_doors_remotely, :door_access
|
||||||
|
can :authorize, Card # used for interlock card/certification auth
|
||||||
|
end
|
||||||
|
|
||||||
|
# Instructors can manage certs and see users
|
||||||
|
if user.instructor?
|
||||||
|
can :manage, Certification
|
||||||
|
can [:create,:read], User, :hidden => [nil,false]
|
||||||
|
can [:create,:read], UserCertification
|
||||||
|
can [:update,:destroy], UserCertification, :created_by => user.id
|
||||||
|
end
|
||||||
|
# Users can see others' stuff if they've been oriented
|
||||||
|
unless user.orientation.blank?
|
||||||
|
can [:read,:new_member_report,:activity], User, :hidden => [nil,false]
|
||||||
|
can :read, UserCertification
|
||||||
|
can [:create,:update], Resource, :user_id => [nil,user.id]
|
||||||
|
can [:create,:update,:destroy], ResourceCategory
|
||||||
|
end
|
||||||
|
|
||||||
|
# Accountants can manage payments
|
||||||
|
if user.accountant?
|
||||||
|
can :manage, Payment
|
||||||
|
can :manage, Ipn
|
||||||
|
can :manage, PaypalCsv
|
||||||
|
end
|
||||||
|
|
||||||
|
# Admins can manage all
|
||||||
|
if user.admin?
|
||||||
|
can :manage, :all
|
||||||
|
end
|
||||||
|
|
||||||
|
# Prevent most destruction for now
|
||||||
|
#cannot :destroy, User
|
||||||
|
#cannot :destroy, Card
|
||||||
|
cannot :destroy, Certification
|
||||||
|
cannot :destroy, Mac
|
||||||
|
cannot :destroy, MacLog
|
||||||
|
#cannot :destroy, UserCertification
|
||||||
|
cannot :destroy, DoorLog
|
||||||
|
# no exception for destroying payments
|
||||||
|
end
|
||||||
|
# Define abilities for the passed in user here. For example:
|
||||||
|
#
|
||||||
|
# user ||= User.new # guest user (not logged in)
|
||||||
|
# if user.admin?
|
||||||
|
# can :manage, :all
|
||||||
|
# else
|
||||||
|
# can :read, :all
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# The first argument to `can` is the action you are giving the user permission to do.
|
||||||
|
# If you pass :manage it will apply to every action. Other common actions here are
|
||||||
|
# :read, :create, :update and :destroy.
|
||||||
|
#
|
||||||
|
# The second argument is the resource the user can perform the action on. If you pass
|
||||||
|
# :all it will apply to every resource. Otherwise pass a Ruby class of the resource.
|
||||||
|
#
|
||||||
|
# The third argument is an optional hash of conditions to further filter the objects.
|
||||||
|
# For example, here the user can only update published articles.
|
||||||
|
#
|
||||||
|
# can :update, Article, :published => true
|
||||||
|
#
|
||||||
|
# See the wiki for details: https://github.com/ryanb/cancan/wiki/Defining-Abilities
|
||||||
|
end
|
||||||
|
end
|
||||||
58
app/models/card.rb
Executable file
58
app/models/card.rb
Executable file
@@ -0,0 +1,58 @@
|
|||||||
|
class Card < ActiveRecord::Base
|
||||||
|
require 'open-uri'
|
||||||
|
|
||||||
|
attr_accessible :id, :user_id, :name, :card_number, :card_permissions
|
||||||
|
validates_presence_of :user_id, :card_number, :card_permissions
|
||||||
|
validates_uniqueness_of :id, :card_number
|
||||||
|
belongs_to :user
|
||||||
|
|
||||||
|
def upload_to_door
|
||||||
|
# load config values
|
||||||
|
door_access_url = APP_CONFIG['door_access_url']
|
||||||
|
door_access_password = APP_CONFIG['door_access_password']
|
||||||
|
|
||||||
|
cardid = self.id.to_s.rjust(3, '0')
|
||||||
|
cardperm = self.card_permissions.to_s.rjust(3, '0')
|
||||||
|
cardnum = self.card_number.rjust(8, '0')
|
||||||
|
|
||||||
|
# login and send the command all in one go (auto-logout is a feature of the arduino when used this way)
|
||||||
|
source = open("#{door_access_url}?m#{cardid}&p#{cardperm}&t#{cardnum}&e=#{door_access_password}").read
|
||||||
|
results = source.scan(/cur/)
|
||||||
|
|
||||||
|
if(results.size > 0) then
|
||||||
|
#only return true if we got some kind of decent response
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
# We didn't get a decent response.
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.upload_all_to_door
|
||||||
|
@cards = Card.all
|
||||||
|
@end_results = Array.new
|
||||||
|
|
||||||
|
# load config values
|
||||||
|
door_access_url = APP_CONFIG['door_access_url']
|
||||||
|
door_access_password = APP_CONFIG['door_access_password']
|
||||||
|
|
||||||
|
@cards.each do |u|
|
||||||
|
cardid = u.id.to_s.rjust(3, '0')
|
||||||
|
cardperm = u.card_permissions.to_s.rjust(3, '0')
|
||||||
|
cardnum = u.card_number.rjust(8, '0')
|
||||||
|
|
||||||
|
# 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/)
|
||||||
|
|
||||||
|
if(results.size > 0) then
|
||||||
|
#only return true if we got some kind of decent response
|
||||||
|
@end_results.push([cardid,"OK"])
|
||||||
|
else
|
||||||
|
@end_results.push([cardid,"FAIL"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return @end_results
|
||||||
|
end
|
||||||
|
end
|
||||||
7
app/models/certification.rb
Executable file
7
app/models/certification.rb
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
class Certification < ActiveRecord::Base
|
||||||
|
attr_accessible :description, :name, :slug
|
||||||
|
has_many :user_certifications
|
||||||
|
has_many :users, :through => :user_certifications
|
||||||
|
|
||||||
|
validates_presence_of :name, :slug
|
||||||
|
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
|
||||||
140
app/models/door_log.rb
Normal file → Executable file
140
app/models/door_log.rb
Normal file → Executable file
@@ -2,15 +2,139 @@ 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
|
||||||
# do shit here
|
# load config values
|
||||||
source = open("http://192.168.1.177?e=1234").read
|
door_access_url = APP_CONFIG['door_access_url']
|
||||||
results = source.scan(/authok/)
|
door_access_password = APP_CONFIG['door_access_password']
|
||||||
|
|
||||||
|
# connect to door access system
|
||||||
|
source = open("#{door_access_url}?e=#{door_access_password}").read
|
||||||
|
results = source.scan(/ok/)
|
||||||
if(results.size > 0) then
|
if(results.size > 0) then
|
||||||
@end_results = Array.new
|
@end_results = Array.new
|
||||||
|
|
||||||
#only continue if we've got an OK login
|
#only continue if we've got an OK login
|
||||||
source = open("http://192.168.1.177?z").read
|
source = open("#{door_access_url}?z").read
|
||||||
results = source.scan(/(.*): (.*)\r\n/)
|
results = source.scan(/(.*): (.*)\r\n/)
|
||||||
|
|
||||||
results.each do |r|
|
results.each do |r|
|
||||||
@@ -20,9 +144,9 @@ class DoorLog < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
#clear log
|
#clear log
|
||||||
open("http://192.168.1.177?y")
|
open("#{door_access_url}?y")
|
||||||
#logout
|
#logout
|
||||||
open("http://192.168.1.177?e=0000")
|
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
|
||||||
@@ -37,4 +161,8 @@ class DoorLog < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.key_legend
|
||||||
|
{'G' => "Granted", "R" => "Read", "D" => "Denied",
|
||||||
|
'g' => "granted", "r" => "read", "d" => "denied"}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
95
app/models/ipn.rb
Executable file
95
app/models/ipn.rb
Executable file
@@ -0,0 +1,95 @@
|
|||||||
|
require 'net/http'
|
||||||
|
class Ipn < ActiveRecord::Base
|
||||||
|
attr_accessible :data
|
||||||
|
belongs_to :payment
|
||||||
|
|
||||||
|
after_create :create_payment
|
||||||
|
|
||||||
|
def date_parsed
|
||||||
|
begin
|
||||||
|
Date.strptime(self.payment_date, "%H:%M:%S %b %e, %Y %Z")
|
||||||
|
rescue
|
||||||
|
Date.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.new_from_dynamic_params(params)
|
||||||
|
ipn = Ipn.new()
|
||||||
|
|
||||||
|
ipn.attributes.each do |c|
|
||||||
|
unless params[c.first.to_sym].nil?
|
||||||
|
ipn[c.first.to_sym] = params[c.first.to_sym]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ipn
|
||||||
|
end
|
||||||
|
|
||||||
|
# Post back to Paypal to make sure it's valid
|
||||||
|
def validate!
|
||||||
|
uri = URI.parse('https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate')
|
||||||
|
|
||||||
|
http = Net::HTTP.new(uri.host, uri.port)
|
||||||
|
http.open_timeout = 60
|
||||||
|
http.read_timeout = 60
|
||||||
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||||
|
http.use_ssl = true
|
||||||
|
response = http.post(uri.request_uri, self.data,
|
||||||
|
'Content-Length' => "#{self.data.size}",
|
||||||
|
'User-Agent' => "Ruby on Rails"
|
||||||
|
).body
|
||||||
|
|
||||||
|
unless ["VERIFIED", "INVALID"].include?(response)
|
||||||
|
Rails.logger.error "Faulty paypal result: #{response}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
unless response == "VERIFIED"
|
||||||
|
Rails.logger.error "Invalid IPN: #{response}"
|
||||||
|
Rails.logger.error "Data: #{self.data}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_payment
|
||||||
|
create_payment
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def create_payment
|
||||||
|
# find user by email, then by payee
|
||||||
|
user = User.where("lower(email) = ?", self._from_email_address.downcase).first
|
||||||
|
user = User.where("lower(payee) = ?", self._from_email_address.downcase).first if user.nil? && self._from_email_address.present?
|
||||||
|
|
||||||
|
# Only create payments if the IPN matches a member
|
||||||
|
if user.present?
|
||||||
|
# And is a payment (not a cancellation, etc)
|
||||||
|
payment_types = ["subscr_payment","send_money"]
|
||||||
|
if payment_types.include?(self.txn_type)
|
||||||
|
# And a member level
|
||||||
|
if User.member_levels[self.payment_gross.to_i].present?
|
||||||
|
payment = Payment.new
|
||||||
|
payment.date = Date.strptime(self.payment_date, "%H:%M:%S %b %e, %Y %Z")
|
||||||
|
payment.user_id = user.id
|
||||||
|
payment.amount = self.payment_gross
|
||||||
|
if payment.save
|
||||||
|
self.payment_id = payment.id
|
||||||
|
self.save!
|
||||||
|
else
|
||||||
|
return [false, "Unable to link payment. Payment error: #{payment.errors.full_messages.first}"]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return [false, "Unable to link payment. Couldn't find membership level '#{self.payment_gross.to_i}'."]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return [false, "Unable to link payment. Transaction is a '#{self.txn_type}' instead of '#{payment_types.inspect}'."]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return [false, "Unable to link payment. Couldn't find user/payee '#{self.payer_email}'."]
|
||||||
|
end
|
||||||
|
|
||||||
|
return [true]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
6
app/models/mac.rb
Executable file
6
app/models/mac.rb
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
class Mac < ActiveRecord::Base
|
||||||
|
belongs_to :user
|
||||||
|
attr_accessible :active, :ip, :mac, :refreshed, :since, :hidden, :note, :user_id
|
||||||
|
|
||||||
|
validates_uniqueness_of :mac, :case_sensitive => false
|
||||||
|
end
|
||||||
5
app/models/mac_log.rb
Executable file
5
app/models/mac_log.rb
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
class MacLog < ActiveRecord::Base
|
||||||
|
attr_accessible :action, :ip, :mac
|
||||||
|
|
||||||
|
scope :desc, order("mac_logs.created_at DESC")
|
||||||
|
end
|
||||||
17
app/models/payment.rb
Executable file
17
app/models/payment.rb
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
class Payment < ActiveRecord::Base
|
||||||
|
belongs_to :user
|
||||||
|
has_one :ipn
|
||||||
|
has_one :paypal_csv
|
||||||
|
attr_accessible :date, :user_id, :created_by, :amount
|
||||||
|
|
||||||
|
validates_presence_of :user_id, :date, :amount # not created_by
|
||||||
|
validates_uniqueness_of :date, :scope => :user_id, :message => ' of payment already exists for this user.'
|
||||||
|
|
||||||
|
def human_date
|
||||||
|
if date.year < DateTime.now.year
|
||||||
|
date.strftime("%b %e, %y")
|
||||||
|
else
|
||||||
|
date.strftime("%b %e")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
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
|
||||||
271
app/models/user.rb
Normal file → Executable file
271
app/models/user.rb
Normal file → Executable file
@@ -1,68 +1,231 @@
|
|||||||
class User < ActiveRecord::Base
|
class User < ActiveRecord::Base
|
||||||
require 'open-uri'
|
include Gravtastic
|
||||||
|
gravtastic :size => 150, :default => ""
|
||||||
|
|
||||||
attr_accessible :card_id, :card_number, :card_permissions, :name
|
# Include default devise modules. Others available are:
|
||||||
validates_uniqueness_of :card_id, :card_number
|
# :token_authenticatable, :confirmable,
|
||||||
|
# :lockable, :timeoutable and :omniauthable
|
||||||
|
devise :database_authenticatable, :registerable,
|
||||||
|
:recoverable, :rememberable, :trackable, :validatable
|
||||||
|
|
||||||
def upload_to_door
|
# Setup accessible (or protected) attributes for your model
|
||||||
# do shit here
|
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
|
||||||
source = open("http://192.168.1.177?e=1234").read
|
|
||||||
results = source.scan(/authok/)
|
|
||||||
if(results.size > 0) then
|
|
||||||
#only continue if we've got an OK login
|
|
||||||
usernum = self.card_id.to_s.rjust(3, '0')
|
|
||||||
userperm = self.card_permissions.to_s.rjust(3, '0')
|
|
||||||
cardnum = self.card_number.rjust(8, '0')
|
|
||||||
|
|
||||||
source = open("http://192.168.1.177?m#{usernum}&p#{userperm}&t#{cardnum}").read
|
belongs_to :oriented_by, :foreign_key => "oriented_by_id", :class_name => "User"
|
||||||
results = source.scan(/cur/)
|
has_many :cards
|
||||||
|
has_many :user_certifications
|
||||||
|
has_many :certifications, :through => :user_certifications
|
||||||
|
has_many :contracts
|
||||||
|
has_many :payments
|
||||||
|
has_many :macs
|
||||||
|
has_many :resources
|
||||||
|
|
||||||
#logout
|
scope :volunteer, -> { where('member_level >= 10 AND member_level < 25') }
|
||||||
open("http://192.168.1.177?e=0000")
|
scope :active, -> { where('member_level >= 10') }
|
||||||
|
scope :paying, -> { joins(:payments).where("payments.date > ?", (DateTime.now - 90.days)).uniq }
|
||||||
|
|
||||||
if(results.size > 0) then
|
validates_format_of [:twitter_url, :facebook_url, :github_url, :website_url], :with => URI::regexp(%w(http https)), :allow_blank => true
|
||||||
#only return true if we got some kind of decent response
|
|
||||||
return true
|
# disable # validates_presence_of :postal_code
|
||||||
|
|
||||||
|
after_create :send_new_user_email
|
||||||
|
|
||||||
|
def absorb_user(user_to_absorb)
|
||||||
|
# copy all attributes except email, password, name, and anything that isn't blank on the destination
|
||||||
|
user_to_absorb.attributes.each_pair {|k,v|
|
||||||
|
unless (v.nil? || k == :id || k == :email || k == :password || k == :name || k == :password_confirmation || k == :hidden || k == 'hidden' || k == :encrypted_password || !self.attributes[k].blank? )
|
||||||
|
Rails.logger.info "Updating "+k.to_s+" from "+self[k].to_s
|
||||||
|
self[k] = v
|
||||||
|
Rails.logger.info "Updated "+k.to_s+" to "+self[k].to_s
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
self.save!
|
||||||
|
|
||||||
|
user_to_absorb.cards.each {|card|
|
||||||
|
Rails.logger.info "CARD BEFORE: "+card.inspect
|
||||||
|
card.user_id = self.id
|
||||||
|
card.save!
|
||||||
|
Rails.logger.info "CARD AFTER: "+card.inspect
|
||||||
|
}
|
||||||
|
user_to_absorb.user_certifications.each {|user_cert|
|
||||||
|
Rails.logger.info "CERT BEFORE: "+user_cert.inspect
|
||||||
|
user_cert.user_id = self.id
|
||||||
|
user_cert.save!
|
||||||
|
Rails.logger.info "CERT AFTER: "+user_cert.inspect
|
||||||
|
}
|
||||||
|
user_to_absorb.payments.each {|payment|
|
||||||
|
Rails.logger.info "PAYMENT BEFORE: "+payment.inspect
|
||||||
|
payment.user_id = self.id
|
||||||
|
payment.amount = 0 if payment.amount.nil? # Bypass validation on amount
|
||||||
|
payment.save!
|
||||||
|
Rails.logger.info "PAYMENT AFTER: "+payment.inspect
|
||||||
|
}
|
||||||
|
|
||||||
|
user_to_absorb.destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
def card_access_enabled
|
||||||
|
# If the user has at least one card with permission level 1, they have access
|
||||||
|
self.cards.where(:card_permissions => 1).count > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def name_with_email_and_visibility
|
||||||
|
if hidden then
|
||||||
|
"#{name} (#{email}) HIDDEN"
|
||||||
else
|
else
|
||||||
# We didn't get a decent response.
|
"#{name} (#{email})"
|
||||||
return false
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def name_with_payee_and_member_level
|
||||||
|
if payee.blank? then
|
||||||
|
"#{name} - #{member_level_string}"
|
||||||
|
else
|
||||||
|
"#{payee} for #{name} - #{member_level_string}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def member_level_string
|
||||||
|
case self.member_level.to_i
|
||||||
|
when 0
|
||||||
|
"None"
|
||||||
|
when 1
|
||||||
|
"Unable"
|
||||||
|
when 10..24
|
||||||
|
"Volunteer"
|
||||||
|
when 25..49
|
||||||
|
"Associate ($25)"
|
||||||
|
when 50..99
|
||||||
|
"Basic ($50)"
|
||||||
|
when 100..999
|
||||||
|
"Plus ($100)"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.member_levels
|
||||||
|
{25 => "Associate", 50 => "Basic", 75 => "Basic", 100 => "Plus"}
|
||||||
|
end
|
||||||
|
|
||||||
|
def payment_status
|
||||||
|
results = payment_status_calculation
|
||||||
|
return results[:paid]
|
||||||
|
end
|
||||||
|
|
||||||
|
def member_status
|
||||||
|
results = member_status_calculation
|
||||||
|
return results[:rank]
|
||||||
|
end
|
||||||
|
|
||||||
|
def member_status_symbol
|
||||||
|
results = member_status_calculation
|
||||||
|
return "<img src='/#{results[:icon]}#{results[:flair]}-coin.png' title='#{results[:message]}' class='member-status-symbol' />"
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_payment_date
|
||||||
|
self.payments.maximum(:date)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delinquency
|
||||||
|
if self.payments.count > 0
|
||||||
|
paydate = self.payments.maximum(:date)
|
||||||
|
(Date.today - paydate).to_i
|
||||||
|
else
|
||||||
|
(Date.today - self.created_at.to_date).to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_email(from_user,subject,body)
|
||||||
|
Rails.logger.info UserMailer.email(self,from_user,subject,body).deliver
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_certification?(cert_slug)
|
||||||
|
if self.certifications.find_by_slug(cert_slug)
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def contract_date
|
||||||
|
self.contracts.first.signed_at unless self.contracts.blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def send_new_user_email
|
||||||
|
Rails.logger.info UserMailer.new_user_email(self).deliver
|
||||||
|
end
|
||||||
|
|
||||||
|
def member_status_calculation
|
||||||
|
# Begin output buffer
|
||||||
|
message = ""
|
||||||
|
icon = ""
|
||||||
|
flair = ""
|
||||||
|
rank = 0
|
||||||
|
|
||||||
|
# First status item is level
|
||||||
|
case self.member_level.to_i
|
||||||
|
when 0..9
|
||||||
|
if self.payments.count > 0 then
|
||||||
|
message = "Former Member (#{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago)"
|
||||||
|
icon = :timeout
|
||||||
|
rank = 1
|
||||||
|
else
|
||||||
|
message = "Not a Member"
|
||||||
|
icon = :no
|
||||||
|
rank = 0
|
||||||
|
end
|
||||||
|
when 10..24
|
||||||
|
message = "Volunteer"
|
||||||
|
icon = :heart
|
||||||
|
rank = 101
|
||||||
|
when 25..49
|
||||||
|
message = member_level_string
|
||||||
|
icon = :copper
|
||||||
|
rank = 250
|
||||||
|
when 50..99
|
||||||
|
message = member_level_string
|
||||||
|
icon = :silver
|
||||||
|
rank = 500
|
||||||
|
when 100..999
|
||||||
|
message = member_level_string
|
||||||
|
icon = :gold
|
||||||
|
rank = 1000
|
||||||
|
end
|
||||||
|
|
||||||
|
payment_results = payment_status_calculation
|
||||||
|
flair = payment_results[:flair]
|
||||||
|
rank = rank/10 unless payment_results[:paid]
|
||||||
|
message = payment_results[:message] unless payment_results[:message].blank?
|
||||||
|
|
||||||
|
return {:message => message, :icon => icon, :flair => flair, :rank => rank}
|
||||||
|
end
|
||||||
|
|
||||||
|
def payment_status_calculation
|
||||||
|
flair = ""
|
||||||
|
message = ""
|
||||||
|
paid = true
|
||||||
|
|
||||||
|
# Second status item is payment status
|
||||||
|
case self.member_level.to_i
|
||||||
|
when 25..999
|
||||||
|
# There are payments
|
||||||
|
if self.payments.count > 0 then
|
||||||
|
# They're on time
|
||||||
|
if self.payments.maximum(:date) > (DateTime.now - 60.days)
|
||||||
|
flair = "-paid"
|
||||||
|
paid = true
|
||||||
|
else
|
||||||
|
message = "Last Payment #{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago"
|
||||||
|
paid = false
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# We didn't get an OK login.
|
message = "No Payments Recorded"
|
||||||
return false
|
paid = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return {:message => message, :paid => paid, :flair => flair}
|
||||||
def self.upload_all_to_door
|
|
||||||
@users = User.all
|
|
||||||
@end_results = Array.new
|
|
||||||
|
|
||||||
source = open("http://192.168.1.177?e=1234").read
|
|
||||||
results = source.scan(/authok/)
|
|
||||||
if(results.size > 0) then
|
|
||||||
@users.each do |u|
|
|
||||||
#only continue if we've got an OK login
|
|
||||||
usernum = u.card_id.to_s.rjust(3, '0')
|
|
||||||
userperm = u.card_permissions.to_s.rjust(3, '0')
|
|
||||||
cardnum = u.card_number.rjust(8, '0')
|
|
||||||
|
|
||||||
source = open("http://192.168.1.177?m#{usernum}&p#{userperm}&t#{cardnum}").read
|
|
||||||
results = source.scan(/cur/)
|
|
||||||
|
|
||||||
if(results.size > 0) then
|
|
||||||
#only return true if we got some kind of decent response
|
|
||||||
@end_results.push([usernum,"OK"])
|
|
||||||
else
|
|
||||||
@end_results.push([usernum,"FAIL"])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#logout
|
|
||||||
open("http://192.168.1.177?e=0000")
|
|
||||||
else
|
|
||||||
@end_results.push([usernum,"FAIL"])
|
|
||||||
end
|
|
||||||
|
|
||||||
return @end_results
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
17
app/models/user_certification.rb
Executable file
17
app/models/user_certification.rb
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
class UserCertification < ActiveRecord::Base
|
||||||
|
attr_accessible :certification_id, :user_id
|
||||||
|
|
||||||
|
validates_presence_of :certification_id, :user_id
|
||||||
|
validates_uniqueness_of :certification_id, :scope => :user_id, :message => 'already exists for this user.' # Makes sure users don't get certified twice
|
||||||
|
|
||||||
|
belongs_to :user
|
||||||
|
belongs_to :certification
|
||||||
|
|
||||||
|
def user_name
|
||||||
|
if user.blank?
|
||||||
|
return "n/a (user ##{user_id} missing)"
|
||||||
|
else
|
||||||
|
return self.user.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
38
app/views/cards/_form.html.erb
Executable file
38
app/views/cards/_form.html.erb
Executable file
@@ -0,0 +1,38 @@
|
|||||||
|
<%= form_for(@card) do |f| %>
|
||||||
|
<% if @card.errors.any? %>
|
||||||
|
<div id="error_explanation">
|
||||||
|
<h2><%= pluralize(@card.errors.count, "error") %> prohibited this card from being saved:</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<% @card.errors.full_messages.each do |msg| %>
|
||||||
|
<li><%= msg %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% @card.user_id = params[:user] if params[:user].present? %>
|
||||||
|
<div class="field">
|
||||||
|
<%= f.label :user %><br />
|
||||||
|
<%= collection_select(:card, :user_id, User.all.sort_by(&:name), :id, :name, :include_blank => true) %>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<%= f.label :name, "Card Note" %><br />
|
||||||
|
<%= f.text_field :name %>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<%= f.label :id, "Card DB ID" %><br />
|
||||||
|
<%= f.number_field :id, :in => 10...201 %>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<%= f.label :card_number, "Card Number" %><br />
|
||||||
|
<%= f.text_field :card_number %>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<%= f.label :card_permissions %><br />
|
||||||
|
<%= f.select :card_permissions, [["Enabled",1],["Disabled",255]] %>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<%= f.submit %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
6
app/views/cards/edit.html.erb
Executable file
6
app/views/cards/edit.html.erb
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
<h1>Editing card</h1>
|
||||||
|
|
||||||
|
<%= render 'form' %>
|
||||||
|
|
||||||
|
<%= link_to 'Show', @card %> |
|
||||||
|
<%= link_to 'Back', cards_path %>
|
||||||
60
app/views/cards/index.html.erb
Executable file
60
app/views/cards/index.html.erb
Executable file
@@ -0,0 +1,60 @@
|
|||||||
|
<h1>Access Cards</h1>
|
||||||
|
|
||||||
|
<%= link_to 'New Card', new_card_path, :class => "btn" if can? :create, Card %>
|
||||||
|
<%= link_to 'Upload all cards', upload_all_path, :class => "btn" if can? :upload_all, Card %>
|
||||||
|
<%= link_to 'Door Logs', door_logs_path, :class => "btn" if can? :read, DoorLog %>
|
||||||
|
<%= link_to 'Space API', space_api_path, :class => "btn" %>
|
||||||
|
<%= link_to 'Remote Door Access', space_api_access_path, :class => "btn" if can? :access_doors_remotely, :door_access %>
|
||||||
|
<p>
|
||||||
|
<b>Most Active Card Last Month:</b> <%= @most_active_card.user.name unless @most_active_card.user.blank? %> (<%= @most_active_card.accesses_this_week unless @most_active_card.blank? %> days)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<% unless @runner_up_card.blank? || @runner_up_card.user.blank? %>
|
||||||
|
<b>Runner Up:</b> <%= @runner_up_card.user.name %> (<%= @runner_up_card.accesses_this_week %> days)
|
||||||
|
<% end %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table class="lined-table">
|
||||||
|
<col />
|
||||||
|
<col />
|
||||||
|
<col class="col_highlight" />
|
||||||
|
<col />
|
||||||
|
<col class="col_highlight" />
|
||||||
|
<tr>
|
||||||
|
<th>User</th>
|
||||||
|
<th>Note</th>
|
||||||
|
<th>DB ID</th>
|
||||||
|
<th>Card #</th>
|
||||||
|
<th>Access?</th>
|
||||||
|
<th>Days Accessed Last Month</th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<% if !@cards.blank? %>
|
||||||
|
<% @cards.each do |card| %>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<% if card.user.nil? %>
|
||||||
|
n/a
|
||||||
|
<% else %>
|
||||||
|
<%= raw(card.user.member_status_symbol) %>
|
||||||
|
<%= link_to card.user.name , card %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<td><%= card.name %></td>
|
||||||
|
<td><%= card.id %></td>
|
||||||
|
<td><%= card.card_number %></td>
|
||||||
|
<td><%= if card.card_permissions == 1 then "Access" end %></td>
|
||||||
|
<td><%= card.accesses_this_week unless card.accesses_this_week < 1 %></td>
|
||||||
|
<td><%= link_to 'Upload', upload_path(card) if can? :upload, card %></td>
|
||||||
|
<td><%= link_to 'Edit', edit_card_path(card) if can? :update, card %></td>
|
||||||
|
<td><%= link_to 'Destroy', card, :confirm => 'Are you sure? WARNING: THIS DOES NOT REMOVE THE CARD FROM THE DOOR SYSTEM! DISABLE AND UPLOAD IT FIRST.', :method => :delete if can? :destroy, card %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
5
app/views/cards/new.html.erb
Executable file
5
app/views/cards/new.html.erb
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
<h1>New card</h1>
|
||||||
|
|
||||||
|
<%= render 'form' %>
|
||||||
|
|
||||||
|
<%= link_to 'Back', cards_path %>
|
||||||
39
app/views/cards/show.html.erb
Executable file
39
app/views/cards/show.html.erb
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
<p>
|
||||||
|
<b>User:</b>
|
||||||
|
<%= link_to @card.user.name, @card.user unless @card.user.blank? %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b>Card Note:</b>
|
||||||
|
<%= @card.name %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b>Card DB ID:</b>
|
||||||
|
<%= @card.id %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b>Card Number:</b>
|
||||||
|
<%= @card.card_number %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b>Card Permissions:</b>
|
||||||
|
<%= if @card.card_permissions == 1 then "Enabled" else "Disabled" end %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<% if can? :read, DoorLog %>
|
||||||
|
<p>
|
||||||
|
<b>Access attempts:</b>
|
||||||
|
<ul>
|
||||||
|
<% @door_logs.each do |log| %>
|
||||||
|
<li><%= log.created_at %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= link_to 'Upload to Door', upload_path(@card) if can? :upload, @card %>
|
||||||
|
<% if can? :update, @card then %><%= link_to 'Edit', edit_card_path(@card) %> |<% end %>
|
||||||
|
<%= link_to 'Back', cards_path %>
|
||||||
14
app/views/cards/upload.html.erb
Executable file
14
app/views/cards/upload.html.erb
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
<% if @upload_result %>
|
||||||
|
<p>
|
||||||
|
<b>Upload result:</b>
|
||||||
|
<%= @card.card_number %> uploaded successfully.
|
||||||
|
</p>
|
||||||
|
<% else %>
|
||||||
|
<p>
|
||||||
|
<b>Upload result:</b>
|
||||||
|
Error uploading <%= @card.card_number %>.
|
||||||
|
</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= link_to 'Back', cards_path %>
|
||||||
3
app/views/users/upload_all.html.erb → app/views/cards/upload_all.html.erb
Normal file → Executable file
3
app/views/users/upload_all.html.erb → app/views/cards/upload_all.html.erb
Normal file → Executable file
@@ -1,4 +1,3 @@
|
|||||||
<p id="notice"><%= notice %></p>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<b>Upload results:</b>
|
<b>Upload results:</b>
|
||||||
@@ -10,4 +9,4 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<%= link_to 'Back', users_path %>
|
<%= link_to 'Back', cards_path %>
|
||||||
29
app/views/certifications/_form.html.erb
Executable file
29
app/views/certifications/_form.html.erb
Executable file
@@ -0,0 +1,29 @@
|
|||||||
|
<%= form_for(@certification) do |f| %>
|
||||||
|
<% if @certification.errors.any? %>
|
||||||
|
<div id="error_explanation">
|
||||||
|
<h2><%= pluralize(@certification.errors.count, "error") %> prohibited this certification from being saved:</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<% @certification.errors.full_messages.each do |msg| %>
|
||||||
|
<li><%= msg %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<%= f.label :name %><br />
|
||||||
|
<%= f.text_field :name %>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<%= f.label :slug, "Slug (lowercase, single-word identifier)" %><br />
|
||||||
|
<%= f.text_field :slug %>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<%= f.label :description %><br />
|
||||||
|
<%= f.text_area :description %>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<%= f.submit %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
6
app/views/certifications/edit.html.erb
Executable file
6
app/views/certifications/edit.html.erb
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
<h1>Editing certification</h1>
|
||||||
|
|
||||||
|
<%= render 'form' %>
|
||||||
|
|
||||||
|
<%= link_to 'Show', @certification %> |
|
||||||
|
<%= link_to 'Back', certifications_path %>
|
||||||
16
app/views/certifications/index.html.erb
Executable file
16
app/views/certifications/index.html.erb
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
<h1>Certification Classes</h1>
|
||||||
|
|
||||||
|
<%= link_to 'Create Certification', new_certification_path if can? :create, Certification %>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<% @certifications.each do |certification| %>
|
||||||
|
<li><%= link_to certification.name, certification %>
|
||||||
|
(<%= certification.slug %>)
|
||||||
|
<% if can? :update, certification %> | <%= link_to 'Edit', edit_certification_path(certification) %><% end %>
|
||||||
|
<% if can? :destroy, certification %> | <%= link_to 'Destroy', certification, :confirm => 'Are you sure?', :method => :delete %><% end %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
5
app/views/certifications/new.html.erb
Executable file
5
app/views/certifications/new.html.erb
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
<h1>New certification</h1>
|
||||||
|
|
||||||
|
<%= render 'form' %>
|
||||||
|
|
||||||
|
<%= link_to 'Back', certifications_path %>
|
||||||
22
app/views/certifications/show.html.erb
Executable file
22
app/views/certifications/show.html.erb
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
<p>
|
||||||
|
<b>Name:</b>
|
||||||
|
<%= @certification.name %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b>Slug (lowercase, single-word identifier):</b>
|
||||||
|
<%= @certification.slug %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b>Description:</b>
|
||||||
|
<%= simple_format @certification.description %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b>Certified Users:</b>
|
||||||
|
<%= link_to "Click Here", user_certifications_path %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<% if can? :update, @certification %><%= link_to 'Edit', edit_certification_path(@certification) %> |<% end %>
|
||||||
|
<%= link_to 'Back', certifications_path %>
|
||||||
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 />
|
||||||
|
|
||||||
12
app/views/devise/confirmations/new.html.erb
Executable file
12
app/views/devise/confirmations/new.html.erb
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
<h2>Resend confirmation instructions</h2>
|
||||||
|
|
||||||
|
<%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
|
||||||
|
<%= devise_error_messages! %>
|
||||||
|
|
||||||
|
<div><%= f.label :email %><br />
|
||||||
|
<%= f.email_field :email %></div>
|
||||||
|
|
||||||
|
<div><%= f.submit "Resend confirmation instructions" %></div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= render "devise/shared/links" %>
|
||||||
5
app/views/devise/mailer/confirmation_instructions.html.erb
Executable file
5
app/views/devise/mailer/confirmation_instructions.html.erb
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
<p>Welcome <%= @resource.email %>!</p>
|
||||||
|
|
||||||
|
<p>You can confirm your account email through the link below:</p>
|
||||||
|
|
||||||
|
<p><%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %></p>
|
||||||
8
app/views/devise/mailer/reset_password_instructions.html.erb
Executable file
8
app/views/devise/mailer/reset_password_instructions.html.erb
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
<p>Hello <%= @resource.email %>!</p>
|
||||||
|
|
||||||
|
<p>Someone has requested a link to change your password, and you can do this through the link below.</p>
|
||||||
|
|
||||||
|
<p><%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %></p>
|
||||||
|
|
||||||
|
<p>If you didn't request this, please ignore this email.</p>
|
||||||
|
<p>Your password won't change until you access the link above and create a new one.</p>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user