waxx 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/LICENSE +201 -0
- data/README.md +879 -0
- data/bin/waxx +120 -0
- data/lib/waxx/app.rb +173 -0
- data/lib/waxx/conf.rb +54 -0
- data/lib/waxx/console.rb +204 -0
- data/lib/waxx/csrf.rb +14 -0
- data/lib/waxx/database.rb +80 -0
- data/lib/waxx/encrypt.rb +38 -0
- data/lib/waxx/error.rb +60 -0
- data/lib/waxx/html.rb +33 -0
- data/lib/waxx/http.rb +268 -0
- data/lib/waxx/init.rb +273 -0
- data/lib/waxx/irb.rb +44 -0
- data/lib/waxx/irb_env.rb +18 -0
- data/lib/waxx/json.rb +23 -0
- data/lib/waxx/mongodb.rb +221 -0
- data/lib/waxx/mysql2.rb +234 -0
- data/lib/waxx/object.rb +115 -0
- data/lib/waxx/patch.rb +138 -0
- data/lib/waxx/pdf.rb +69 -0
- data/lib/waxx/pg.rb +246 -0
- data/lib/waxx/process.rb +270 -0
- data/lib/waxx/req.rb +116 -0
- data/lib/waxx/res.rb +98 -0
- data/lib/waxx/server.rb +304 -0
- data/lib/waxx/sqlite3.rb +237 -0
- data/lib/waxx/supervisor.rb +47 -0
- data/lib/waxx/test.rb +162 -0
- data/lib/waxx/util.rb +57 -0
- data/lib/waxx/version.rb +3 -0
- data/lib/waxx/view.rb +389 -0
- data/lib/waxx/waxx.rb +73 -0
- data/lib/waxx/x.rb +103 -0
- data/lib/waxx.rb +50 -0
- data/skel/README.md +11 -0
- data/skel/app/app/app.rb +39 -0
- data/skel/app/app/error/app_error.rb +16 -0
- data/skel/app/app/error/dhtml.rb +9 -0
- data/skel/app/app/error/html.rb +8 -0
- data/skel/app/app/error/json.rb +8 -0
- data/skel/app/app/error/pdf.rb +13 -0
- data/skel/app/app/log/app_log.rb +13 -0
- data/skel/app/app.rb +20 -0
- data/skel/app/home/home.rb +16 -0
- data/skel/app/home/html.rb +145 -0
- data/skel/app/html.rb +192 -0
- data/skel/app/usr/email.rb +66 -0
- data/skel/app/usr/html.rb +115 -0
- data/skel/app/usr/list.rb +51 -0
- data/skel/app/usr/password.rb +54 -0
- data/skel/app/usr/record.rb +98 -0
- data/skel/app/usr/usr.js +67 -0
- data/skel/app/usr/usr.rb +277 -0
- data/skel/app/waxx/waxx.rb +109 -0
- data/skel/bin/README.md +1 -0
- data/skel/db/README.md +11 -0
- data/skel/db/app/0-init.sql +88 -0
- data/skel/lib/README.md +1 -0
- data/skel/log/README.md +1 -0
- data/skel/opt/dev/config.yaml +1 -0
- data/skel/opt/prod/config.yaml +1 -0
- data/skel/opt/stage/config.yaml +1 -0
- data/skel/opt/test/config.yaml +1 -0
- data/skel/private/README.md +1 -0
- data/skel/public/lib/site.css +202 -0
- data/skel/public/lib/waxx/w.ico +0 -0
- data/skel/public/lib/waxx/w.png +0 -0
- data/skel/public/lib/waxx/waxx.js +111 -0
- data/skel/tmp/pids/README.md +1 -0
- data.tar.gz.sig +0 -0
- metadata +140 -0
- metadata.gz.sig +3 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module App::Usr::Email
|
|
2
|
+
extend Waxx::Html
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
def password_reset(x, u, k)
|
|
6
|
+
{
|
|
7
|
+
to_email: u['usr_name'],
|
|
8
|
+
from_email: Conf['site']['support_email'],
|
|
9
|
+
from_name: Conf['site']['name'],
|
|
10
|
+
subject: "Password Reset",
|
|
11
|
+
body_text: password_reset_text(x, u, k),
|
|
12
|
+
body_html: password_reset_html(x, u, k)
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def email_not_found(x, email)
|
|
17
|
+
{
|
|
18
|
+
to_email: email,
|
|
19
|
+
from_email: Conf['site']['support_email'],
|
|
20
|
+
from_name: Conf['site']['name'],
|
|
21
|
+
subject: "Account Not Found",
|
|
22
|
+
body_text: email_not_found_text(x)
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def email_not_found_text(x)
|
|
27
|
+
App::Email::Email.text(x, title: "Email Address Not Found", content: %(
|
|
28
|
+
We received a request to reset your password on #{h Conf['site']['name']}.
|
|
29
|
+
|
|
30
|
+
Unfortunately, we do not have an account with your email address. Please setup your account here:",
|
|
31
|
+
|
|
32
|
+
#{Conf['site']['url']}
|
|
33
|
+
|
|
34
|
+
If you did not request a password reset, please disregard this email. Most likely someone else mistyped their email.
|
|
35
|
+
|
|
36
|
+
#{App::Email::Email.support_signature_text(x)}
|
|
37
|
+
))
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def password_reset_text(x, u, k)
|
|
42
|
+
App::Email::Email.text(x, title: "Password Reset", content: %(
|
|
43
|
+
We received a request to reset your password on #{h Conf['site']['name']}.
|
|
44
|
+
|
|
45
|
+
Please click the link below to change your password. The link is valid for six hours.
|
|
46
|
+
|
|
47
|
+
#{Conf['site']['url']}usr/password/#{u['usr_id']}/#{k}
|
|
48
|
+
|
|
49
|
+
If you did not request a password reset, please disregard this email. Most likely someone else mistyped their email.
|
|
50
|
+
|
|
51
|
+
#{App::Email::Email.support_signature_text(x)}
|
|
52
|
+
))
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def password_reset_html(x, u, k)
|
|
57
|
+
App::Email::Email.html(x, title: "Password Reset", content: %(
|
|
58
|
+
<p>We received a request to reset your password on #{h Conf['site']['name']}.</p>
|
|
59
|
+
<p>Please click the link below to change your password. The link is valid for six hours.</p>
|
|
60
|
+
<p><a href="#{Conf['site']['url']}usr/password/#{u['usr_id']}/#{k}">Reset Password</a></p>
|
|
61
|
+
<p>If you did not request a password reset, please disregard this email. Most likely someone else mistyped their email.</p>
|
|
62
|
+
#{App::Email::Email.support_signature_html(x)}
|
|
63
|
+
))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
module App::Usr::Html
|
|
2
|
+
extend Waxx::Html
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
def home(x, usr:{}, person:{})
|
|
6
|
+
%(
|
|
7
|
+
<div class="row">
|
|
8
|
+
<div class="col-md-3 nav2">#{App::Person::Html.nav2(x)}</div>
|
|
9
|
+
<div class="col-md-8">
|
|
10
|
+
<h1>#{person['first_name'].h} #{person['last_name'].h}</h1>
|
|
11
|
+
<div>#{usr['usr_name'].h}</div>
|
|
12
|
+
<div>#{person['phone'].h}</div>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def login(x, return_to:"/")
|
|
19
|
+
%(
|
|
20
|
+
<div class="container">
|
|
21
|
+
<h1>Client Portal</h1>
|
|
22
|
+
<div class="row">
|
|
23
|
+
<div class="col-md-4">#{login_form(x, return_to:return_to)}</div>
|
|
24
|
+
<div class="col-md-8">#{App::WebsitePage.by_uri(x, uri:"/portal")['content']}</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def login_form(x, return_to: "/")
|
|
31
|
+
%(
|
|
32
|
+
<form action="/usr/login" method="post">
|
|
33
|
+
<!--#{Waxx::Csrf.ht(x)}-->
|
|
34
|
+
<input type="hidden" name="return_to" value="#{h return_to}">
|
|
35
|
+
<div class="form-group">
|
|
36
|
+
<label for="usr_name">Email</label>
|
|
37
|
+
<input type="email" class="form-control" id="usr_name" name="usr_name" placeholder="" value="#{x.ua['un'] if x.ua['rm']}">
|
|
38
|
+
</div>
|
|
39
|
+
<div class="form-group">
|
|
40
|
+
<label for="password">Password</label>
|
|
41
|
+
<input name="password" type="password" class="form-control" id="password" placeholder="">
|
|
42
|
+
</div>
|
|
43
|
+
<div class="checkbox">
|
|
44
|
+
<label><input name="remember_me" type="checkbox" #{"checked" if x.ua['rm']} value="1"> Remember me </label>
|
|
45
|
+
</div>
|
|
46
|
+
<button type="submit" class="btn btn-primary">Login</button>
|
|
47
|
+
</form>
|
|
48
|
+
<p style="margin-top: 2em;"><a href="#password_reset_form" onclick="$('#password_reset_form').toggle('blind')">Forgot password?</a></p>
|
|
49
|
+
<div id="password_reset_form" style="display:none;">
|
|
50
|
+
<form action="/usr/password_reset" method="post">
|
|
51
|
+
<!--#{Waxx::Csrf.ht(x)}-->
|
|
52
|
+
<div class="form-group">
|
|
53
|
+
<label for="email">Enter your email address</label>
|
|
54
|
+
<input type="email" class="form-control" id="email" name="email" placeholder="you@example.com" value="#{x.ua['un'] if x.ua['rm'] == 1}">
|
|
55
|
+
</div>
|
|
56
|
+
<button type="submit" class="btn btn-warning">Send Password Reset Link</button>
|
|
57
|
+
</form>
|
|
58
|
+
</div>
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def change_password(x, u=nil, key=nil)
|
|
63
|
+
content = App::Content.by_slug(x, slug: "password-rules")
|
|
64
|
+
%(
|
|
65
|
+
<div class="row">
|
|
66
|
+
<div class="col-md-3 nav2">#{App::Person::Html.nav2(x) if x.usr?}</div>
|
|
67
|
+
<div class="col-md-5">
|
|
68
|
+
<h1>Change Password</h1>
|
|
69
|
+
<form action="" method="post">
|
|
70
|
+
#{Waxx::Csrf.ht(x)}
|
|
71
|
+
<div class="form-group">
|
|
72
|
+
<!-- <label for="usr_name">User Name</label> -->
|
|
73
|
+
#{h(u ? u['usr_name'] : x.ua['un'])}
|
|
74
|
+
</div>
|
|
75
|
+
#{new_password_field(x)}
|
|
76
|
+
<button type="submit" class="btn btn-primary" id="btn-submit" disabled="disabled">Change Password</button>
|
|
77
|
+
</form>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def new_password_field(x)
|
|
84
|
+
%(
|
|
85
|
+
<div class="form-group">
|
|
86
|
+
<label for="password">Password</label>
|
|
87
|
+
<input name="password1" type="password" class="form-control" id="pw1" onkeyup="app.passwordNew('#pw1', '#pw2', '#btn-submit');">
|
|
88
|
+
<div class="text-muted">Score 60+. Use upper & lower case, numbers, and symbols.</div>
|
|
89
|
+
<div class="text-muted">Score: <span id="pw1-status" style="color:#000; font-weight: normal;">0 Continue</span></div>
|
|
90
|
+
<div style="border: 1px solid #ccc; background-color:#eee;">
|
|
91
|
+
<div id="pw1-meter" style="height: 4px; width:0; background-color:red;color:white;overflow:visible;font-size:9px;"></div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="form-group">
|
|
95
|
+
<label for="password">Confirm Password <span id="pw2-icon" class="glyphicon glyphicon-unchecked"></span></label>
|
|
96
|
+
<input name="password2" type="password" class="form-control" id="pw2" onkeyup="app.passwordNew('#pw1', '#pw2', '#btn-submit');">
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def list(x, usrs)
|
|
102
|
+
re = [%(<table class="table">
|
|
103
|
+
<tr><th>ID</th><th>User Name</th><th>Last Login</th><th>Failed Logins</th></tr>
|
|
104
|
+
)]
|
|
105
|
+
re << usrs.map{|u|
|
|
106
|
+
%(<tr><td>#{u/:id}</td>
|
|
107
|
+
<td>#{u/:usr_name}</td>
|
|
108
|
+
<td>#{u['last_login_date'].f("%d-%b-%Y @%H:%M")} from #{u/:last_login_host}</td>
|
|
109
|
+
<td>#{u/:failed_login_count}</td>
|
|
110
|
+
</tr>)
|
|
111
|
+
}
|
|
112
|
+
re << %(</table><a href="/usr/record/0" class="btn btn-success"><span class="glyphicon glyphicon-plus"></span> Add User</a>)
|
|
113
|
+
re.join
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module App::Usr::List
|
|
2
|
+
extend Waxx::View
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
has(
|
|
6
|
+
:id,
|
|
7
|
+
:usr_name,
|
|
8
|
+
:last_login_date,
|
|
9
|
+
:failed_login_count,
|
|
10
|
+
#"person_id: person.id",
|
|
11
|
+
#"person.first_name",
|
|
12
|
+
#"person.last_name",
|
|
13
|
+
#"company_name: company.company_name"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
module Html
|
|
17
|
+
extend Waxx::Html
|
|
18
|
+
extend self
|
|
19
|
+
|
|
20
|
+
def get(x, d, message:{})
|
|
21
|
+
title = "People"
|
|
22
|
+
App::Html.admin(x,
|
|
23
|
+
title: title,
|
|
24
|
+
content: content(x, data: (d||{}), title: title),
|
|
25
|
+
js_ready: %(
|
|
26
|
+
$('#usrs tr').click(function(ev){
|
|
27
|
+
location=$(ev.target.parentElement).attr('href');
|
|
28
|
+
})
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def content(x, data:{}, title:"Untitled")
|
|
34
|
+
re = [%(<table id="usrs" class="table table-hover">
|
|
35
|
+
<tr><th>ID</th><th>Name</th><th>Company</th><th>User Name</th><th>Last Login</th><th>Failed Logins</th></tr>
|
|
36
|
+
)]
|
|
37
|
+
re << data.map{|u|
|
|
38
|
+
%(<tr href="/usr/record/#{u/:id}" style="cursor:pointer;"><td>#{u/:id}</td>
|
|
39
|
+
<td>#{h u/:first_name} #{h u/:last_name}</td>
|
|
40
|
+
<td>#{h u/:company_name}</td>
|
|
41
|
+
<td>#{u/:usr_name}</td>
|
|
42
|
+
<td>#{u['last_login_date'].nil? ? "" : "#{u['last_login_date'].f("%d-%b-%Y @%H:%M")} from #{u/:last_login_host}"}</td>
|
|
43
|
+
<td>#{u/:failed_login_count}</td>
|
|
44
|
+
</tr>)
|
|
45
|
+
}
|
|
46
|
+
re << %(</table><a href="/usr/record/0" class="btn btn-success"><span class="glyphicon glyphicon-plus"></span> Add User</a>)
|
|
47
|
+
re.join
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module App::Usr::Password
|
|
2
|
+
extend self
|
|
3
|
+
|
|
4
|
+
def send_email(x)
|
|
5
|
+
App::Email.post(x,
|
|
6
|
+
to_email: x['usr_name'],
|
|
7
|
+
from_email: Conf['site']['support_email'],
|
|
8
|
+
from_name: Conf['site']['name'],
|
|
9
|
+
subject: "Password Reset",
|
|
10
|
+
body_text: ["You requested a password reset for #{Conf['site']['name']}.",
|
|
11
|
+
"If you did not request a password reset, please ignore this email.",
|
|
12
|
+
"\n\nThe link below is valid for 3 hours.",
|
|
13
|
+
"Please click this link to reset your password:\n\n",
|
|
14
|
+
" #{Conf['site']['url']}/usr/password/#{u['usr_id']}/#{u['key']}",
|
|
15
|
+
"\n\nThank you,\n\nThe #{Conf['site']['name']} Team"
|
|
16
|
+
].join
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def text(x, usr, key, as)
|
|
21
|
+
%(
|
|
22
|
+
Hopefully it was you who requested a password reset for #{Conf['site']['name']}.
|
|
23
|
+
If you did not request a password reset, please ignore this email.
|
|
24
|
+
|
|
25
|
+
The link below is valid for 3 hours.
|
|
26
|
+
Please click the link to reset your password:
|
|
27
|
+
|
|
28
|
+
#{Conf['site']['url']}/usr/password/#{usr['usr_id']}/#{key}
|
|
29
|
+
|
|
30
|
+
Please reply to this email if you need any help or have any questions.
|
|
31
|
+
|
|
32
|
+
Thank you,
|
|
33
|
+
|
|
34
|
+
The #{Conf['site']['name']} Team
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def html
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def post(x)
|
|
43
|
+
# See if the user exists
|
|
44
|
+
u = App::Usr.usr(x, usr_name: x['usr_name'])
|
|
45
|
+
if u['usr_id'].to_i > 0
|
|
46
|
+
k = App::Usr.key_for_reset(x, u['usr_id'])
|
|
47
|
+
App::Html.page(x, title:"Password Reset Sent", content:"A link to reset your password has been sent to #{x['email'].h}. Please check your email. (It may be in your SPAM folder.)")
|
|
48
|
+
else
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module App::Usr::Record
|
|
2
|
+
extend Waxx::View
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
@joins = {
|
|
6
|
+
person:"LEFT JOIN person ON usr.person_id = person.person_id"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
has(
|
|
10
|
+
:id,
|
|
11
|
+
:usr_name,
|
|
12
|
+
#"person_id:person.id",
|
|
13
|
+
#"first_name:person.first_name",
|
|
14
|
+
#"last_name:person.last_name",
|
|
15
|
+
#"company_id:person.company_id"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
def save(x, id:0, data:{})
|
|
19
|
+
if id.to_i == 0
|
|
20
|
+
person = App::Person.post(x, {
|
|
21
|
+
first_name: data['first_name'],
|
|
22
|
+
last_name: data['last_name'],
|
|
23
|
+
company_id: data['company_id']
|
|
24
|
+
})
|
|
25
|
+
App::Usr.create(x, {
|
|
26
|
+
usr_name: data['usr_name'],
|
|
27
|
+
password: data['password'],
|
|
28
|
+
person_id: person['id']
|
|
29
|
+
}, returning:"id")
|
|
30
|
+
else
|
|
31
|
+
App::Usr.set_password(x, id, data['password'])
|
|
32
|
+
App::Usr.put(x, id, {usr_name: data['usr_name']})
|
|
33
|
+
App::Person.put(x, id, {
|
|
34
|
+
first_name: data['first_name'],
|
|
35
|
+
last_name: data['last_name'],
|
|
36
|
+
company_id: data['company_id']
|
|
37
|
+
})
|
|
38
|
+
end
|
|
39
|
+
Html.post(x,{})
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
module Html
|
|
43
|
+
extend Waxx::Html
|
|
44
|
+
extend self
|
|
45
|
+
|
|
46
|
+
def get(x, d, message:{})
|
|
47
|
+
title = d.nil? ? "New User" : "#{d['first_name']} #{d['last_name']}"
|
|
48
|
+
App::Html.admin(x,
|
|
49
|
+
title: title,
|
|
50
|
+
content: content(x, data: (d||{}), title: title)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def post(x, data, message={})
|
|
55
|
+
#App::Usr::List.view(x, meth: "get", message: {type: "success", message:"The user was updated successfully."})
|
|
56
|
+
x.res.redirect "/usr"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def content(x, data:{}, title:"Untitled")
|
|
60
|
+
%(
|
|
61
|
+
<form action="" method="post">
|
|
62
|
+
<div class="form-group">
|
|
63
|
+
<label for="usr_name">First Name</label>
|
|
64
|
+
<input type="text" id="first_name" name="first_name" class="form-control" value="#{h data['first_name']}">
|
|
65
|
+
</div>
|
|
66
|
+
<div class="form-group">
|
|
67
|
+
<label for="usr_name">Last Name</label>
|
|
68
|
+
<input type="text" id="last_name" name="last_name" class="form-control" value="#{h data['last_name']}">
|
|
69
|
+
</div>
|
|
70
|
+
<div class="form-group">
|
|
71
|
+
<label for="company_id">Company</label>
|
|
72
|
+
<select id="company_id" name="company_id" class="form-control">
|
|
73
|
+
#{company_options(x, data)}
|
|
74
|
+
</select>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="form-group">
|
|
77
|
+
<label for="usr_name">Email</label>
|
|
78
|
+
<input type="text" id="usr_name" name="usr_name" class="form-control" value="#{h data['usr_name']}">
|
|
79
|
+
</div>
|
|
80
|
+
<div class="form-group">
|
|
81
|
+
<label for="password">Password</label>
|
|
82
|
+
<input type="password" id="password" name="password" class="form-control" value="">
|
|
83
|
+
</div>
|
|
84
|
+
<br>
|
|
85
|
+
<button type="submit" class="btn btn-primary" name="btn" value="save">Save</button>
|
|
86
|
+
</form>
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def company_options(x, data)
|
|
91
|
+
App::Company.get(x, select:"id, name", order:"name").map{|r|
|
|
92
|
+
selected = r['id'] == data['company_id'] ? "selected" : ""
|
|
93
|
+
"<option value='#{r['id']}' #{selected}>#{h r['name']}</option>"
|
|
94
|
+
}.join
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|
data/skel/app/usr/usr.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
app.usr = {
|
|
2
|
+
humanClick: function(icon){
|
|
3
|
+
$('#bot_nav .btn-primary').removeClass('btn-primary').addClass('btn-link');
|
|
4
|
+
$('#'+icon).removeClass('btn-link').addClass('btn-primary');
|
|
5
|
+
$('#bot_check').val(icon);
|
|
6
|
+
},
|
|
7
|
+
passwordNew: function(pw1, pw2, btn){
|
|
8
|
+
var score = app.passwordScore($(pw1).val());
|
|
9
|
+
var match = $(pw1).val() == $(pw2).val();
|
|
10
|
+
app.passwordStrengthMeter(score, pw1+"-status", pw1+"-meter");
|
|
11
|
+
app.passwordMatchIcon(pw1, pw2);
|
|
12
|
+
$(btn).attr('disabled',!(match && score >= 60));
|
|
13
|
+
},
|
|
14
|
+
// Thanks to tm_lv on stackoverflow
|
|
15
|
+
passwordScore: function(pw){
|
|
16
|
+
var score = 0;
|
|
17
|
+
if (!pw){return score;}
|
|
18
|
+
// award every unique letter until 5 repetitions
|
|
19
|
+
var letters = new Object();
|
|
20
|
+
for (var i=0; i<pw.length; i++) {
|
|
21
|
+
letters[pw[i]] = (letters[pw[i]] || 0) + 1;
|
|
22
|
+
score += 5.0 / letters[pw[i]];
|
|
23
|
+
}
|
|
24
|
+
// bonus points for mixing it up
|
|
25
|
+
var variations = {
|
|
26
|
+
digits: /\d/.test(pw),
|
|
27
|
+
lower: /[a-z]/.test(pw),
|
|
28
|
+
upper: /[A-Z]/.test(pw),
|
|
29
|
+
nonWords: /\W/.test(pw),
|
|
30
|
+
}
|
|
31
|
+
variationCount = 0;
|
|
32
|
+
for (var check in variations) {
|
|
33
|
+
variationCount += (variations[check] == true) ? 1 : 0;
|
|
34
|
+
}
|
|
35
|
+
score += (variationCount - 1) * 10;
|
|
36
|
+
return parseInt(score);
|
|
37
|
+
},
|
|
38
|
+
passwordStrengthMeter: function(score, text, meter){
|
|
39
|
+
$(meter).css('width',Math.min(score, 100)+"%");
|
|
40
|
+
if (score >= 80){
|
|
41
|
+
$(text).html(score + " Strong");
|
|
42
|
+
$(meter).css('background-color','green');
|
|
43
|
+
}else if (score >= 60){
|
|
44
|
+
$(text).html(score + " Good");
|
|
45
|
+
$(meter).css('background-color','orange');
|
|
46
|
+
}else if (score >= 30){
|
|
47
|
+
$(text).html(score + " Weak");
|
|
48
|
+
$(meter).css('background-color','red');
|
|
49
|
+
}else{
|
|
50
|
+
$(text).html(score + " Continue");
|
|
51
|
+
$(meter).css('background-color','#aaa');
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
passwordMatchIcon: function(pw1, pw2){
|
|
55
|
+
if($(pw1).val() == $(pw2).val()){
|
|
56
|
+
$(pw2+"-icon")
|
|
57
|
+
.removeClass('glyphicon-unchecked')
|
|
58
|
+
.addClass('glyphicon-check')
|
|
59
|
+
.css('color','green');
|
|
60
|
+
}else{
|
|
61
|
+
$(pw2+"-icon")
|
|
62
|
+
.removeClass('glyphicon-check')
|
|
63
|
+
.addClass('glyphicon-unchecked')
|
|
64
|
+
.css('color','red');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|