ditty 0.9.1 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.env.test +2 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +23 -4
  5. data/.travis.yml +2 -7
  6. data/Gemfile.ci +0 -15
  7. data/ditty.gemspec +19 -15
  8. data/lib/ditty.rb +2 -2
  9. data/lib/ditty/cli.rb +5 -0
  10. data/lib/ditty/components/ditty.rb +4 -7
  11. data/lib/ditty/controllers/application_controller.rb +6 -5
  12. data/lib/ditty/controllers/audit_logs_controller.rb +2 -0
  13. data/lib/ditty/controllers/auth_controller.rb +5 -2
  14. data/lib/ditty/controllers/component_controller.rb +3 -3
  15. data/lib/ditty/controllers/user_login_traits_controller.rb +28 -1
  16. data/lib/ditty/controllers/users_controller.rb +3 -2
  17. data/lib/ditty/db.rb +4 -3
  18. data/lib/ditty/emails/base.rb +32 -30
  19. data/lib/ditty/generators/crud_generator.rb +51 -41
  20. data/lib/ditty/generators/project_generator.rb +1 -0
  21. data/lib/ditty/helpers/pundit.rb +4 -4
  22. data/lib/ditty/helpers/response.rb +6 -11
  23. data/lib/ditty/helpers/views.rb +21 -3
  24. data/lib/ditty/listener.rb +1 -1
  25. data/lib/ditty/models/base.rb +5 -0
  26. data/lib/ditty/models/identity.rb +7 -7
  27. data/lib/ditty/models/user.rb +9 -1
  28. data/lib/ditty/policies/user_policy.rb +1 -1
  29. data/lib/ditty/services/authentication.rb +19 -9
  30. data/lib/ditty/services/email.rb +13 -13
  31. data/lib/ditty/services/logger.rb +26 -20
  32. data/lib/ditty/services/pagination_wrapper.rb +7 -5
  33. data/lib/ditty/services/settings.rb +7 -6
  34. data/lib/ditty/tasks/ditty.rake +2 -1
  35. data/lib/ditty/templates/application.rb +1 -1
  36. data/lib/ditty/templates/config.ru +2 -2
  37. data/lib/ditty/templates/controller.rb.erb +7 -1
  38. data/{public → lib/ditty/templates/public}/browserconfig.xml +0 -0
  39. data/{public → lib/ditty/templates/public}/css/styles.css +0 -0
  40. data/lib/ditty/templates/public/favicon.ico +0 -0
  41. data/{public → lib/ditty/templates/public}/images/apple-icon.png +0 -0
  42. data/{public → lib/ditty/templates/public}/images/favicon-16x16.png +0 -0
  43. data/{public → lib/ditty/templates/public}/images/favicon-32x32.png +0 -0
  44. data/{public → lib/ditty/templates/public}/images/launcher-icon-1x.png +0 -0
  45. data/{public → lib/ditty/templates/public}/images/launcher-icon-2x.png +0 -0
  46. data/{public → lib/ditty/templates/public}/images/launcher-icon-4x.png +0 -0
  47. data/{public → lib/ditty/templates/public}/images/mstile-150x150.png +0 -0
  48. data/{public → lib/ditty/templates/public}/images/safari-pinned-tab.svg +0 -0
  49. data/{public → lib/ditty/templates/public}/js/scripts.js +0 -0
  50. data/{public/manifest.json → lib/ditty/templates/public/manifest.json.erb} +2 -2
  51. data/lib/ditty/templates/settings.yml.erb +1 -0
  52. data/lib/ditty/templates/spec_helper.rb +1 -1
  53. data/lib/ditty/templates/views/display.haml.tt +1 -1
  54. data/lib/ditty/templates/views/edit.haml.tt +1 -1
  55. data/lib/ditty/templates/views/index.haml.tt +1 -1
  56. data/lib/ditty/templates/views/new.haml.tt +1 -1
  57. data/lib/ditty/version.rb +1 -1
  58. data/spec/ditty/api_spec.rb +1 -1
  59. data/spec/ditty/emails/base_spec.rb +3 -3
  60. data/spec/ditty/emails/forgot_password_spec.rb +3 -2
  61. data/spec/ditty/models/user_spec.rb +3 -3
  62. data/spec/ditty/services/logger_spec.rb +7 -6
  63. data/spec/ditty/services/settings_spec.rb +2 -2
  64. data/spec/factories.rb +4 -4
  65. data/spec/spec_helper.rb +5 -1
  66. data/views/403.haml +1 -1
  67. data/views/500.haml +11 -0
  68. data/views/audit_logs/index.haml +12 -11
  69. data/views/auth/forgot_password.haml +29 -24
  70. data/views/auth/ldap.haml +1 -1
  71. data/views/auth/login.haml +3 -2
  72. data/views/auth/register.haml +3 -2
  73. data/views/auth/reset_password.haml +36 -19
  74. data/views/blank.haml +1 -0
  75. data/views/embedded.haml +17 -11
  76. data/views/layout.haml +16 -8
  77. data/views/partials/actions.haml +15 -14
  78. data/views/partials/filter_control.haml +1 -1
  79. data/views/partials/footer.haml +10 -2
  80. data/views/partials/form_tag.haml +1 -1
  81. data/views/partials/navitems.haml +25 -27
  82. data/views/partials/pager.haml +44 -25
  83. data/views/partials/search.haml +14 -9
  84. data/views/partials/sidebar.haml +2 -2
  85. data/views/partials/sort_ui.haml +2 -0
  86. data/views/partials/timespan_selector.haml +64 -0
  87. data/views/partials/topbar.haml +0 -15
  88. data/views/partials/user_associations.haml +32 -0
  89. data/views/quick_start.haml +23 -0
  90. data/views/roles/display.haml +3 -3
  91. data/views/roles/edit.haml +1 -1
  92. data/views/roles/index.haml +2 -2
  93. data/views/roles/new.haml +1 -1
  94. data/views/user_login_traits/display.haml +1 -1
  95. data/views/user_login_traits/edit.haml +1 -1
  96. data/views/user_login_traits/index.haml +23 -25
  97. data/views/user_login_traits/new.haml +1 -1
  98. data/views/users/display.haml +5 -5
  99. data/views/users/edit.haml +1 -1
  100. data/views/users/index.haml +5 -5
  101. data/views/users/login_traits.haml +2 -2
  102. data/views/users/new.haml +1 -1
  103. data/views/users/profile.haml +4 -4
  104. data/views/users/user.haml +1 -1
  105. metadata +116 -54
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'dotenv'
4
+ Dotenv.load('.env.test')
5
+ Dotenv.overload('.env.test.local')
6
+
3
7
  ENV['APP_ENV'] ||= 'test'
