ditty 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.pryrc +6 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +15 -0
  6. data/.travis.yml +15 -0
  7. data/Gemfile.ci +19 -0
  8. data/License.txt +7 -0
  9. data/Rakefile +10 -0
  10. data/Readme.md +67 -0
  11. data/config.ru +33 -0
  12. data/ditty.gemspec +46 -0
  13. data/lib/ditty/components/app.rb +78 -0
  14. data/lib/ditty/controllers/application.rb +79 -0
  15. data/lib/ditty/controllers/audit_logs.rb +44 -0
  16. data/lib/ditty/controllers/component.rb +161 -0
  17. data/lib/ditty/controllers/main.rb +86 -0
  18. data/lib/ditty/controllers/roles.rb +16 -0
  19. data/lib/ditty/controllers/users.rb +183 -0
  20. data/lib/ditty/db.rb +12 -0
  21. data/lib/ditty/helpers/authentication.rb +58 -0
  22. data/lib/ditty/helpers/component.rb +63 -0
  23. data/lib/ditty/helpers/pundit.rb +34 -0
  24. data/lib/ditty/helpers/views.rb +50 -0
  25. data/lib/ditty/helpers/wisper.rb +14 -0
  26. data/lib/ditty/listener.rb +23 -0
  27. data/lib/ditty/models/audit_log.rb +14 -0
  28. data/lib/ditty/models/base.rb +7 -0
  29. data/lib/ditty/models/identity.rb +70 -0
  30. data/lib/ditty/models/role.rb +16 -0
  31. data/lib/ditty/models/user.rb +63 -0
  32. data/lib/ditty/policies/application_policy.rb +21 -0
  33. data/lib/ditty/policies/audit_log_policy.rb +41 -0
  34. data/lib/ditty/policies/identity_policy.rb +25 -0
  35. data/lib/ditty/policies/role_policy.rb +41 -0
  36. data/lib/ditty/policies/user_policy.rb +47 -0
  37. data/lib/ditty/rake_tasks.rb +85 -0
  38. data/lib/ditty/seed.rb +1 -0
  39. data/lib/ditty/services/logger.rb +48 -0
  40. data/lib/ditty/version.rb +5 -0
  41. data/lib/ditty.rb +142 -0
  42. data/migrate/20170207_base_tables.rb +40 -0
  43. data/migrate/20170208_audit_log.rb +12 -0
  44. data/migrate/20170416_audit_log_details.rb +9 -0
  45. data/public/browserconfig.xml +9 -0
  46. data/public/images/apple-icon.png +0 -0
  47. data/public/images/favicon-16x16.png +0 -0
  48. data/public/images/favicon-32x32.png +0 -0
  49. data/public/images/launcher-icon-1x.png +0 -0
  50. data/public/images/launcher-icon-2x.png +0 -0
  51. data/public/images/launcher-icon-4x.png +0 -0
  52. data/public/images/mstile-150x150.png +0 -0
  53. data/public/images/safari-pinned-tab.svg +43 -0
  54. data/public/manifest.json +25 -0
  55. data/views/404.haml +7 -0
  56. data/views/audit_logs/index.haml +30 -0
  57. data/views/error.haml +4 -0
  58. data/views/identity/login.haml +19 -0
  59. data/views/identity/register.haml +14 -0
  60. data/views/index.haml +1 -0
  61. data/views/layout.haml +55 -0
  62. data/views/partials/delete_form.haml +4 -0
  63. data/views/partials/footer.haml +5 -0
  64. data/views/partials/form_control.haml +20 -0
  65. data/views/partials/navbar.haml +24 -0
  66. data/views/partials/notifications.haml +24 -0
  67. data/views/partials/pager.haml +14 -0
  68. data/views/partials/sidebar.haml +35 -0
  69. data/views/roles/display.haml +18 -0
  70. data/views/roles/edit.haml +11 -0
  71. data/views/roles/form.haml +1 -0
  72. data/views/roles/index.haml +22 -0
  73. data/views/roles/new.haml +10 -0
  74. data/views/users/display.haml +50 -0
  75. data/views/users/edit.haml +11 -0
  76. data/views/users/identity.haml +3 -0
  77. data/views/users/index.haml +23 -0
  78. data/views/users/new.haml +11 -0
  79. data/views/users/profile.haml +39 -0
  80. data/views/users/user.haml +3 -0
  81. metadata +431 -0
