diff --git a/app/controllers/admin/connected_app_settings_controller.rb b/app/controllers/admin/connected_app_settings_controller.rb new file mode 100644 index 0000000000..c95cd17fac --- /dev/null +++ b/app/controllers/admin/connected_app_settings_controller.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Admin + class ConnectedAppSettingsController < Spree::Admin::BaseController + def update + Spree::Config.set(connected_apps_enabled:) + + respond_to do |format| + format.html { + flash[:success] = t(:successfully_updated, resource: t('.resource')) + redirect_to main_app.edit_admin_connected_app_settings_path + } + end + end + + private + + def connected_apps_enabled + params.require(:preferences).require(:connected_apps_enabled).compact_blank.join(",") + end + end +end diff --git a/app/helpers/admin/enterprises_helper.rb b/app/helpers/admin/enterprises_helper.rb index 1aa61f2a5b..67ed726282 100644 --- a/app/helpers/admin/enterprises_helper.rb +++ b/app/helpers/admin/enterprises_helper.rb @@ -26,7 +26,8 @@ module Admin show_enterprise_fees = can?(:manage_enterprise_fees, enterprise) && (is_shop || enterprise.is_primary_producer) show_connected_apps = can?(:manage_connected_apps, enterprise) && - feature?(:connected_apps, spree_current_user, enterprise) + feature?(:connected_apps, spree_current_user, enterprise) && + Spree::Config.connected_apps_enabled.present? build_enterprise_side_menu_items( is_shop:, @@ -38,6 +39,11 @@ module Admin ) end + def connected_apps_enabled + connected_apps_enabled = Spree::Config.connected_apps_enabled&.split(',') || [] + ConnectedApp::TYPES & connected_apps_enabled + end + private def build_enterprise_side_menu_items( diff --git a/app/models/connected_app.rb b/app/models/connected_app.rb index 9c99ba7dcc..966df543b5 100644 --- a/app/models/connected_app.rb +++ b/app/models/connected_app.rb @@ -4,6 +4,8 @@ # # Here we store keys and links to access the app. class ConnectedApp < ApplicationRecord + TYPES = ['discover_regen', 'affiliate_sales_data'].freeze + belongs_to :enterprise after_destroy :disconnect diff --git a/app/models/spree/app_configuration.rb b/app/models/spree/app_configuration.rb index 0490c6432f..f624928703 100644 --- a/app/models/spree/app_configuration.rb +++ b/app/models/spree/app_configuration.rb @@ -139,5 +139,8 @@ module Spree # Available units preference :available_units, :string, default: "g,kg,T,mL,L,kL" + + # Connected Apps + preference :connected_apps_enabled, :string, default: nil end end diff --git a/app/views/admin/connected_app_settings/edit.html.haml b/app/views/admin/connected_app_settings/edit.html.haml new file mode 100644 index 0000000000..cae8f316c0 --- /dev/null +++ b/app/views/admin/connected_app_settings/edit.html.haml @@ -0,0 +1,25 @@ += render :partial => 'spree/admin/shared/configuration_menu' + +- content_for :page_title do + = t('.title') + += form_tag main_app.admin_connected_app_settings_path, :method => :put do + + %fieldset + %legend= t('.enabled_legend') + + = t('.info_html') + + .field + -# Blank value in case nothing is selected + = hidden_field_tag("preferences[connected_apps_enabled][]", "") + + - ConnectedApp::TYPES.each do |type| + %label + = check_box_tag("preferences[connected_apps_enabled][]", type, + Spree::Config.connected_apps_enabled&.split(',')&.include?(type)) + = t('.connected_apps_enabled.' + type) + %br + + .form-buttons + = button t(:update), 'icon-refresh' diff --git a/app/views/admin/enterprises/form/_connected_apps.html.haml b/app/views/admin/enterprises/form/_connected_apps.html.haml index 1c7904b2ed..f24c2b34d9 100644 --- a/app/views/admin/enterprises/form/_connected_apps.html.haml +++ b/app/views/admin/enterprises/form/_connected_apps.html.haml @@ -1,4 +1,3 @@ -= render partial: "/admin/enterprises/form/connected_apps/discover_regen", - locals: { enterprise:, connected_app: enterprise.connected_apps.discover_regen.first } -= render partial: "/admin/enterprises/form/connected_apps/affiliate_sales_data", - locals: { enterprise:, connected_app: enterprise.connected_apps.affiliate_sales_data.first } +- connected_apps_enabled.each do |type| + = render partial: "/admin/enterprises/form/connected_apps/#{type}", + locals: { enterprise:, connected_app: enterprise.connected_apps.public_send(type).first } diff --git a/app/views/spree/admin/shared/_configuration_menu.html.haml b/app/views/spree/admin/shared/_configuration_menu.html.haml index af00f89928..215011f6b6 100644 --- a/app/views/spree/admin/shared/_configuration_menu.html.haml +++ b/app/views/spree/admin/shared/_configuration_menu.html.haml @@ -23,3 +23,4 @@ = configurations_sidebar_menu_item t('admin.invoice_settings.edit.title'), main_app.edit_admin_invoice_settings_path = configurations_sidebar_menu_item t('admin.matomo_settings.edit.title'), main_app.edit_admin_matomo_settings_path = configurations_sidebar_menu_item t('admin.stripe_connect_settings.edit.title'), main_app.edit_admin_stripe_connect_settings_path + = configurations_sidebar_menu_item t('admin.connected_app_settings.edit.title'), main_app.edit_admin_connected_app_settings_path diff --git a/app/views/spree/admin/shared/_tabs.html.haml b/app/views/spree/admin/shared/_tabs.html.haml index 019b45d948..f9a9c6613f 100644 --- a/app/views/spree/admin/shared/_tabs.html.haml +++ b/app/views/spree/admin/shared/_tabs.html.haml @@ -3,7 +3,7 @@ = 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, icon: 'icon-shopping-cart' = tab :reports, url: main_app.admin_reports_path, icon: 'icon-file' -= tab :general_settings, :terms_of_service_files, :mail_methods, :tax_categories, :tax_rates, :tax_settings, :zones, :countries, :states, :payment_methods, :taxonomies, :shipping_methods, :shipping_categories, :enterprise_fees, :contents, :invoice_settings, :matomo_settings, :stripe_connect_settings, label: 'configuration', icon: 'icon-wrench', url: edit_admin_general_settings_path += tab :general_settings, :terms_of_service_files, :mail_methods, :tax_categories, :tax_rates, :tax_settings, :zones, :countries, :states, :payment_methods, :taxonomies, :shipping_methods, :shipping_categories, :enterprise_fees, :contents, :invoice_settings, :matomo_settings, :stripe_connect_settings, :connected_app_settings, label: 'configuration', icon: 'icon-wrench', url: edit_admin_general_settings_path = tab :enterprises, :enterprise_relationships, :vouchers, :oidc_settings, 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' diff --git a/config/locales/en.yml b/config/locales/en.yml index d834a7c4f1..74e5b24b68 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -685,6 +685,8 @@ en: terms_of_service_have_been_updated_html: "Open Food Network's Terms of Service have been updated: %{tos_link}" terms_of_service: Read Terms of Service accept_terms_of_service: Accept Terms of Service + + # Pages shopfront_settings: embedded_shopfront_settings: "Embedded Shopfront Settings" enable_embedded_shopfronts: "Enable Embedded Shopfronts" @@ -741,6 +743,17 @@ en: config_instructions_html: "Here you can configure the OFN Matomo integration. The Matomo URL below should point to the Matomo instance where the user tracking information will be sent to; if it is left empty, Matomo user tracking will be disabled. The Site ID field is not mandatory but useful if you are tracking more than one website on a single Matomo instance; it can be found on the Matomo instance console." config_instructions_tag_manager_html: "Setting the Matomo Tag Manager URL enables Matomo Tag Manager. This tool allows you to set up analytics events. The Matomo Tag Manager URL is copied from the Install Code section of Matomo Tag Manager. Ensure you select the right container and environment as these options change the URL." + connected_app_settings: + edit: + title: "Connected app settings" + info_html: "Enabled apps will appear under Enterprise Settings > Connected Apps." + enabled_legend: "Enabled connected apps" + connected_apps_enabled: + discover_regen: Discover Regenerative portal + affiliate_sales_data: DFC anonymised orders API for research purposes + update: + resource: Connected app settings + customers: index: new_customer: "New Customer" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index a8bf999f1b..fe91db5633 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -108,6 +108,8 @@ Openfoodnetwork::Application.routes.draw do resource :matomo_settings, only: [:edit, :update] + resource :connected_app_settings, only: [:edit, :update] + resources :stripe_accounts, only: [:destroy] do get :connect, on: :collection get :status, on: :collection diff --git a/db/migrate/20240801034710_set_connected_apps_enabled_if_any.rb b/db/migrate/20240801034710_set_connected_apps_enabled_if_any.rb new file mode 100644 index 0000000000..680da2dd78 --- /dev/null +++ b/db/migrate/20240801034710_set_connected_apps_enabled_if_any.rb @@ -0,0 +1,14 @@ +class SetConnectedAppsEnabledIfAny < ActiveRecord::Migration[7.0] + class ConnectedApp < ApplicationRecord + scope :discover_regen, -> { where(type: "ConnectedApp") } + scope :affiliate_sales_data, -> { where(type: "ConnectedApps::AffiliateSalesData") } + end + + def up + enabled = [] + enabled << "discover_regen" if ConnectedApp.discover_regen.any? + enabled << "affiliate_sales_data" if ConnectedApp.affiliate_sales_data.any? + + Spree::Config.connected_apps_enabled = enabled.presence&.join(",") + end +end diff --git a/db/schema.rb b/db/schema.rb index b1a239f42a..04013a7caf 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_07_31_065321) do +ActiveRecord::Schema[7.0].define(version: 2024_08_01_034710) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" enable_extension "plpgsql" diff --git a/spec/helpers/admin/enterprises_helper_spec.rb b/spec/helpers/admin/enterprises_helper_spec.rb index 2c9a0ac531..aae01ab5ea 100644 --- a/spec/helpers/admin/enterprises_helper_spec.rb +++ b/spec/helpers/admin/enterprises_helper_spec.rb @@ -28,6 +28,8 @@ RSpec.describe Admin::EnterprisesHelper, type: :helper do end it "lists enabled features when allowed", feature: :connected_apps do + allow(Spree::Config).to receive(:connected_apps_enabled).and_return "discover_regen" + user.enterprises << enterprise expect(visible_items.pluck(:name)).to include "connected_apps" end diff --git a/spec/system/admin/configuration/connected_app_settings_spec.rb b/spec/system/admin/configuration/connected_app_settings_spec.rb new file mode 100644 index 0000000000..73b38360d9 --- /dev/null +++ b/spec/system/admin/configuration/connected_app_settings_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'system_helper' + +RSpec.describe "Connected App Settings", feature: :connected_apps do + include AuthenticationHelper + + before do + login_as_admin + visit spree.admin_dashboard_path + click_link "Configuration" + click_link "Connected app settings" + end + + it "should update connected app enabled preferences" do + expect(page).to have_field "Discover Regenerative portal", checked: false + expect(page).to have_field "DFC anonymised orders API for research purposes", checked: false + + check "Discover Regenerative portal" + check "DFC anonymised orders API for research purposes" + + expect{ + click_button "Update" + }.to change{ Spree::Config.connected_apps_enabled }.to("discover_regen,affiliate_sales_data") + + expect(page).to have_field "Discover Regenerative portal", checked: true + expect(page).to have_field "DFC anonymised orders API for research purposes", checked: true + + uncheck "Discover Regenerative portal" + uncheck "DFC anonymised orders API for research purposes" + + expect{ + click_button "Update" + }.to change{ Spree::Config.connected_apps_enabled }.to("") + end +end diff --git a/spec/system/admin/enterprises/connected_apps_spec.rb b/spec/system/admin/enterprises/connected_apps_spec.rb index 4f1a67b3f7..8d1e9fa2c1 100644 --- a/spec/system/admin/enterprises/connected_apps_spec.rb +++ b/spec/system/admin/enterprises/connected_apps_spec.rb @@ -9,7 +9,10 @@ RSpec.describe "Connected Apps", feature: :connected_apps, vcr: true do login_as enterprise.owner end - it "is only visible when enabled" do + it "is only visible when feature is enabled" do + allow(Spree::Config).to receive(:connected_apps_enabled). + and_return("discover_regen,affiliate_sales_data") + # Assuming that this feature will be the default one day, I'm treating this # as special case and disable the feature. I don't want to wrap all other # test cases in a context block for the feature toggle which will need @@ -28,7 +31,30 @@ RSpec.describe "Connected Apps", feature: :connected_apps, vcr: true do expect(page).to have_content "CONNECTED APPS" end + it "is only visible when setting is enabled" do + allow(Spree::Config).to receive(:connected_apps_enabled).and_return(nil) + visit edit_admin_enterprise_path(enterprise) + expect(page).not_to have_content "CONNECTED APPS" + + allow(Spree::Config).to receive(:connected_apps_enabled). + and_return("discover_regen,affiliate_sales_data") + visit edit_admin_enterprise_path(enterprise) + expect(page).to have_content "CONNECTED APPS" + end + + it "only shows enabled apps" do + allow(Spree::Config).to receive(:connected_apps_enabled).and_return("discover_regen") + + visit "#{edit_admin_enterprise_path(enterprise)}#/connected_apps_panel" + + expect(page).to have_selector "h3", text: "Discover Regenerative" + expect(page).not_to have_selector "h3", text: "INRAE / UFC QUE CHOISIR Research" + end + describe "Discover Regenerative" do + before do + allow(Spree::Config).to receive(:connected_apps_enabled).and_return("discover_regen") + end let(:section_heading) { self.class.description } it "can be enabled and disabled" do @@ -76,6 +102,9 @@ RSpec.describe "Connected Apps", feature: :connected_apps, vcr: true do end describe "Affiliate Sales Data" do + before do + allow(Spree::Config).to receive(:connected_apps_enabled).and_return("affiliate_sales_data") + end let(:section_heading) { "INRAE / UFC QUE CHOISIR Research" } it "can be enabled and disabled" do