4
8
  ENV['RACK_ENV'] ||= 'test'
5
9
  require 'simplecov'
@@ -16,7 +20,7 @@ require 'database_cleaner'
16
20
  require 'timecop'
17
21
 
18
22
  if ENV['DATABASE_URL'] == 'sqlite::memory:'
19
- folder = File.expand_path(File.dirname(__FILE__) + '/../migrate')
23
+ folder = File.expand_path("#{File.dirname(__FILE__)}/../migrate")
20
24
  Sequel.extension :migration
21
25
  Sequel::Migrator.apply(DB, folder)
22
26
 
data/views/403.haml CHANGED
@@ -1,2 +1,2 @@
1
- %p.lead
1
+ %p.lead.text-center
2
2
  Your user is not allowed access to the requested URL.
data/views/500.haml ADDED
@@ -0,0 +1,11 @@
1
+ %p.lead.text-center
2
+ A call to Elasticsearch failed unexpectedly.
3
+
4
+ - if current_user.admin?
5
+ %p
6
+ The error was:
7
+ %bt
8
+ = error.message
9
+ - if current_user.super_admin?
10
+ %pre
11
+ %code= error.backtrace.join("\n")
@@ -1,17 +1,17 @@
1
- = haml :'partials/search'
2
1
  .row
3
- .col-12
2
+ .col-md-12
3
+ = haml :'partials/search'
4
4
  %table.table.table-striped.table-bordered.table-hover
5
5
  %thead.thead-dark
6
6
  %tr
