administrate 0.12.0 → 0.20.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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -2
  3. data/app/assets/javascripts/administrate/application.js +0 -2
  4. data/app/assets/javascripts/administrate/components/associative.js +5 -0
  5. data/app/assets/javascripts/administrate/components/select.js +3 -0
  6. data/app/assets/javascripts/administrate/components/table.js +1 -1
  7. data/app/assets/stylesheets/administrate/application.scss +0 -1
  8. data/app/assets/stylesheets/administrate/base/_forms.scss +1 -1
  9. data/app/assets/stylesheets/administrate/base/_tables.scss +3 -0
  10. data/app/assets/stylesheets/administrate/components/_attributes.scss +4 -3
  11. data/app/assets/stylesheets/administrate/components/_buttons.scss +20 -0
  12. data/app/assets/stylesheets/administrate/components/_cells.scss +2 -0
  13. data/app/assets/stylesheets/administrate/components/_field-unit.scss +24 -4
  14. data/app/assets/stylesheets/administrate/components/_flashes.scss +2 -10
  15. data/app/assets/stylesheets/administrate/components/_main-content.scss +1 -0
  16. data/app/assets/stylesheets/administrate/components/_navigation.scss +2 -3
  17. data/app/assets/stylesheets/administrate/library/_variables.scss +11 -9
  18. data/app/controllers/administrate/application_controller.rb +127 -25
  19. data/app/controllers/concerns/administrate/punditize.rb +44 -20
  20. data/app/helpers/administrate/application_helper.rb +56 -20
  21. data/app/views/administrate/application/_collection.html.erb +26 -29
  22. data/app/views/administrate/application/_collection_header_actions.html.erb +4 -0
  23. data/app/views/administrate/application/_collection_item_actions.html.erb +17 -0
  24. data/app/views/administrate/application/_flashes.html.erb +1 -0
  25. data/app/views/administrate/application/_form.html.erb +20 -5
  26. data/app/views/administrate/application/_icons.html.erb +1 -1
  27. data/app/views/administrate/application/_index_header.html.erb +28 -0
  28. data/app/views/administrate/application/_navigation.html.erb +6 -4
  29. data/app/views/administrate/application/_pagination.html.erb +1 -0
  30. data/app/views/administrate/application/edit.html.erb +2 -2
  31. data/app/views/administrate/application/index.html.erb +9 -29
  32. data/app/views/administrate/application/new.html.erb +1 -1
  33. data/app/views/administrate/application/show.html.erb +28 -12
  34. data/app/views/fields/belongs_to/_form.html.erb +3 -3
  35. data/app/views/fields/belongs_to/_index.html.erb +1 -1
  36. data/app/views/fields/belongs_to/_show.html.erb +1 -1
  37. data/app/views/fields/date/_form.html.erb +22 -0
  38. data/app/views/fields/date/_index.html.erb +21 -0
  39. data/app/views/fields/date/_show.html.erb +21 -0
  40. data/app/views/fields/date_time/_form.html.erb +1 -3
  41. data/app/views/fields/has_many/_index.html.erb +1 -1
  42. data/app/views/fields/has_many/_show.html.erb +2 -1
  43. data/app/views/fields/has_one/_form.html.erb +15 -5
  44. data/app/views/fields/has_one/_index.html.erb +3 -2
  45. data/app/views/fields/has_one/_show.html.erb +22 -15
  46. data/app/views/fields/number/_form.html.erb +1 -1
  47. data/app/views/fields/polymorphic/_index.html.erb +2 -1
  48. data/app/views/fields/polymorphic/_show.html.erb +1 -1
  49. data/app/views/fields/select/_form.html.erb +9 -8
  50. data/app/views/fields/string/_show.html.erb +2 -2
  51. data/app/views/fields/text/_show.html.erb +2 -3
  52. data/app/views/fields/time/_form.html.erb +2 -2
  53. data/app/views/fields/time/_index.html.erb +3 -1
  54. data/app/views/fields/time/_show.html.erb +3 -1
  55. data/app/views/fields/url/_index.html.erb +2 -2
  56. data/app/views/fields/url/_show.html.erb +2 -2
  57. data/app/views/layouts/administrate/application.html.erb +2 -1
  58. data/config/locales/administrate.ar.yml +2 -0
  59. data/config/locales/administrate.bs.yml +2 -0
  60. data/config/locales/administrate.ca.yml +2 -0
  61. data/config/locales/administrate.da.yml +2 -0
  62. data/config/locales/administrate.de.yml +4 -2
  63. data/config/locales/administrate.en.yml +2 -0
  64. data/config/locales/administrate.es.yml +2 -0
  65. data/config/locales/administrate.fi.yml +30 -0
  66. data/config/locales/administrate.fr.yml +4 -2
  67. data/config/locales/administrate.id.yml +2 -0
  68. data/config/locales/administrate.it.yml +2 -0
  69. data/config/locales/administrate.ja.yml +7 -5
  70. data/config/locales/administrate.ko.yml +2 -0
  71. data/config/locales/administrate.nl.yml +7 -5
  72. data/config/locales/administrate.pl.yml +2 -0
  73. data/config/locales/administrate.pt-BR.yml +4 -2
  74. data/config/locales/administrate.pt.yml +4 -2
  75. data/config/locales/administrate.ru.yml +2 -0
  76. data/config/locales/administrate.sl.yml +30 -0
  77. data/config/locales/{administrate.al.yml → administrate.sq.yml} +3 -1
  78. data/config/locales/administrate.sv.yml +2 -0
  79. data/config/locales/administrate.tr.yml +30 -0
  80. data/config/locales/administrate.uk.yml +2 -0
  81. data/config/locales/administrate.vi.yml +2 -0
  82. data/config/locales/administrate.zh-CN.yml +2 -0
  83. data/config/locales/administrate.zh-TW.yml +2 -0
  84. data/docs/adding_controllers_without_related_model.md +52 -0
  85. data/docs/adding_custom_field_types.md +3 -1
  86. data/docs/authentication.md +3 -1
  87. data/docs/authorization.md +47 -22
  88. data/docs/customizing_attribute_partials.md +4 -1
  89. data/docs/customizing_controller_actions.md +67 -1
  90. data/docs/customizing_dashboards.md +171 -40
  91. data/docs/customizing_page_views.md +18 -4
  92. data/docs/extending_administrate.md +27 -0
  93. data/docs/getting_started.md +35 -11
  94. data/docs/guides/customising_search.md +149 -0
  95. data/docs/guides/hiding_dashboards_from_sidebar.md +21 -0
  96. data/docs/guides/scoping_has_many_relations.md +27 -0
  97. data/docs/guides.md +7 -0
  98. data/docs/rails_api.md +5 -3
  99. data/lib/administrate/base_dashboard.rb +73 -14
  100. data/lib/administrate/custom_dashboard.rb +15 -0
  101. data/lib/administrate/engine.rb +9 -2
  102. data/lib/administrate/field/associative.rb +61 -7
  103. data/lib/administrate/field/base.rb +39 -9
  104. data/lib/administrate/field/belongs_to.rb +22 -5
  105. data/lib/administrate/field/date.rb +20 -0
  106. data/lib/administrate/field/deferred.rb +26 -3
  107. data/lib/administrate/field/has_many.rb +35 -8
  108. data/lib/administrate/field/has_one.rb +36 -12
  109. data/lib/administrate/field/number.rb +13 -2
  110. data/lib/administrate/field/polymorphic.rb +7 -6
  111. data/lib/administrate/field/select.rb +23 -4
  112. data/lib/administrate/field/time.rb +11 -0
  113. data/lib/administrate/field/url.rb +4 -0
  114. data/lib/administrate/namespace.rb +5 -1
  115. data/lib/administrate/not_authorized_error.rb +20 -0
  116. data/lib/administrate/order.rb +85 -19
  117. data/lib/administrate/page/base.rb +5 -3
  118. data/lib/administrate/page/collection.rb +1 -0
  119. data/lib/administrate/page/form.rb +12 -4
  120. data/lib/administrate/page/show.rb +10 -2
  121. data/lib/administrate/resource_resolver.rb +4 -3
  122. data/lib/administrate/search.rb +47 -36
  123. data/lib/administrate/version.rb +1 -1
  124. data/lib/administrate/view_generator.rb +14 -3
  125. data/lib/administrate.rb +42 -0
  126. data/lib/generators/administrate/dashboard/dashboard_generator.rb +38 -16
  127. data/lib/generators/administrate/dashboard/templates/controller.rb.erb +23 -11
  128. data/lib/generators/administrate/dashboard/templates/dashboard.rb.erb +4 -4
  129. data/lib/generators/administrate/install/install_generator.rb +43 -2
  130. data/lib/generators/administrate/install/templates/application_controller.rb.erb +4 -4
  131. data/lib/generators/administrate/routes/routes_generator.rb +26 -27
  132. data/lib/generators/administrate/test_record.rb +21 -0
  133. data/lib/generators/administrate/views/views_generator.rb +5 -4
  134. metadata +57 -78
  135. data/app/assets/javascripts/administrate/components/date_time_picker.js +0 -10
  136. data/app/assets/javascripts/administrate/components/has_many_form.js +0 -3
  137. data/config/i18n-tasks.yml +0 -18
  138. data/config/routes.rb +0 -2
  139. data/config/unicorn.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 901108f5715bed268b4395d6edadf3738b127425910dc365390847710f19c98f
