139 Commits
ipn ... master

Author SHA1 Message Date
04d9b0bf68 Creating a simpler API endpoint for embedded parsing 2016-09-12 21:13:06 -07:00
6957ab7ac1 Door log filtering, user case-insensitive and status-insensitive sorting, new user scope
Presumably changed by meznak or blhack, not myself
2016-09-12 20:56:31 -07:00
96a748ba0b Adding note to MAC registration 2016-02-11 20:29:13 -07:00
2cd1966250 Board member changes to registration forms and reports 2016-02-11 20:27:28 -07:00
f19e822d88 Merge branch 'master' of github.com:zyphlar/Open-Source-Access-Control-Web-Interface 2015-10-27 09:46:45 -07:00
6e13ec34ef Updating paypal csv import to new type 2015-10-27 09:43:27 -07:00
Will Bradley
63a9b1b2f0 Update and rename README.rdoc to README.md 2015-06-30 12:07:14 -07:00
5a8375581f It appears that the PayPal CSV has magically changed formats to no longer include a preceding space in the column names, which gets turned into a preceding underscore.
The first 6 lines of the header do need to be removed, however.
2015-06-30 11:49:32 -07:00
3097803d05 Removing "looks like you haven't paid" alert 2015-01-04 21:05:48 -07:00
40102775ea Fixing mac index unauth 2014-11-20 13:22:39 -07:00
d522d0a43b Fix 404 error on macs/index unauthenticated 2014-11-20 13:18:58 -07:00
c976a6c216 Adding payment index 2014-11-20 13:13:38 -07:00
d1c5dba667 Adding indexes and speeding up doorlog query 2014-11-20 12:56:28 -07:00
98b9db5fe1 Merge branch 'master' of github.com:zyphlar/Open-Source-Access-Control-Web-Interface 2014-11-20 12:38:19 -07:00
cd84e33e56 Switching back to group mail 2014-10-21 20:27:34 -07:00
a6f6e2e141 Merge branch 'master' of github.com:heatsynclabs/Open-Source-Access-Control-Web-Interface 2014-10-15 13:44:52 -07:00
6334928779 Making door alert / webpage status work as expected 2014-10-07 19:55:34 -07:00
85ec806b22 Emailing board instead of discussion list 2014-09-26 21:24:45 -07:00
b42236d228 HTMLifying alert text properly 2014-09-11 21:03:46 -07:00
e0e43ccdbd Adjusting email 2014-09-04 19:40:00 -07:00
3ce0d3c45a Adding door alert emails (test mode) and adjusting door log graph view 2014-09-04 19:24:33 -07:00
3cb1d4e730 Merge pull request #2 from zyphlar/master
Updating hsl repo
2014-07-06 17:01:02 -07:00
1d6699ae9f Increasing visible detail of mac log 2014-06-24 12:20:17 -07:00
3a028ca7fc Updating with https 2014-04-21 11:12:12 -07:00
b23de1c1a2 Styling the members-without-cards report 2014-04-21 11:03:59 -07:00
409227f2d5 Adjusting user summary styling 2014-04-21 10:58:22 -07:00
6198ad419c Adjusting display of most active card 2014-04-16 16:07:16 -07:00
57a5a00352 Adding graph to door log (open/closed) 2014-04-14 18:09:02 -07:00
2f4872218e Making contracts awesome 2014-04-12 15:05:57 -07:00
daeb7713e0 Removing force_ssl for /space_api and /macs 2014-04-11 13:15:42 -07:00
0cd63b085b Merge branch 'master' of github.com:zyphlar/Open-Source-Access-Control-Web-Interface 2014-04-10 23:24:39 -07:00
a95c1220bf Forcing ssl 2014-04-10 23:24:47 -07:00
7e8c8e1bb6 Merge branch 'master' of github.com:zyphlar/Open-Source-Access-Control-Web-Interface 2014-04-10 23:21:40 -07:00
11f837cf7e HTTPS 2014-04-10 23:20:50 -07:00
18a52b4fca Merge branch 'master' of github.com:zyphlar/Open-Source-Access-Control-Web-Interface 2014-04-10 21:05:29 -07:00
b3279299cf Adjusting payment graph 2014-04-10 21:05:14 -07:00
cb0f203f82 Updating final user views 2014-04-10 21:01:42 -07:00
bedd80d555 Updating user index with new layout 2014-04-10 20:47:25 -07:00
8f069cee4d Removing faker 2014-04-10 20:13:21 -07:00
16c75b2d0f Fixing issue #19 for realsies (oriented by tracking) 2014-04-10 20:10:04 -07:00
13e72fef23 Fixing issue #19 and also styling homepage 2014-04-10 19:45:29 -07:00
83442c4bff Merge branch 'master' of github.com:zyphlar/Open-Source-Access-Control-Web-Interface 2014-04-10 19:19:48 -07:00
74d3fc45dd Adding faker 2014-04-10 19:19:28 -07:00
d4286e4ddf Improving logging of macs 2014-03-12 20:46:24 -07:00
3cd6c59df0 More contract tweaks 2014-03-02 21:48:49 -07:00
d8311fa7cc Merge branch 'master' of github.com:zyphlar/Open-Source-Access-Control-Web-Interface 2014-03-02 19:42:10 -07:00
0cfa83a9f7 Display changes for contract 2014-03-02 19:41:57 -07:00
691089dd1c Merge branch 'master' of github.com:zyphlar/Open-Source-Access-Control-Web-Interface 2014-03-02 19:39:21 -07:00
e8ef430733 Working around temporary lack of last names in users 2014-03-02 19:39:04 -07:00
20594d651c Gemfile updates, schema update 2014-03-02 19:17:09 -07:00
cac59e9de5 Finishing contracts, changing from s3.yml to .env 2014-03-02 19:06:39 -07:00
c74da562bc Adding contracts 2014-02-27 04:13:53 -07:00
095b6d3965 Adding interlock authentication 2014-02-23 05:55:00 -07:00
c5556a0d50 Adding bio to summary 2014-02-14 04:04:28 -07:00
03a0588073 Creating sweet user summary page 2014-02-14 03:48:03 -07:00
17aa2b971c Changing pirate icon 2014-02-14 02:14:22 -07:00
d943adc458 Creating user summary page 2014-02-14 02:12:13 -07:00
8425aaecd2 Adding pictures to resources 2014-02-14 01:09:16 -07:00
8a31655fd3 Merge branch 'master' of github.com:zyphlar/Open-Source-Access-Control-Web-Interface 2014-02-14 00:02:53 -07:00
3d97e92eb7 Adding resource category edit abilities 2014-02-14 00:02:11 -07:00
3f5a7012bd Removing required postal code
Was causing issues with resetting passwords
2014-02-12 20:11:21 -07:00
fb47188af2 Upgrading to ruby-version from rvmrc 2014-02-10 21:07:44 -07:00
bff2be7cc9 Updating readme with arp-scan doc 2014-02-09 05:20:08 -07:00
ee7e79a433 Allowing people to view/edit 2014-02-09 05:13:35 -07:00
2c4cd4a020 Mooooore resources 2014-02-09 05:01:52 -07:00
a2cbcac798 More resource tweaks 2014-02-09 04:32:45 -07:00
23dcb0715c More resource adding 2014-02-09 03:42:17 -07:00
74e60d4ec9 Adding bootstrap 2014-02-09 02:10:12 -07:00
e61895d20f Chmod 2014-02-09 02:09:03 -07:00
58926cf3ab Updating resources 2014-02-09 02:08:08 -07:00
e16e35e098 Massive chmod/chown 2014-02-09 00:56:48 -07:00
1d0e8721e1 Adding resources (imported from old toolshare) 2014-02-09 00:54:03 -07:00
56450cf319 Adding postal code to users 2014-02-08 21:06:07 -07:00
92c3a6d2be Adding db seed, fixing migration issues 2014-02-08 19:13:45 -07:00
73833dd2bf Updating door log key
Adding reboot logging
2014-01-31 18:41:52 -07:00
c6bb164f73 Fixing MAC form and index 2014-01-24 04:27:21 -07:00
aa29f30f30 Allowing users to see member activity 2014-01-23 17:50:00 -07:00
a2a9d082d8 Adding json formats to space api access 2014-01-21 19:38:11 -07:00
8060f76cca Adjusting icon and adding meta tag for icon 2013-12-29 22:28:54 -07:00
f7d26ab69e Adding iOS bookmark icon 2013-12-29 22:20:00 -07:00
6157280655 Changing door access tablet interface 2013-12-29 21:56:47 -07:00
cbadcc5c73 Fixing user-emailing abilities 2013-12-13 03:34:52 -07:00
ef590793f2 Card upload would fail due to being logged out halfway thru; changed arduino auth mechanism 2013-12-03 01:42:08 -07:00
d807aad668 Merge pull request #1 from zyphlar/master
Getting the HSL copy up to date
2013-12-02 01:26:33 -08:00
41a8431be5 Adding direct door access commands and interface 2013-12-02 02:03:40 -07:00
d2434be109 Moving spaceapi endpoint and OAC door status checking here 2013-12-01 19:32:08 -07:00
9dc8645c32 Update README.rdoc 2013-10-26 03:21:48 -07:00
02b997b838 Adding mac history graph 2013-10-26 03:22:03 -07:00
d88abe7d32 Whups, integer error in text email causing signup failures 2013-10-22 01:38:57 -07:00
0d1894d6e8 Updating payment and front page status alerts to work properly 2013-10-10 20:58:19 -07:00
008ceaed9e Updating emailer 2013-10-06 05:51:41 -07:00
15836b4f4e Adding zombie report 2013-10-06 05:17:24 -07:00
57e6ac1fc5 Adding user email feature 2013-10-06 04:09:56 -07:00
04bab84293 Adding new member report 2013-10-05 19:53:31 -07:00
d61289abaf Adding analytics, adjusting door log issues 2013-10-04 02:59:41 -07:00
75da7ffb6a Wording on graph 2013-10-02 05:01:30 -07:00
0eeaddabd7 Adjusting wording and stat counts 2013-10-02 04:57:46 -07:00
370c82e075 Wording 2013-10-02 04:46:35 -07:00
7f7cd6e9ff Adjusting wording, styling, and graph visibility 2013-10-02 04:45:07 -07:00
f11d5ebe1e Adding individual statistics/data-dumps 2013-10-02 04:31:10 -07:00
4da2ec9463 Tweaking main page stats to show member counts 2013-09-28 20:17:38 -07:00
7cd33ff030 Also cancan issue in mac controller 2013-09-28 19:36:23 -07:00
7bfd45a163 Added more_info notice and fixed payments cancan bug 2013-09-28 19:34:08 -07:00
42679aa410 Updating settings & fixing auth of objects through other objects cancan bug? 2013-09-28 19:31:28 -07:00
9e08a0d036 Updated stats and privacy 2013-09-28 18:55:22 -07:00
2c204a8f9d Adding wysiwyg and settings 2013-09-28 04:59:19 -07:00
f03ffcd356 Adding settings 2013-09-28 04:24:24 -07:00
b267da7b37 Style 2013-09-28 04:18:52 -07:00
ec84d8f5e0 Button style 2013-09-28 04:13:21 -07:00
e002907da7 Fixed graph 2013-09-28 04:12:43 -07:00
5acb3cd272 Updating payment graph 2013-09-28 03:33:36 -07:00
16e5e8d584 Adding payment graph 2013-09-28 02:42:30 -07:00
b5383eaafb Readme update 2013-09-28 02:42:14 -07:00
d1985dae12 Adding payment stats 2013-09-28 01:30:52 -07:00
4631489eba Changing card stats to month and adding runner up 2013-09-27 01:19:58 -07:00
e61ac5d32e Adding most active mac and runner up stats 2013-09-27 00:56:52 -07:00
3ddfb3442b Tabs with spaces 2013-09-25 21:45:12 -07:00
530f4834ca Changing user exit reason to fulltext 2013-09-24 23:14:50 -07:00
3c02724488 Fixing string comparison on member level 2013-09-23 23:55:16 -07:00
ec139a97a8 Separating certs into columns 2013-09-23 20:24:38 -07:00
c5e02a51be Making it possible for users to easily register existing MACs 2013-09-23 18:51:22 -07:00
cad46e45c5 Adding users without cards report, fixing mac registration for existing macs 2013-09-23 18:34:18 -07:00
377170d47c Adjusting sql for macs for postgres compatibility 2013-09-22 01:08:05 -07:00
df0e91c4ee Migrations and tweaks to facilitate sqlite-postgres switch 2013-09-22 00:50:41 -07:00
84525ca65f Updating email address 2013-09-21 02:38:03 -07:00
faaeb2b4a2 Allowing nil social media urls 2013-08-29 16:28:57 -07:00
c61b8ac8d4 Adding recent users report 2013-08-29 02:50:05 -07:00
8820dc4986 Adding activity and form fields for user profiles 2013-08-29 02:27:48 -07:00
37aba522cf Profile tweaks 2013-08-29 01:42:26 -07:00
d1ba469767 Adjusting profile display 2013-08-29 01:38:40 -07:00
f4868a32ae Social icons 2013-08-29 01:22:06 -07:00
880488c73e Adding social and exit reasons 2013-08-29 01:23:21 -07:00
2231d124a6 Style tweak and stats 2013-08-28 08:40:36 -07:00
accec4edb1 Merge branch 'csv'
Conflicts:
	app/models/user_certification.rb
	app/views/user_certifications/index.html.erb
	app/views/user_certifications/show.html.erb