7
- %th User email
8
- %th Action
9
- %th Details
10
- %th IP Address
11
- %th Browser
12
- %th Device
13
- %th Platform
14
- %th Created at
7
+ %th= "User #{sort_ui(:user)}"
8
+ %th= "Action #{sort_ui(:action)}"
9
+ %th= "Details #{sort_ui(:details)}"
10
+ %th= "IP Address #{sort_ui(:ip_address)}"
11
+ %th= "Browser #{sort_ui(:browser)}"
12
+ %th= "Device #{sort_ui(:device)}"
13
+ %th= "Platform #{sort_ui(:platform)}"
14
+ %th= "Created At #{sort_ui(:created_at)}"
15
15
  %tbody
16
16
  - if list.count > 0
17
17
  - list.all.each do |entity|
@@ -32,4 +32,5 @@
32
32
  %tr
33
33
  %td.text-center{ colspan: 4 } No records
34
34
 
35
- =pagination(list, base_path)
35
+ - if list.count > 0
36
+ = pagination(list, base_path)
@@ -1,27 +1,32 @@
1
1
  .container
2
- .card.o-hidden.border-0.shadow-lg.my-5
3
- .card-body.p-0
4
- / Nested Row within Card Body
5
- .row
6
- .col-lg-6.d-none.d-lg-block.bg-password-image
7
- .col-lg-6
8
- .p-5
9
- .text-center
10
- %h1.h4.text-gray-900.mb-2
11
- Forgot Your Password?
12
- %p.mb-4
13
- We get it, stuff happens. Just enter your email address below and we'll send you a link to reset your password!
14
- = form_tag("#{settings.map_path}/auth/forgot-password", attributes: { class: 'user' }) do
15
- .form-group
16
- %input.form-control.form-control-user{ type: 'email', placeholder: 'Enter Email Address...' }
17
- %button.btn.btn-primary.btn-user.btn-block{ type: 'submit' }
18
- Reset Password
19
- %hr
20
- - if policy(::Ditty::User).register?
21
- .text-center
22
- %a.small{ href: "#{settings.map_path}/auth/register" } Create an Account!
23
- .text-center
24
- %a.small{ href: "#{settings.map_path}/auth/login" } Already have an account? Login!
25
-
2
+ / Outer Row
26
3
  .row.justify-content-center
27
4
  .col-xl-10.col-lg-12.col-md-9
5
+ .card.card-default.o-hidden.border-0.shadow-lg.my-5
6
+ .card-body.p-0
7
+ / Nested Row within Card Body
8
+ .row
9
+ .col-lg-6.d-none.d-lg-block.bg-password-image
10
+ .col-lg-6
11
+ .p-5
12
+ .text-center
13
+ %h1.h4.text-gray-900.mb-2
14
+ Forgot Your Password?
15
+ %p.mb-4
16
+ We get it, stuff happens. Just enter your email address below and we'll send you a link to reset your password!
17
+ .small
18
+ = haml :'partials/notifications'
19
+ = form_tag("#{settings.map_path}/auth/forgot-password", attributes: { class: 'user' }) do
20
+ .form-group
21
+ %input.form-control.form-control-user{ name: 'email', type: 'email', placeholder: 'Enter Email Address...' }
22
+ %button.btn.btn-primary.btn-user.btn-block{ type: 'submit' }
23
+ Reset Password
24
+ %hr
25
+ - if policy(::Ditty::User).register?
26
+ .text-center
27
+ %a.small{ href: "#{settings.map_path}/auth/register" } Create an Account!
28
+ .text-center
29
+ %a.small{ href: "#{settings.map_path}/auth/login" } Already have an account? Login!
30
+
31
+ .row.justify-content-center
32
+ .col-xl-10.col-lg-12.col-md-9
data/views/auth/ldap.haml CHANGED
@@ -1,7 +1,7 @@
1
1
  .row
2
2
  .col-sm-2
3
3
  .col-sm-8
4
- .card.card-default
4
+ .card.card-default.shadow
5
5
  .card-body
6
6
  = form_tag("#{settings.map_path}/auth/ldap/callback", attributes: { class: '' }) do
7
7
  .form-group
@@ -2,7 +2,7 @@
2
2
  / Outer Row
3
3
  .row.justify-content-center
4
4
  .col-xl-10.col-lg-12.col-md-9
5
- .card.o-hidden.border-0.shadow-lg.my-5
5
+ .card.card-default.o-hidden.border-0.shadow-lg.my-5
6
6
  .card-body.p-0
