kadmin 0.1.7 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -1
- data/app/assets/stylesheets/kadmin/application.scss +9 -0
- data/app/assets/stylesheets/kadmin/{finder.scss → typeahead-select.scss} +0 -0
- data/app/decorators/kadmin/finder_decorator.rb +33 -0
- data/app/decorators/kadmin/pager_decorator.rb +33 -0
- data/app/views/kadmin/components/_finder.html.erb +18 -0
- data/app/views/kadmin/components/finder/_empty.html.erb +3 -0
- data/app/views/kadmin/components/finder/_form.erb +10 -0
- data/app/views/kadmin/components/finder/_header.html.erb +7 -0
- data/app/views/kadmin/{_alerts.html.erb → helpers/_alerts.html.erb} +0 -0
- data/app/views/kadmin/helpers/_form_errors.html.erb +10 -0
- data/app/views/layouts/kadmin/application.html.erb +2 -2
- data/config/locales/de.yml +5 -0
- data/config/locales/en.yml +5 -0
- data/lib/kadmin/finder.rb +15 -6
- data/lib/kadmin/form.rb +94 -44
- data/lib/kadmin/pager.rb +14 -3
- data/lib/kadmin/version.rb +1 -1
- data/test/dummy/app/controllers/admin/application_controller.rb +1 -1
- data/test/dummy/app/controllers/admin/{persons_controller.rb → people_controller.rb} +23 -20
- data/test/dummy/app/models/person.rb +6 -1
- data/test/dummy/app/views/admin/people/_form.html.erb +34 -0
- data/test/dummy/app/views/admin/people/_table.html.erb +33 -0
- data/test/dummy/app/views/admin/people/edit.html.erb +4 -0
- data/test/dummy/app/views/admin/people/index.html.erb +3 -0
- data/test/dummy/app/views/admin/people/new.html.erb +5 -0
- data/test/dummy/app/views/admin/people/show.html.erb +3 -0
- data/test/dummy/config/application.rb +6 -0
- data/test/dummy/config/locales/en.yml +13 -25
- data/test/dummy/config/routes.rb +2 -3
- data/test/dummy/db/dummy_development.sqlite3 +0 -0
- data/test/dummy/db/dummy_test.sqlite3 +0 -0
- data/test/dummy/db/migrate/20161006114509_create_people.rb +1 -1
- data/test/dummy/db/schema.rb +1 -1
- data/test/dummy/lib/forms/group_form.rb +1 -13
- data/test/dummy/lib/forms/person_form.rb +4 -13
- data/test/dummy/log/development.log +12819 -0
- data/test/dummy/log/test.log +38 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/-F/-FsNbFK52v0pNBxKy1HNNm6PvilpJ-x7Wnv9h6tmyB0.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/0n/0n6SGYdqNeEUJSsu-imL90L-yIUKPi_NDSkmMySPdWQ.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/0t/0tn2q8esWnF5fEcbJLYg62c_FP9JanA9PSXdq_tYLYY.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/1I/1IRm2UzZL2EDGDrmidisFycmYvgjxsm88Qhn2Xgpob8.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/1M/1MqoHGqxGd5cjs2GFOFxmpuvnUUEqu7VgvlhIQiJyOg.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/55/55XufN8FnqyYo8_Q9Fjekgd9NNgLIf51DyqmHCunvXE.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/5i/5i0sweEEf0_vvcK_Zu6WD6pyCfLIOQE-861C6dFShUY.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/63/637_sfmF7zODk6Dzv4BT1sf91iXjjlcxAvnToFqUlDI.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/6M/6MU_r-ycVrZjzTQTJGMi6ufWq8z7emfSF41_aIN-KB8.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/7q/7qsE29MVuPYwZNAgNwNeH8xVhpOsc_b6ieDbhbhvqpw.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/8P/8PiD2jpkOTKwCqmTucDpZfcaYIoDaLpRmCTDikmBAMk.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/8T/8TAB-jrsQAvLWhG3fwfbx6ZVwjI7j6sAWWQTfPn91iU.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/8g/8gcjIdXkz5dMqwDP6vmyotAcXH7cn0PUj3_5Wmp3CtI.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/8m/8m_OIflQITiNEo__Te4xgHNCGjqG1584BRqm-cjmjcs.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/9L/9LEx970_JauYhUWfuivS75eV0Iyfbt9ubccTHIsAfYw.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/9_/9_pVK00xoiNV1Rlusq__AhBd-RI2wkk8CYsmetStEr0.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/9y/9yz1HSty42fFgm9G7oOB99fA470iGKKnSXO5t5PEHXw.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/C_/C_apQOWNpEW8SnzKgrc57IADlEkNTHj8h_oDFtY9064.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Cy/Cy_UGoqxMmEq4sj5RxYjcR-aIdYvnjZvP_Pnrz1_9e8.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/DP/DPzrQSMXLpVyt0hbMLGC5DJZyW9kFE4CP0vt6WNOp48.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/GW/GW4OJ-JDKmC4uwvYKzjrIhVq-P4t_9zaLGKSydO1f-U.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/H8/H8slRzi1H2WqciDo1jBGxJw6DZeRgEuv5MXdQ-y6mWg.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Iy/IyL_kOqVN1xwoPTt5LY6WW15l0RdXMbqH4eG9BIy9eo.cache +2 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Iy/IyfLjIElmUEnBz0fjO4ofGF04F_HqLxAbuft8mp0yBU.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/JR/JRJaUThDzGQo0YF40Lfk5goZBKJKxJJBtxXLmxwRx2w.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Lf/Lf6bmdizyOM18a9qBS2SAloyVi3kwlh5LDQ9rf09AVs.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Lf/lFitMap7CRaoT13khB0t9biKi45L1Gx053Qqb0V7xZ0.cache +2 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/NL/NLORenk-WvjF2ETu3Vd8oPQSkm56nqI0w4FNB4PlOhw.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/O3/o3x01lJRlMuBMc1c5TIFuEysbQ5hJ-u8oDEu3O8LYpM.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Oe/Oe-3DQv4rb1wBwCSKfPxsFWLQEG2uwzsN3r1NlhUtGg.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/QR/qrn7imioXU-GzX_YDKunySyxOQQPdYnm8cFRc9_0Bbg.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Qk/qK1FqgxCmejbunaDgFtqgzegZ-MWYa9sGmkdsy2hXbg.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Sj/Sj9P9v_5nRDm_FbYyYHC7B00vqxjZxYc27y9Cm2vpb4.cache +2 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/UG/UGW_N3c5WIGHIsGBAlRBI-sD2QLfW6LMGFiCsdfC2xE.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/VZ/VZjlQNeP2hr_AciJNPbb7z4G6K4AKe3Z0gn4WdC7-Tc.cache +3 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/WK/WKjVrAg3EXJ83bQHhANNBB8gXiSSQwcWJfzQUOrfly4.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/WS/WSKgaB4x1F6sDxk0fM4Fk8nA4EVyHSSJesu1-X82yjQ.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/YY/YyJCDg1eiYs1CZSWGv3k7E8PC2u8ujMHnHruKAZB_ns.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Yt/ytqIuGGRQKNExye5HUeNNpLiTb2bz_DUdCNF-PNgG5s.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/ZN/ZNsrQk4C9XhLrV8_ccxeh7iUpUxCU8p6HzJnLQ1JzTs.cache +2 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/_c/_chctmRf-pIheCJNY7hl-Sx59opmqdxFL2PevfZwq2o.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/a2/a2cuBhgUfDqIn_cESf3bZCYgOfUL1pr90zUxIqJ9kKk.cache +2 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/aP/aPmiEIWGz1eNfYaCxOihmOj8gH_FiMuy2vyfa2dSm74.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/br/BRgaKx5w_y5SAfZ01RZMdp6Z7-ggaICbwPGgGGYnMvo.cache +4 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/cc/cc_H3Ys29GBr0Mwo8mhwVpU1ZD29Orm_zrrlsK04ZxY.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/cp/cpltkoEnlCxGELgo5pzYraQtUFtncB66ULMh61y9UmU.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/d2/d2pyKhpApMC_82fcSpBr3rJiQi0GXmd5nAxW4gjjbqI.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/dG/DgWTCx-Rb2jbSHoEnFqjHWsBClV-3r80EPU7mB2IOlg.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/fb/fbnO4L8jq7mKY_872G6-R_1g55VkHGsFLV5_L9N_8JY.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/gK/gKggrFUEs3kvWMzHpuEICvH6zL86Giqv50EMpi91rUk.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/n1/n1pvlbASbj39sCgVSMFGCy33gQAhAGlLscNk8Q_hGA0.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/r8/r8Tej1MuIktw1pbDz86gRXteeQymjEsgGYzC6nBpwxI.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/tK/tKjzj5lmM8TAi-WoE8QAQEBI5vJv7bmYYxuEH1UXb9c.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/tb/TbBwthojLrWKVI_yFccgzGN9cXRY9iHGWVV3lr8uXTM.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/tq/TQpnYfdZO_10UMuNVcEY29LaqvuW8gtzhA6qvSt4iQM.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/u7/u75Pn-UKc5QtTrPD6IPz43HZYUDrevf8iMkv7_LabOA.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/zE/zEE95VsN7pfJ2w6HuECxe6KtA0vg0I7liX2HuPK92Tk.cache +1 -0
- metadata +137 -14
- data/test/dummy/app/views/admin/persons/_form.html.erb +0 -0
- data/test/dummy/app/views/admin/persons/edit.html.erb +0 -0
- data/test/dummy/app/views/admin/persons/index.html.erb +0 -0
- data/test/dummy/app/views/admin/persons/new.html.erb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ab1df6f025d446075d7ea0ff8e6f5e6267e5048
|
4
|
+
data.tar.gz: e01b485202b100b31014c57680fe99ded5017518
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 221c1f51318638636bbafbd2c7dd135cd21f69f0cf4df6edbd97be4eda02ee07e2b3013a041aa9138b4c106eaf296747f0139ee7df80d6ff1e7392260cbd944e
|
7
|
+
data.tar.gz: cbc7767909e0b85ff93f7fa6ee6a08d4d03d57ac4ad0db975b35f9d8b79fd17bc487f290313c25f8e3f712aadb0fc4f588583f572631cb081984632b6f7926ef
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Kadmin
|
2
2
|
|
3
|
-
[![GitHub release](https://img.shields.io/badge/release-0.
|
3
|
+
[![GitHub release](https://img.shields.io/badge/release-0.2.2-blue.png)](https://github.com/barcoo/kadmin/releases/tag/0.2.2)
|
4
4
|
|
5
5
|
Collection of utility, configuration, etc., for admin areas in different projects.
|
6
6
|
|
@@ -33,3 +33,11 @@ When you want to create a new release, use the rake task ```cim:release``` (in t
|
|
33
33
|
```shell
|
34
34
|
bundle exec rake cim:release
|
35
35
|
```
|
36
|
+
|
37
|
+
## Roadmap
|
38
|
+
|
39
|
+
TODOs:
|
40
|
+
|
41
|
+
* [ ] Finish form objects (destruction and creation) + tests + examples
|
42
|
+
* [ ] Make a generic typehead-select form object
|
43
|
+
* [x] Wrap Finder objects + view helpers
|
@@ -2,6 +2,15 @@
|
|
2
2
|
@import "bootstrap-sprockets";
|
3
3
|
@import "bootstrap";
|
4
4
|
|
5
|
+
/**
|
6
|
+
* Highlights fields properly when they have errors
|
7
|
+
* Rails automatically appends .field_with_errors on any form element whose model
|
8
|
+
* attributes has an error (as marked in model.errors)
|
9
|
+
*/
|
10
|
+
.field_with_errors {
|
11
|
+
@extend .has-error;
|
12
|
+
}
|
13
|
+
|
5
14
|
a.thumbnail {
|
6
15
|
max-width: 100%;
|
7
16
|
overflow: hidden;
|
File without changes
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Kadmin
|
2
|
+
class FinderDecorator
|
3
|
+
# @return [Kadmin::Finder] underlying finder model
|
4
|
+
attr_reader :finder
|
5
|
+
|
6
|
+
delegate :filters, :scope, :results, to: :finder
|
7
|
+
|
8
|
+
def initialize(finder)
|
9
|
+
@finder = finder
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [String] human readable, singular/plural form of the finder's model
|
13
|
+
def resource_name
|
14
|
+
return @finder.scope.model_name.human(count: pager.displayed_items)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [String] a description of the current applied filters
|
18
|
+
def applied_filters
|
19
|
+
filters = @finder.filters.reduce([]) do |acc, (name, filter)|
|
20
|
+
next(acc) if filter.value.blank?
|
21
|
+
acc << %(<strong>#{filter.value}</strong> on <em>#{name}</em>).html_safe
|
22
|
+
end
|
23
|
+
applied_filters = "(filtering: #{filters.join('; ')})" unless filters.empty?
|
24
|
+
|
25
|
+
return applied_filters
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Kadmin::PagerDecorator] decorated pager of the underlying finder
|
29
|
+
def pager
|
30
|
+
return @pager ||= Kadmin::PagerDecorator.new(@finder.pager)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Kadmin
|
2
|
+
class PagerDecorator
|
3
|
+
# @return [Kadmin::Pager] underlying pager model
|
4
|
+
attr_reader :pager
|
5
|
+
|
6
|
+
delegate :total, :size, :offset, :pages, :current_page, :contains?, :next_page?,
|
7
|
+
:previous_page?, :offset_at, :current_page?,
|
8
|
+
to: :pager
|
9
|
+
|
10
|
+
def initialize(pager)
|
11
|
+
@pager = pager
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Integer] the current number of items displayed for this page
|
15
|
+
def displayed_items
|
16
|
+
return page_end - offset
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Integer] the index number of the last item for this page
|
20
|
+
def page_end
|
21
|
+
return [next_page_offset, total].min
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Integer] the index number of the start item for this page
|
25
|
+
def page_start
|
26
|
+
return offset + 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def next_page_offset
|
30
|
+
return @pager.offset_at(current_page + 1)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%= render partial: 'kadmin/components/finder/header', locals: { finder: finder } %>
|
2
|
+
|
3
|
+
<div class='container-fluid'>
|
4
|
+
<% if finder.filters.present? %>
|
5
|
+
<div class='row'>
|
6
|
+
<%= render partial: 'kadmin/components/finder/form', locals: { finder: finder } %>
|
7
|
+
</div>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<div class='row' style='padding-top: 15px'>
|
11
|
+
<% if finder.results.empty? %>
|
12
|
+
<%= render partial: 'kadmin/components/finder/empty', locals: { finder: finder } %>
|
13
|
+
<% else %>
|
14
|
+
<%= yield %>
|
15
|
+
<%= paginate(finder.pager, [15, 30, 50, 100]) %>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
18
|
+
</div>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<%= form_tag(request.path, method: 'get', class: 'form-inline') do %>
|
2
|
+
<div class='form-group'>
|
3
|
+
<%= link_to('Create', url_for(action: :new), class: 'btn btn-primary') %>
|
4
|
+
<% finder.filters.each do |name, filter| %>
|
5
|
+
<%= text_field_tag("filter_#{name}", filter&.value, class: 'form-control', placeholder: "Filter by #{name}...") %>
|
6
|
+
<% end %>
|
7
|
+
<%= submit_tag('Filter', class: 'btn btn-default form-control') %>
|
8
|
+
<%= link_to('Clear', request.path, class: 'btn btn-danger form-control') %>
|
9
|
+
</div>
|
10
|
+
<% end %>
|
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<% if model.errors.present? %>
|
2
|
+
<%= alert('danger') do %>
|
3
|
+
<p><%= glyphicon('exclamation-sign') %> <%= t('kadmin.forms.please_correct') %></p>
|
4
|
+
<ul>
|
5
|
+
<% model.errors.full_messages.each do |message| %>
|
6
|
+
<li><%= message.html_safe %></li>
|
7
|
+
<% end %>
|
8
|
+
</ul>
|
9
|
+
<% end %>
|
10
|
+
<% end %>
|
@@ -46,7 +46,7 @@
|
|
46
46
|
<% if content_for?(:sidebar) %>
|
47
47
|
<!-- main -->
|
48
48
|
<div class="col-sm-8 col-md-8 col-lg-9 main">
|
49
|
-
<%= render partial: 'kadmin/alerts' %>
|
49
|
+
<%= render partial: 'kadmin/helpers/alerts' %>
|
50
50
|
<%= yield %>
|
51
51
|
</div>
|
52
52
|
<!-- sidebar -->
|
@@ -55,7 +55,7 @@
|
|
55
55
|
</div>
|
56
56
|
<% else %>
|
57
57
|
<div class="col-sm-12 col-md-12 main">
|
58
|
-
<%= render partial: 'kadmin/alerts' %>
|
58
|
+
<%= render partial: 'kadmin/helpers/alerts' %>
|
59
59
|
<%= yield %>
|
60
60
|
</div>
|
61
61
|
<% end %>
|
data/config/locales/de.yml
CHANGED
@@ -6,9 +6,14 @@ de:
|
|
6
6
|
please_login: Bitte melden Sie sich an
|
7
7
|
unauthorized: Unberechtigte Zugriff
|
8
8
|
unauthorized_message: Sie haben für die gewünschte Seite leider keine Zugriffsrechte; falls Sie haben sollten, bitte das Apps & Services Team kontaktieren
|
9
|
+
components:
|
10
|
+
finder:
|
11
|
+
empty: Nichts zu zeigen
|
9
12
|
dash_message: Schaue Dir mal die Bereiche in der Navigationsleiste oben. Wenn es irgenwelche Probleme gibt, oder es fehlt Dir Zugriffsrechte, bitte das Apps & Services Team kontaktieren.
|
10
13
|
error: Fehler
|
11
14
|
errors:
|
12
15
|
not_found: Objekt nicht gefunden
|
13
16
|
params_missing: Fehlenden erforderlichen Parameter
|
17
|
+
forms:
|
18
|
+
please_correct: "Bitte die folgende Felde korrigieren:"
|
14
19
|
welcome: Willkommen zurück, %{user}!
|
data/config/locales/en.yml
CHANGED
@@ -6,9 +6,14 @@ en:
|
|
6
6
|
please_login: Please authenticate yourself
|
7
7
|
unauthorized: Unauthorized access
|
8
8
|
unauthorized_message: You are not authorized to access this resource; if you think you should be, contact the Apps & Services team
|
9
|
+
components:
|
10
|
+
finder:
|
11
|
+
empty: Nothing to show
|
9
12
|
dash_message: See the top navigation bar for the different admin sections. If you are missing authorizations, or if there is any issue at all, contact the Apps & Services team!
|
10
13
|
error: Error
|
11
14
|
errors:
|
12
15
|
not_found: Requested object not found
|
13
16
|
params_missing: Missing required parameters
|
17
|
+
forms:
|
18
|
+
please_correct: "Please correct the following:"
|
14
19
|
welcome: Welcome %{user}!
|
data/lib/kadmin/finder.rb
CHANGED
@@ -17,16 +17,19 @@ module Kadmin
|
|
17
17
|
@scope = scope
|
18
18
|
@pager = nil
|
19
19
|
@filters = {}
|
20
|
+
@results = nil
|
20
21
|
end
|
21
22
|
|
22
23
|
# @param [String] name the filter name (should be unique)
|
23
24
|
# @param [String, Array<String>] column the column(s) name to filter on
|
24
25
|
# @param [String, Array<String>] value the value or values to look for (OR'd)
|
25
26
|
def filter(name:, column:, value:)
|
26
|
-
if column.present? &&
|
27
|
+
if column.present? && !@filters.key?(name)
|
27
28
|
@filters[name] = Kadmin::Finder::Filter.new(column, value)
|
28
|
-
|
29
|
-
|
29
|
+
if value.present?
|
30
|
+
@scope = @scope.where("#{@scope.table_name}.`#{column}` LIKE ?", value.tr('*', '%'))
|
31
|
+
@pager&.total = @scope.count
|
32
|
+
end
|
30
33
|
end
|
31
34
|
|
32
35
|
return self
|
@@ -47,10 +50,16 @@ module Kadmin
|
|
47
50
|
end
|
48
51
|
|
49
52
|
# @return [ActiveRecord::Relation] the filtered (and optionally paginated) results
|
50
|
-
def
|
51
|
-
results
|
52
|
-
|
53
|
+
def results
|
54
|
+
return @results ||= begin
|
55
|
+
results = @scope
|
56
|
+
results = @pager.page(results) unless @pager.nil?
|
57
|
+
results
|
58
|
+
end
|
59
|
+
end
|
53
60
|
|
61
|
+
def find!
|
62
|
+
@results = nil
|
54
63
|
return results
|
55
64
|
end
|
56
65
|
end
|
data/lib/kadmin/form.rb
CHANGED
@@ -36,7 +36,7 @@ module Kadmin
|
|
36
36
|
# @return [ActiveModel::Model] underlying model to populate
|
37
37
|
attr_reader :model
|
38
38
|
|
39
|
-
delegate :id, :persisted?, to: :model
|
39
|
+
delegate :id, :persisted?, :to_key, :to_query, :to_param, :type_for_attribute, to: :model
|
40
40
|
|
41
41
|
def initialize(model)
|
42
42
|
@errors = ActiveModel::Errors.new(self)
|
@@ -44,52 +44,23 @@ module Kadmin
|
|
44
44
|
@form_input = {}
|
45
45
|
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
# Populates the model based on the form input. The input is typically obtained
|
50
|
-
# from the controller's params method, but can be any hash which conforms to
|
51
|
-
# whatever the form object is expecting.
|
52
|
-
# If some input was previously parsed, there is no "rollback" on the state
|
53
|
-
# of the model; it should be done prior to reparsing if it is necessary.
|
54
|
-
# @param [Hash<String, Object>] form_input a hash representing the raw form input
|
55
|
-
def assign_attributes(form_input)
|
56
|
-
@errors.clear
|
57
|
-
form_input.each do |attr, value|
|
58
|
-
setter = "#{attr}="
|
59
|
-
send(setter, value) if respond_to?(setter)
|
60
|
-
end
|
47
|
+
def to_model
|
48
|
+
return @model
|
61
49
|
end
|
62
50
|
|
63
|
-
# @!
|
51
|
+
# @!group Attributes assignment/manipulation
|
64
52
|
|
65
|
-
#
|
53
|
+
# Allows parsing of multi parameter attributes, such as those returned by
|
54
|
+
# the form helpers date_select, datetime_select, etc.
|
55
|
+
# Also allows nested attributes, but this is not currently in use.
|
56
|
+
include ActiveRecord::AttributeAssignment
|
66
57
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
# Overload if you need to validate associations.
|
72
|
-
# @example
|
73
|
-
# class PersonForm < Form
|
74
|
-
# def model_valid?
|
75
|
-
# super
|
76
|
-
# if @model&.child&.changed? && !@model.child.valid?
|
77
|
-
# @errors.add(:base, :invalid, message: 'child model is invalid')
|
78
|
-
# end
|
79
|
-
# end
|
80
|
-
# end
|
81
|
-
def model_valid?
|
82
|
-
unless @model.valid?
|
83
|
-
@model.errors.each do |attribute, error|
|
84
|
-
@errors.add(attribute, error)
|
85
|
-
end
|
86
|
-
end
|
58
|
+
# For now, we overload the method to accept all attributes.
|
59
|
+
# This is removed in Rails 5, so once we upgrade we can remove the overload.
|
60
|
+
def sanitize_for_mass_assignment(attributes)
|
61
|
+
return attributes
|
87
62
|
end
|
88
63
|
|
89
|
-
# @!endgroup
|
90
|
-
|
91
|
-
# @!group Helper methods
|
92
|
-
|
93
64
|
class << self
|
94
65
|
# Delegates the list of attributes to the model, both readers and writers.
|
95
66
|
# If the attribute value passed is a hash and not a symbol, assumes it is
|
@@ -98,23 +69,102 @@ module Kadmin
|
|
98
69
|
# delegate_attributes :first_name, { last_name: [:reader] }
|
99
70
|
# @param [Array<Symbol, Hash<Symbol, Array<Symbol>>>] attributes list of attributes to delegate to the model
|
100
71
|
def delegate_attributes(*attributes)
|
101
|
-
delegates = attributes.
|
72
|
+
delegates = attributes.each_with_object([]) do |attribute, acc|
|
102
73
|
case attribute
|
103
74
|
when Hash
|
104
75
|
key, value = attribute.first
|
105
76
|
acc << key if value.include?(:reader)
|
106
77
|
acc << "#{key}=" if value.include?(:writer)
|
107
78
|
when Symbol, String
|
108
|
-
acc
|
79
|
+
acc.push(attribute, "#{attribute}=")
|
109
80
|
else
|
110
81
|
raise(ArgumentError, 'Attribute must be one of: Hash, Symbol, String')
|
111
82
|
end
|
83
|
+
end
|
112
84
|
|
113
|
-
|
85
|
+
delegate(*delegates, to: :model)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Delegates a specified associations to other another form object
|
89
|
+
# @example
|
90
|
+
# delegate_associations :child, :parent, to: 'Forms::PersonForm'
|
91
|
+
cattr_accessor(:associations) { {} }
|
92
|
+
def delegate_association(association, to:)
|
93
|
+
self.associations[association] = to
|
94
|
+
|
95
|
+
# add a reader attribute
|
96
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
97
|
+
def #{association}
|
98
|
+
return self.associated_forms['#{association}']
|
99
|
+
end
|
100
|
+
METHOD
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def associated_forms
|
105
|
+
return @associated_forms ||= begin
|
106
|
+
self.class.associations.map do |name, form_class_name|
|
107
|
+
form_class = form_class_name.constantize
|
108
|
+
form_class.new(@model.public_send(name))
|
114
109
|
end
|
115
110
|
end
|
116
111
|
end
|
117
112
|
|
118
113
|
# @!endgroup
|
114
|
+
|
115
|
+
# @!group Validation
|
116
|
+
|
117
|
+
validate :validate_model
|
118
|
+
def validate_model
|
119
|
+
unless @model.valid?
|
120
|
+
@model.errors.each do |attribute, error|
|
121
|
+
@errors.add(attribute, error)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
protected :validate_model
|
126
|
+
|
127
|
+
validate :validate_associated_forms
|
128
|
+
def validate_associated_forms
|
129
|
+
self.associated_forms.each do |_name, form|
|
130
|
+
next if form.valid?
|
131
|
+
form.errors.each do |_attribute, _error|
|
132
|
+
@errors.add(:base, :association_error, "associated #{form.model_name.human} form has some errors")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
protected :validate_associated_forms
|
137
|
+
|
138
|
+
# @!endgroup
|
139
|
+
|
140
|
+
# @!group Persistence
|
141
|
+
|
142
|
+
def save
|
143
|
+
saved = false
|
144
|
+
@model.class.transaction do
|
145
|
+
saved = @model.save
|
146
|
+
self.associated_forms.each do |_name, form|
|
147
|
+
saved &&= form.save
|
148
|
+
end
|
149
|
+
|
150
|
+
raise ActiveRecord::Rollback unless saved
|
151
|
+
end
|
152
|
+
|
153
|
+
return saved
|
154
|
+
end
|
155
|
+
|
156
|
+
def save!
|
157
|
+
saved = false
|
158
|
+
@model.class.transaction do
|
159
|
+
saved = @model.save!
|
160
|
+
self.associated_forms.each do |_name, form|
|
161
|
+
saved &&= form.save! # no need to raise anything, save! will do so
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
return saved
|
166
|
+
end
|
167
|
+
|
168
|
+
# @!endgroup
|
119
169
|
end
|
120
170
|
end
|