ditty 0.9.1 → 0.10.1

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.
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()' }