4
- data.tar.gz: 6b61113c5c116250f2e46eaa5dbf77972473b16a6aae8042a6bcaef74f45308e
3
+ metadata.gz: 8e5544a7c600e80f4f0059f7b4aea8903e46d926b06dea598660baa758ed67ec
4
+ data.tar.gz: b4aefefcdb03dc10c0570e8d6b1a15629c4a3cee9f5ceb46d306e2b3a57e2454
5
5
  SHA512:
6
- metadata.gz: ac4790d3bca102b4a27cc73f154e7167620f4589088b1207008f0915521735cfed483fbf085d24e7844e7b092f9f1a7d198ec6113cbc4be877758886c192f097
7
- data.tar.gz: da2c64204adee3b43a566c67cf2aa42f37a0a38ea5f35b9988f704491f2b15dcd76210fb2f56f57de1576e77da71f429fe6572493e65933877b29f9eb9e5920f
6
+ metadata.gz: 18ac7ba09f2f7e36d5c86d492fda86cf2f31239146bcac7dacd47eb124fc793e9c425f1646cdcdcce8a672b1666322b6178cf5a602bc358d794e315050f94d02
7
+ data.tar.gz: 183d4aafdeeef34031acf4f0fbd29401d39ad368a2b889d509d36560dfcd2b222043ddde8504fddfddcf505017467e6eec74848c422b960233b3492e9198e06d
data/Rakefile CHANGED
@@ -28,5 +28,3 @@ if defined? RSpec
28
28
  t.verbose = false