7
7
  / Nested Row within Card Body
8
8
  .row
@@ -11,6 +11,8 @@
11
11
  .p-5
12
12
  .text-center
13
13
  %h1.h4.text-gray-900.mb-4 Welcome Back!
14
+ .small
15
+ = haml :'partials/notifications'
14
16
  - ::Ditty::Services::Authentication.providers.each do |name|
15
17
  - provider = ::Ditty::Services::Authentication[name]
16
18
  - next if provider[:login_prompt].nil?
@@ -18,6 +20,5 @@
18
20
  %a.btn.btn-block.btn-secondary{ href: "#{settings.map_path}/auth/#{name}" }
19
21
  %i.fab.fw{ class: "fa-#{provider[:icon] || 'key'}"}
20
22
  = provider[:login_prompt]
21
- = haml :'partials/notifications'
22
23
  - if ::Ditty::Services::Authentication.provides? 'identity'
23
24
  = haml :'auth/identity'
@@ -1,5 +1,5 @@
1
1
  .container
2
- .card.o-hidden.border-0.shadow-lg.my-5
2
+ .card.card-default.o-hidden.border-0.shadow-lg.my-5
3
3
  .card-body.p-0
4
4
  / Nested Row within Card Body
5
5
  .row
@@ -8,7 +8,8 @@
8
8
  .p-5
9
9
  .text-center
10
10
  %h1.h4.text-gray-900.mb-4 Create an Account!
11
- = haml :'partials/notifications'
11
+ .small
12
+ = haml :'partials/notifications'
12
13
  - ::Ditty::Services::Authentication.providers.each do |name|
13
14
  - provider = ::Ditty::Services::Authentication[name]
14
15
  - next if provider[:register_prompt].nil?
@@ -1,19 +1,36 @@
1
- .row
2
- .col-md-2
3
- .col-md-8
4
- .card.card-default
5
- .card-body
6
- = edit_form_tag("#{settings.map_path}/auth/reset-password") do
7
- %input{ name: 'token', value: params[:token], type: 'hidden' }
8
- - if identity.errors[:password] && identity.errors[:password].include?('is not strong enough')
9
- .alert.alert-warning
10
- %p Make sure your password is at least 8 characters long, and including the following
11
- %ul
12
- %li Upper- and lowercase letters
13
- %li Numbers
14
- %li Special Characters
15
- = form_control(:password, identity, type: 'password', placeholder: 'Your password', group: 'identity')
16
- = form_control(:password_confirmation, identity, type: 'password', label: 'Confirm Password', placeholder: 'Confirm your password', group: 'identity')
17
- %button.btn.btn-primary{ type: 'submit' }
18
- Reset Password
19
- .col-md-2
1
+ .container
2
+ / Outer Row
3
+ .row.justify-content-center
4
+ .col-xl-10.col-lg-12.col-md-9
5
+ .card.card-default.o-hidden.border-0.shadow-lg.my-5
6
+ .card-body.p-0
7
+ / Nested Row within Card Body
8
+ .row
9
+ .col-lg-6.d-none.d-lg-block.bg-password-image
10
+ .col-lg-6
11
+ .p-5
12
+ .text-center
13
+ %h1.h4.text-gray-900.mb-2
14
+ Reset your Password
15
+ = edit_form_tag("#{settings.map_path}/auth/reset-password") do
16
+ %input{ name: 'token', value: params[:token], type: 'hidden' }
17
+ - if identity.errors[:password] && identity.errors[:password].include?('is not strong enough')
18
+ .alert.alert-warning
19
+ %p Make sure your password is at least 8 characters long, and including the following
20
+ %ul
21
+ %li Upper- and lowercase letters
22
+ %li Numbers
23
+ %li Special Characters
24
+ = form_control(:password, identity, type: 'password', placeholder: 'Your password', group: 'identity')
25
+ = form_control(:password_confirmation, identity, type: 'password', label: 'Confirm Password', placeholder: 'Confirm your password', group: 'identity')
26
+ %button.btn.btn-primary{ type: 'submit' }
27
+ Reset Password
28
+ %hr
29
+ - if policy(::Ditty::User).register?
30
+ .text-center
31
+ %a.small{ href: "#{settings.map_path}/auth/register" } Create an Account!
32
+ .text-center
33
+ %a.small{ href: "#{settings.map_path}/auth/login" } Already have an account? Login!
34
+
35
+ .row.justify-content-center
36
+ .col-xl-10.col-lg-12.col-md-9
data/views/blank.haml CHANGED
@@ -5,6 +5,7 @@
5
5
  %meta{ 'http-equiv' => 'X-UA-Compatible', 'content' => 'IE=edge,chrome=1' }
