ditty 0.8.0 → 0.10.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.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/.env.test +2 -0
  3. data/.gitignore +3 -0
  4. data/.pryrc +2 -0
  5. data/.rubocop.yml +23 -4
  6. data/.travis.yml +4 -8
  7. data/CNAME +1 -0
  8. data/Dockerfile +18 -0
  9. data/Gemfile.ci +0 -17
  10. data/Rakefile +2 -2
  11. data/_config.yml +1 -0
  12. data/config.ru +4 -4
  13. data/ditty.gemspec +28 -18
  14. data/docs/CNAME +1 -0
  15. data/docs/_config.yml +1 -0
  16. data/docs/index.md +34 -0
  17. data/exe/ditty +2 -0
  18. data/lib/ditty/cli.rb +41 -5
  19. data/lib/ditty/components/{app.rb → ditty.rb} +18 -16
  20. data/lib/ditty/controllers/{application.rb → application_controller.rb} +63 -34
  21. data/lib/ditty/controllers/{audit_logs.rb → audit_logs_controller.rb} +4 -2
  22. data/lib/ditty/controllers/{auth.rb → auth_controller.rb} +22 -18
  23. data/lib/ditty/controllers/{component.rb → component_controller.rb} +23 -20
  24. data/lib/ditty/controllers/{main.rb → main_controller.rb} +6 -2
  25. data/lib/ditty/controllers/roles_controller.rb +23 -0
  26. data/lib/ditty/controllers/user_login_traits_controller.rb +46 -0
  27. data/lib/ditty/controllers/{users.rb → users_controller.rb} +13 -11
  28. data/lib/ditty/db.rb +7 -5
  29. data/lib/ditty/emails/base.rb +37 -32
  30. data/lib/ditty/generators/crud_generator.rb +114 -0
  31. data/lib/ditty/generators/migration_generator.rb +26 -0
  32. data/lib/ditty/generators/project_generator.rb +52 -0
  33. data/lib/ditty/helpers/component.rb +2 -1
  34. data/lib/ditty/helpers/pundit.rb +24 -8
  35. data/lib/ditty/helpers/response.rb +15 -13
  36. data/lib/ditty/helpers/views.rb +28 -6
  37. data/lib/ditty/listener.rb +6 -4
  38. data/lib/ditty/memcached.rb +8 -0
  39. data/lib/ditty/middleware/accept_extension.rb +2 -2
  40. data/lib/ditty/middleware/error_catchall.rb +2 -2
  41. data/lib/ditty/models/base.rb +9 -0
  42. data/lib/ditty/models/identity.rb +11 -7
  43. data/lib/ditty/models/role.rb +1 -0
  44. data/lib/ditty/models/user.rb +23 -2
  45. data/lib/ditty/policies/role_policy.rb +1 -1
  46. data/lib/ditty/policies/user_login_trait_policy.rb +1 -1
  47. data/lib/ditty/policies/user_policy.rb +1 -1
  48. data/lib/ditty/services/authentication.rb +27 -16
  49. data/lib/ditty/services/email.rb +19 -15
  50. data/lib/ditty/services/logger.rb +26 -20
  51. data/lib/ditty/services/pagination_wrapper.rb +7 -5
  52. data/lib/ditty/services/settings.rb +7 -6
  53. data/lib/ditty/tasks/ditty.rake +19 -1
  54. data/lib/ditty/tasks/omniauth-ldap.rake +2 -2
  55. data/lib/ditty/templates/.gitignore +5 -0
  56. data/lib/ditty/templates/.rspec +2 -0
  57. data/lib/ditty/templates/.rubocop.yml +7 -0
  58. data/lib/ditty/templates/Rakefile +12 -0
  59. data/lib/ditty/templates/application.rb +12 -0
  60. data/lib/ditty/templates/config.ru +37 -0
  61. data/lib/ditty/templates/controller.rb.erb +64 -0
  62. data/lib/ditty/templates/env.example +4 -0
  63. data/lib/ditty/templates/lib/project.rb.erb +5 -0
  64. data/lib/ditty/templates/logs/.empty_directory +0 -0
  65. data/lib/ditty/templates/migration.rb.erb +7 -0
  66. data/lib/ditty/templates/model.rb.erb +26 -0
  67. data/lib/ditty/templates/pids/.empty_directory +0 -0
  68. data/lib/ditty/templates/policy.rb.erb +48 -0
  69. data/{public → lib/ditty/templates/public}/browserconfig.xml +0 -0
  70. data/lib/ditty/templates/public/css/sb-admin-2.min.css +10 -0
  71. data/lib/ditty/templates/public/css/styles.css +13 -0
  72. data/lib/ditty/templates/public/favicon.ico +0 -0
  73. data/{public → lib/ditty/templates/public}/images/apple-icon.png +0 -0
  74. data/{public → lib/ditty/templates/public}/images/favicon-16x16.png +0 -0
  75. data/{public → lib/ditty/templates/public}/images/favicon-32x32.png +0 -0
  76. data/{public → lib/ditty/templates/public}/images/launcher-icon-1x.png +0 -0
  77. data/{public → lib/ditty/templates/public}/images/launcher-icon-2x.png +0 -0
  78. data/{public → lib/ditty/templates/public}/images/launcher-icon-4x.png +0 -0
  79. data/{public → lib/ditty/templates/public}/images/mstile-150x150.png +0 -0
  80. data/{public → lib/ditty/templates/public}/images/safari-pinned-tab.svg +0 -0
  81. data/lib/ditty/templates/public/js/sb-admin-2.min.js +7 -0
  82. data/lib/ditty/templates/public/js/scripts.js +1 -0
  83. data/{public/manifest.json → lib/ditty/templates/public/manifest.json.erb} +2 -2
  84. data/lib/ditty/templates/settings.yml.erb +29 -0
  85. data/lib/ditty/templates/sidekiq.rb +18 -0
  86. data/lib/ditty/templates/sidekiq.yml +9 -0
  87. data/lib/ditty/templates/spec_helper.rb +43 -0
  88. data/lib/ditty/templates/type.rb.erb +21 -0
  89. data/lib/ditty/templates/views/display.haml.tt +20 -0
  90. data/lib/ditty/templates/views/edit.haml.tt +10 -0
  91. data/lib/ditty/templates/views/form.haml.tt +11 -0
  92. data/lib/ditty/templates/views/index.haml.tt +29 -0
  93. data/lib/ditty/templates/views/new.haml.tt +10 -0
  94. data/lib/ditty/version.rb +1 -1
  95. data/lib/ditty.rb +6 -4
  96. data/lib/rubocop/cop/ditty/call_services_directly.rb +2 -2
  97. data/migrate/20181209_add_user_login_traits.rb +4 -4
  98. data/migrate/20190220_add_parent_id_to_roles.rb +9 -0
  99. data/spec/ditty/api_spec.rb +51 -0
  100. data/spec/ditty/controllers/roles_spec.rb +67 -0
  101. data/spec/ditty/controllers/user_login_traits_spec.rb +72 -0
  102. data/spec/ditty/controllers/users_spec.rb +72 -0
  103. data/spec/ditty/emails/base_spec.rb +76 -0
  104. data/spec/ditty/emails/forgot_password_spec.rb +20 -0
  105. data/spec/ditty/helpers/component_spec.rb +85 -0
  106. data/spec/ditty/models/user_spec.rb +36 -0
  107. data/spec/ditty/services/email_spec.rb +36 -0
  108. data/spec/ditty/services/logger_spec.rb +68 -0
  109. data/spec/ditty/services/settings_spec.rb +63 -0
  110. data/spec/ditty_spec.rb +9 -0
  111. data/spec/factories.rb +46 -0
  112. data/spec/fixtures/logger.yml +17 -0
  113. data/spec/fixtures/section.yml +3 -0
  114. data/spec/fixtures/settings.yml +8 -0
  115. data/spec/spec_helper.rb +51 -0
  116. data/spec/support/api_shared_examples.rb +250 -0
  117. data/spec/support/crud_shared_examples.rb +145 -0
  118. data/views/403.haml +1 -1
  119. data/views/404.haml +2 -4
  120. data/views/500.haml +11 -0
  121. data/views/audit_logs/index.haml +32 -33
  122. data/views/auth/forgot_password.haml +32 -16
  123. data/views/auth/identity.haml +14 -13
  124. data/views/auth/ldap.haml +2 -2
  125. data/views/auth/login.haml +23 -17
  126. data/views/auth/register.haml +20 -18
  127. data/views/auth/register_identity.haml +27 -12
  128. data/views/auth/reset_password.haml +36 -19
  129. data/views/blank.haml +43 -0
  130. data/views/embedded.haml +17 -11
  131. data/views/index.haml +1 -1
  132. data/views/layout.haml +45 -30
  133. data/views/partials/actions.haml +15 -14
  134. data/views/partials/content_tag.haml +0 -0
  135. data/views/partials/delete_form.haml +1 -1
  136. data/views/partials/filter_control.haml +2 -2
  137. data/views/partials/footer.haml +6 -5
  138. data/views/partials/form_control.haml +19 -12
  139. data/views/partials/form_tag.haml +1 -1
  140. data/views/partials/navitems.haml +42 -0
  141. data/views/partials/notifications.haml +12 -8
  142. data/views/partials/pager.haml +44 -25
  143. data/views/partials/search.haml +15 -11
  144. data/views/partials/sidebar.haml +15 -37
  145. data/views/partials/sort_ui.haml +2 -0
  146. data/views/partials/topbar.haml +53 -0
  147. data/views/partials/user_associations.haml +32 -0
  148. data/views/quick_start.haml +23 -0
  149. data/views/roles/display.haml +27 -6
  150. data/views/roles/edit.haml +3 -3
  151. data/views/roles/form.haml +1 -0
  152. data/views/roles/index.haml +23 -16
  153. data/views/roles/new.haml +2 -2
  154. data/views/user_login_traits/display.haml +4 -4
  155. data/views/user_login_traits/edit.haml +3 -3
  156. data/views/user_login_traits/index.haml +23 -25
  157. data/views/user_login_traits/new.haml +2 -2
  158. data/views/users/display.haml +14 -15
  159. data/views/users/edit.haml +3 -3
  160. data/views/users/form.haml +0 -0
  161. data/views/users/index.haml +31 -24
  162. data/views/users/login_traits.haml +6 -8
  163. data/views/users/new.haml +2 -2
  164. data/views/users/profile.haml +15 -15
  165. data/views/users/user.haml +1 -1
  166. metadata +271 -63
  167. data/lib/ditty/controllers/roles.rb +0 -13
  168. data/lib/ditty/controllers/user_login_traits.rb +0 -18
  169. data/views/partials/navbar.haml +0 -22
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'active_support/core_ext/hash/except'
5
+
6
+ shared_examples 'an API interface' do |subject, params|
7
+ before { create(subject) }
8
+
9
+ context 'GET /' do
10
+ it 'returns HTML when requested' do
11
+ header 'Accept', 'text/html'
12
+ get '/'
13
+
14
+ expect(last_response).to be_ok
15
+ expect(last_response.headers['Content-Type']).to include('text/html;charset=utf-8')
16
+ end
17
+
18
+ it 'returns JSON when requested' do
19
+ header 'Accept', 'application/json'
20
+ get '/'
21
+
22
+ expect(last_response).to be_ok
23
+ expect(last_response.headers).to include('Content-Type' => 'application/json')
24
+ expect { JSON.parse(last_response.body) }.not_to raise_error
25
+ end
26
+
27
+ it 'returns a list object' do
28
+ header 'Accept', 'application/json'
29
+ get '/'
30
+
31
+ response = JSON.parse last_response.body
32
+ expect(response).to include('page', 'count', 'total', 'items')
33
+ expect(response['page']).to be_an Integer
34
+ expect(response['count']).to be_an Integer
35
+ expect(response['total']).to be_an Integer
36
+ expect(response['items']).to be_an Array
37
+ end
38
+ end
39
+
40
+ context 'GET /id' do
41
+ let(:entity) { create(subject) }
42
+
43
+ it 'returns HTML when requested' do
44
+ header 'Accept', 'text/html'
45
+ get "/#{entity.id}"
46
+
47
+ expect(last_response).to be_ok
48
+ expect(last_response.headers).to include('Content-Type' => 'text/html;charset=utf-8')
49
+ end
50
+
51
+ it 'returns JSON when requested' do
52
+ header 'Accept', 'application/json'
53
+ get "/#{entity.id}"
54
+
55
+ expect(last_response).to be_ok
56
+ expect(last_response.headers).to include('Content-Type' => 'application/json')
57
+ expect { JSON.parse(last_response.body) }.not_to raise_error
58
+ end
59
+
60
+ it 'returns the fetched object' do
61
+ header 'Accept', 'application/json'
62
+ get "/#{entity.id}"
63
+
64
+ response = JSON.parse last_response.body
65
+ expect(response).to be_a Hash
66
+ entity_to_json = JSON.parse entity.values.to_json
67
+ expect(response).to include(entity_to_json)
68
+ end
69
+ end
70
+
71
+ context 'POST /' do
72
+ it 'returns HTML when requested' do
73
+ header 'Accept', 'text/html'
74
+ header 'Content-Type', 'application/x-www-form-urlencoded'
75
+ params[subject] = build(subject).to_hash
76
+ post '/', params
77
+
78
+ expect(last_response.headers).to include('Content-Type' => 'text/html;charset=utf-8')
79
+ end
80
+
81
+ it 'returns a 302 Redirect response for a HTML Request' do
82
+ header 'Accept', 'text/html'
83
+ header 'Content-Type', 'application/x-www-form-urlencoded'
84
+ params[subject] = build(subject).to_hash
85
+ post '/', params
86
+
87
+ expect(last_response.status).to eq 302
88
+ expect(last_response.headers).to include('Location')
89
+ end
90
+
91
+ it 'returns JSON when requested' do
92
+ header 'Accept', 'application/json'
93
+ header 'Content-Type', 'application/json'
94
+ params[subject] = build(subject).to_hash
95
+ post '/', params.to_json
96
+
97
+ expect(last_response.headers).to include('Content-Type' => 'application/json')
98
+ end
99
+
100
+ it 'returns a 201 Created response for a JSON Request' do
101
+ header 'Accept', 'application/json'
102
+ header 'Content-Type', 'application/json'
103
+ params[subject] = build(subject).to_hash
104
+ post '/', params.to_json
105
+
106
+ expect(last_response.status).to eq 201
107
+ end
108
+
109
+ it 'returns a Location Header for a JSON Request' do
110
+ header 'Accept', 'application/json'
111
+ header 'Content-Type', 'application/json'
112
+ params[subject] = build(subject).to_hash
113
+ post '/', params.to_json
114
+
115
+ expect(last_response.headers).to include 'Location'
116
+ end
117
+
118
+ it 'returns an empty body for a JSON Request' do
119
+ header 'Accept', 'application/json'
120
+ header 'Content-Type', 'application/json'
121
+ params[subject] = build(subject).to_hash
122
+ post '/', params.to_json
123
+
124
+ expect(last_response.body).to eq ''
125
+ end
126
+ end
127
+
128
+ context 'PUT /:id' do
129
+ let(:entity) { create(subject) }
130
+
131
+ it 'returns HTML when requested' do
132
+ header 'Accept', 'text/html'
133
+ header 'Content-Type', 'application/x-www-form-urlencoded'
134
+
135
+ values = entity.to_hash.except(:id)
136
+ params[subject] = values
137
+ put "/#{entity.id}", params
138
+
139
+ expect(last_response.headers).to include('Content-Type' => 'text/html;charset=utf-8')
140
+ end
141
+
142
+ it 'returns a 302 Redirect response for a HTML Request' do
143
+ header 'Accept', 'text/html'
144
+ header 'Content-Type', 'application/x-www-form-urlencoded'
145
+
146
+ values = entity.to_hash.except(:id)
147
+ params[subject] = values
148
+ put "/#{entity.id}", params
149
+
150
+ expect(last_response.status).to eq 302
151
+ expect(last_response.headers).to include('Location')
152
+ end
153
+
154
+ it 'returns JSON when requested' do
155
+ header 'Accept', 'application/json'
156
+ header 'Content-Type', 'application/json'
157
+
158
+ values = entity.to_hash.except(:id)
159
+ params[subject] = values
160
+ put "/#{entity.id}", params.to_json
161
+
162
+ expect(last_response.headers).to include('Content-Type' => 'application/json')
163
+ end
164
+
165
+ it 'returns a 200 OK response for a JSON Request' do
166
+ header 'Accept', 'application/json'
167
+ header 'Content-Type', 'application/json'
168
+
169
+ values = entity.to_hash.except(:id)
170
+ params[subject] = values
171
+ put "/#{entity.id}", params.to_json
172
+
173
+ expect(last_response.status).to eq 200
174
+ end
175
+
176
+ it 'returns a Location Header for a JSON Request' do
177
+ header 'Accept', 'application/json'
178
+ header 'Content-Type', 'application/json'
179
+
180
+ values = entity.to_hash.except(:id)
181
+ params[subject] = values
182
+ put "/#{entity.id}", params.to_json
183
+
184
+ expect(last_response.headers).to include 'Location'
185
+ end
186
+
187
+ it 'returns the updated entity in the body for a JSON Request' do
188
+ header 'Accept', 'application/json'
189
+ header 'Content-Type', 'application/json'
190
+
191
+ values = entity.to_hash.except(:id)
192
+ params[subject] = values
193
+ put "/#{entity.id}", params.to_json
194
+
195
+ response = JSON.parse last_response.body
196
+ entity_to_hash = JSON.parse entity.values.to_json
197
+ expect(response).to eq entity_to_hash
198
+ end
199
+ end
200
+
201
+ context 'DELETE /:id' do
202
+ let(:entity) { create(subject) }
203
+
204
+ it 'returns HTML when requested' do
205
+ header 'Accept', 'text/html'
206
+ header 'Content-Type', 'application/x-www-form-urlencoded'
207
+
208
+ delete "/#{entity.id}"
209
+
210
+ expect(last_response.headers).to include('Content-Type' => 'text/html;charset=utf-8')
211
+ end
212
+
213
+ it 'returns a 302 Redirect response for a HTML Request' do
214
+ header 'Accept', 'text/html'
215
+ header 'Content-Type', 'application/x-www-form-urlencoded'
216
+
217
+ delete "/#{entity.id}"
218
+
219
+ expect(last_response.status).to eq 302
220
+ expect(last_response.headers).to include('Location')
221
+ end
222
+
223
+ it 'returns JSON when requested' do
224
+ header 'Accept', 'application/json'
225
+ header 'Content-Type', 'application/json'
226
+
227
+ delete "/#{entity.id}"
228
+
229
+ expect(last_response.headers).to include('X-Content-Type-Options' => 'nosniff')
230
+ end
231
+
232
+ it 'returns a 204 No Content response for a JSON Request' do
233
+ header 'Accept', 'application/json'
234
+ header 'Content-Type', 'application/json'
235
+
236
+ delete "/#{entity.id}"
237
+
238
+ expect(last_response.status).to eq 204
239
+ end
240
+
241
+ it 'returns an empty body for a JSON Request' do
242
+ header 'Accept', 'application/json'
243
+ header 'Content-Type', 'application/json'
244
+
245
+ delete "/#{entity.id}"
246
+
247
+ expect(last_response.body).to eq ''
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ shared_examples 'a CRUD Controller' do |route|
4
+ context 'GET' do
5
+ it '/doesnotexist' do
6
+ get '/doesnotexist'
7
+ expect(last_response).not_to be_ok
8
+ expect(last_response.status).to eq 404
9
+ end
10
+
11
+ it route.to_s do
12
+ model # Ensure that there's at least one item in the list
13
+ get '/'
14
+
15
+ if Pundit.policy(user, app.model_class).list?
16
+ expect(last_response).to be_ok, "Expected OK response, got #{last_response.status}"
17
+ else
18
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
19
+ end
20
+ end
21
+
22
+ it "#{route}?count=1&page=1" do
23
+ model # Ensure that there's at least one item in the list
24
+ get '/?count=1&page=1'
25
+
26
+ if Pundit.policy(user, app.model_class).list?
27
+ expect(last_response).to be_ok, "Expected OK response, got #{last_response.status}"
28
+ else
29
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
30
+ end
31
+ end
32
+
33
+ it "#{route}/new" do
34
+ get '/new'
35
+
36
+ if Pundit.policy(user, app.model_class).create?
37
+ expect(last_response).to be_ok, "Expected OK response, got #{last_response.status}"
38
+ else
39
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
40
+ end
41
+ end
42
+
43
+ it "#{route}/id" do
44
+ get "/#{model.id}"
45
+
46
+ if Pundit.policy(user, model).read?
47
+ expect(last_response).to be_ok, "Expected OK response, got #{last_response.status}"
48
+ else
49
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
50
+ end
51
+ end
52
+
53
+ it "#{route}/id/edit" do
54
+ get "/#{model.id}/edit"
55
+
56
+ if Pundit.policy(user, model).update?
57
+ expect(last_response).to be_ok, "Expected OK response, got #{last_response.status}"
58
+ else
59
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'POST' do
65
+ it '/doesnotexist' do
66
+ header 'Accept', 'text/html'
67
+ post '/doesnotexist'
68
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
69
+ expect(last_response.status).to eq 404
70
+ end
71
+
72
+ it route.to_s do
73
+ header 'Accept', 'text/html'
74
+ post '/', create_data
75
+
76
+ if Pundit.policy(user, app.model_class).create?
77
+ expect(last_response.status).to eq 302
78
+ else
79
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
80
+ end
81
+ end
82
+
83
+ it "#{route} with invalid parameters" do
84
+ header 'Accept', 'text/html'
85
+ header 'Content-Type', 'application/x-www-form-urlencoded'
86
+ post '/', invalid_create_data
87
+
88
+ if Pundit.policy(user, app.model_class).create?
89
+ expect(last_response.status).to eq 400
90
+ else
91
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'PUT' do
97
+ it '/doesnotexist' do
98
+ header 'Accept', 'text/html'
99
+ put '/doesnotexist'
100
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
101
+ expect(last_response.status).to eq 404
102
+ end
103
+
104
+ it "#{route}/:id" do
105
+ header 'Accept', 'text/html'
106
+ put "/#{model.id}", update_data
107
+
108
+ if Pundit.policy(user, model).update?
109
+ expect(last_response.status).to eq 302
110
+ else
111
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
112
+ end
113
+ end
114
+
115
+ it "#{route} with invalid parameters" do
116
+ header 'Accept', 'text/html'
117
+ put "/#{model.id}", invalid_update_data
118
+
119
+ if Pundit.policy(user, model).update?
120
+ expect(last_response.status).to eq 400
121
+ else
122
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
123
+ end
124
+ end
125
+ end
126
+
127
+ context 'DELETE' do
128
+ it '/doesnotexist' do
129
+ delete '/doesnotexist'
130
+ expect(last_response).not_to be_ok, "Expected a NOT OK response, got #{last_response.status}"
131
+ expect(last_response.status).to eq 404
132
+ end
133
+
134
+ it "#{route}/id" do
135
+ header 'Accept', 'text/html'
136
+ delete "/#{model.id}"
137
+
138
+ if Pundit.policy(user, model).delete?
139
+ expect(last_response.status).to eq 302
140
+ else
141
+ expect(last_response).not_to be_ok
142
+ end
143
+ end
144
+ end
145
+ end
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/404.haml CHANGED
@@ -1,7 +1,5 @@
1
- %h2 Whoops!
2
-
3
- %p.lead
1
+ %p.lead.text-center
4
2
  We could not find the page you were looking for...