data/views/404.haml ADDED
@@ -0,0 +1,7 @@
1
+ %h2 Whoops!
2
+
3
+ %p.lead
4
+ We could not find the page you were looking for...
5
+
6
+ .text-center
7
+ %iframe{ width: 560, height: 315, src: 'https://www.youtube.com/embed/e3-5YC_oHjE', frameborder: 0, allowfullscreen: true }
@@ -0,0 +1,30 @@
1
+ .row
2
+ .col-md-12
3
+ .panel.panel-default
4
+ %table.table.table-striped
5
+ %thead
6
+ %tr
7
+ %th User email
8
+ %th Action
9
+ %th Details
10
+ %th Created at
11
+ %tbody
12
+ - if list.count > 0
13
+ - list.all.each do |entity|
14
+ %tr
15
+ %td
16
+ -if entity.user
17
+ %a{ href: "/users/#{entity.user.id}" }= entity.user.email
18
+ -else
19
+ None
20
+ %td
21
+ = entity.action
22
+ %td
23
+ = entity.details
24
+ %td
25
+ = entity.created_at.strftime('%Y-%m-%d %H:%M:%S')
26
+ - else
27
+ %tr
28
+ %td.text-center{ colspan: 4 } No records
29
+
30
+ =pagination(list, base_path)
data/views/error.haml ADDED
@@ -0,0 +1,4 @@
1
+ %h2 Whoops!
2
+
3
+ %p.lead
4
+ = error.class
@@ -0,0 +1,19 @@
1
+ .row
2
+ .col-sm-3
3
+ .col-sm-6
4
+ .panel.panel-default
5
+ .panel-heading
6
+ %h4 Login
7
+ .panel-body
8
+ %form{ method: 'post', action: "#{settings.map_path}/auth/identity/callback" }
9
+ .form-group
10
+ %label.control-label Username
11
+ %input.form-control.border-input{ name: 'username' }
12
+ .form-group
13
+ %label.control-label Password
14
+ %input.form-control.border-input{ name: 'password', type: 'password' }
15
+ %button.btn.btn-primary{ type: 'submit' } Log In
16
+ .pull-right
17
+ No account yet?
18
+ %a.btn.btn-default{ href: "#{settings.map_path}/auth/identity/register" } Register
19
+ .col-sm-3
@@ -0,0 +1,14 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-heading
6
+ %h4 Ditty Registration
7
+ .panel-body
8
+ %form.form-horizontal{ method: 'post', action: "#{settings.map_path}/auth/identity/new" }
9
+ = form_control(:username, identity, label: 'Email', placeholder: 'Your email address')
10
+ = form_control(:password, identity, label: 'Password', type: :password)
11
+ = form_control(:password_confirmation, identity, label: 'Confirm Password', type: :password)
12
+
13
+ %button.btn.btn-primary{ type: 'submit' } Register
14
+ .col-md-2
data/views/index.haml ADDED
@@ -0,0 +1 @@
1
+ %h2 Hello! Ready to get this Ditty started?
data/views/layout.haml ADDED
@@ -0,0 +1,55 @@
1
+ !!! 5
2
+ %html{ lang: 'en' }
3
+ %head
4
+ %meta{ charset: 'utf-8' }
5
+ %meta{ 'http-equiv' => 'X-UA-Compatible', 'content' => 'IE=edge,chrome=1' }
6
+ %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
7
+ %meta{ name: 'theme-color', content: '#ffffff' }
8
+ %link{ rel: 'manifest', href: '/manifest.json' }
9
+ %link{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '/images/favicon-32x32.png' }
10
+ %link{ rel: 'icon', type: 'image/png', sizes: '16x16', href: '/images/favicon-16x16.png' }
11
+ %link{ rel: 'apple-touch-icon', sizes: '76x76', href: '/images/apple-icon.png' }
12
+ %link{ rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#5bbad5' }
13
+
14
+ %title
15
+ Ditty
16
+ - if defined? title
17
+ = "- #{title}"
18
+
19
+ %meta{ name: 'description', content: '' }
20
+ %meta{ name: 'author', content: '' }
21
+
22
+ / Le styles
23
+ %link{ rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css', media: 'screen' }
24
+ %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/3.3.7+1/css/sb-admin-2.min.css', media: 'screen' }
25
+ %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.css', media: 'screen' }
26
+ %link{ rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css', media: 'screen' }
27
+ /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
28
+ /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
29
+
30
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js' }
31
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/3.3.7+1/js/sb-admin-2.min.js' }
32
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.js' }
33
+ %body
34
+ #wrapper
35
+ = haml :'partials/navbar', locals: { title: (defined?(title) ? title : 'Ditty') }
36
+ #page-wrapper
37
+ .row
38
+ .col-md-12
39
+ -if defined? title
40
+ %h1.page-header= title
41
+ = haml :'partials/notifications'
42
+
43
+ = yield
44
+ = haml :'partials/footer'
45
+
46
+
47
+ / Placed at the end of the document so the pages load faster
48
+ %script{ type: 'text/javascript', src: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js' }
49
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.min.js' }
50
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js' }
51
+ :javascript
52
+ $(function() {
53
+ $('.sidebar-nav').metisMenu();
54
+ });
55
+
@@ -0,0 +1,4 @@
1
+ <form method="post" action="<%= base_path %>/<%= entity.id %>">
2
+ <input type="hidden" name="_method" value="DELETE">
3
+ <button type="submit" class="btn btn-danger"><%= delete_label %></button>
4
+ </form>
@@ -0,0 +1,5 @@
1
+ %footer.footer.text-muted.text-center
2
+ %hr
3
+ .copyright
4
+ :plain
5
+ &copy; <script>document.write(new Date().getFullYear())</script>, Automation Exchange
@@ -0,0 +1,20 @@
1
+ %div{ class: "form-group#{model.errors[field] ? ' has-error' : ''}" }
2
+ %label.col-sm-3.control-label{ for: attributes[:id] }= label
3
+ .col-sm-9
4
+ - type = attributes.delete(:type)
5
+ - if type == 'select'
6
+ - options = attributes.delete(:options)
7
+ %select{attributes}
8
+ - if attributes[:multiple]
9
+ - options.each do |k,v| k ||= v; v ||= k;
10
+ %option{ value: k, selected: (model.send(field).map(&:id).include? k)}= v
11
+ - else
12
+ %option{ value: ""} -- Select One --
13
+ - options.each do |k,v| k ||= v; v ||= k;
14
+ %option{ value: k, selected: [model[field].to_s, default].include?(k.to_s)}= v
15
+ - elsif type == 'textarea'
16
+ %textarea{attributes}= preserve(model[field])
17
+ - else
18
+ %input{attributes, type: type, value: model[field] || default}
19
+ - if model.errors[field]
20
+ %p.help-block.text-danger= model.errors[field].join(', ')
@@ -0,0 +1,24 @@
1
+ .navbar.navbar-default.navbar-static-top{ role: 'navigation', style: 'margin-bottom: 0' }
2
+ .navbar-header
3
+ %button.navbar-toggle.collapsed{ 'type' => 'button', 'data-toggle' => 'collapse', 'data-target' => '.navbar-collapse' }
4
+ %span.sr-only Toggle navigation
5
+ %span.icon-bar
6
+ %span.icon-bar
7
+ %span.icon-bar
8
+ %span.navbar-brand
9
+ Ditty
10
+
11
+ -if authenticated?
12
+ %form.nav.navbar-top-links.navbar-form.navbar-right{ action: "#{settings.map_path}/auth/identity", method: 'post' }
13
+ %a.btn.btn-default{ href: "#{settings.map_path}/users/profile" } My Account
14
+ %input{ name: '_method', value: 'DELETE', type: 'hidden' }
15
+ %button.btn.btn-default{ type: 'submit' }
16
+ / %i.ti-panel
17
+ Logout
18
+ - else
19
+ %ul.nav.navbar-top-links.navbar-right
20
+ %li
21
+ %a.btn.btn-link{ href: "#{settings.map_path}/auth/identity" }
22
+ Log In
23
+ .navbar-default.sidebar{ role: 'navigation' }
24
+ = haml :'partials/sidebar'
@@ -0,0 +1,24 @@
1
+ - if flash[:danger]
2
+ .alert.alert-danger.alert-dismissible{ role: "alert" }
3
+ %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
4
+ - (flash[:danger].is_a?(Array) ? flash[:danger] : [flash[:danger]]).each do |msg|
5
+ %span= msg
6
+
7
+ - if flash[:warning]
8
+ .alert.alert-warning.alert-dismissible{ role: "alert" }
9
+ %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
10
+ - (flash[:warning].is_a?(Array) ? flash[:warning] : [flash[:warning]]).each do |msg|
11
+ %span= msg
12
+
13
+ - if flash[:success]
14
+ .alert.alert-success.alert-dismissible{ role: "alert" }
15
+ %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
16
+ - (flash[:success].is_a?(Array) ? flash[:success] : [flash[:success]]).each do |msg|
17
+ %span= msg
18
+
19
+
20
+ - if flash[:info]
21
+ .alert.alert-info.alert-dismissible{ role: "alert" }
22
+ %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
23
+ - (flash[:info].is_a?(Array) ? flash[:info] : [flash[:info]]).each do |msg|
24
+ %span= msg
@@ -0,0 +1,14 @@
1
+ %nav{"aria-label" => "Page navigation"}
2
+ - if list.pagination_record_count > 0
3
+ %p.text-center Showing #{list.current_page_record_range} of #{list.pagination_record_count} records
4
+ %ul.pager
5
+ %li
6
+ %a{href: "#{base_path}?page=1&count=#{list.page_size}"} First
7
+ %li{class: ("disabled" if list.first_page?)}
8
+ %a{href: prev_link} Previous
9
+ %li{class: ("disabled" if list.last_page?)}
10
+ %a{href: next_link} Next
11
+ %li
12
+ %a{href: "#{base_path}?page=#{list.page_count}&count=#{list.page_size}"} Last
13
+ - else
14
+ %p.text-center No records to show
@@ -0,0 +1,35 @@
1
+ .sidebar-nav.navbar-collapse
2
+ %ul.nav.nav-pills.nav-stacked
3
+ - if authenticated?
4
+ %li
5
+ %a{ href: "#{settings.map_path}/" }
6
+ %i.fa.fa-home.fa-fw
7
+ Home
8
+ - Ditty::Components.navigation.each do |item|
9
+ - if item[:target].nil? || policy(item[:target]).list?
10
+ - if item[:group]
11
+ %li
12
+ %a{ href: '#' }
13
+ %i.fa.fa-fw{ class: "fa-#{item[:icon]}" }
14
+ =item[:group]
15
+ %span.fa.arrow
16
+ %ul.nav.nav-second-level
17
+ - item[:items].each do |sub_item|
18
+ %li
19
+ %a{ href: "#{settings.map_path}#{sub_item[:link]}" }
20
+ %i.fa.fa-fw{ class: "fa-#{sub_item[:icon]}" }
21
+ = sub_item[:text]
22
+ - else
23
+ %li
24
+ %a{ href: "#{settings.map_path}#{item[:link]}" }
25
+ %i.fa.fa-fw{ class: "fa-#{item[:icon]}" }
26
+ = item[:text]
27
+ - else
28
+ %li.active
29
+ %a{ href: "#{settings.map_path}/auth/identity" }
30
+ %i.fa.fa-user.fa-fw
31
+ Log In
32
+ %li
33
+ %a{ href: "#{settings.map_path}/auth/identity/register" }
34
+ %i.fa.fa-pencil-square-o.fa-fw
35
+ Register
@@ -0,0 +1,18 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %p.description
7
+ %label Name:
8
+ = entity.name
9
+
10
+ .row
11
+ .col-md-6
12
+ %a.btn.btn-default{ href: "#{base_path}/#{entity.id}/edit" } Edit
13
+ .col-md-6.text-right
14
+ - if policy(entity).delete?
15
+ %form{ method: 'post', action: "#{base_path}/#{entity.id}" }
16
+ %input{ name: '_method', value: 'DELETE', type: 'hidden' }
17
+ %button.btn.btn-warning{ type: 'submit' } Delete
18
+ .col-md-2
@@ -0,0 +1,11 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %form.form-horizontal{ method: 'post', action: "#{base_path}/#{entity.id}" }
7
+ %input{ name: '_method', value: 'PUT', type: 'hidden' }
8
+ = haml :'roles/form', locals: { entity: entity }
9
+ %button.btn.btn-primary{ type: 'submit' }
10
+ Update Role
11
+ .col-md-2
@@ -0,0 +1 @@
1
+ = form_control(:name, entity)
@@ -0,0 +1,22 @@
1
+ .row
2
+ .col-md-12
3
+ .panel.panel-default
4
+ %table.table.table-striped
5
+ %thead
6
+ %tr
7
+ %th Name
8
+ %tbody
9
+ - if list.count > 0
10
+ - list.all.each do |entity|
11
+ %tr
12
+ %td
13
+ %a{ href: "#{base_path}/#{entity.id}" }= entity.name
14
+ - else
15
+ %tr
16
+ %td.text-center{ colspan: 1 } No records
17
+
18
+ - if policy(::Ditty::Role).create?
19
+ .panel-body.text-right
20
+ %a.btn.btn-primary{ href: "#{base_path}/new" } New Role
21
+
22
+ =pagination(list, base_path)
@@ -0,0 +1,10 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %form.form-horizontal{ method: 'post', action: base_path }
7
+ = haml :'roles/form', locals: { entity: entity }
8
+ %button.btn.btn-primary{ type: 'submit' }
9
+ Create Role
10
+ .col-md-2
@@ -0,0 +1,50 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ .author
7
+ %img.pull-right.thumbnail{ src: entity.gravatar }
8
+ %h4= entity.email
9
+
10
+ %hr
11
+ %p.description
12
+ %label Name:
13
+ = entity.name
14
+ %p.description
15
+ %label Surname:
16
+ = entity.surname
17
+ %p.description
18
+ %label Roles:
19
+ = entity.roles_dataset.map(:name).map(&:titlecase).join(', ')
20
+ %p.description
21
+ %label Signed up:
22
+ = entity.created_at.strftime('%Y-%m-%d %H:%M:%S')
23
+
24
+ .row
25
+ .col-md-6
26
+ %a.btn.btn-default{ href: "#{base_path}/#{entity.id}/edit" } Edit
27
+ .col-md-6.text-right
28
+ - if policy(entity).delete?
29
+ %form{ method: 'post', action: "#{base_path}/#{entity.id}" }
30
+ %input{ name: '_method', value: 'DELETE', type: 'hidden' }
31
+ %button.btn.btn-warning{ type: 'submit' } Delete
32
+ .col-md-2
33
+
34
+ - if entity.identity.first
35
+ .row
36
+ .col-md-2
37
+ .col-md-8
38
+ .panel.panel-default
39
+ .panel-heading
40
+ %h4 Change Password
41
+ .panel-body
42
+ %form.form-horizontal{ method: 'post', action: "#{base_path}/#{entity.id}/identity" }
43
+ %input{ name: '_method', value: 'PUT', type: 'hidden' }
44
+ = form_control(:password, entity.identity.first, type: 'password', placeholder: 'Your password', group: 'identity')
45
+ = form_control(:password_confirmation, entity.identity.first, type: 'password', label: 'Confirm Password', placeholder: 'Confirm your password', group: 'identity')
46
+ %button.btn.btn-primary{ type: 'submit' }
47
+ Change Password
48
+ .col-md-2
49
+
50
+
@@ -0,0 +1,11 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %form.form-horizontal{ method: 'post', action: "#{base_path}/#{entity.id}" }
7
+ %input{ name: '_method', value: 'PUT', type: 'hidden' }
8
+ = haml :'users/user', locals: { user: entity }
9
+ %button.btn.btn-primary{ type: 'submit' }
10
+ Update User
11
+ .col-md-2
@@ -0,0 +1,3 @@
1
+ = form_control(:username, identity, label: 'Email', placeholder: 'Your email address')
2
+ = form_control(:password, identity, type: 'password', placeholder: 'Your password')
3
+ = form_control(:password_confirmation, identity, type: 'password', label: 'Confirm Password', placeholder: 'Confirm your password')
@@ -0,0 +1,23 @@
1
+ .row
2
+ .col-md-12
3
+ .panel.panel-default
4
+ %table.table.table-striped
5
+ %thead
6
+ %tr
7
+ %th Email
8
+ %th Name
9
+ %th Surname
10
+ %th Roles
11
+ %tbody
12
+ - list.all.each do |entity|
13
+ %tr
14
+ %td
15
+ %a{ href: "#{base_path}/#{entity.id}" }= entity.email
16
+ %td= entity.name
17
+ %td= entity.surname
18
+ %td= entity.roles_dataset.map(:name).map(&:titlecase).join(', ')
19
+ - if policy(::Ditty::User).create?
20
+ .panel-body.text-right
21
+ %a.btn.btn-primary{ href: "#{base_path}/new" } New User
22
+
23
+ =pagination(list, base_path)
@@ -0,0 +1,11 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %form.form-horizontal{ method: 'post', action: base_path }
7
+ = haml :'users/identity', locals: { identity: identity }
8
+ = haml :'users/user', locals: { user: entity }
9
+ %button.btn.btn-primary{ type: 'submit' }
10
+ Create User
11
+ .col-md-2
@@ -0,0 +1,39 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ .author
7
+ %img.pull-right.thumbnail{ src: entity.gravatar }
8
+ %h4= entity.email
9
+
10
+ %hr
11
+ %p.description
12
+ %label Name:
13
+ = entity.name
14
+ %p.description
15
+ %label Surname:
16
+ = entity.surname
17
+ %p.description
18
+ %label Roles:
19
+ = entity.roles_dataset.map(:name).map(&:titlecase).join(', ')
20
+ %p.description
21
+ %label Signed up:
22
+ = entity.created_at.strftime('%Y-%m-%d %H:%M:%S')
23
+ .col-md-2
24
+
25
+ .row
26
+ .col-md-2
27
+ .col-md-8
28
+ .panel.panel-default
29
+ .panel-heading
30
+ %h4 Change Password
31
+ .panel-body
32
+ %form.form-horizontal{ method: 'post', action: "#{base_path}/#{entity.id}/identity" }
33
+ %input{ name: '_method', value: 'PUT', type: 'hidden' }
34
+ = form_control(:old_password, identity, type: 'password', placeholder: 'Your current password')
35
+ = form_control(:password, identity, type: 'password', placeholder: 'Your new password')
36
+ = form_control(:password_confirmation, identity, type: 'password', label: 'Confirm Password', placeholder: 'Confirm your password')
37
+ %button.btn.btn-primary{ type: 'submit' }
38
+ Change Password
39
+ .col-md-2
@@ -0,0 +1,3 @@
1
+ = form_control(:name, user)
2
+ = form_control(:surname, user)
3
+ = form_control(:role_id, user, type: 'select', options: Ditty::Role.as_hash(:id, :name), name: 'user[role_id][]', field: :roles, multiple: true)