29
29
  end
30
30
  end
31
-
32
- task default: "bundler:audit"
@@ -1,6 +1,4 @@
1
1
  //= require jquery
2
2
  //= require jquery_ujs
3
3
  //= require selectize
4
- //= require moment
5
- //= require datetime_picker
6
4
  //= require_tree .
@@ -0,0 +1,5 @@
1
+ $(function() {
2
+ $('.field-unit--belongs-to select').selectize({});
3
+ $(".field-unit--has-many select").selectize({});
4
+ $('.field-unit--polymorphic select').selectize({});
5
+ });
@@ -0,0 +1,3 @@
1
+ $(function() {
2
+ $('.field-unit--select select').selectize({});
3
+ });
@@ -13,7 +13,7 @@ $(function() {
13
13
  var dataUrl = $(event.target).closest("tr").data("url");
14
14
  var selection = window.getSelection().toString();
15
15
  if (selection.length === 0 && dataUrl) {
16
- window.location = dataUrl;
16
+ window.location = window.location.protocol + '//' + window.location.host + dataUrl;
17
17
  }
18
18
  }
19
19
  };
@@ -3,7 +3,6 @@
3
3
  @import "reset/normalize";
4
4
 
5
5
  @import "selectize";
6
- @import "datetime_picker";
7
6
 