2013-08-28 08:21:28 -07:00
25c0d1e1cb Finalized payments and CSVs and inactivity 2013-08-28 08:19:01 -07:00
e8e024c042 Adding CSV show 2013-08-28 05:14:10 -07:00
10a1e4eb84 Switching to RVM, View tweaks 2013-08-28 05:15:50 -07:00
211d79cbcd Adding csv import 2013-08-28 04:25:54 -07:00
a1ef8a5fd5 User cert tweaks (handling nulls) 2013-08-24 02:23:12 -07:00
437 changed files with 39515 additions and 618 deletions

8
.gitignore vendored
View File

@@ -9,13 +9,19 @@
# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3.*
# Ignore all logfiles and tempfiles.
/log/*.log
/tmp
tmp/
# Ignore compiled assets
/public/assets
# Ignore config file
# 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
View File

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

1
.ruby-version Normal file
View File

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

14
Gemfile
View File

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

View File

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

25
README.md Normal file
View 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/
![screenshot](https://cloud.githubusercontent.com/assets/48434/8439253/a9a810e6-1f1f-11e5-8b5c-3f0b22f14a9f.png)
Contributions welcome! Simply send a pull request via Github.
To use:
* Install Imagemagick (for Paperclip / image uploads)
* Install arp-scan (for LAN Mac address scanning)
* Load into a Rails 3 environment
* Copy config/config.yml.example to config/config.yml and edit appropriately
* Copy config/database.yml.example to config/database.yml and edit appropriately
* Copy env.example to .env and edit appropriately for your Amazon S3 account OR adjust the resource.rb and contract.rb model settings to use different storage for picture attachments (via Paperclip)
* Copy config/initializers/secret_token.rb.example to config/config/initializers/secret_token.rb and edit appropriately
* See/edit db/seeds.rb for the initial admin account info.
* Run bundle install, rake db:migrate, rake db:seed, etc.

View File

@@ -1,15 +0,0 @@
== Open Access Control Web Interface
Web software for managing a database of members in a collaborative grassroots workshop,
and also controlling Arclight of 23b Hackerspace's Arduino access control system
via Ethernet ( see: https://github.com/zyphlar/Open_Access_Control_Ethernet )
https://github.com/zyphlar/Open-Source-Access-Control-Web-Interface
Copyright Will Bradley, 2012-2013
Distributed under a Creative Commons Attribution 3.0 license http://creativecommons.org/licenses/by/3.0/
To use:
* Load into a Rails 3 environment
* Rename config/config.yml.example to config/config.yml and edit appropriately
* Use the Rails console to create a new User and set user.admin = true
* Run bundle install, rake db:migrate, etc.

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

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

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

Before

Width:  |  Height:  |  Size: 95 B

After

Width:  |  Height:  |  Size: 95 B

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

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

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

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

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

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

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

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

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

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

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

@@ -46,3 +46,12 @@ box-shadow: inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);
cursor: pointer;
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.member-status-symbol, .social-icon {
vertical-align: middle;
}
a.social-link:hover {
background: transparent;
}
.lined-table td {
border-bottom: 1px dashed black;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

@@ -1,15 +1,49 @@
class ApplicationController < ActionController::Base
protect_from_forgery
force_ssl if: :ssl_forced?
def ssl_forced?
# Non-production environments and read-only stuff like the space API and MACs should not require SSL. (APIs hate following 301s).
if Rails.env.development? || Rails.env.test? || ["space_api","macs"].include?(params[:controller])
return false
else
return true
end
end
rescue_from CanCan::AccessDenied do |exception|
if !current_user.nil? && current_user.orientation.blank? then
flash[:alert] = "Sorry, you probably need to complete New Member Orientation before having access to this page. <br/>Please check your email and schedule a New Member Orientation with a volunteer."
else
flash[:alert] = "Nothing to see here!"
end
Rails.logger.warn "----------\r\nWARNING: AccessDenied Exception: #{exception.inspect} User: #{current_user.inspect}\r\n----------"
redirect_to root_url
end
@payment_methods = [[nil],["PayPal"],["Dwolla"],["Bill Pay"],["Check"],["Cash"],["Other"]]
@payment_instructions = {nil => nil, :paypal => "Set up a monthly recurring payment to hslfinances@gmail.com", :dwolla => "Set up a monthly recurring payment to hslfinances@gmail.com", :billpay => "Have your bank send a monthly check to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201", :check => "Mail to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201 OR put in the drop safe at the Lab with a deposit slip firmly attached each month.", :cash => "Put in the drop safe at the Lab with a deposit slip firmly attached each month.", :other => "Hmm... talk to a Treasurer!"}
# Check authorization of a user / sign them in manually
def check_auth(email,password)
resource = User.find_by_email(email)
if resource && resource.valid_password?(password)
resource.remember_me = true
sign_in :user, resource
return true
else
return false
end
end
end
# Add a "fit" function to sanitize inputs for mac history
class Fixnum
def fit(range)
self > range.max ? range.max : (self < range.min ? range.min : self)
end
end

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

@@ -1,6 +1,6 @@
class CardsController < ApplicationController
load_and_authorize_resource
before_filter :authenticate_user!
load_and_authorize_resource except: :authorize
before_filter :authenticate_user!, except: :authorize
# GET /cards
# GET /cards.json
@@ -10,20 +10,17 @@ class CardsController < ApplicationController
@cards = @cards.sort_by{|e| e[:id]}
if can? :read, DoorLog then
Rails.logger.info "CARD STATS:"
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
Rails.logger.info card_num_R
card[:accesses_this_week] = DoorLog.where('key = "G" AND data =? AND created_at > ?', card_num_R, DateTime.now - 7.days).order("created_at DESC").count
Rails.logger.info card[:accesses_this_week]
if(card[:accesses_this_week] > most_active_count) then
Rails.logger.info "ACTIVE"
most_active_count = card[:accesses_this_week]
@most_active_card = card
end
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|
@@ -37,7 +34,7 @@ class CardsController < ApplicationController
def show
if can? :read, DoorLog then
card_num_R = @card.card_number.to_i(16)%32767
@door_logs = DoorLog.where('key = "R" AND data =?', card_num_R).order("created_at DESC")
@door_logs = DoorLog.where('key = ? AND data = ?', "G", card_num_R).order("created_at DESC")
end
respond_to do |format|
format.html # show.html.erb
@@ -114,6 +111,41 @@ class CardsController < ApplicationController
end
end
def authorize
# Stop unless signed in already, OR if the supplied user/pass params are good.
unless current_user || check_auth(params['user'],params['pass'])
@auth = "bad_user_or_pass"
else
# Stop unless the user can access the door system
unless can? :authorize, Card
@auth = "bad_user_permissions"
Rails.logger.warn "----------\r\nWARNING: CARD AUTH ATTEMPT DENIED. USER #{current_user.inspect}\r\n----------"
else
begin
@card = Card.find(:first, :conditions => ["lower(card_number) = ?", params[:id].downcase])
@auth = @card.inspect
if @card && @card.user
@auth = @card.user.has_certification?(params[:device])
else
@auth = false
end
rescue
@auth = false
end
end
end
if @card && @card.user
username = @card.user.name
else
username = nil
end
render json: [@auth, username]
end
# DELETE /cards/1
# DELETE /cards/1.json
def destroy

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

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

View File

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

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

@@ -5,7 +5,36 @@ class DoorLogsController < ApplicationController
# GET /door_logs
# GET /door_logs.json
def index
@door_logs = DoorLog.find(:all, :order => "created_at DESC", :limit => 500)
# @door_logs = DoorLog.find(:all, :order => "created_at DESC", :limit => 1000)
@door_logs = DoorLog.where("key NOT LIKE 'alarm%' AND key != 'armed' AND key != 'activated'").order("created_at DESC").limit(1000)
begin
@start_date = DateTime.parse(params[:start])
@end_date = DateTime.parse(params[:end])
rescue
@start_date = DateTime.now - 2.weeks
@end_date = DateTime.now
end
@door_logs_by_hour = DoorLog.where("created_at > ? AND created_at < ? AND (key = ? OR key = ?)", @start_date, @end_date,"door_1_locked","door_2_locked").order("created_at ASC").group_by(&:key)
@door_log_graph = [
@door_logs_by_hour["door_1_locked"].map{|d| [d.created_at.to_time.to_i*1000, 1^d.data.to_i]}, # use XOR to invert 1 into 0 and vice versa
@door_logs_by_hour["door_2_locked"].map{|d| [d.created_at.to_time.to_i*1000, 1^d.data.to_i]}
]
# @door_logs_by_hour.each do |door_log|
# # Add one computer for activate, subtract one for deactivate
# if door_log.data == 1
#
# elsif door_log.data == 0
# mac_running_balance -= 1
# end
# @door_log_graph << [time.to_time.to_i*1000,mac_running_balance]
# end
respond_to do |format|
format.html # index.html.erb
@@ -26,6 +55,7 @@ class DoorLogsController < ApplicationController
# GET /door_logs/auto_download
def auto_download
@results = DoorLog.download_from_door
@status = DoorLog.download_status # for space_api
respond_to do |format|
format.html # show.html.erb

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

@@ -1,24 +1,54 @@
class HomeController < ApplicationController
layout 'resources'
def index
@num_certs = UserCertification.count
@recent_certs = UserCertification.where("created_at > ?", DateTime.now - 7.days).count
@num_users = User.count
@recent_users = User.where("created_at > ?", DateTime.now - 7.days).count
if can? :read, User then
@recent_user_names = User.where("member_level > 10").accessible_by(current_ability).order('created_at desc').limit(5)
end
@num_door_opens = DoorLog.where("key = 'G'").count
@today_door_opens = DoorLog.where("key = 'G' AND created_at > ?", DateTime.now - 1.day).count
@recent_door_opens = DoorLog.where("key = 'G' AND created_at > ?", DateTime.now - 7.days).count
@num_door_denieds = DoorLog.where("key = 'f'").count
@recent_door_denieds = DoorLog.where("key = 'f' AND created_at > ?", DateTime.now - 7.days).count
@num_macs = Mac.count
@recent_macs = Mac.where("since > ?", DateTime.now - 1.day).count
respond_to do |format|
format.html # index.html.erb
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
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

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

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

@@ -4,6 +4,10 @@ before_filter :authenticate_user!
def index
@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

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

@@ -1,22 +1,66 @@
class MacsController < ApplicationController
rescue_from CanCan::AccessDenied do |exception|
today = Date.today
event = Date.new(2013,9,1)
load_and_authorize_resource :mac, :except => [:index, :create, :history]
#load_and_authorize_resource :user, :through => :mac, :except => [:index, :show, :scan, :import]
if today == event
redirect_to main_app.root_url, :alert => "CryptoParty today; no MAC scanning. Sorry, NSA!"
else
redirect_to main_app.root_url, :alert => "Nothing to see here!"
end
end
load_and_authorize_resource :mac
load_and_authorize_resource :user, :through => :mac, :except => [:index, :show, :scan, :import]
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)
@@ -26,7 +70,7 @@ def index
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")
@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")
@@ -42,10 +86,52 @@ def index
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
@@ -80,26 +166,34 @@ end
end
# POST /macs
# POST /user
def create
@mac = Mac.new(params[:mac])
authorize! :update, @mac
@existing_mac = Mac.find_by_mac(@mac.mac)
if can? :manage, Mac then
@users = User.accessible_by(current_ability).sort_by(&:name)
else
@users = [current_user]
end
if can? :manage, Mac then
@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 created.' }
format.json { render :json => @mac, :status => :created, :location => @mac }
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
format.html { render :action => "new" }
format.json { render :json => @mac.errors, :status => :unprocessable_entity }
respond_to do |format|
if @mac.save
format.html { redirect_to macs_path, :notice => 'MAC was successfully created.' }
format.json { render :json => @mac, :status => :created, :location => @mac }
else
format.html { render :action => "new" }
format.json { render :json => @mac.errors, :status => :unprocessable_entity }
end
end
end
end
end
# PUT /macs/1
@@ -119,7 +213,7 @@ end
respond_to do |format|
if @mac.save
format.html { redirect_to macs_path, :notice => 'Mac was successfully updated.' }
format.html { redirect_to macs_path, :notice => 'MAC was successfully updated.' }
format.json { head :no_content }
else
format.html { render :action => "edit" }
@@ -128,115 +222,122 @@ 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"
# 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[: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[: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[: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_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_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_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
}
options[:db_password] = "configure_me"
opts.on("p", "--db-password=password", "Database Password") { |password|
options[:db_password] = password
}
}.parse!
}.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])
# 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 Mac < ActiveRecord::Base
#end
#class MacLog < 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|
Rails.logger.info "Reading stdin: "+stdin.inspect
stdin.each { |line|
next if line !~ /^([\d\.]+)\s+([[:xdigit:]:]+)\s/;
macs[$2] = $1;
}
}
# 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 ! 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
# 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
}
# 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
}
# 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

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

@@ -1,6 +1,6 @@
class PaymentsController < ApplicationController
load_and_authorize_resource :payment
load_and_authorize_resource :user, :through => :payment
#load_and_authorize_resource :user, :through => :payment
before_filter :authenticate_user!
# Load users and certs based on current ability
@@ -16,6 +16,10 @@ class PaymentsController < ApplicationController
# 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
@@ -23,6 +27,57 @@ class PaymentsController < ApplicationController
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

View File

@@ -0,0 +1,28 @@
class PaypalCsvsController < ApplicationController
load_and_authorize_resource :paypal_csv
before_filter :authenticate_user!
def index
end
def show
end
def new
end
def create
PaypalCsv.batch_import_from_csv(params[:file].path)
redirect_to paypal_csvs_path, :notice => 'Paypal CSV batch was successfully loaded.'
end
def link
result = @paypal_csv.link_payment
if result.first
redirect_to paypal_csvs_url, :notice => 'Payment was successfully linked.'
else
redirect_to paypal_csvs_url, :notice => result.last
end
end
end

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

View File

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

View File

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

View 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

View 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

View File

@@ -0,0 +1,68 @@
class StatisticsController < ApplicationController
before_filter :load_and_authorize_user
def index
end
def door_log
# Get own user's door data
cards = @user.cards
card_hash = {}
cards.each{|c| card_hash[c.card_number.to_i(16)%32767] = c.card_number}
card_num_Rs = cards.map{|c| c.card_number.to_i(16)%32767}
@door_logs = DoorLog.where("data = ?", card_num_Rs).order("created_at ASC")
@door_logs.map{|l|
l.data = card_hash[l.data.to_i].to_i(16)
l.key = DoorLog.key_legend[l.key]
}
@door_log_graph = []
@door_logs.where("key = 'G'").group_by{|l| l.created_at.beginning_of_day}.each{|l| @door_log_graph << [l.first.to_time.to_i*1000,l.last.size]}
respond_to do |format|
format.html
format.json { render :json => @door_logs }
end
end
def mac_log
macs = @user.macs.where(:hidden => false).map{|m| m.mac}
@mac_logs = MacLog.where(:mac => macs)
@mac_log_graph = {}
macs.each do |mac|
mac_log = MacLog.where(:mac => mac)
mac_times = []
last_active = nil
mac_log.each do |entry|
# Find an activate followed immediately by a deactivate
if entry.action == "activate"
last_active = entry
else
if last_active && entry.action == "deactivate"
# Calculate the time difference between the two and append to this mac's total time
mac_times << [entry.created_at, ((entry.created_at - last_active.created_at)/60/60)]
else
# No pair found; discard.
last_active = nil
end
end
end
mac_log_graph = []
mac_times.group_by{|m| m.first.beginning_of_day}.each{|m| mac_log_graph << [m.first.to_time.to_i*1000,m.last.map{|n| n.last}.sum.round(2)]}
# Store each mac in the hash with its graph
@mac_log_graph[mac] = mac_log_graph
end
#@mac_log_graph = mac_log_grouped.map{|g| [g.first.to_time.to_i*1000, g.last.size] }
respond_to do |format|
format.html
format.json { render :json => @mac_logs }
end
end
def load_and_authorize_user
@user = current_user
authorize! :read, @user
end
end

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

@@ -1,7 +1,7 @@
class UserCertificationsController < ApplicationController
load_and_authorize_resource :user_certification
load_and_authorize_resource :user, :through => :user_certification
load_and_authorize_resource :certification, :through => :user_certification
#load_and_authorize_resource :user, :through => :user_certification
#load_and_authorize_resource :certification, :through => :user_certification
before_filter :authenticate_user!
# Load users and certs based on current ability

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

@@ -1,38 +1,81 @@
class UsersController < ApplicationController
load_and_authorize_resource
before_filter :authenticate_user!
layout 'resources'
def sort_by_cert(certs,id)
result = 0
certs.each do |c|
if c.id == id
result = 1
end
end
return result
end
# GET /users
# GET /users.json
def index
case params[:sort]
when "name"
@users = @users.sort_by(&:name)
when "certifications"
@users = @users.sort_by{ |u| [-u.certifications.count,u.name] }
when "orientation"
@users = @users.sort_by{ |u| [-u.orientation.to_i,u.name] }
when "waiver"
@users = @users.sort_by{ |u| [-u.waiver.to_i,u.name] }
when "member"
@users = @users.sort_by{ |u| [-u.member_status.to_i,u.name] }
when "card"
@users = @users.sort_by{ |u| [-u.cards.count,u.name] }
when "instructor"
@users = @users.sort{ |a,b| [b.instructor.to_s,a.name] <=> [a.instructor.to_s,b.name] }
when "admin"
@users = @users.sort{ |a,b| [b.admin.to_s,a.name] <=> [a.admin.to_s,b.name] }
else
@users = @users.sort_by(&:name)
end
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|
format.html # index.html.erb
format.json { render :json => @users }
respond_to do |format|
format.html # index.html.erb
format.json { render :json => @users }
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.json
def show
@@ -43,6 +86,22 @@ class UsersController < ApplicationController
end
end
def compose_email
@user = User.find(params[:user_id])
end
def send_email
@user = User.find(params[:user_id])
@subject = params[:subject]
@body = params[:body]
if @user.send_email(current_user,@subject,@body)
redirect_to user_path(@user), :notice => "Email sent successfully."
else
flash[:alert] = "Error sending email."
render :compose_email
end
end
# GET /user_summary/1
def user_summary
respond_to do |format|
@@ -67,6 +126,9 @@ class UsersController < ApplicationController
# POST /users
# POST /users.json
def create
# update oriented_by only if orientation has been set
@user.oriented_by_id = current_user.id unless @user.orientation.blank?
respond_to do |format|
if @user.save
format.html { redirect_to @user, :notice => 'User was successfully created.' }
@@ -81,6 +143,10 @@ class UsersController < ApplicationController
# PUT /users/1
# PUT /users/1.json
def update
# update oriented_by only if it's blank but the (new) orientation isn't blank
# gotta test the params because they don't get applied til below.
@user.oriented_by_id = current_user.id if @user.oriented_by.blank? && (!params[:user]["orientation(1i)"].blank?)
respond_to do |format|
if @user.update_attributes(params[:user])
format.html { redirect_to @user, :notice => 'User was successfully updated.' }

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

@@ -1,4 +1,31 @@
module ApplicationHelper
@payment_methods = [[nil],["PayPal"],["Dwolla"],["Bill Pay"],["Check"],["Cash"],["Other"]]
@payment_instructions = {nil => nil, :paypal => "Set up a monthly recurring payment to hslfinances@gmail.com", :dwolla => "Set up a monthly recurring payment to hslfinances@gmail.com", :billpay => "Have your bank send a monthly check to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201", :check => "Mail to HeatSync Labs Treasurer, 140 W Main St, Mesa AZ 85201 OR put in the drop safe at the Lab with a deposit slip firmly attached each month.", :cash => "Put in the drop safe at the Lab with a deposit slip firmly attached each month.", :other => "Hmm... talk to a Treasurer!"}
def sort_link(title, column, options = {})
condition = options[:unless] if options.has_key?(:unless)
sort_dir = params[:dir] == 'up' ? 'down' : 'up'
link_to_unless condition, title, request.parameters.merge( {:sort => column, :dir => sort_dir} )
end
def li_link_to(name = nil, options = nil, html_options = nil, &block)
html_options, options, name = options, name, block if block_given?
options ||= {}
html_options = convert_options_to_data_attributes(options, html_options)
url = url_for(options)
html_options['href'] ||= url
if current_page?(url)
content_tag(:li, class: "active") do
content_tag(:a, name || url, html_options, &block)
end
else
content_tag(:li) do
content_tag(:a, name || url, html_options, &block)
end
end
end
end

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

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

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

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

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

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

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

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

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

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

12
app/mailers/door_mailer.rb Executable file
View File

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

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

@@ -1,13 +1,20 @@
class UserMailer < ActionMailer::Base
default :from => "wiki@heatsynclabs.org"
default :from => "no-reply@heatsynclabs.org"
def new_user_email(user)
@user = user
@url = "http://members.heatsynclabs.org"
#@admins = User.where(:name => "Will Bradley")
#@admins.each do |admin|
mail(:to => 'info@heatsynclabs.org', :subject => "New HSL Member: "+user.name)
#end
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
View File

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

@@ -2,25 +2,30 @@ class Ability
include CanCan::Ability
def initialize(user)
# Anonymous can read mac
today = Date.today
event = Date.new(2013,9,1)
unless today == event
can :read, Mac
can :scan, Mac # Need anonymous so CRON can scan
end
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 unless today == event
can :read_details, Mac
can [:update], Mac, :user_id => nil
can [:create,:update], Mac, :user_id => user.id
can :read, User, :id => user.id #TODO: why can users update themselves?
can [:create,:update,:destroy], Resource, :user_id => user.id
can :read, Payment, :user_id => user.id
can :read, UserCertification, :user_id => user.id
can :read, 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?
@@ -31,13 +36,17 @@ class Ability
end
# Users can see others' stuff if they've been oriented
unless user.orientation.blank?
can :read, User, :hidden => [nil,false]
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 all
# Accountants can manage payments
if user.accountant?
can :manage, Payment
can :manage, Ipn
can :manage, PaypalCsv
end
# Admins can manage all
@@ -45,7 +54,7 @@ class Ability
can :manage, :all
end
# Prevent all destruction for now
# Prevent most destruction for now
#cannot :destroy, User
#cannot :destroy, Card
cannot :destroy, Certification

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

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

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

@@ -1,5 +1,7 @@
class Certification < ActiveRecord::Base
attr_accessible :description, :name
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
View File

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

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

@@ -2,6 +2,126 @@ class DoorLog < ActiveRecord::Base
attr_accessible :data, :key
require 'open-uri'
def self.execute_command(command)
output = ""
# load config values
door_access_url = APP_CONFIG['door_access_url']
door_access_password = APP_CONFIG['door_access_password']
#login
source = open("#{door_access_url}?e=#{door_access_password}").read
results = source.scan(/ok/)
#only continue if we've got an OK login
if(results.size > 0) then
# Parse the command and result
parsing = parse_command(command)
output += parsing[:output]
url_param = parsing[:url_param]
# Execute the command
open("#{door_access_url}?#{url_param}")
self.download_status # Update the status cache
else
output += 'Failed to connect to door system.'
end
#logout
open("#{door_access_url}?e=0000")
return output
end
def self.parse_command(command)
output = ""
url_param = ""
# @commands = [
# ["Unlock all doors","unlock"],
# ["Unlock Front door","unlock-front"],
# ["Unlock Rear door","unlock-rear"],
# ["Lock all doors","lock"],
# ["Lock Front door","lock-front"],
# ["Lock Rear door","lock-rear"],
# ["Open Front door","open-front"],
# ["Open Rear door","open-rear"],
# ["Arm alarm","arm"],
# ["Disarm alarm","disarm"]
# ]
case command
when "open-front"
output += "Front door opened."
url_param = "o1"
when "open-rear"
output += "Rear door opened."
url_param = "o2"
when "u", "unlock"
output += "Doors unlocked, remember to re-lock them."
url_param = "u"
when "u1", "unlock-front"
output += "Front Door unlocked, remember to re-lock it."
url_param = "u=1"
when "u2", "unlock-rear"
output += "Rear Door unlocked, remember to re-lock it."
url_param = "u=2"
when "lock", "l"
output += "Doors locked."
url_param = "l"
when "lock-front", "l1"
output += "Front Door locked."
url_param = "l=1"
when "lock-rear", "l2"
output += "Rear Door locked."
url_param = "l=2"
when "arm"
output += "Armed."
url_param = "2"
when "disarm"
output += "Disarmed."
url_param = "1"
else
output += "Fail. Don't be a naughty user!"
url_param = "9" # Using 9 because it's just status, no harm done
end
return {:output => output, :url_param => url_param}
end
def self.show_status
door_logs = DoorLog.order('created_at DESC').where(key: ["door_1_locked","door_2_locked"]).limit(2)
door_1_locked = parse_locked_status(door_logs, "door_1_locked")
door_2_locked = parse_locked_status(door_logs, "door_2_locked")
# Doors are unlocked if 1 OR 2 are NOT locked
status = {:unlocked => (!door_1_locked || !door_2_locked), :door_1_locked => door_1_locked, :door_2_locked => door_2_locked }
end
def self.parse_locked_status(door_logs, door_key)
door_logs_selected = door_logs.select{|s| s.key == door_key }
if door_logs_selected.present?
door_data = door_logs_selected.first.data
if door_data == 0 # 0 = unlocked
return false
else
return true # 1 = locked
end
end
end
def self.download_status
# load config values
door_access_url = APP_CONFIG['door_access_url']
door_access_password = APP_CONFIG['door_access_password']
# query for status
source = open("#{door_access_url}?9").read
# expect {"armed"=>255, "activated"=>255, "alarm_3"=>1, "alarm_2"=>1, "door_1_locked"=>1, "door_2_locked"=>1}
# See https://github.com/heatsynclabs/Open_Access_Control_Ethernet for more info
@status = JSON.parse(source)
@status.each do |key,value|
DoorLog.create!({:key => key, :data => value})
end
end
def self.download_from_door
# load config values
door_access_url = APP_CONFIG['door_access_url']
@@ -41,4 +161,8 @@ class DoorLog < ActiveRecord::Base
end
end
def self.key_legend
{'G' => "Granted", "R" => "Read", "D" => "Denied",
'g' => "granted", "r" => "read", "d" => "denied"}
end
end

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

@@ -5,6 +5,14 @@ class Ipn < ActiveRecord::Base
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()
@@ -37,6 +45,7 @@ class Ipn < ActiveRecord::Base
end
unless response == "VERIFIED"
Rails.logger.error "Invalid IPN: #{response}"
Rails.logger.error "Data: #{self.data}"
return false
end
@@ -50,8 +59,8 @@ class Ipn < ActiveRecord::Base
private
def create_payment
# find user by email, then by payee
user = User.find_by_email(self.payer_email)
user = User.find_by_payee(self.payer_email) if user.nil? && self.payer_email.present?
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?

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

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

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

@@ -1,12 +1,12 @@
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")

80
app/models/paypal_csv.rb Executable file
View 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
View File

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

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

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

3
app/models/setting.rb Executable file
View File

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

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

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

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

@@ -1,6 +1,6 @@
class User < ActiveRecord::Base
include Gravtastic
gravtastic :size => 120, :default => ""
gravtastic :size => 150, :default => ""
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
@@ -9,12 +9,24 @@ class User < ActiveRecord::Base
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :admin, :instructor, :member, :emergency_name, :emergency_phone, :current_skills, :desired_skills, :waiver, :emergency_email, :phone, :payment_method, :orientation, :member_level, :certifications, :hidden, :marketing_source, :payee, :accountant #TODO: make admin/instructor/member/etc not accessible
attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :admin, :instructor, :member, :emergency_name, :emergency_phone, :current_skills, :desired_skills, :waiver, :emergency_email, :phone, :payment_method, :orientation, :member_level, :certifications, :hidden, :marketing_source, :payee, :accountant, :exit_reason, :twitter_url, :facebook_url, :github_url, :website_url, :email_visible, :phone_visible, :postal_code #TODO: make admin/instructor/member/etc not accessible
belongs_to :oriented_by, :foreign_key => "oriented_by_id", :class_name => "User"
has_many :cards
has_many :user_certifications
has_many :certifications, :through => :user_certifications
has_many :contracts
has_many :payments
has_many :macs
has_many :resources
scope :volunteer, -> { where('member_level >= 10 AND member_level < 25') }
scope :active, -> { where('member_level >= 10') }
scope :paying, -> { joins(:payments).where("payments.date > ?", (DateTime.now - 90.days)).uniq }
validates_format_of [:twitter_url, :facebook_url, :github_url, :website_url], :with => URI::regexp(%w(http https)), :allow_blank => true
# disable # validates_presence_of :postal_code
after_create :send_new_user_email
@@ -45,6 +57,7 @@ class User < ActiveRecord::Base
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
}
@@ -52,6 +65,11 @@ class User < ActiveRecord::Base
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"
@@ -89,6 +107,11 @@ class User < ActiveRecord::Base
{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]
@@ -96,10 +119,43 @@ class User < ActiveRecord::Base
def member_status_symbol
results = member_status_calculation
return "<img src='/#{results[:icon]}#{results[:flair]}-coin.png' title='#{results[:message]}' />"
return "<img src='/#{results[:icon]}#{results[:flair]}-coin.png' title='#{results[:message]}' class='member-status-symbol' />"
end
private
def last_payment_date
self.payments.maximum(:date)
end
def delinquency
if self.payments.count > 0
paydate = self.payments.maximum(:date)
(Date.today - paydate).to_i
else
(Date.today - self.created_at.to_date).to_i
end
end
def send_email(from_user,subject,body)
Rails.logger.info UserMailer.email(self,from_user,subject,body).deliver
end
def has_certification?(cert_slug)
if self.certifications.find_by_slug(cert_slug)
true
else
false
end
end
def contract_date
self.contracts.first.signed_at unless self.contracts.blank?
end
private
def send_new_user_email
Rails.logger.info UserMailer.new_user_email(self).deliver
end
def member_status_calculation
# Begin output buffer
@@ -112,7 +168,7 @@ class User < ActiveRecord::Base
case self.member_level.to_i
when 0..9
if self.payments.count > 0 then
message = "Former Member (#{(DateTime.now - self.payments.last.date).to_i} days ago)"
message = "Former Member (#{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago)"
icon = :timeout
rank = 1
else
@@ -138,29 +194,38 @@ class User < ActiveRecord::Base
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.last.date > (DateTime.now - 60.days)
if self.payments.maximum(:date) > (DateTime.now - 60.days)
flair = "-paid"
paid = true
else
message = "Last Payment #{(DateTime.now - self.payments.last.date).to_i/30} months ago"
rank = rank/10
message = "Last Payment #{(DateTime.now - self.payments.maximum(:date)).to_i/30} months ago"
paid = false
end
else
message = "No Payments Recorded"
rank = rank/10
paid = false
end
end
return {:message => message, :icon => icon, :flair => flair, :rank => rank}
end
def send_new_user_email
Rails.logger.info UserMailer.new_user_email(self).deliver
return {:message => message, :paid => paid, :flair => flair}
end
end

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

@@ -8,10 +8,10 @@ class UserCertification < ActiveRecord::Base
belongs_to :certification
def user_name
if self.user.blank?
""
if user.blank?
return "n/a (user ##{user_id} missing)"
else
self.user.name
return self.user.name
end
end
end

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

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

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

@@ -1,13 +1,20 @@
<h1>Access Cards</h1>
<%= link_to 'New Card', new_card_path if can? :create, Card %>
<%= link_to 'Upload all cards', upload_all_path if can? :upload_all, Card %>
<%= link_to 'Door Logs', door_logs_path if can? :read, DoorLog %>
<%= 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 7 Days:</b> <%= @most_active_card.name unless @most_active_card.blank? %> (<%= @most_active_card.accesses_this_week unless @most_active_card.blank? %> times)
<b>Most Active Card Last Month:</b> <%= @most_active_card.user.name unless @most_active_card.user.blank? %> (<%= @most_active_card.accesses_this_week unless @most_active_card.blank? %> days)
</p>
<p>
<% unless @runner_up_card.blank? || @runner_up_card.user.blank? %>
<b>Runner Up:</b> <%= @runner_up_card.user.name %> (<%= @runner_up_card.accesses_this_week %> days)
<% end %>
</p>
<table>
<table class="lined-table">
<col />
<col />
<col class="col_highlight" />
@@ -19,7 +26,7 @@
<th>DB ID</th>
<th>Card #</th>
<th>Access?</th>
<th>Accesses Last 7 Days</th>
<th>Days Accessed Last Month</th>
<th></th>
<th></th>
<th></th>
@@ -32,6 +39,7 @@
<% if card.user.nil? %>
n/a
<% else %>
<%= raw(card.user.member_status_symbol) %>
<%= link_to card.user.name , card %>
<% end %>
</td>

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

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

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

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

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

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

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

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

View File

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

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