ditty 0.8.0 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
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)