diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index f2a0f1d7f9..6546d6b4e4 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -56,7 +56,6 @@ Layout/LineLength: - app/helpers/order_cycles_helper.rb - app/helpers/shop_helper.rb - app/helpers/spree/admin/base_helper_decorator.rb - - app/helpers/spree/admin/navigation_helper_decorator.rb - app/helpers/spree/orders_helper.rb - app/jobs/subscription_confirm_job.rb - app/mailers/subscription_mailer.rb @@ -388,7 +387,7 @@ Metrics/AbcSize: - app/helpers/checkout_helper.rb - app/helpers/i18n_helper.rb - app/helpers/order_cycles_helper.rb - - app/helpers/spree/admin/navigation_helper_decorator.rb + - app/helpers/spree/admin/navigation_helper.rb - app/helpers/spree/orders_helper.rb - app/jobs/subscription_placement_job.rb - app/mailers/producer_mailer.rb @@ -501,7 +500,7 @@ Metrics/CyclomaticComplexity: - app/helpers/checkout_helper.rb - app/helpers/i18n_helper.rb - app/helpers/order_cycles_helper.rb - - app/helpers/spree/admin/navigation_helper_decorator.rb + - app/helpers/spree/admin/navigation_helper.rb - app/models/enterprise.rb - app/models/enterprise_relationship.rb - app/models/product_import/entry_processor.rb @@ -531,6 +530,7 @@ Metrics/PerceivedComplexity: - app/helpers/checkout_helper.rb - app/helpers/i18n_helper.rb - app/helpers/order_cycles_helper.rb + - app/helpers/spree/admin/navigation_helper.rb - app/models/enterprise_relationship.rb - app/models/product_import/entry_processor.rb - app/models/product_import/entry_validator.rb @@ -584,6 +584,7 @@ Metrics/MethodLength: - app/controllers/user_registrations_controller.rb - app/helpers/checkout_helper.rb - app/helpers/order_cycles_helper.rb + - app/helpers/spree/admin/navigation_helper.rb - app/jobs/subscription_placement_job.rb - app/mailers/producer_mailer.rb - app/models/column_preference.rb @@ -682,6 +683,7 @@ Metrics/ModuleLength: Exclude: - app/helpers/admin/injection_helper.rb - app/helpers/injection_helper.rb + - app/helpers/spree/admin/navigation_helper.rb - lib/open_food_network/column_preference_defaults.rb - spec/controllers/admin/enterprises_controller_spec.rb - spec/controllers/admin/order_cycles_controller_spec.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 705e84dc03..5b0facbd00 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -332,6 +332,7 @@ Rails/OutputSafety: - 'app/controllers/spree/admin/reports_controller.rb' - 'app/helpers/angular_form_helper.rb' - 'app/helpers/spree/reports_helper.rb' + - 'app/helpers/spree/admin/navigation_helper.rb' - 'app/serializers/api/product_serializer.rb' - 'lib/spree/money_decorator.rb' - 'spec/features/admin/orders_spec.rb' diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 0733ed0e02..5b2744d718 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -1,4 +1,5 @@ require 'open_food_network/referer_parser' +require 'open_food_network/permissions' module Admin class EnterprisesController < ResourceController diff --git a/app/helpers/spree/admin/navigation_helper.rb b/app/helpers/spree/admin/navigation_helper.rb new file mode 100644 index 0000000000..3a5495488a --- /dev/null +++ b/app/helpers/spree/admin/navigation_helper.rb @@ -0,0 +1,148 @@ +module Spree + module Admin + module NavigationHelper + # Make an admin tab that coveres one or more resources supplied by symbols + # Option hash may follow. Valid options are + # * :label to override link text, otherwise based on the first resource name (translated) + # * :route to override automatically determining the default route + # * :match_path as an alternative way to control when the tab is active, + # /products would match /admin/products, /admin/products/5/variants etc. + def tab(*args) + options = { label: args.first.to_s } + if args.last.is_a?(Hash) + options = options.merge(args.last) + end + + # Return if resource is found and user is not allowed to :admin + klass = klass_for(options[:label]) + return '' if klass && cannot?(:admin, klass) + + options[:route] ||= "admin_#{args.first}" + + destination_url = options[:url] || spree.public_send("#{options[:route]}_path") + titleized_label = Spree.t(options[:label], + default: options[:label], + scope: [:admin, :tab]).titleize + + css_classes = [] + + if options[:icon] + link = link_to_with_icon(options[:icon], titleized_label, destination_url) + css_classes << 'tab-with-icon' + else + link = link_to(titleized_label, destination_url) + end + + selected = if options[:match_path] + request.fullpath.starts_with?("#{spree.root_path}admin#{options[:match_path]}") + else + args.include?(controller.controller_name.to_sym) + end + css_classes << 'selected' if selected + + if options[:css_class] + css_classes << options[:css_class] + end + content_tag('li', link, class: css_classes.join(' ')) + end + + # finds class for a given symbol / string + # + # Example : + # :products returns Spree::Product + # :my_products returns MyProduct if MyProduct is defined + # :my_products returns My::Product if My::Product is defined + # + # If it cannot constantize, it tries a collection of custom translations + # + # This allows us to use cancan abilities on tab + def klass_for(name) + model_name = name.to_s + klass = ["Spree::#{model_name.classify}", + model_name.classify, + model_name.tr('_', '/').classify]. + find(&:safe_constantize). + try(:safe_constantize) + + klass ||= name.singularize.to_sym + + klass = :overview if klass == :dashboard + klass = Spree::Order if klass == :bulk_order_management + klass = EnterpriseGroup if klass == :group + klass = VariantOverride if klass == :Inventory + klass = ProductImport::ProductImporter if klass == :import + klass = Spree::Admin::ReportsController if klass == :report + klass + end + + def link_to_edit(resource, options = {}) + options[:data] = { action: 'edit' } + link_to_with_icon('icon-edit', Spree.t(:edit), edit_object_url(resource), options) + end + + def link_to_delete(resource, options = {}) + url = options[:url] || object_url(resource) + name = options[:name] || Spree.t(:delete) + options[:class] = "delete-resource" + options[:data] = { confirm: Spree.t(:are_you_sure), action: 'remove' } + link_to_with_icon 'icon-trash', name, url, options + end + + def link_to_with_icon(icon_name, text, url, options = {}) + options[:class] = (options[:class].to_s + " icon_link with-tip #{icon_name}").strip + options[:class] += ' no-text' if options[:no_text] + options[:title] = text if options[:no_text] + text = options[:no_text] ? '' : raw("#{text}") + options.delete(:no_text) + link_to(text, url, options) + end + + def icon(icon_name) + icon_name ? content_tag(:i, '', class: icon_name) : '' + end + + def button(text, icon_name = nil, button_type = 'submit', options = {}) + button_tag(text, options.merge(type: button_type, class: "#{icon_name} button")) + end + + def button_link_to(text, url, html_options = {}) + if html_options[:method] && + html_options[:method].to_s.downcase != 'get' && + !html_options[:remote] + form_tag(url, method: html_options.delete(:method)) do + button(text, html_options.delete(:icon), nil, html_options) + end + else + if html_options['data-update'].nil? && html_options[:remote] + object_name, action = url.split('/')[-2..-1] + html_options['data-update'] = [action, object_name.singularize].join('_') + end + + html_options.delete('data-update') unless html_options['data-update'] + + html_options[:class] = 'button' + + if html_options[:icon] + html_options[:class] += " #{html_options[:icon]}" + end + link_to(text_for_button_link(text, html_options), url, html_options) + end + end + + def text_for_button_link(text, _html_options) + s = '' + s << text + raw(s) + end + + def configurations_sidebar_menu_item(link_text, url, options = {}) + is_active = url.ends_with?(controller.controller_name) || + url.ends_with?( "#{controller.controller_name}/edit") + options.merge!(class: is_active ? 'active' : nil) + content_tag(:li, options) do + link_to(link_text, url) + end + end + end + end +end diff --git a/app/helpers/spree/admin/navigation_helper_decorator.rb b/app/helpers/spree/admin/navigation_helper_decorator.rb deleted file mode 100644 index 89423d3f16..0000000000 --- a/app/helpers/spree/admin/navigation_helper_decorator.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'spree/admin/navigation_helper' - -module Spree - module Admin - module NavigationHelper - # TEMP: import method until it is re-introduced into Spree. - def klass_for(name) - model_name = name.to_s - - ["Spree::#{model_name.classify}", model_name.classify, model_name.tr('_', '/').classify].find(&:safe_constantize).try(:safe_constantize) - end - - # Make it so that the Reports admin tab can be enabled/disabled through the cancan - # :report resource, since it does not have a corresponding resource class (unlike - # eg. Spree::Product). - def klass_for_with_sym_fallback(name) - klass = klass_for_without_sym_fallback(name) - klass ||= name.singularize.to_sym - klass = :overview if klass == :dashboard - klass = Spree::Order if klass == :bulk_order_management - klass = EnterpriseGroup if klass == :group - klass = VariantOverride if klass == :Inventory - klass = ProductImport::ProductImporter if klass == :import - klass = Spree::Admin::ReportsController if klass == :report - klass - end - alias_method_chain :klass_for, :sym_fallback - - # TEMP: override method until it is fixed in Spree. - def tab_with_cancan_check(*args) - options = { label: args.first.to_s } - if args.last.is_a?(Hash) - options = options.merge(args.last) - end - return '' if (klass = klass_for(options[:label])) && cannot?(:admin, klass) - - tab_without_cancan_check(*args) - end - alias_method_chain :tab, :cancan_check - end - end -end diff --git a/app/views/spree/admin/shared/_order_sub_menu.html.erb b/app/views/spree/admin/shared/_order_sub_menu.html.erb deleted file mode 100644 index 171b458f7b..0000000000 --- a/app/views/spree/admin/shared/_order_sub_menu.html.erb +++ /dev/null @@ -1,9 +0,0 @@ -<% content_for :sub_menu do %> -
-<% end %> diff --git a/app/views/spree/admin/shared/_order_sub_menu.html.haml b/app/views/spree/admin/shared/_order_sub_menu.html.haml new file mode 100644 index 0000000000..ba9a15407b --- /dev/null +++ b/app/views/spree/admin/shared/_order_sub_menu.html.haml @@ -0,0 +1,6 @@ +- content_for :sub_menu do + %ul#sub_nav.inline-menu + = tab :orders, :match_path => '/orders' + = tab :bulk_order_management, :match_path => '/orders/bulk_management' + - if subscriptions_enabled? + = tab :subscriptions, :match_path => '/subscriptions', url: main_app.admin_subscriptions_path diff --git a/app/views/spree/admin/shared/_tabs.html.haml b/app/views/spree/admin/shared/_tabs.html.haml index d0e3e04ab5..219305ca40 100644 --- a/app/views/spree/admin/shared/_tabs.html.haml +++ b/app/views/spree/admin/shared/_tabs.html.haml @@ -1,11 +1,11 @@ = tab :overview, label: 'dashboard', url: spree.admin_dashboard_path, icon: 'icon-dashboard' -= tab :products, :option_types, :properties, :variants, :product_properties, :taxons, :url => admin_products_path, :icon => 'icon-th-large' -= tab :order_cycles, :url => main_app.admin_order_cycles_path, :icon => 'icon-refresh' -= tab :orders, :payments, :creditcard_payments, :shipments, :credit_cards, :return_authorizations, :url => admin_orders_path('q[s]' => 'completed_at desc'), :icon => 'icon-shopping-cart' -= tab :reports, :icon => 'icon-file' -= tab :configurations, :general_settings, :mail_methods, :tax_categories, :zones, :states, :payment_methods, :inventory_settings, :taxonomies, :shipping_methods, :trackers, :label => 'configuration', :icon => 'icon-wrench', :url => edit_admin_general_settings_path -= tab :enterprises, :url => main_app.admin_enterprises_path -= tab :customers, :url => main_app.admin_customers_path -= tab :enterprise_groups, :url => main_app.admin_enterprise_groups_path, label: 'groups' += tab :products, :properties, :inventory, :product_import, :images, :variants, :product_properties, :group_buy_options, :seo, url: admin_products_path, icon: 'icon-th-large' += tab :order_cycles, url: main_app.admin_order_cycles_path, icon: 'icon-refresh' += tab :orders, :subscriptions, :customer_details, :adjustments, :payments, :return_authorizations, url: admin_orders_path('q[s]' => 'completed_at desc'), icon: 'icon-shopping-cart' += tab :reports, icon: 'icon-file' += tab :general_settings, :mail_method, :image_settings, :tax_categories, :tax_rates, :tax_settings, :zones, :countries, :states, :payment_methods, :taxonomies, :shipping_methods, :shipping_categories, :enterprise_fees, :trackers, :contents, :invoice_settings, :matomo_settings, :stripe_connect_settings, label: 'configuration', icon: 'icon-wrench', url: edit_admin_general_settings_path += tab :enterprises, :enterprise_relationships, url: main_app.admin_enterprises_path += tab :customers, url: main_app.admin_customers_path += tab :enterprise_groups, url: main_app.admin_enterprise_groups_path, label: 'groups' - if can? :admin, Spree::User = tab(:users, url: spree.admin_users_path, icon: 'icon-user')