5
3
 
6
4
  .text-center
7
- %iframe{ width: 560, height: 315, src: 'https://www.youtube.com/embed/e3-5YC_oHjE', frameborder: 0, allowfullscreen: true }
5
+ %iframe{ width: 560, height: 315, src: 'https://www.youtube.com/embed/e3-5YC_oHjE', frameborder: 0, allowfullscreen: true }
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,37 +1,36 @@
1
1
  .row
2
2
  .col-md-12
3
- .panel.panel-default
4
- .panel-body
5
- = haml :'partials/search'
6
- %table.table.table-striped
7
- %thead
8
- %tr
9
- %th User email
10
- %th Action
11
- %th Details
12
- %th IP Address
13
- %th Browser
14
- %th Device
15
- %th Platform
16
- %th Created at
17
- %tbody
18
- - if list.count > 0
19
- - list.all.each do |entity|
20
- %tr
21
- %td
22
- -if entity.user
23
- %a{ href: "#{settings.map_path}/users/#{entity.user.id}" }= entity.user.email
24
- -else
25
- None
26
- %td= entity.action
27
- %td= entity.details
28
- %td= entity.ip_address || 'Unknown'
29
- %td= entity.browser || 'Unknown'
30
- %td= entity.device || 'Unknown'
31
- %td= entity.platform || 'Unknown'
32
- %td= entity.created_at&.strftime('%Y-%m-%d %H:%M:%S') || 'Unknown'
33
- - else
3
+ = haml :'partials/search'
4
+ %table.table.table-striped.table-bordered.table-hover
5
+ %thead.thead-dark
6
+ %tr
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
+ %tbody
16
+ - if list.count > 0
17
+ - list.all.each do |entity|
34
18
  %tr
35
- %td.text-center{ colspan: 4 } No records
19
+ %td
20
+ -if entity.user
21
+ %a{ href: "#{settings.map_path}/users/#{entity.user.id}" }= entity.user.email
22
+ -else
23
+ None
24
+ %td= entity.action
25
+ %td= entity.details
26
+ %td= entity.ip_address || 'Unknown'
27
+ %td= entity.browser || 'Unknown'
28
+ %td= entity.device || 'Unknown'
29
+ %td= entity.platform || 'Unknown'
30
+ %td= entity.created_at&.strftime('%Y-%m-%d %H:%M:%S') || 'Unknown'
31
+ - else
32
+ %tr
33
+ %td.text-center{ colspan: 4 } No records
36
34
 
37
- =pagination(list, base_path)
35
+ - if list.count > 0
36
+ = pagination(list, base_path)