8
7
  @import "library/clearfix";
9
8
  @import "library/data-label";
@@ -85,7 +85,7 @@ textarea {
85
85
  [type="checkbox"],
86
86
  [type="radio"] {
87
87
  display: inline;
88
- margin-right: $small-spacing / 2;
88
+ margin-right: $small-spacing * 0.5;
89
89
  }
90
90
 
91
91
  [type="file"] {
@@ -21,6 +21,9 @@ tr {
21
21
  tbody tr {
22
22
  &:hover {
23
23
  background-color: $base-background-color;
24
+ }
25
+
26
+ [role=link] {
24
27
  cursor: pointer;
25
28
  }
26
29
 
@@ -3,9 +3,9 @@
3
3
  clear: left;
4
4
  float: left;
5
5
  margin-bottom: $base-spacing;
6
- margin-top: 0;
6
+ margin-top: 0.25em;
7
7
  text-align: right;
8
- width: calc(15% - 1rem);
8
+ width: calc(20% - 1rem);
9
9
  }
10
10
 
11
11
  .preserve-whitespace {
@@ -17,7 +17,8 @@
17
17
  float: left;
18
18
  margin-bottom: $base-spacing;
19
19
  margin-left: 2rem;
20
- width: calc(85% - 1rem);
20
+ width: calc(80% - 1rem);
21
+ word-break: break-word;
21
22
  }
22
23
 
23
24
  .attribute--nested {
@@ -41,3 +41,23 @@ input[type="submit"],
41
41
  }
42
42
  }
43
43
  }
44
+
45
+ .button--alt {
46
+ background-color: transparent;
47
+ border: $base-border;
48
+ border-color: $blue;
49
+ color: $blue;
50
+ }
51
+
52
+ .button--danger {
53
+ background-color: $red;
54
+
55
+ &:hover {
56
+ background-color: mix($black, $red, 20%);
57
+ color: $white;
58
+ }
59
+ }
60
+
61
+ .button--nav {
62
+ margin-bottom: $base-spacing;
63
+ }
@@ -1,4 +1,6 @@
1
1
  .cell-label {
2
+ padding-top: 0.15em;
3
+
2
4
  &:hover {
3
5
  a {
4
6
  color: $action-color;
@@ -2,6 +2,7 @@
2
2
  @include administrate-clearfix;
3
3
  align-items: center;
4
4
  display: flex;
5
+ flex-wrap: wrap;
5
6
  margin-bottom: $base-spacing;
6
7
  position: relative;
7
8
  width: 100%;
@@ -17,20 +18,39 @@
17
18
  .field-unit__field {
18
19
  float: left;
19
20
  margin-left: 2rem;
20
- width: 45%;
21
+ max-width: 50rem;
22
+ width: 100%;
23
+
24
+ .optgroup-header {
25
+ font-weight: $bold-font-weight;
26
+ }
27
+ }
28
+
29
+ .field-unit__hint {
30
+ font-size: 90%;
31
+ margin-left: calc(15% + 2rem);
32
+ width: 100%;
21
33
  }
22
34
 
23
35
  .field-unit--nested {
24
36
  border: $base-border;
25
37
  margin-left: 7.5%;
38
+ max-width: 60rem;
26
39
  padding: $small-spacing;
27
- width: 50%;
40
+ width: 100%;
28
41
 
29
42
  .field-unit__field {
30
- width: calc(75% - 1rem);
43
+ width: 100%;
31
44
  }
32
45
 
33
46
  .field-unit__label {
34
- width: calc(25% - 1rem);
47
+ width: 10rem;
48
+ }
49
+ }
50
+
51
+ .field-unit--required {
52
+ label::after {
53
+ color: $red;
54
+ content: " *";
35
55
  }
36
56
  }
@@ -1,18 +1,10 @@
1
- $base-spacing: 1.5em !default;
2
- $flashes: (
3
- "alert": #fff6bf,
4
- "error": #fbe3e4,
5
- "notice": #e5edf8,
6
- "success": #e6efc2,
7
- ) !default;
8
-
9
1
  @each $flash-type, $color in $flashes {
10
2
  .flash-#{$flash-type} {
11
3
  background-color: $color;
12
4
  color: mix($black, $color, 60%);
13
5
  display: block;
14
- margin-bottom: $base-spacing / 2;
15
- padding: $base-spacing / 2;
6
+ margin-bottom: $base-spacing * 0.5;
7
+ padding: $base-spacing * 0.5;
16
8
  text-align: center;
17
9
 
18
10
  a {
@@ -5,6 +5,7 @@
5
5
  0 2px 2px rgba($black, 0.2);
6
6
  flex: 1 1 100%;
7
7
  padding-bottom: 10vh;
8
+ min-width: 800px;
8
9
  }
9
10
 
10
11
  .main-content__header,
@@ -2,9 +2,8 @@ $_navigation-link-padding: 0.6em;
2
2
 
3
3
  .navigation {
4
4
  flex: 1 0 10rem;
5
- padding-bottom: $base-spacing;
6
- padding-right: calc(#{$base-spacing} - #{$_navigation-link-padding});
7
- padding-top: $base-spacing;
5
+ padding: $base-spacing;
6
+ padding-left: 0;
8
7
  }
9
8
 
10
9
  .navigation__link {
@@ -14,7 +14,7 @@ $heading-line-height: 1.2 !default;
14
14
  // Other Sizes
15
15
  $base-border-radius: 4px !default;
16
16
  $base-spacing: $base-line-height * 1em !default;
17
- $small-spacing: $base-spacing / 2 !default;
17
+ $small-spacing: $base-spacing * 0.5 !default;
18
18
 
19
19
  // Colors
20
20
  $white: #fff !default;
@@ -22,8 +22,10 @@ $black: #000 !default;
22
22
 
23
23
  $blue: #1976d2 !default;
24
24
  $red: #d32f2f !default;
25
- $light-yellow: #f0cd66 !default;
26
- $light-green: #4ab471 !default;
25
+ $light-yellow: #fff6bf !default;
26
+ $light-red: #fbe3e4 !default;
27
+ $light-green: #e6efc2 !default;
28
+ $light-blue: #e5edf8 !default;
27
29
 
28
30
  $grey-0: #f6f7f7 !default;
29
31
  $grey-1: #dfe0e1 !default;
@@ -47,12 +49,12 @@ $focus-outline: $focus-outline-width solid $focus-outline-color;
47
49
  $focus-outline-offset: 1px;
48
50
 
49
51
  // Flash Colors
50
- $flash-colors: (
51
- alert: $light-yellow,
52
- error: $red,
53
- notice: mix($white, $blue, 50%),
54
- success: $light-green
55
- );
52
+ $flashes: (
53
+ "alert": $light-yellow,
54
+ "error": $light-red,
55
+ "notice": $light-blue,
56
+ "success": $light-green
57
+ ) !default;
56
58
 
57
59
  // Border
58
60
  $base-border-color: $grey-1 !default;
@@ -3,13 +3,12 @@ module Administrate
3
3
  protect_from_forgery with: :exception
4
4
 
5
5
  def index
6
+ authorize_resource(resource_class)
6
7
  search_term = params[:search].to_s.strip
7
- resources = Administrate::Search.new(scoped_resource,
8
- dashboard_class,
9
- search_term).run
8
+ resources = filter_resources(scoped_resource, search_term: search_term)
10
9
  resources = apply_collection_includes(resources)
11
10
  resources = order.apply(resources)
12
- resources = resources.page(params[:page]).per(records_per_page)
11
+ resources = paginate_resources(resources)
13
12
  page = Administrate::Page::Collection.new(dashboard, order: order)
14
13
 
15
14
  render locals: {
@@ -27,7 +26,7 @@ module Administrate
27
26
  end
28
27
 
29
28
  def new
30
- resource = resource_class.new
29
+ resource = new_resource
31
30
  authorize_resource(resource)
32
31
  render locals: {
33
32
  page: Administrate::Page::Form.new(dashboard, resource),
@@ -41,31 +40,32 @@ module Administrate
41
40
  end
42
41
 
43
42
  def create
44
- resource = resource_class.new(resource_params)
43
+ resource = new_resource(resource_params)
45
44
  authorize_resource(resource)
46
45
 
47
46
  if resource.save
47
+ yield(resource) if block_given?
48
48
  redirect_to(
49
- [namespace, resource],
49
+ after_resource_created_path(resource),
50
50
  notice: translate_with_resource("create.success"),
51
51
  )
52
52
  else
53
53
  render :new, locals: {
54
54
  page: Administrate::Page::Form.new(dashboard, resource),
55
- }
55
+ }, status: :unprocessable_entity
56
56
  end
57
57
  end
58
58
 
59
59
  def update
60
60
  if requested_resource.update(resource_params)
61
61
  redirect_to(
62
- [namespace, requested_resource],
62
+ after_resource_updated_path(requested_resource),
63
63
  notice: translate_with_resource("update.success"),
64
64
  )
65
65
  else
66
66
  render :edit, locals: {
67
67
  page: Administrate::Page::Form.new(dashboard, requested_resource),
68
- }
68
+ }, status: :unprocessable_entity
69
69
  end
70
70
  end
71
71
 
@@ -75,25 +75,61 @@ module Administrate
75
75
  else
76
76
  flash[:error] = requested_resource.errors.full_messages.join("<br/>")
77
77
  end
78
- redirect_to action: :index
78
+ redirect_to after_resource_destroyed_path(requested_resource)
79
79
  end
80
80
 
81
81
  private
82
82
 
83
+ def filter_resources(resources, search_term:)
84
+ Administrate::Search.new(
85
+ resources,
86
+ dashboard,
87
+ search_term,
88
+ ).run
89
+ end
90
+
91
+ def after_resource_destroyed_path(_requested_resource)
92
+ { action: :index }
93
+ end
94
+
95
+ def after_resource_created_path(requested_resource)
96
+ [namespace, requested_resource]
97
+ end
98
+
99
+ def after_resource_updated_path(requested_resource)
100
+ [namespace, requested_resource]
101
+ end
102
+
83
103
  helper_method :nav_link_state
84
104
  def nav_link_state(resource)
85
- resource_name.to_s.pluralize == resource.to_s ? :active : :inactive
105
+ underscore_resource = resource.to_s.split("/").join("__")
106
+ resource_name.to_s.pluralize == underscore_resource ? :active : :inactive
86
107
  end
87
108
 
88
- helper_method :valid_action?
89
- def valid_action?(name, resource = resource_class)
90
- !!routes.detect do |controller, action|
91
- controller == resource.to_s.underscore.pluralize && action == name.to_s
92
- end
109
+ # Whether the named action route exists for the resource class.
110
+ #
111
+ # @param resource [Class, String, Symbol] A class of resources, or the name
112
+ # of a class of resources.
113
+ # @param action_name [String, Symbol] The name of an action that might be
114
+ # possible to perform on a resource or resource class.
115
+ # @return [Boolean] `true` if a route exists for the resource class and the
116
+ # action. `false` otherwise.
117
+ def existing_action?(resource, action_name)
118
+ routes.include?([resource.to_s.underscore.pluralize, action_name.to_s])
93
119
  end
120
+ helper_method :existing_action?
121
+
122
+ # @deprecated Use {#existing_action} instead. Note that, in
123
+ # {#existing_action}, the order of parameters is reversed and
124
+ # there is no default value for the `resource` parameter.
125
+ def valid_action?(action_name, resource = resource_class)
126
+ Administrate.warn_of_deprecated_authorization_method(__method__)
127
+ existing_action?(resource, action_name)
128
+ end
129
+ helper_method :valid_action?
94
130
 
95
131
  def routes
96
- @routes ||= Namespace.new(namespace).routes
132
+ @routes ||= Namespace.new(namespace).routes.to_set
97
133
  end
98
134
 
99
135
  def records_per_page
@@ -102,11 +138,44 @@ module Administrate
102
138
 
103
139
  def order
104
140
  @order ||= Administrate::Order.new(
105
- params.fetch(resource_name, {}).fetch(:order, nil),
106
- params.fetch(resource_name, {}).fetch(:direction, nil),
141
+ sorting_attribute,
142
+ sorting_direction,
143
+ association_attribute: order_by_field(
144
+ dashboard_attribute(sorting_attribute),
145
+ ),
107
146
  )
108
147
  end
109
148
 
149
+ def order_by_field(dashboard)
150
+ return unless dashboard.try(:options)
151
+
152
+ dashboard.options.fetch(:order, nil)
153
+ end
154
+
155
+ def dashboard_attribute(attribute)
156
+ dashboard.attribute_types[attribute.to_sym] if attribute
157
+ end
158
+
159
+ def sorting_attribute
160
+ sorting_params.fetch(:order) { default_sorting_attribute }
161
+ end
162
+
163
+ def default_sorting_attribute
164
+ nil
165
+ end
166
+
167
+ def sorting_direction
168
+ sorting_params.fetch(:direction) { default_sorting_direction }
169
+ end
170
+
171
+ def default_sorting_direction
172
+ nil
173
+ end
174
+
175
+ def sorting_params
176
+ Hash.try_convert(request.query_parameters[resource_name]) || {}
177
+ end
178
+
110
179
  def dashboard
111
180
  @dashboard ||= dashboard_class.new
112
181
  end
@@ -133,7 +202,7 @@ module Administrate
133
202
 
134
203
  def resource_params
135
204
  params.require(resource_class.model_name.param_key).
136
- permit(dashboard.permitted_attributes).
205
+ permit(dashboard.permitted_attributes(action_name)).
137
206
  transform_values { |v| read_param_value(v) }
138
207
  end
139
208
 
@@ -144,6 +213,10 @@ module Administrate
144
213
  else
145
214
  raise "Unrecognised param data: #{data.inspect}"
146
215
  end
216
+ elsif data.is_a?(ActionController::Parameters)
217
+ data.transform_values { |v| read_param_value(v) }
218
+ elsif data.is_a?(String) && data.blank?
219
+ nil
147
220
  else
148
221
  data
149
222
  end
@@ -153,6 +226,7 @@ module Administrate
153
226
  to: :resource_resolver
154
227
  helper_method :namespace
155
228
  helper_method :resource_name
229
+ helper_method :resource_class
156
230
 
157
231
  def resource_resolver
158
232
  @resource_resolver ||=
@@ -172,18 +246,46 @@ module Administrate
172
246
  ).any? { |_name, attribute| attribute.searchable? }
173
247
  end
174
248
 
175
- def show_action?(_action, _resource)
249
+ # Whether the current user is authorized to perform the named action on the
250
+ # resource.
251
+ #
252
+ # @param _resource [ActiveRecord::Base, Class, String, Symbol] The
253
+ # temptative target of the action, or the name of its class.
254
+ # @param _action_name [String, Symbol] The name of an action that might be
255
+ # possible to perform on a resource or resource class.
256
+ # @return [Boolean] `true` if the current user is authorized to perform the
257
+ # action on the resource. `false` otherwise.
258
+ def authorized_action?(_resource, _action_name)
176
259
  true
177
260
  end
261
+ helper_method :authorized_action?
262
+
263
+ # @deprecated Use {#authorized_action} instead. Note that the order of
264
+ # parameters is reversed in {#authorized_action}.
265
+ def show_action?(action, resource)
266
+ Administrate.warn_of_deprecated_authorization_method(__method__)
267
+ authorized_action?(resource, action)
268
+ end
178
269
  helper_method :show_action?
179
270
 
180
- def new_resource
181
- resource_class.new
271
+ def new_resource(params = {})
272
+ resource_class.new(params)
182
273
  end
183
274
  helper_method :new_resource
184
275
 
185
276
  def authorize_resource(resource)
186
- resource
277
+ if authorized_action?(resource, action_name)
278
+ resource
279
+ else
280
+ raise Administrate::NotAuthorizedError.new(
281
+ action: action_name,
282
+ resource: resource,
283
+ )
284
+ end
285
+ end
286
+
287
+ def paginate_resources(resources)
288
+ resources.page(params[:_page]).per(records_per_page)
187
289
  end
188
290
  end
189
291
  end
@@ -2,35 +2,59 @@ module Administrate
2
2
  module Punditize
3
3
  if Object.const_defined?("Pundit")
4
4
  extend ActiveSupport::Concern
5
- include Pundit
6
5
 
7
- included do
8
- def scoped_resource
9
- policy_scope_admin super
10
- end
6
+ if Pundit.const_defined?(:Authorization)
7
+ include Pundit::Authorization
8
+ else
9
+ include Pundit
10
+ end
11
11
 
12
- def authorize_resource(resource)
13
- authorize resource
14
- end
12
+ private
15
13
 
16
- def show_action?(action, resource)
17
- Pundit.policy!(pundit_user, resource).send("#{action}?".to_sym)
18
- end
14
+ def policy_namespace
15
+ []
19
16
  end
20
17
 
21
- private
18
+ def scoped_resource
19
+ namespaced_scope = policy_namespace + [super]
20
+ policy_scope!(pundit_user, namespaced_scope)
21
+ end
22
+
23
+ def authorize_resource(resource)
24
+ namespaced_resource = policy_namespace + [resource]
25
+ authorize namespaced_resource
26
+ end
27
+
28
+ def authorized_action?(resource, action)
29
+ namespaced_resource = policy_namespace + [resource]
30
+ policy = Pundit.policy!(pundit_user, namespaced_resource)
31
+ policy.send("#{action}?".to_sym)
32
+ end
33
+
34
+ def policy_scope!(user, scope)
35
+ policy_scope_class = Pundit::PolicyFinder.new(scope).scope!
22
36
 
23
- # Like the policy_scope method in stock Pundit, but allows the 'resolve'
24
- # to be overridden by 'resolve_admin' for a different index scope in Admin
25
- # controllers.
26
- def policy_scope_admin(scope)
27
- ps = Pundit::PolicyFinder.new(scope).scope!.new(pundit_user, scope)
28
- if ps.respond_to? :resolve_admin
29
- ps.resolve_admin
37
+ begin
38
+ policy_scope = policy_scope_class.new(user, pundit_model(scope))
39
+ rescue ArgumentError
40
+ raise(Pundit::InvalidConstructorError,
41
+ "Invalid #<#{policy_scope_class}> constructor is called")
42
+ end
43
+
44
+ if policy_scope.respond_to? :resolve_admin
45
+ Administrate.deprecator.warn(
46
+ "Pundit policy scope `resolve_admin` method is deprecated. " +
47
+ "Please use a namespaced pundit policy instead.",
48
+ )
49
+ policy_scope.resolve_admin
30
50
  else
31
- ps.resolve
51
+ policy_scope.resolve
32
52
  end
33
53
  end
54
+
55
+ def pundit_model(record)
56
+ record.is_a?(Array) ? record.last : record
57
+ end
34
58
  end
35
59
  end
36
60
  end