6
6
  %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
7
7
  %meta{ name: 'theme-color', content: '#ffffff' }
8
+ = Rack::Csrf.csrf_metatag(env)
8
9
  %link{ rel: 'manifest', href: '/manifest.json' }
9
10
  %link{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '/images/favicon-32x32.png' }
10
11
  %link{ rel: 'icon', type: 'image/png', sizes: '16x16', href: '/images/favicon-16x16.png' }
data/views/embedded.haml CHANGED
@@ -5,6 +5,7 @@
5
5
  %meta{ 'http-equiv' => 'X-UA-Compatible', 'content' => 'IE=edge,chrome=1' }
6
6
  %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
7
7
  %meta{ name: 'theme-color', content: '#ffffff' }
8
+ = Rack::Csrf.csrf_metatag(env)
8
9
  %link{ rel: 'manifest', href: '/manifest.json' }
9
10
  %link{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '#{request.base_url}/images/favicon-32x32.png' }
10
11
  %link{ rel: 'icon', type: 'image/png', sizes: '16x16', href: '#{request.base_url}/images/favicon-16x16.png' }
@@ -20,14 +21,9 @@
20
21
  %meta{ name: 'author', content: '' }
21
22
 
22
23
  / 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://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css', media: 'screen' }
26
- /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
27
- /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
28
-
29
- %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js' }
30
- %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' }
24
+ %link{ rel: 'stylesheet', href: '/css/sb-admin-2.min.css', media: 'screen' }
25
+ %link{ rel: 'stylesheet', href: '/css/styles.css', media: 'screen' }
26
+ %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css', media: 'screen' }
31
27
  %body
32
28
  .container-fluid
33
29
  .row
@@ -37,6 +33,16 @@
37
33
  = yield
38
34
 
39
35
  / Placed at the end of the document so the pages load faster
