activeadmin 2.6.1 → 3.2.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +438 -9
- data/CONTRIBUTING.md +12 -36
- data/README.md +8 -8
- data/app/assets/javascripts/active_admin/base.js +27 -25
- data/app/assets/stylesheets/active_admin/_base.scss +53 -37
- data/app/assets/stylesheets/active_admin/_forms.scss +2 -13
- data/app/assets/stylesheets/active_admin/_header.scss +37 -3
- data/app/assets/stylesheets/active_admin/_normalize.scss +25 -123
- data/app/assets/stylesheets/active_admin/components/_comments.scss +2 -2
- data/app/assets/stylesheets/active_admin/components/_date_picker.scss +1 -2
- data/app/assets/stylesheets/active_admin/components/_dropdown_menu.scss +7 -5
- data/app/assets/stylesheets/active_admin/components/_pagination.scss +5 -2
- data/app/assets/stylesheets/active_admin/components/_table_tools.scss +9 -6
- data/app/assets/stylesheets/active_admin/components/_tabs.scss +10 -7
- data/app/assets/stylesheets/active_admin/mixins/_buttons.scss +2 -2
- data/app/assets/stylesheets/active_admin/mixins/_variables.scss +12 -0
- data/app/assets/stylesheets/active_admin/pages/_logged_out.scss +1 -1
- data/app/assets/stylesheets/active_admin/structure/_footer.scss +6 -1
- data/app/assets/stylesheets/active_admin/structure/_title_bar.scss +6 -4
- data/app/javascript/active_admin/initializers/has-many.js +4 -1
- data/app/javascript/active_admin/initializers/per-page.js +1 -1
- data/app/javascript/active_admin/lib/checkbox-toggler.js +3 -3
- data/app/javascript/active_admin/lib/dropdown-menu.js +1 -1
- data/app/javascript/active_admin/lib/modal-dialog.js +7 -7
- data/app/javascript/active_admin/lib/per-page.js +1 -1
- data/app/javascript/active_admin/lib/table-checkbox-toggler.js +1 -1
- data/app/views/active_admin/devise/mailer/reset_password_instructions.html.erb +1 -1
- data/app/views/active_admin/devise/mailer/unlock_instructions.html.erb +2 -2
- data/app/views/active_admin/devise/shared/_links.erb +1 -1
- data/app/views/active_admin/page/index.html.arb +1 -0
- data/app/views/active_admin/resource/edit.html.arb +1 -0
- data/app/views/active_admin/resource/index.html.arb +1 -0
- data/app/views/active_admin/resource/new.html.arb +1 -0
- data/app/views/active_admin/resource/show.html.arb +1 -0
- data/app/views/layouts/active_admin.html.arb +1 -0
- data/app/views/layouts/active_admin_logged_out.html.erb +18 -7
- data/config/locales/ar.yml +6 -13
- data/config/locales/az.yml +0 -7
- data/config/locales/bg.yml +0 -7
- data/config/locales/bs.yml +0 -7
- data/config/locales/ca.yml +0 -7
- data/config/locales/cs.yml +0 -7
- data/config/locales/da.yml +0 -7
- data/config/locales/de-CH.yml +0 -7
- data/config/locales/de.yml +0 -8
- data/config/locales/el.yml +0 -7
- data/config/locales/en-CA.yml +0 -7
- data/config/locales/en-GB.yml +0 -7
- data/config/locales/en.yml +0 -8
- data/config/locales/eo.yml +0 -8
- data/config/locales/es-MX.yml +2 -8
- data/config/locales/es.yml +2 -10
- data/config/locales/fa.yml +0 -7
- data/config/locales/fi.yml +0 -7
- data/config/locales/fr.yml +3 -11
- data/config/locales/he.yml +0 -9
- data/config/locales/hr.yml +0 -7
- data/config/locales/hu.yml +0 -8
- data/config/locales/id.yml +0 -7
- data/config/locales/it.yml +18 -8
- data/config/locales/ja.yml +2 -10
- data/config/locales/ko.yml +1 -8
- data/config/locales/lt.yml +0 -8
- data/config/locales/lv.yml +0 -7
- data/config/locales/mk.yml +0 -8
- data/config/locales/nb.yml +0 -8
- data/config/locales/nl.yml +1 -9
- data/config/locales/pl.yml +0 -8
- data/config/locales/pt-BR.yml +0 -8
- data/config/locales/pt-PT.yml +0 -7
- data/config/locales/ro.yml +3 -9
- data/config/locales/ru.yml +0 -6
- data/config/locales/sk.yml +0 -8
- data/config/locales/sv-SE.yml +58 -39
- data/config/locales/tr.yml +0 -11
- data/config/locales/uk.yml +0 -6
- data/config/locales/vi.yml +38 -19
- data/config/locales/zh-CN.yml +34 -23
- data/config/locales/zh-TW.yml +0 -7
- data/lib/active_admin/abstract_view_factory.rb +1 -0
- data/lib/active_admin/application.rb +19 -19
- data/lib/active_admin/application_settings.rb +4 -3
- data/lib/active_admin/asset_registration.rb +4 -3
- data/lib/active_admin/authorization_adapter.rb +6 -3
- data/lib/active_admin/base_controller/authorization.rb +15 -13
- data/lib/active_admin/base_controller/menu.rb +1 -0
- data/lib/active_admin/base_controller.rb +6 -5
- data/lib/active_admin/batch_actions/controller.rb +4 -3
- data/lib/active_admin/batch_actions/resource_extension.rb +10 -8
- data/lib/active_admin/batch_actions/views/batch_action_form.rb +4 -3
- data/lib/active_admin/batch_actions/views/batch_action_selector.rb +7 -6
- data/lib/active_admin/batch_actions/views/selection_cells.rb +4 -3
- data/lib/active_admin/batch_actions.rb +1 -0
- data/lib/active_admin/callbacks.rb +1 -0
- data/lib/active_admin/cancan_adapter.rb +2 -1
- data/lib/active_admin/collection_decorator.rb +32 -0
- data/lib/active_admin/component.rb +1 -0
- data/lib/active_admin/controller_action.rb +1 -0
- data/lib/active_admin/csv_builder.rb +25 -17
- data/lib/active_admin/dependency.rb +12 -15
- data/lib/active_admin/devise.rb +16 -5
- data/lib/active_admin/dsl.rb +2 -1
- data/lib/active_admin/dynamic_setting.rb +1 -0
- data/lib/active_admin/dynamic_settings_node.rb +3 -2
- data/lib/active_admin/engine.rb +17 -9
- data/lib/active_admin/error.rb +1 -2
- data/lib/active_admin/filters/active.rb +2 -1
- data/lib/active_admin/filters/active_filter.rb +6 -6
- data/lib/active_admin/filters/active_sidebar.rb +4 -30
- data/lib/active_admin/filters/dsl.rb +1 -0
- data/lib/active_admin/filters/forms.rb +9 -8
- data/lib/active_admin/filters/formtastic_addons.rb +3 -7
- data/lib/active_admin/filters/resource_extension.rb +6 -5
- data/lib/active_admin/filters.rb +8 -7
- data/lib/active_admin/form_builder.rb +25 -20
- data/lib/active_admin/generators/boilerplate.rb +2 -1
- data/lib/active_admin/helpers/collection.rb +2 -0
- data/lib/active_admin/helpers/i18n.rb +1 -0
- data/lib/active_admin/helpers/optional_display.rb +3 -2
- data/lib/active_admin/helpers/routes/url_helpers.rb +1 -0
- data/lib/active_admin/helpers/scope_chain.rb +1 -0
- data/lib/active_admin/inputs/datepicker_input.rb +2 -1
- data/lib/active_admin/inputs/filters/base/search_method_select.rb +7 -6
- data/lib/active_admin/inputs/filters/base.rb +2 -1
- data/lib/active_admin/inputs/filters/boolean_input.rb +2 -1
- data/lib/active_admin/inputs/filters/check_boxes_input.rb +2 -1
- data/lib/active_admin/inputs/filters/date_picker_input.rb +1 -0
- data/lib/active_admin/inputs/filters/date_range_input.rb +3 -2
- data/lib/active_admin/inputs/filters/numeric_input.rb +2 -1
- data/lib/active_admin/inputs/filters/select_input.rb +5 -2
- data/lib/active_admin/inputs/filters/string_input.rb +2 -1
- data/lib/active_admin/inputs/filters/text_input.rb +1 -0
- data/lib/active_admin/inputs.rb +1 -0
- data/lib/active_admin/localizers/resource_localizer.rb +4 -3
- data/lib/active_admin/localizers.rb +2 -1
- data/lib/active_admin/menu.rb +8 -3
- data/lib/active_admin/menu_collection.rb +1 -0
- data/lib/active_admin/menu_item.rb +8 -7
- data/lib/active_admin/namespace.rb +16 -15
- data/lib/active_admin/namespace_settings.rb +9 -5
- data/lib/active_admin/order_clause.rb +2 -1
- data/lib/active_admin/orm/active_record/comments/comment.rb +12 -3
- data/lib/active_admin/orm/active_record/comments/namespace_helper.rb +1 -0
- data/lib/active_admin/orm/active_record/comments/resource_helper.rb +1 -0
- data/lib/active_admin/orm/active_record/comments/show_page_helper.rb +1 -0
- data/lib/active_admin/orm/active_record/comments/views/active_admin_comments.rb +21 -20
- data/lib/active_admin/orm/active_record/comments/views.rb +3 -2
- data/lib/active_admin/orm/active_record/comments.rb +28 -27
- data/lib/active_admin/orm/active_record.rb +2 -1
- data/lib/active_admin/orm/mongoid.rb +1 -0
- data/lib/active_admin/page.rb +2 -1
- data/lib/active_admin/page_controller.rb +1 -0
- data/lib/active_admin/page_dsl.rb +1 -0
- data/lib/active_admin/page_presenter.rb +1 -0
- data/lib/active_admin/pundit_adapter.rb +58 -16
- data/lib/active_admin/resource/action_items.rb +6 -5
- data/lib/active_admin/resource/attributes.rb +9 -1
- data/lib/active_admin/resource/belongs_to.rb +3 -2
- data/lib/active_admin/resource/controllers.rb +2 -1
- data/lib/active_admin/resource/includes.rb +1 -0
- data/lib/active_admin/resource/menu.rb +5 -4
- data/lib/active_admin/resource/model.rb +1 -0
- data/lib/active_admin/resource/naming.rb +5 -4
- data/lib/active_admin/resource/ordering.rb +1 -0
- data/lib/active_admin/resource/page_presenters.rb +1 -0
- data/lib/active_admin/resource/pagination.rb +1 -0
- data/lib/active_admin/resource/routes.rb +6 -7
- data/lib/active_admin/resource/scope_to.rb +8 -7
- data/lib/active_admin/resource/scopes.rb +1 -0
- data/lib/active_admin/resource/sidebars.rb +2 -1
- data/lib/active_admin/resource.rb +20 -19
- data/lib/active_admin/resource_collection.rb +1 -0
- data/lib/active_admin/resource_controller/action_builder.rb +1 -0
- data/lib/active_admin/resource_controller/data_access.rb +31 -5
- data/lib/active_admin/resource_controller/decorators.rb +7 -28
- data/lib/active_admin/resource_controller/polymorphic_routes.rb +1 -0
- data/lib/active_admin/resource_controller/resource_class_methods.rb +1 -0
- data/lib/active_admin/resource_controller/scoping.rb +1 -0
- data/lib/active_admin/resource_controller/sidebars.rb +1 -0
- data/lib/active_admin/resource_controller/streaming.rb +9 -7
- data/lib/active_admin/resource_controller.rb +13 -11
- data/lib/active_admin/resource_dsl.rb +11 -25
- data/lib/active_admin/router.rb +1 -0
- data/lib/active_admin/scope.rb +7 -6
- data/lib/active_admin/settings_node.rb +1 -0
- data/lib/active_admin/sidebar_section.rb +1 -0
- data/lib/active_admin/version.rb +2 -1
- data/lib/active_admin/view_factory.rb +18 -17
- data/lib/active_admin/view_helpers/active_admin_application_helper.rb +1 -0
- data/lib/active_admin/view_helpers/auto_link_helper.rb +2 -1
- data/lib/active_admin/view_helpers/breadcrumb_helper.rb +4 -3
- data/lib/active_admin/view_helpers/display_helper.rb +23 -8
- data/lib/active_admin/view_helpers/download_format_links_helper.rb +2 -1
- data/lib/active_admin/view_helpers/fields_for.rb +3 -2
- data/lib/active_admin/view_helpers/flash_helper.rb +1 -0
- data/lib/active_admin/view_helpers/form_helper.rb +1 -0
- data/lib/active_admin/view_helpers/method_or_proc_helper.rb +1 -0
- data/lib/active_admin/view_helpers/scope_name_helper.rb +1 -0
- data/lib/active_admin/view_helpers/sidebar_helper.rb +1 -0
- data/lib/active_admin/view_helpers/title_helper.rb +1 -0
- data/lib/active_admin/view_helpers/view_factory_helper.rb +1 -0
- data/lib/active_admin/view_helpers.rb +2 -1
- data/lib/active_admin/views/action_items.rb +1 -0
- data/lib/active_admin/views/components/active_admin_form.rb +8 -11
- data/lib/active_admin/views/components/active_filters_sidebar_content.rb +59 -0
- data/lib/active_admin/views/components/attributes_table.rb +6 -5
- data/lib/active_admin/views/components/blank_slate.rb +2 -1
- data/lib/active_admin/views/components/columns.rb +1 -0
- data/lib/active_admin/views/components/dropdown_menu.rb +7 -9
- data/lib/active_admin/views/components/index_list.rb +4 -3
- data/lib/active_admin/views/components/menu.rb +2 -1
- data/lib/active_admin/views/components/menu_item.rb +5 -4
- data/lib/active_admin/views/components/paginated_collection.rb +19 -18
- data/lib/active_admin/views/components/panel.rb +2 -1
- data/lib/active_admin/views/components/scopes.rb +8 -5
- data/lib/active_admin/views/components/sidebar.rb +1 -0
- data/lib/active_admin/views/components/sidebar_section.rb +1 -0
- data/lib/active_admin/views/components/site_title.rb +2 -1
- data/lib/active_admin/views/components/status_tag.rb +12 -11
- data/lib/active_admin/views/components/table_for.rb +18 -17
- data/lib/active_admin/views/components/tabs.rb +4 -3
- data/lib/active_admin/views/components/unsupported_browser.rb +1 -0
- data/lib/active_admin/views/footer.rb +3 -1
- data/lib/active_admin/views/header.rb +3 -2
- data/lib/active_admin/views/index_as_block.rb +1 -0
- data/lib/active_admin/views/index_as_blog.rb +2 -1
- data/lib/active_admin/views/index_as_grid.rb +2 -1
- data/lib/active_admin/views/index_as_table.rb +16 -19
- data/lib/active_admin/views/pages/base.rb +17 -11
- data/lib/active_admin/views/pages/form.rb +1 -0
- data/lib/active_admin/views/pages/index.rb +15 -13
- data/lib/active_admin/views/pages/layout.rb +1 -0
- data/lib/active_admin/views/pages/page.rb +1 -0
- data/lib/active_admin/views/pages/show.rb +2 -7
- data/lib/active_admin/views/tabbed_navigation.rb +3 -2
- data/lib/active_admin/views/title_bar.rb +2 -1
- data/lib/active_admin/views.rb +2 -1
- data/lib/active_admin.rb +63 -61
- data/lib/activeadmin.rb +2 -1
- data/lib/generators/active_admin/assets/assets_generator.rb +3 -2
- data/lib/generators/active_admin/assets/templates/active_admin.scss +2 -2
- data/lib/generators/active_admin/devise/devise_generator.rb +6 -5
- data/lib/generators/active_admin/install/install_generator.rb +15 -8
- data/lib/generators/active_admin/install/templates/active_admin.rb.erb +27 -3
- data/lib/generators/active_admin/install/templates/dashboard.rb +1 -0
- data/lib/generators/active_admin/page/page_generator.rb +2 -1
- data/lib/generators/active_admin/page/templates/page.rb +1 -0
- data/lib/generators/active_admin/resource/resource_generator.rb +4 -3
- data/lib/generators/active_admin/webpacker/plugins/jquery.js +7 -0
- data/lib/generators/active_admin/webpacker/templates/active_admin.js +5 -0
- data/lib/generators/active_admin/webpacker/templates/active_admin.scss +17 -0
- data/lib/generators/active_admin/webpacker/templates/print.scss +2 -0
- data/lib/generators/active_admin/webpacker/webpacker_generator.rb +27 -0
- data/vendor/assets/javascripts/jquery-ui/data.js +12 -8
- data/vendor/assets/javascripts/jquery-ui/disable-selection.js +10 -7
- data/vendor/assets/javascripts/jquery-ui/focusable.js +12 -9
- data/vendor/assets/javascripts/jquery-ui/form-reset-mixin.js +60 -57
- data/vendor/assets/javascripts/jquery-ui/form.js +15 -12
- data/vendor/assets/javascripts/jquery-ui/ie.js +5 -2
- data/vendor/assets/javascripts/jquery-ui/keycode.js +11 -7
- data/vendor/assets/javascripts/jquery-ui/labels.js +46 -40
- data/vendor/assets/javascripts/jquery-ui/plugin.js +5 -2
- data/vendor/assets/javascripts/jquery-ui/position.js +30 -17
- data/vendor/assets/javascripts/jquery-ui/safe-active-element.js +6 -2
- data/vendor/assets/javascripts/jquery-ui/safe-blur.js +6 -2
- data/vendor/assets/javascripts/jquery-ui/scroll-parent.js +10 -7
- data/vendor/assets/javascripts/jquery-ui/tabbable.js +11 -8
- data/vendor/assets/javascripts/jquery-ui/unique-id.js +10 -7
- data/vendor/assets/javascripts/jquery-ui/version.js +6 -3
- data/vendor/assets/javascripts/jquery-ui/widget.js +53 -30
- data/vendor/assets/javascripts/jquery-ui/widgets/button.js +87 -24
- data/vendor/assets/javascripts/jquery-ui/widgets/checkboxradio.js +276 -273
- data/vendor/assets/javascripts/jquery-ui/widgets/controlgroup.js +15 -11
- data/vendor/assets/javascripts/jquery-ui/widgets/datepicker.js +182 -62
- data/vendor/assets/javascripts/jquery-ui/widgets/dialog.js +53 -36
- data/vendor/assets/javascripts/jquery-ui/widgets/draggable.js +28 -19
- data/vendor/assets/javascripts/jquery-ui/widgets/mouse.js +22 -11
- data/vendor/assets/javascripts/jquery-ui/widgets/resizable.js +47 -26
- data/vendor/assets/javascripts/jquery-ui/widgets/sortable.js +186 -125
- data/vendor/assets/javascripts/jquery-ui/widgets/tabs.js +20 -20
- metadata +48 -114
- data/docs/.gitignore +0 -1
- data/docs/0-installation.md +0 -118
- data/docs/1-general-configuration.md +0 -224
- data/docs/10-custom-pages.md +0 -150
- data/docs/11-decorators.md +0 -59
- data/docs/12-arbre-components.md +0 -214
- data/docs/13-authorization-adapter.md +0 -285
- data/docs/14-gotchas.md +0 -138
- data/docs/2-resource-customization.md +0 -466
- data/docs/3-index-pages/custom-index.md +0 -35
- data/docs/3-index-pages/index-as-block.md +0 -19
- data/docs/3-index-pages/index-as-blog.md +0 -69
- data/docs/3-index-pages/index-as-grid.md +0 -27
- data/docs/3-index-pages/index-as-table.md +0 -234
- data/docs/3-index-pages.md +0 -328
- data/docs/4-csv-format.md +0 -74
- data/docs/5-forms.md +0 -232
- data/docs/6-show-pages.md +0 -81
- data/docs/7-sidebars.md +0 -75
- data/docs/8-custom-actions.md +0 -177
- data/docs/9-batch-actions.md +0 -237
- data/docs/CNAME +0 -1
- data/docs/Gemfile +0 -3
- data/docs/Gemfile.lock +0 -248
- data/docs/README.md +0 -24
- data/docs/_config.yml +0 -4
- data/docs/_includes/footer.html +0 -8
- data/docs/_includes/google-analytics.html +0 -16
- data/docs/_includes/head.html +0 -7
- data/docs/_includes/toc.html +0 -98
- data/docs/_includes/top-menu.html +0 -17
- data/docs/_layouts/default.html +0 -21
- data/docs/documentation.md +0 -60
- data/docs/images/activeadmin.png +0 -0
- data/docs/images/code-header.png +0 -0
- data/docs/images/divider.png +0 -0
- data/docs/images/features.png +0 -0
- data/docs/images/tidelift.svg +0 -14
- data/docs/index.html +0 -226
- data/docs/stylesheets/main.css +0 -1205
- data/lib/active_admin/deprecation.rb +0 -10
- data/lib/ransack_ext.rb +0 -20
- data/vendor/assets/javascripts/jquery-ui/escape-selector.js +0 -23
- /data/app/assets/stylesheets/active_admin/{print.scss → _print.scss} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
# ActiveRecord-specific plugins should be required here
|
|
2
3
|
|
|
3
4
|
ActiveAdmin::DatabaseHitDuringLoad.database_error_classes << ActiveRecord::StatementInvalid
|
|
4
5
|
|
|
5
|
-
require
|
|
6
|
+
require "active_admin/orm/active_record/comments"
|
data/lib/active_admin/page.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ActiveAdmin
|
|
2
3
|
# Page is the primary data storage for page configuration in Active Admin
|
|
3
4
|
#
|
|
@@ -65,7 +66,7 @@ module ActiveAdmin
|
|
|
65
66
|
end
|
|
66
67
|
|
|
67
68
|
def controller_name
|
|
68
|
-
[namespace.module_name, camelized_resource_name + "Controller"].compact.join(
|
|
69
|
+
[namespace.module_name, camelized_resource_name + "Controller"].compact.join("::")
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
# Override from `ActiveAdmin::Resource::Controllers`
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
ActiveAdmin::Dependency.pundit!
|
|
2
3
|
|
|
3
|
-
require
|
|
4
|
+
require "pundit"
|
|
4
5
|
|
|
5
6
|
# Add a setting to the application to configure the pundit default policy
|
|
6
7
|
ActiveAdmin::Application.inheritable_setting :pundit_default_policy, nil
|
|
@@ -30,25 +31,20 @@ module ActiveAdmin
|
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
def retrieve_policy(subject)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
rescue Pundit::NotDefinedError => e
|
|
39
|
-
if default_policy_class
|
|
40
|
-
default_policy(user, subject)
|
|
34
|
+
target = policy_target(subject)
|
|
35
|
+
if (policy = policy(namespace(target)) || compat_policy(subject))
|
|
36
|
+
policy
|
|
37
|
+
elsif default_policy_class
|
|
38
|
+
default_policy(subject)
|
|
41
39
|
else
|
|
42
|
-
raise
|
|
40
|
+
raise Pundit::NotDefinedError, "unable to find a compatible policy for `#{target}`"
|
|
43
41
|
end
|
|
44
42
|
end
|
|
45
43
|
|
|
46
44
|
def format_action(action, subject)
|
|
47
|
-
# https://github.com/varvet/pundit/blob/
|
|
45
|
+
# https://github.com/varvet/pundit/blob/main/lib/generators/pundit/install/templates/application_policy.rb
|
|
48
46
|
case action
|
|
49
|
-
when Auth::
|
|
50
|
-
when Auth::UPDATE then :update?
|
|
51
|
-
when Auth::READ then subject.is_a?(Class) ? :index? : :show?
|
|
47
|
+
when Auth::READ then subject.is_a?(Class) ? :index? : :show?
|
|
52
48
|
when Auth::DESTROY then subject.is_a?(Class) ? :destroy_all? : :destroy?
|
|
53
49
|
else "#{action}?"
|
|
54
50
|
end
|
|
@@ -56,8 +52,42 @@ module ActiveAdmin
|
|
|
56
52
|
|
|
57
53
|
private
|
|
58
54
|
|
|
55
|
+
def policy_target(subject)
|
|
56
|
+
case subject
|
|
57
|
+
when nil then resource
|
|
58
|
+
when Class then subject.new
|
|
59
|
+
else subject
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# This method is needed to fallback to our previous policy searching logic.
|
|
64
|
+
# I.e.: when class name contains `default_policy_namespace` (eg: ShopAdmin)
|
|
65
|
+
# we should try to search it without namespace. This is because that's
|
|
66
|
+
# the only thing that worked in this case before we fixed our buggy namespace
|
|
67
|
+
# detection, so people are probably relying on it.
|
|
68
|
+
# This fallback might be removed in future versions of ActiveAdmin, so
|
|
69
|
+
# pundit_adapter search will work consistently with provided namespaces
|
|
70
|
+
def compat_policy(subject)
|
|
71
|
+
return unless default_policy_namespace
|
|
72
|
+
|
|
73
|
+
target = policy_target(subject)
|
|
74
|
+
|
|
75
|
+
return unless target.class.to_s.include?(default_policy_module) &&
|
|
76
|
+
(policy = policy(target))
|
|
77
|
+
|
|
78
|
+
policy_name = policy.class.to_s
|
|
79
|
+
|
|
80
|
+
ActiveAdmin.deprecator.warn "You have `pundit_policy_namespace` configured as `#{default_policy_namespace}`, " \
|
|
81
|
+
"but ActiveAdmin was unable to find policy #{default_policy_module}::#{policy_name}. " \
|
|
82
|
+
"#{policy_name} will be used instead. " \
|
|
83
|
+
"This behavior will be removed in future versions of ActiveAdmin. " \
|
|
84
|
+
"To fix this warning, move your #{policy_name} policy to the #{default_policy_module} namespace"
|
|
85
|
+
|
|
86
|
+
policy
|
|
87
|
+
end
|
|
88
|
+
|
|
59
89
|
def namespace(object)
|
|
60
|
-
if default_policy_namespace && !object.class.to_s.
|
|
90
|
+
if default_policy_namespace && !object.class.to_s.start_with?("#{default_policy_module}::")
|
|
61
91
|
[default_policy_namespace.to_sym, object]
|
|
62
92
|
else
|
|
63
93
|
object
|
|
@@ -68,7 +98,7 @@ module ActiveAdmin
|
|
|
68
98
|
ActiveAdmin.application.pundit_default_policy && ActiveAdmin.application.pundit_default_policy.constantize
|
|
69
99
|
end
|
|
70
100
|
|
|
71
|
-
def default_policy(
|
|
101
|
+
def default_policy(subject)
|
|
72
102
|
default_policy_class.new(user, subject)
|
|
73
103
|
end
|
|
74
104
|
|
|
@@ -76,6 +106,18 @@ module ActiveAdmin
|
|
|
76
106
|
ActiveAdmin.application.pundit_policy_namespace
|
|
77
107
|
end
|
|
78
108
|
|
|
109
|
+
def default_policy_module
|
|
110
|
+
default_policy_namespace.to_s.camelize
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def policy(target)
|
|
114
|
+
policies[target] ||= Pundit.policy(user, target)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def policies
|
|
118
|
+
@policies ||= {}
|
|
119
|
+
end
|
|
120
|
+
|
|
79
121
|
end
|
|
80
122
|
|
|
81
123
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "active_admin/helpers/optional_display"
|
|
2
3
|
|
|
3
4
|
module ActiveAdmin
|
|
4
5
|
|
|
@@ -64,7 +65,7 @@ module ActiveAdmin
|
|
|
64
65
|
# Adds the default New link on index
|
|
65
66
|
def add_default_new_action_item
|
|
66
67
|
add_action_item :new, only: :index do
|
|
67
|
-
if controller.action_methods.include?(
|
|
68
|
+
if controller.action_methods.include?("new") && authorized?(ActiveAdmin::Auth::NEW, active_admin_config.resource_class)
|
|
68
69
|
localizer = ActiveAdmin::Localizers.resource(active_admin_config)
|
|
69
70
|
link_to localizer.t(:new_model), new_resource_path
|
|
70
71
|
end
|
|
@@ -74,7 +75,7 @@ module ActiveAdmin
|
|
|
74
75
|
# Adds the default Edit link on show
|
|
75
76
|
def add_default_edit_action_item
|
|
76
77
|
add_action_item :edit, only: :show do
|
|
77
|
-
if controller.action_methods.include?(
|
|
78
|
+
if controller.action_methods.include?("edit") && authorized?(ActiveAdmin::Auth::EDIT, resource)
|
|
78
79
|
localizer = ActiveAdmin::Localizers.resource(active_admin_config)
|
|
79
80
|
link_to localizer.t(:edit_model), edit_resource_path(resource)
|
|
80
81
|
end
|
|
@@ -84,10 +85,10 @@ module ActiveAdmin
|
|
|
84
85
|
# Adds the default Destroy link on show
|
|
85
86
|
def add_default_show_action_item
|
|
86
87
|
add_action_item :destroy, only: :show do
|
|
87
|
-
if controller.action_methods.include?(
|
|
88
|
+
if controller.action_methods.include?("destroy") && authorized?(ActiveAdmin::Auth::DESTROY, resource)
|
|
88
89
|
localizer = ActiveAdmin::Localizers.resource(active_admin_config)
|
|
89
90
|
link_to localizer.t(:delete_model), resource_path(resource), method: :delete,
|
|
90
|
-
|
|
91
|
+
data: { confirm: localizer.t(:delete_confirmation) }
|
|
91
92
|
end
|
|
92
93
|
end
|
|
93
94
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ActiveAdmin
|
|
2
3
|
|
|
3
4
|
class Resource
|
|
@@ -36,7 +37,14 @@ module ActiveAdmin
|
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
def counter_cache_col?(c)
|
|
39
|
-
|
|
40
|
+
# This helper is called inside a loop. Let's memoize the result.
|
|
41
|
+
@counter_cache_columns ||= begin
|
|
42
|
+
resource_class.reflect_on_all_associations(:has_many)
|
|
43
|
+
.select(&:has_cached_counter?)
|
|
44
|
+
.map(&:counter_cache_column)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@counter_cache_columns.include?(c.name)
|
|
40
48
|
end
|
|
41
49
|
|
|
42
50
|
def filtered_col?(c)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "active_admin/resource"
|
|
2
3
|
|
|
3
4
|
module ActiveAdmin
|
|
4
5
|
class Resource
|
|
@@ -46,7 +47,7 @@ module ActiveAdmin
|
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
def to_param
|
|
49
|
-
:"#{@target_name}_id"
|
|
50
|
+
(@options[:param] || "#{@target_name}_id").to_sym
|
|
50
51
|
end
|
|
51
52
|
end
|
|
52
53
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ActiveAdmin
|
|
2
3
|
class Resource
|
|
3
4
|
module Controllers
|
|
@@ -6,7 +7,7 @@ module ActiveAdmin
|
|
|
6
7
|
# Returns a properly formatted controller name for this
|
|
7
8
|
# config within its namespace
|
|
8
9
|
def controller_name
|
|
9
|
-
[namespace.module_name, resource_name.plural.camelize + "Controller"].compact.join(
|
|
10
|
+
[namespace.module_name, resource_name.plural.camelize + "Controller"].compact.join("::")
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
# Returns the controller for this config
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ActiveAdmin
|
|
2
3
|
class Resource
|
|
3
4
|
|
|
@@ -7,12 +8,12 @@ module ActiveAdmin
|
|
|
7
8
|
# To disable this menu item, call `menu(false)` from the DSL
|
|
8
9
|
def menu_item_options=(options)
|
|
9
10
|
if options == false
|
|
10
|
-
@include_in_menu
|
|
11
|
+
@include_in_menu = false
|
|
11
12
|
@menu_item_options = {}
|
|
12
13
|
else
|
|
13
14
|
@include_in_menu = true
|
|
14
15
|
@navigation_menu_name = options[:menu_name]
|
|
15
|
-
@menu_item_options
|
|
16
|
+
@menu_item_options = default_menu_options.merge options
|
|
16
17
|
end
|
|
17
18
|
end
|
|
18
19
|
|
|
@@ -27,8 +28,8 @@ module ActiveAdmin
|
|
|
27
28
|
{
|
|
28
29
|
id: resource_name.plural,
|
|
29
30
|
label: proc { resource.plural_resource_label },
|
|
30
|
-
url:
|
|
31
|
-
if:
|
|
31
|
+
url: proc { resource.route_collection_path(params, url_options) },
|
|
32
|
+
if: proc { authorized?(Auth::READ, menu_resource_class) }
|
|
32
33
|
}
|
|
33
34
|
end
|
|
34
35
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ActiveAdmin
|
|
2
3
|
class Resource
|
|
3
4
|
|
|
4
5
|
module Naming
|
|
5
6
|
def resource_name
|
|
6
7
|
@resource_name ||= begin
|
|
7
|
-
as = @options[:as].gsub /\s/,
|
|
8
|
+
as = @options[:as].gsub /\s/, "" if @options[:as]
|
|
8
9
|
|
|
9
10
|
if as || !resource_class.respond_to?(:model_name)
|
|
10
11
|
Name.new resource_class, as
|
|
@@ -17,13 +18,13 @@ module ActiveAdmin
|
|
|
17
18
|
# Returns the name to call this resource such as "Bank Account"
|
|
18
19
|
def resource_label
|
|
19
20
|
resource_name.translate count: 1,
|
|
20
|
-
|
|
21
|
+
default: resource_name.to_s.gsub("::", " ").titleize
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
# Returns the plural version of this resource such as "Bank Accounts"
|
|
24
25
|
def plural_resource_label(options = {})
|
|
25
|
-
defaults = { count:
|
|
26
|
-
|
|
26
|
+
defaults = { count: Helpers::I18n::PLURAL_MANY_COUNT,
|
|
27
|
+
default: resource_label.pluralize.titleize }
|
|
27
28
|
resource_name.translate defaults.merge options
|
|
28
29
|
end
|
|
29
30
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ActiveAdmin
|
|
2
3
|
class Resource
|
|
3
4
|
module Routes
|
|
@@ -42,8 +43,6 @@ module ActiveAdmin
|
|
|
42
43
|
config[:route_collection_name] == config[:route_instance_name]
|
|
43
44
|
end
|
|
44
45
|
|
|
45
|
-
private
|
|
46
|
-
|
|
47
46
|
class RouteBuilder
|
|
48
47
|
def initialize(resource)
|
|
49
48
|
@resource = resource
|
|
@@ -98,13 +97,13 @@ module ActiveAdmin
|
|
|
98
97
|
suffix = options[:suffix] || "path"
|
|
99
98
|
route = []
|
|
100
99
|
|
|
101
|
-
route << options[:action]
|
|
102
|
-
route << resource.route_prefix
|
|
100
|
+
route << options[:action] # "batch_action", "edit" or "new"
|
|
101
|
+
route << resource.route_prefix # "admin"
|
|
103
102
|
route << belongs_to_name if nested? # "category"
|
|
104
|
-
route << resource_path_name
|
|
105
|
-
route << suffix
|
|
103
|
+
route << resource_path_name # "posts" or "post"
|
|
104
|
+
route << suffix # "path" or "index path"
|
|
106
105
|
|
|
107
|
-
route.compact.join(
|
|
106
|
+
route.compact.join("_").to_sym # :admin_category_posts_path
|
|
108
107
|
end
|
|
109
108
|
|
|
110
109
|
# @return params to pass to instance path
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ActiveAdmin
|
|
2
3
|
class Resource
|
|
3
4
|
module ScopeTo
|
|
@@ -38,10 +39,10 @@ module ActiveAdmin
|
|
|
38
39
|
options = args.extract_options!
|
|
39
40
|
method = args.first
|
|
40
41
|
|
|
41
|
-
scope_to_config[:method]
|
|
42
|
-
scope_to_config[:association_method]
|
|
43
|
-
scope_to_config[:if]
|
|
44
|
-
scope_to_config[:unless]
|
|
42
|
+
scope_to_config[:method] = block || method
|
|
43
|
+
scope_to_config[:association_method] = options[:association_method]
|
|
44
|
+
scope_to_config[:if] = options[:if]
|
|
45
|
+
scope_to_config[:unless] = options[:unless]
|
|
45
46
|
|
|
46
47
|
end
|
|
47
48
|
|
|
@@ -55,10 +56,10 @@ module ActiveAdmin
|
|
|
55
56
|
|
|
56
57
|
def scope_to_config
|
|
57
58
|
@scope_to_config ||= {
|
|
58
|
-
method:
|
|
59
|
+
method: nil,
|
|
59
60
|
association_method: nil,
|
|
60
|
-
if:
|
|
61
|
-
unless:
|
|
61
|
+
if: nil,
|
|
62
|
+
unless: nil
|
|
62
63
|
}
|
|
63
64
|
end
|
|
64
65
|
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
require
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
11
|
-
require
|
|
12
|
-
require
|
|
13
|
-
require
|
|
14
|
-
require
|
|
15
|
-
require
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "active_admin/resource/action_items"
|
|
3
|
+
require "active_admin/resource/attributes"
|
|
4
|
+
require "active_admin/resource/controllers"
|
|
5
|
+
require "active_admin/resource/menu"
|
|
6
|
+
require "active_admin/resource/page_presenters"
|
|
7
|
+
require "active_admin/resource/pagination"
|
|
8
|
+
require "active_admin/resource/routes"
|
|
9
|
+
require "active_admin/resource/naming"
|
|
10
|
+
require "active_admin/resource/scopes"
|
|
11
|
+
require "active_admin/resource/includes"
|
|
12
|
+
require "active_admin/resource/scope_to"
|
|
13
|
+
require "active_admin/resource/sidebars"
|
|
14
|
+
require "active_admin/resource/belongs_to"
|
|
15
|
+
require "active_admin/resource/ordering"
|
|
16
|
+
require "active_admin/resource/model"
|
|
16
17
|
|
|
17
18
|
module ActiveAdmin
|
|
18
19
|
|
|
@@ -27,7 +28,7 @@ module ActiveAdmin
|
|
|
27
28
|
class Resource
|
|
28
29
|
|
|
29
30
|
# Event dispatched when a new resource is registered
|
|
30
|
-
RegisterEvent =
|
|
31
|
+
RegisterEvent = "active_admin.resource.register".freeze
|
|
31
32
|
|
|
32
33
|
# The namespace this config belongs to
|
|
33
34
|
attr_reader :namespace
|
|
@@ -44,7 +45,7 @@ module ActiveAdmin
|
|
|
44
45
|
# The default sort order to use in the controller
|
|
45
46
|
attr_writer :sort_order
|
|
46
47
|
def sort_order
|
|
47
|
-
@sort_order ||= (resource_class.respond_to?(:primary_key) ? resource_class.primary_key.to_s :
|
|
48
|
+
@sort_order ||= (resource_class.respond_to?(:primary_key) ? resource_class.primary_key.to_s : "id") + "_desc"
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
# Set the configuration for the CSV
|
|
@@ -98,11 +99,11 @@ module ActiveAdmin
|
|
|
98
99
|
# The class this resource wraps. If you register the Post model, Resource#resource_class
|
|
99
100
|
# will point to the Post class
|
|
100
101
|
def resource_class
|
|
101
|
-
|
|
102
|
+
resource_class_name.constantize
|
|
102
103
|
end
|
|
103
104
|
|
|
104
105
|
def decorator_class
|
|
105
|
-
|
|
106
|
+
decorator_class_name&.constantize
|
|
106
107
|
end
|
|
107
108
|
|
|
108
109
|
def resource_name_extension
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ActiveAdmin
|
|
2
3
|
class ResourceController < BaseController
|
|
3
4
|
|
|
@@ -23,9 +24,9 @@ module ActiveAdmin
|
|
|
23
24
|
|
|
24
25
|
COLLECTION_APPLIES = [
|
|
25
26
|
:authorization_scope,
|
|
26
|
-
:sorting,
|
|
27
27
|
:filtering,
|
|
28
28
|
:scoping,
|
|
29
|
+
:sorting,
|
|
29
30
|
:includes,
|
|
30
31
|
:pagination,
|
|
31
32
|
:collection_decorator
|
|
@@ -128,7 +129,7 @@ module ActiveAdmin
|
|
|
128
129
|
#
|
|
129
130
|
# @return [ActiveRecord::Base] An un-saved active record base object
|
|
130
131
|
def build_new_resource
|
|
131
|
-
scoped_collection.send(
|
|
132
|
+
apply_authorization_scope(scoped_collection).send(
|
|
132
133
|
method_for_build,
|
|
133
134
|
*resource_params.map { |params| params.slice(active_admin_config.resource_class.inheritance_column) }
|
|
134
135
|
)
|
|
@@ -249,21 +250,32 @@ module ActiveAdmin
|
|
|
249
250
|
end
|
|
250
251
|
|
|
251
252
|
def apply_pagination(chain)
|
|
253
|
+
# skip pagination if CSV format was requested
|
|
254
|
+
return chain if params["format"] == "csv"
|
|
252
255
|
# skip pagination if already was paginated by scope
|
|
253
256
|
return chain if chain.respond_to?(:total_pages)
|
|
254
|
-
page_method_name = Kaminari.config.page_method_name
|
|
255
257
|
page = params[Kaminari.config.param_name]
|
|
256
258
|
|
|
257
|
-
chain
|
|
259
|
+
paginate(chain, page, per_page)
|
|
258
260
|
end
|
|
259
261
|
|
|
260
262
|
def collection_applies(options = {})
|
|
261
|
-
only
|
|
263
|
+
only = Array(options.fetch(:only, COLLECTION_APPLIES))
|
|
262
264
|
except = Array(options.fetch(:except, []))
|
|
263
265
|
|
|
264
266
|
COLLECTION_APPLIES & only - except
|
|
265
267
|
end
|
|
266
268
|
|
|
269
|
+
def in_paginated_batches(&block)
|
|
270
|
+
ActiveRecord::Base.uncached do
|
|
271
|
+
(1..paginated_collection.total_pages).each do |page|
|
|
272
|
+
paginated_collection(page).each do |resource|
|
|
273
|
+
yield apply_decorator(resource)
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
267
279
|
def per_page
|
|
268
280
|
if active_admin_config.paginate
|
|
269
281
|
dynamic_per_page || configured_per_page
|
|
@@ -317,6 +329,20 @@ module ActiveAdmin
|
|
|
317
329
|
def create_another?
|
|
318
330
|
params[:create_another].present?
|
|
319
331
|
end
|
|
332
|
+
|
|
333
|
+
def paginated_collection(page_no = 1)
|
|
334
|
+
paginate(collection, page_no, batch_size)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def paginate(chain, page, per_page)
|
|
338
|
+
page_method_name = Kaminari.config.page_method_name
|
|
339
|
+
|
|
340
|
+
chain.public_send(page_method_name, page).per(per_page)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def batch_size
|
|
344
|
+
1000
|
|
345
|
+
end
|
|
320
346
|
end
|
|
321
347
|
end
|
|
322
348
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ActiveAdmin
|
|
2
3
|
class ResourceController < BaseController
|
|
3
4
|
module Decorators
|
|
@@ -28,7 +29,7 @@ module ActiveAdmin
|
|
|
28
29
|
|
|
29
30
|
def decorate?
|
|
30
31
|
case action_name
|
|
31
|
-
when
|
|
32
|
+
when "new", "edit", "create", "update"
|
|
32
33
|
form = active_admin_config.get_page_presenter :form
|
|
33
34
|
form && form.options[:decorate] && decorator_class.present?
|
|
34
35
|
else
|
|
@@ -53,49 +54,27 @@ module ActiveAdmin
|
|
|
53
54
|
|
|
54
55
|
def self.wrap(decorator)
|
|
55
56
|
collection_decorator = find_collection_decorator(decorator)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
name = "#{collection_decorator.name} of #{decorator} + ActiveAdmin"
|
|
59
|
-
@cache[name] ||= wrap! collection_decorator, name
|
|
60
|
-
else
|
|
61
|
-
collection_decorator
|
|
62
|
-
end
|
|
57
|
+
name = "#{collection_decorator.name} of #{decorator} + ActiveAdmin"
|
|
58
|
+
@cache[name] ||= wrap! collection_decorator, name
|
|
63
59
|
end
|
|
64
60
|
|
|
65
|
-
private
|
|
66
|
-
|
|
67
61
|
def self.wrap!(parent, name)
|
|
68
62
|
::Class.new parent do
|
|
69
63
|
delegate :reorder, :page, :current_page, :total_pages, :limit_value,
|
|
70
64
|
:total_count, :total_pages, :offset, :to_key, :group_values,
|
|
71
|
-
:except, :find_each, :ransack
|
|
65
|
+
:except, :find_each, :ransack, to: :object
|
|
72
66
|
|
|
73
67
|
define_singleton_method(:name) { name }
|
|
74
68
|
end
|
|
75
69
|
end
|
|
76
70
|
|
|
77
|
-
# Draper::CollectionDecorator was introduced in 1.0.0
|
|
78
|
-
# Draper::Decorator#collection_decorator_class was introduced in 1.3.0
|
|
79
71
|
def self.find_collection_decorator(decorator)
|
|
80
|
-
if
|
|
72
|
+
if decorator.respond_to?(:collection_decorator_class)
|
|
81
73
|
decorator.collection_decorator_class
|
|
82
|
-
elsif Dependency.draper? '>= 1.0.0'
|
|
83
|
-
draper_collection_decorator
|
|
84
74
|
else
|
|
85
|
-
|
|
75
|
+
CollectionDecorator
|
|
86
76
|
end
|
|
87
77
|
end
|
|
88
|
-
|
|
89
|
-
def self.draper_collection_decorator?(decorator)
|
|
90
|
-
decorator && decorator <= draper_collection_decorator
|
|
91
|
-
rescue NameError
|
|
92
|
-
false
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def self.draper_collection_decorator
|
|
96
|
-
::Draper::CollectionDecorator
|
|
97
|
-
end
|
|
98
|
-
|
|
99
78
|
end
|
|
100
79
|
end
|
|
101
80
|
end
|