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 }