diff --git a/app/models/filter.rb b/app/models/filter.rb
index d96ae6e9175..1c4ff7b4d69 100644
--- a/app/models/filter.rb
+++ b/app/models/filter.rb
@@ -165,7 +165,22 @@ def enforce_inherited_taxonomies
def inherit_taxonomies!
self.organization_ids = role.organization_ids if resource_taxable_by_organization?
self.location_ids = role.location_ids if resource_taxable_by_location?
- build_taxonomy_search
+ build_taxonomy_search_from_ids
+ end
+
+ def build_taxonomy_search_from_ids
+ orgs = build_taxonomy_search_string_from_ids('organization')
+ locs = build_taxonomy_search_string_from_ids('location')
+
+ taxonomies = [orgs, locs].reject { |t| t.blank? }
+ self.taxonomy_search = taxonomies.join(' and ').presence
+ end
+
+ def build_taxonomy_search_string_from_ids(name)
+ return '' unless send("resource_taxable_by_#{name}?")
+ relation = send("#{name}_ids")
+ return '' if relation.empty?
+ parenthesize("#{name}_id ^ (#{relation.join(',')})")
end
private
diff --git a/db/migrate/20241105143353_regenerate_taxonomy_search_for_filters_with_override_false.rb b/db/migrate/20241105143353_regenerate_taxonomy_search_for_filters_with_override_false.rb
new file mode 100644
index 00000000000..9b3931ffeb4
--- /dev/null
+++ b/db/migrate/20241105143353_regenerate_taxonomy_search_for_filters_with_override_false.rb
@@ -0,0 +1,10 @@
+class RegenerateTaxonomySearchForFiltersWithOverrideFalse < ActiveRecord::Migration[6.1]
+ def change
+ filters = Filter.where(role_id: Role.where(origin: nil).or(Role.where(builtin: 2))).where(override: false).where(taxonomy_search: nil)
+
+ filters.each do |filter|
+ filter.build_taxonomy_search_from_ids
+ filter.update_column(:taxonomy_search, filter.taxonomy_search)
+ end
+ end
+end
diff --git a/test/models/filter_test.rb b/test/models/filter_test.rb
index ade9b53f924..bca1965a77d 100644
--- a/test/models/filter_test.rb
+++ b/test/models/filter_test.rb
@@ -245,8 +245,17 @@ class FilterTest < ActiveSupport::TestCase
test 'saving nilifies empty taxonomy search' do
f = FactoryBot.build(:filter, :resource_type => 'Domain')
- f.role = FactoryBot.build(:role, :organizations => [FactoryBot.build(:organization)])
+ f.role = FactoryBot.build(:role)
f.save
assert_nil f.taxonomy_search
end
+
+ test 'creating a filter inherits taxonomies from the role' do
+ f = FactoryBot.create(:filter, :resource_type => 'Domain')
+ organization_test = FactoryBot.create(:organization)
+ location_test = FactoryBot.create(:location)
+ f.role = FactoryBot.create(:role, :organizations => [organization_test], :locations => [location_test])
+ f.save
+ assert_equal f.taxonomy_search(), "(organization_id ^ (#{organization_test.id})) and (location_id ^ (#{location_test.id}))"
+ end
end
diff --git a/webpack/assets/javascripts/react_app/routes/FiltersForm/FiltersForm.js b/webpack/assets/javascripts/react_app/routes/FiltersForm/FiltersForm.js
index 765b71a91a2..65c2fa598a4 100644
--- a/webpack/assets/javascripts/react_app/routes/FiltersForm/FiltersForm.js
+++ b/webpack/assets/javascripts/react_app/routes/FiltersForm/FiltersForm.js
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect, useMemo } from 'react';
import PropTypes from 'prop-types';
import { useDispatch } from 'react-redux';
import {
@@ -39,6 +39,17 @@ export const FiltersForm = ({ roleName, roleId, isNew, data, history }) => {
show_organizations: showOrgs = false,
show_locations: showLocations = false,
} = type;
+
+ const isTaxonomySearch = useMemo(
+ () => chosenOrgs.length || chosenLocations.length,
+ [chosenOrgs.length, chosenLocations.length]
+ );
+
+ useEffect(() => {
+ if (isTaxonomySearch) {
+ setIsUnlimited(false);
+ }
+ }, [isTaxonomySearch]);
const dispatch = useDispatch();
const [autocompleteQuery, setAutocompleteQuery] = useState(data.search || '');
const submit = async () => {
@@ -46,7 +57,8 @@ export const FiltersForm = ({ roleName, roleId, isNew, data, history }) => {
filter: {
role_id: role,
search: isUnlimited ? null : autocompleteQuery,
- unlimited: isUnlimited,
+ unlimited:
+ isUnlimited || (!autocompleteQuery.length && !isTaxonomySearch),
override: isOverride,
permission_ids: chosenPermissions,
organization_ids: chosenOrgs,
@@ -163,8 +175,20 @@ export const FiltersForm = ({ roleName, roleId, isNew, data, history }) => {
defaultLocations={data.locations}
/>
)}
+
{isGranular ? (
<>
+ {!!isTaxonomySearch && (
+ <>
+
+ >
+ )}
{
>
{
setAutocompleteQuery('');
setIsUnlimited(checked);
}}
+ isDisabled={isTaxonomySearch}
aria-label="is unlimited"
id="unlimited-check"
name="unlimited"
diff --git a/webpack/assets/javascripts/react_app/routes/FiltersForm/NewFiltersFormPage.js b/webpack/assets/javascripts/react_app/routes/FiltersForm/NewFiltersFormPage.js
index e059164ba1c..500f952b218 100644
--- a/webpack/assets/javascripts/react_app/routes/FiltersForm/NewFiltersFormPage.js
+++ b/webpack/assets/javascripts/react_app/routes/FiltersForm/NewFiltersFormPage.js
@@ -9,7 +9,7 @@ import { FiltersForm } from './FiltersForm';
const NewFiltersFormPage = ({ location: { search }, history }) => {
const { role_id: urlRoleId } = URI.parseQuery(search);
const {
- response: { name: roleName, id: roleId },
+ response: { name: roleName, id: roleId, locations, organizations },
} = useAPI('get', `/api/v2/roles/${urlRoleId}`);
const breadcrumbOptions = {
breadcrumbItems: [
@@ -29,7 +29,7 @@ const NewFiltersFormPage = ({ location: { search }, history }) => {
isNew
roleName={roleName || ''}
roleId={urlRoleId}
- data={{}}
+ data={{ locations, organizations }}
history={history}
/>
) : (