diff --git a/CONTRIBUTING.MD b/CONTRIBUTING.MD index 818555fd46..80aea4195e 100644 --- a/CONTRIBUTING.MD +++ b/CONTRIBUTING.MD @@ -68,6 +68,17 @@ gem 'avo', path: '../avo' Avo's assets will not show up by default, resulting in 404 errors on `/avo-assets/avo.base.js` and `/avo-assets/avo.base.css`. To avoid this, you need to compile the asset bundles, and symlink them into `public/avo-assets`. +First, make sure you have `yarn` installed and then install Avo's dependencies: +```bash +yarn install +``` + +Run the first build to generate the files `app/assets/builds/avo.base.js` and `app/assets/builds/avo.base.css`: + +```bash +yarn build +``` + Create symlinks for compiled assets into the `public` directory. You'll only need to do this once. ```bash diff --git a/app/components/avo/views/resource_index_component.html.erb b/app/components/avo/views/resource_index_component.html.erb index 51af9159c6..bfb619d93d 100644 --- a/app/components/avo/views/resource_index_component.html.erb +++ b/app/components/avo/views/resource_index_component.html.erb @@ -31,7 +31,7 @@ <% end %> <% c.body do %>
- <% unless hide_search_input %> + <% if show_search_input %>
<%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.route_key, via_reflection: via_reflection} %>
diff --git a/app/components/avo/views/resource_index_component.rb b/app/components/avo/views/resource_index_component.rb index 8bfc7fbba6..55713851d9 100644 --- a/app/components/avo/views/resource_index_component.rb +++ b/app/components/avo/views/resource_index_component.rb @@ -124,10 +124,19 @@ def description @resource.resource_description end - def hide_search_input - return true unless @resource.search_query.present? + def show_search_input + return false unless authorized_to_search? + return false unless @resource.search_query.present? + return false if field&.hide_search_input - field&.hide_search_input || false + true + end + + def authorized_to_search? + # Hide the search if the authorization prevents it + return true unless @resource.authorization.has_action_method?("search") + + @resource.authorization.authorize_action("search", raise_exception: false) end private diff --git a/app/controllers/avo/search_controller.rb b/app/controllers/avo/search_controller.rb index ea9882a7c3..c7b23aa901 100644 --- a/app/controllers/avo/search_controller.rb +++ b/app/controllers/avo/search_controller.rb @@ -27,7 +27,8 @@ def search_resources(resources) resources .map do |resource| # Apply authorization - next unless @authorization.set_record(resource.model_class).authorize_action(:index, raise_exception: false) + next unless @authorization.set_record(resource.model_class).authorize_action(:search, raise_exception: false) + # Filter out the models without a search_query next if resource.search_query.nil? @@ -120,10 +121,16 @@ def apply_search_metadata(models, avo_resource) models.map do |model| resource = avo_resource.dup.hydrate(model: model).hydrate_fields(model: model) + record_path = if resource.search_result_path.present? + Avo::Hosts::ResourceRecordHost.new(block: resource.search_result_path, resource: resource, record: model).handle + else + resource.record_path + end + result = { _id: model.id, _label: resource.label, - _url: resource.record_path, + _url: record_path } if App.license.has_with_trial(:enhanced_search_results) diff --git a/lib/avo/base_resource.rb b/lib/avo/base_resource.rb index 4d3cf67367..26ab2289d7 100644 --- a/lib/avo/base_resource.rb +++ b/lib/avo/base_resource.rb @@ -30,6 +30,7 @@ class BaseResource class_attribute :description, default: :id class_attribute :search_query, default: nil class_attribute :search_query_help, default: "" + class_attribute :search_result_path class_attribute :includes, default: [] class_attribute :authorization_policy class_attribute :translation_key diff --git a/lib/avo/hosts/base_host.rb b/lib/avo/hosts/base_host.rb index 09ca61202e..6a335ae369 100644 --- a/lib/avo/hosts/base_host.rb +++ b/lib/avo/hosts/base_host.rb @@ -10,6 +10,8 @@ class BaseHost option :params, default: proc { Avo::App.params } option :view_context, default: proc { Avo::App.view_context } option :current_user, default: proc { Avo::App.current_user } + option :main_app, default: proc { view_context.main_app } + option :avo, default: proc { view_context.avo } # This is optional because we might instantiate the `Host` first and later hydrate it with a block. option :block, optional: true diff --git a/lib/avo/services/authorization_service.rb b/lib/avo/services/authorization_service.rb index 1d489dc0f7..5b2f55b602 100644 --- a/lib/avo/services/authorization_service.rb +++ b/lib/avo/services/authorization_service.rb @@ -130,8 +130,16 @@ def defined_methods(model, **args) end def has_method?(method, **args) + method = "#{method}?" unless method.to_s.end_with? "?" defined_methods(args[:record] || record, **args).include? method.to_sym end + + # Check the received method to see if the user overrode it in their config and then checks if it's present on the policy. + def has_action_method?(method, **args) + method = Avo.configuration.authorization_methods.stringify_keys[method.to_s] || method + + has_method? method, **args + end end end end diff --git a/lib/generators/avo/templates/initializer/avo.tt b/lib/generators/avo/templates/initializer/avo.tt index 562441ca51..a6801e0f3e 100644 --- a/lib/generators/avo/templates/initializer/avo.tt +++ b/lib/generators/avo/templates/initializer/avo.tt @@ -29,6 +29,7 @@ Avo.configure do |config| # update: 'update?', # create: 'create?', # destroy: 'destroy?', + # search: 'search?', # } # config.raise_error_on_missing_policy = false # config.authorization_client = :pundit diff --git a/spec/dummy/app/avo/resources/city_resource.rb b/spec/dummy/app/avo/resources/city_resource.rb index 20bdc72007..8f6cd536c5 100644 --- a/spec/dummy/app/avo/resources/city_resource.rb +++ b/spec/dummy/app/avo/resources/city_resource.rb @@ -1,9 +1,12 @@ class CityResource < Avo::BaseResource self.title = :name self.includes = [] - # self.search_query = ->(params:) do - # scope.ransack(id_eq: params[:q], m: "or").result(distinct: false) - # end + self.search_query = -> do + scope.ransack(name_eq: params[:q]).result(distinct: false) + end + self.search_result_path = -> do + avo.resources_city_path record, custom: "yup" + end self.extra_params = [:fish_type, :something_else, properties: [], information: [:name, :history]] field :id, as: :id diff --git a/spec/dummy/app/policies/application_policy.rb b/spec/dummy/app/policies/application_policy.rb index eefe976c6c..947088d230 100644 --- a/spec/dummy/app/policies/application_policy.rb +++ b/spec/dummy/app/policies/application_policy.rb @@ -26,6 +26,10 @@ def update? false end + def avo_search? + false + end + def edit? update? end diff --git a/spec/dummy/app/policies/course_link_policy.rb b/spec/dummy/app/policies/course_link_policy.rb index 67d87c5034..b865142672 100644 --- a/spec/dummy/app/policies/course_link_policy.rb +++ b/spec/dummy/app/policies/course_link_policy.rb @@ -35,6 +35,10 @@ def act_on? true end + def avo_search? + true + end + def upload_attachments? true end diff --git a/spec/dummy/app/policies/course_policy.rb b/spec/dummy/app/policies/course_policy.rb index e16b0601a9..800d6cf810 100644 --- a/spec/dummy/app/policies/course_policy.rb +++ b/spec/dummy/app/policies/course_policy.rb @@ -59,6 +59,10 @@ def reorder_links? false end + def avo_search? + true + end + class Scope < Scope def resolve scope.all diff --git a/spec/dummy/app/policies/post_policy.rb b/spec/dummy/app/policies/post_policy.rb index 9626acdf8f..bf84318757 100644 --- a/spec/dummy/app/policies/post_policy.rb +++ b/spec/dummy/app/policies/post_policy.rb @@ -31,6 +31,10 @@ def act_on? true end + def avo_search? + true + end + def upload_attachments? true end diff --git a/spec/dummy/app/policies/team_policy.rb b/spec/dummy/app/policies/team_policy.rb index 1cf94ca0eb..e5993a6771 100644 --- a/spec/dummy/app/policies/team_policy.rb +++ b/spec/dummy/app/policies/team_policy.rb @@ -27,6 +27,10 @@ def destroy? true end + def avo_search? + true + end + # Attachments def upload_attachments? true diff --git a/spec/dummy/app/policies/user_policy.rb b/spec/dummy/app/policies/user_policy.rb index 9ed4d66d63..f0ca4237a3 100644 --- a/spec/dummy/app/policies/user_policy.rb +++ b/spec/dummy/app/policies/user_policy.rb @@ -35,6 +35,10 @@ def act_on? true end + def avo_search? + true + end + def attach_post? true end diff --git a/spec/dummy/config/initializers/avo.rb b/spec/dummy/config/initializers/avo.rb index f74891512c..f99e087ed1 100644 --- a/spec/dummy/config/initializers/avo.rb +++ b/spec/dummy/config/initializers/avo.rb @@ -23,6 +23,9 @@ params: request.params } end + config.authorization_methods = { + search: "avo_search?" # override this method + } # config.raise_error_on_missing_policy = true # config.authorization_client = "Avo::Services::AuthorizationClients::ExtraPunditClient" diff --git a/spec/features/avo/hidden_from_global_search_spec.rb b/spec/features/avo/hidden_from_global_search_spec.rb index 440ba35704..39111c3361 100644 --- a/spec/features/avo/hidden_from_global_search_spec.rb +++ b/spec/features/avo/hidden_from_global_search_spec.rb @@ -6,7 +6,7 @@ let!(:team_membership) { team.team_members << user } describe "global search" do - it "does not return the ream membership" do + it "does not return the team membership" do get :index expect(json['users']['count']).to eq 1 @@ -17,7 +17,7 @@ end describe "resource search" do - it "does not return the ream membership" do + it "does not return the team membership" do get :show, params: { resource_name: 'memberships' } diff --git a/spec/features/avo/search_with_custom_path.rb b/spec/features/avo/search_with_custom_path.rb new file mode 100644 index 0000000000..9ef0626dd1 --- /dev/null +++ b/spec/features/avo/search_with_custom_path.rb @@ -0,0 +1,38 @@ +require "rails_helper" + +RSpec.feature "Search with custom path", type: :system do + let(:url) { "/admin/resources/cities" } + + subject do + visit url + page + end + + describe "global search" do + let!(:city) { create :city, name: "São Paulo" } + + context "when search result path has been changed" do + it "can find the city" do + visit url + open_global_search_box + expect_search_panel_open + + write_in_search "São Paulo" + expect(page).to have_content "São Paulo" + find(".aa-Panel").find(".aa-Item div", text: "São Paulo", match: :first).click + sleep 0.8 + + expect(page.current_url).to include("custom=yup") + end + end + end +end + +def open_global_search_box + find(".global-search").click +end + +def expect_search_panel_open + expect(page).to have_css ".aa-InputWrapper .aa-Input" + expect(page).to have_selector(".aa-Input:focus") +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 3dde2579df..58999e285e 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -129,6 +129,7 @@ def driver_options(headless: false) # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true + config.filter_run_when_matching :focus config.before(:each, type: :system) { driven_by test_driver }