administrate 0.12.0 → 0.20.1

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