40
- %script{ type: 'text/javascript', src: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js' }
41
- %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.min.js' }
42
- %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js' }
36
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js' }
37
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js' }
38
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js' }
39
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js' }
40
+ / %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js' }
41
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js' }
42
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.7/js/select2.min.js' }
43
+ %script{ type: 'text/javascript', src: '/js/sb-admin-2.min.js' }
44
+ %script{ type: 'text/javascript', src: '/js/scripts.js' }
45
+ :javascript
46
+ $(function() {
47
+ $('.select2').select2();
48
+ });
data/views/layout.haml CHANGED
@@ -5,6 +5,7 @@
5
5
  %meta{ 'http-equiv' => 'X-UA-Compatible', 'content' => 'IE=edge,chrome=1' }
6
6
  %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
7
7
  %meta{ name: 'theme-color', content: '#ffffff' }
8
+ = Rack::Csrf.csrf_metatag(env)
8
9
  %link{ rel: 'manifest', href: '/manifest.json' }
9
10
  %link{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '/images/favicon-32x32.png' }
10
11
  %link{ rel: 'icon', type: 'image/png', sizes: '16x16', href: '/images/favicon-16x16.png' }
@@ -24,6 +25,20 @@
24
25
  %link{ rel: 'stylesheet', href: '/css/styles.css', media: 'screen' }
25
26
  %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css', media: 'screen' }
26
27
  %body#page-top
28
+ / Logout Modal
29
+ #logoutModal.modal.fade{ tabindex: -1, role: 'dialog', 'aria-hidden': 'true' }
30
+ .modal-dialog{ role: 'document' }
31
+ .modal-content
32
+ .modal-header
33
+ %h5.modal-title Ready to Leave?
34
+ %button.close{ type: 'button', 'data-dismiss': 'modal', 'aria-label': 'Close' }
35
+ %span{ 'aria-hidden': 'true' } ×
36
+ .modal-body
37
+ Select "Logout" below if you are ready to end your current session.
38
+ .modal-footer
39
+ %button.btn.btn-secondary{ type: 'button', 'data-dismiss': 'modal' } Cancel
40
+ = delete_form_tag("#{settings.map_path}/auth") do
41
+ %button.btn.btn-primary{ type: 'submit' } Logout
27
42
  / Page Wrapper
28
43
  #wrapper
29
44
  = haml :'partials/sidebar', locals: { title: (defined?(title) ? title : 'Ditty') }
@@ -36,14 +51,6 @@
36
51
  / .row
37
52
  / .col-md-12
38
53
  = haml :'partials/notifications'
39
- - if defined?(title) || defined?(actions)
40
- .row
41
- .col-md-9
42
- %h1.text-dark.mb-2= defined?(title) ? title : '&nbsp;'
43
- .col-md-3.text-right.mt-1
44
- = haml :'partials/actions', locals: { actions: defined?(actions) ? actions : {} }
45
- - else
46
- %div{ style: 'padding-top: 20px' }
47
54
 
48
55
  = yield
49
56
 
@@ -56,6 +63,7 @@
56
63
  / Placed at the end of the document so the pages load faster
57
64
  %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js' }
58
65
  %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js' }
66
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js' }
59
67
  %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js' }
60
68
  / %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js' }
61
69
  %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js' }
@@ -1,14 +1,15 @@
1
- - if actions.count > 1
2
- - link, text = actions.shift
3
- .btn-group.text-right
4
- %a.btn.btn-primary{ href: link }= text
5
- %button.btn.btn-primary.dropdown-toggle{ type: 'button', id: 'actions-toggle', data: { toggle: 'dropdown' } }
6
- %span.caret
7
- %span.sr-only Toggle Dropdown
8
- %ul.dropdown-menu{ 'aria-labelledby': 'actions-toggle' }
9
- -actions.each do |k, v|
10
- %li
11
- %a{ href: k }= v
12
- - elsif actions.count > 0
13
- -actions.each do |k, v|
14
- %a.btn.btn-primary{ href: k }= v
1
+ - if defined?(actions)
2
+ - if actions.count > 1
3
+ - link, text = actions.shift
4
+ .btn-group.text-right
5
+ %a.btn.btn-primary{ href: link }= text
6
+ %button.btn.btn-primary.dropdown-toggle{ type: 'button', id: 'actions-toggle', data: { toggle: 'dropdown' } }
7
+ %span.caret
8
+ %span.sr-only Toggle Dropdown
9
+ %ul.dropdown-menu{ 'aria-labelledby': 'actions-toggle' }
10
+ -actions.each do |k, v|
11
+ %li
12
+ %a{ href: k }= v
13
+ - elsif actions.count > 0
14
+ -actions.each do |k, v|
15
+ %a.btn.btn-primary{ href: k }= v
@@ -1,6 +1,6 @@
1
1
  .form-group{ class: total_filters.to_i > 4 ? 'col-3' : 'col-md'}
2
2
  %label{ for: "filter_#{name}" }= label
3
- %select.form-control{ name: name, id: "filter_#{name}" }
3
+ %select.form-control.select2{ style: 'width: 100%', name: name, id: "filter_#{name}" }
4
4
  %option{ value: '' } -- Select One --
5
5
  - options.each do |k,v| k ||= v; v ||= k;
6
6
  %option{ value: k, selected: (params[name].eql? k)}= v
@@ -1,5 +1,13 @@
1
1
  / Footer
2
2
  %footer.sticky-footer.bg-light
3
3
  .container.my-auto
4
- .copyright.text-center.my-auto.text-dark
5
- %span Copyright &copy; Ditty.io 2019
4
+ -if ENV['SKIP_BRANDING'].blank?
5
+ .copyright.text-center.my-auto.text-dark
6
+ %p
7
+ Copyright &copy;
8
+ %a{ href: 'https://hackerpla.net/requestd', target: '_blank' }
9
+ HackerPlanet
10
+ 2020
11
+ %p.mb-0
12
+ %a{ href: 'https://hackerpla.net/requestd', target: '_blank' }
13
+ %img.rounded{ src: 'http://hackerpla.net/wp-content/uploads/2014/07/HP_1C_128x128.png', height: 60 }
@@ -1,5 +1,5 @@
1
1
  %form{ { method: %i[get post].include?(form_verb.to_sym) ? form_verb : :post, action: url }.merge(attributes) }
2
- = Rack::Csrf.csrf_tag(env) unless ENV['APP_ENV'] == 'test'
2
+ = Rack::Csrf.csrf_tag(env) unless form_verb.to_sym == :get || ENV['APP_ENV'] == 'test'
3
3
  - if form_verb.to_sym == :get && layout
4
4
  %input{ name: 'layout', value: layout, type: 'hidden' }
5
5
  - if %i[get post].include?(form_verb.to_sym) == false
@@ -1,31 +1,29 @@
1
1
  - if authenticated?
2
- - Ditty::Components.components.each do |key, comp|
3
- - next unless comp.respond_to?(:navigation) && comp.navigation.count.positive?
4
- - if comp.respond_to?(:title)
5
- .sidebar-heading= comp.title
6
- - comp.navigation.each do |item|
7
- - if item[:target].nil? || policy(item[:target]).list?
8
- - if item[:group]
9
- %li.nav-item
10
- %a.nav-link.collapsed{ href: '#', 'data-toggle': 'collapse', 'data-target': "##{item[:group].parameterize}", 'aria-expanded': 'true', 'aria-controls': item[:group].parameterize }
11
- - if item[:icon]
12
- %i.fas.fa-fw{ class: "fa-#{item[:icon]}" }
13
- %span= item[:group]
14
- .collapse{ id: item[:group].parameterize, 'data-parent': '#accordionSidebar' }
15
- .bg-white.py-2.collapse-inner.rounded
16
- - item[:items].each do |sub_item|
17
- - next unless sub_item[:target] && policy(sub_item[:target]).list?
18
- %a.collapse-item{ href: "#{settings.map_path}#{sub_item[:link]}" }
19
- - if sub_item[:icon]
20
- %i.fa.fa-fw{ class: "fa-#{sub_item[:icon]}" }
21
- = sub_item[:text]
22
- - else
23
- %li.nav-item
24
- %a.nav-link{ href: "#{settings.map_path}#{item[:link]}" }
25
- - if item[:icon]
26
- %i.fas.fa-fw{ class: "fa-#{item[:icon]}" }
27
- %span= item[:text]
28
- %hr.sidebar-divider
2
+ - Ditty::Components.navigation(request).each do |item|
3
+ - if item[:type] == 'divider'
4
+ %hr.sidebar-divider.my-0
5
+ - elsif item[:target].nil? || policy(item[:target]).list?
6
+ - if item[:group]
7
+ %li.nav-item
8
+ %a.nav-link.collapsed{ href: '#', 'data-toggle': 'collapse', 'data-target': "##{item[:group].parameterize}", 'aria-expanded': 'true', 'aria-controls': item[:group].parameterize }
9
+ - if item[:icon]
10
+ %i.fas.fa-fw{ class: "fa-#{item[:icon]}" }
11
+ %span= item[:group]
12
+ .collapse{ id: item[:group].parameterize, 'data-parent': '#accordionSidebar' }
13
+ .bg-white.py-2.collapse-inner.rounded
14
+ - item[:items].each do |sub_item|
15
+ - next unless sub_item[:target] && policy(sub_item[:target]).list?
16
+ %a.collapse-item{ href: "#{settings.map_path}#{sub_item[:link]}" }
17
+ - if sub_item[:icon]
18
+ %i.fa.fa-fw{ class: "fa-#{sub_item[:icon]}" }
19
+ = sub_item[:text]
20
+ - else
21
+ %li.nav-item
22
+ %a.nav-link{ href: "#{settings.map_path}#{item[:link]}" }
23
+ - if item[:icon]
24
+ %i.fas.fa-fw{ class: "fa-#{item[:icon]}" }
25
+ %span= item[:text]
26
+ %hr.sidebar-divider.my-0
29
27
  %li.nav-item
30
28
  = delete_form_tag("#{settings.map_path}/auth", attributes: { id: 'logout-form' }) do
31
29
  %a.nav-link{ type: 'submit', onClick: 'document.getElementById("logout-form").submit()' }