diff --git a/corehq/apps/locations/forms.py b/corehq/apps/locations/forms.py index 2cc4466a6c5a..b10172251e46 100644 --- a/corehq/apps/locations/forms.py +++ b/corehq/apps/locations/forms.py @@ -45,14 +45,15 @@ class LocationSelectWidget(forms.Widget): def __init__(self, domain, attrs=None, id='supply-point', multiselect=False, placeholder=None, - include_locations_with_no_users_allowed=True): + for_user_location_selection=False): super(LocationSelectWidget, self).__init__(attrs) self.domain = domain self.id = id self.multiselect = multiselect self.placeholder = placeholder url_name = 'location_search' - if not include_locations_with_no_users_allowed and toggles.LOCATION_HAS_USERS.enabled(self.domain): + if (for_user_location_selection + and toggles.USH_RESTORE_FILE_LOCATION_CASE_SYNC_RESTRICTION.enabled(self.domain)): url_name = 'location_search_has_users_only' self.query_url = reverse(url_name, args=[self.domain]) self.template = 'locations/manage/partials/autocomplete_select_widget.html' diff --git a/corehq/apps/locations/templates/locations/location_types.html b/corehq/apps/locations/templates/locations/location_types.html index 1a2fb794cea5..4d069ed7c7f7 100644 --- a/corehq/apps/locations/templates/locations/location_types.html +++ b/corehq/apps/locations/templates/locations/location_types.html @@ -167,8 +167,6 @@

{% trans "Organization Levels" %}

{% endblocktrans %}"> - {% endif %} - {% if request|toggle_enabled:"LOCATION_HAS_USERS" %} {% trans "Has Users" %} {% trans "Organization Levels" %} enable: view_descendants"> - {% endif %} - {% if request|toggle_enabled:"LOCATION_HAS_USERS" %} diff --git a/corehq/apps/locations/tests/test_views.py b/corehq/apps/locations/tests/test_views.py index 516bf9128d75..28ea3662c510 100644 --- a/corehq/apps/locations/tests/test_views.py +++ b/corehq/apps/locations/tests/test_views.py @@ -146,7 +146,7 @@ def test_invalid_child_data(self): with self.assertRaises(LocationConsistencyError): self.send_request(data) - @flag_enabled('LOCATION_HAS_USERS') + @flag_enabled('USH_RESTORE_FILE_LOCATION_CASE_SYNC_RESTRICTION') @mock.patch('corehq.apps.locations.views.does_location_type_have_users', return_value=True) def test_invalid_remove_has_users(self, _): loc_type1 = OTHER_DETAILS.copy() @@ -203,7 +203,7 @@ def test_search_view_basic(self): self.assertEqual(results[0]['id'], self.loc1.location_id) self.assertEqual(results[1]['id'], self.loc2.location_id) - @flag_enabled('LOCATION_HAS_USERS') + @flag_enabled('USH_RESTORE_FILE_LOCATION_CASE_SYNC_RESTRICTION') def test_search_view_has_users_only(self): loc_type2 = LocationType(domain=self.domain, name='type2', code='code2') loc_type2.has_users = False diff --git a/corehq/apps/locations/urls.py b/corehq/apps/locations/urls.py index d4c76a0fbbba..fc7abf6241d2 100644 --- a/corehq/apps/locations/urls.py +++ b/corehq/apps/locations/urls.py @@ -33,7 +33,7 @@ url(r'^list/$', LocationsListView.as_view(), name=LocationsListView.urlname), url(r'^location_search/$', LocationsSearchView.as_view(), name='location_search'), url(r'^location_search_has_users_only/$', LocationsSearchView.as_view( - include_locations_with_no_users_allowed=False), name='location_search_has_users_only'), + include_locations_with_no_users=False), name='location_search_has_users_only'), url(r'^location_types/$', LocationTypesView.as_view(), name=LocationTypesView.urlname), url(r'^import/$', waf_allow('XSS_BODY')(LocationImportView.as_view()), name=LocationImportView.urlname), url(r'^import/bulk_location_upload_api/$', bulk_location_upload_api, name='bulk_location_upload_api'), diff --git a/corehq/apps/locations/views.py b/corehq/apps/locations/views.py index 11088b6e3d6f..70dcfc05cdcb 100644 --- a/corehq/apps/locations/views.py +++ b/corehq/apps/locations/views.py @@ -266,9 +266,9 @@ class LocationOptionsController(EmwfOptionsController): namespace_locations = False case_sharing_only = False - def __init__(self, *args, include_locations_with_no_users_allowed=True): - self.include_locations_with_no_users_allowed = include_locations_with_no_users_allowed + def __init__(self, *args, include_locations_with_no_users=True): super().__init__(*args) + self.include_locations_with_no_users = include_locations_with_no_users @property def data_sources(self): @@ -280,13 +280,13 @@ def data_sources(self): @method_decorator(locations_access_required, name='dispatch') @location_safe class LocationsSearchView(EmwfOptionsView): - include_locations_with_no_users_allowed = True + include_locations_with_no_users = True @property @memoized def options_controller(self): return LocationOptionsController(self.request, self.domain, self.search, - include_locations_with_no_users_allowed=self.include_locations_with_no_users_allowed) + include_locations_with_no_users=self.include_locations_with_no_users) @method_decorator(use_bootstrap5, name='dispatch') @@ -434,7 +434,7 @@ def _validate_has_users_config(loc_type_payload, pk): payload_loc_type_name_by_pk[loc_type['pk']] = loc_type['name'] if loc_type.get('code'): payload_loc_type_code_by_pk[loc_type['pk']] = loc_type['code'] - if toggles.LOCATION_HAS_USERS.enabled(self.domain): + if toggles.USH_RESTORE_FILE_LOCATION_CASE_SYNC_RESTRICTION.enabled(self.domain): _validate_has_users_config(loc_type, pk) names = list(payload_loc_type_name_by_pk.values()) names_are_unique = len(names) == len(set(names)) @@ -819,7 +819,7 @@ def products_form(self): def users_form(self): if not (self.can_edit_commcare_users or self.can_access_all_locations): return None - if toggles.LOCATION_HAS_USERS.enabled(self.domain) and not self.location.location_type.has_users: + if not self.location.location_type.has_users: return None form = UsersAtLocationForm( request=self.request, diff --git a/corehq/apps/reports/filters/controllers.py b/corehq/apps/reports/filters/controllers.py index 3596d4d713f9..fe2f56f918d2 100644 --- a/corehq/apps/reports/filters/controllers.py +++ b/corehq/apps/reports/filters/controllers.py @@ -1,6 +1,5 @@ from memoized import memoized -from corehq import toggles from corehq.apps.enterprise.models import EnterprisePermissions from corehq.apps.es import GroupES, UserES, groups from corehq.apps.locations.models import SQLLocation @@ -46,6 +45,7 @@ def __init__(self, request, domain, search, case_sharing_only=False): self.domain = domain self.search = search self.case_sharing_only = case_sharing_only + self.include_locations_with_no_users = True @property @memoized @@ -97,8 +97,7 @@ def get_locations_query(self, query): if self.case_sharing_only: locations = locations.filter(location_type__shares_cases=True) - if (toggles.LOCATION_HAS_USERS.enabled(self.domain) - and not self.include_locations_with_no_users_allowed): + if not self.include_locations_with_no_users: locations = locations.filter(location_type__has_users=True) return locations.accessible_to_user(self.domain, self.request.couch_user) diff --git a/corehq/apps/user_importer/tests/test_validators.py b/corehq/apps/user_importer/tests/test_validators.py index de60e74d5083..89064ad8a995 100644 --- a/corehq/apps/user_importer/tests/test_validators.py +++ b/corehq/apps/user_importer/tests/test_validators.py @@ -386,7 +386,7 @@ def test_cant_remove_location(self): assert validation_result == self.validator.error_message_location_access.format( self.locations['Suffolk'].site_code) - @flag_enabled('LOCATION_HAS_USERS') + @flag_enabled('USH_RESTORE_FILE_LOCATION_CASE_SYNC_RESTRICTION') def test_location_not_has_users(self): self.editable_user.reset_locations(self.domain, [self.locations['Middlesex'].location_id]) self.locations['Cambridge'].location_type.has_users = False diff --git a/corehq/apps/user_importer/validation.py b/corehq/apps/user_importer/validation.py index a732da845f30..86ee1be3e157 100644 --- a/corehq/apps/user_importer/validation.py +++ b/corehq/apps/user_importer/validation.py @@ -511,7 +511,7 @@ def _validate_location_has_users(self, spec): def validate_spec(self, spec): user_access_error = self._validate_uploading_user_access(spec) location_cannot_have_users_error = None - if toggles.LOCATION_HAS_USERS.enabled(self.domain): + if toggles.USH_RESTORE_FILE_LOCATION_CASE_SYNC_RESTRICTION.enabled(self.domain): location_cannot_have_users_error = self._validate_location_has_users(spec) return user_access_error or location_cannot_have_users_error diff --git a/corehq/apps/users/forms.py b/corehq/apps/users/forms.py index bc9bf6e710e5..1247b8bc2017 100644 --- a/corehq/apps/users/forms.py +++ b/corehq/apps/users/forms.py @@ -58,7 +58,6 @@ from corehq.const import LOADTEST_HARD_LIMIT, USER_CHANGE_VIA_WEB from corehq.pillows.utils import MOBILE_USER_TYPE, WEB_USER_TYPE from corehq.toggles import ( - LOCATION_HAS_USERS, TWO_STAGE_USER_PROVISIONING, TWO_STAGE_USER_PROVISIONING_BY_SMS, ) @@ -1161,7 +1160,7 @@ def __init__(self, domain: str, *args, **kwargs): self.domain = domain self.fields['assigned_locations'].widget = LocationSelectWidget( self.domain, multiselect=True, id='id_assigned_locations', - include_locations_with_no_users_allowed=False + for_user_location_selection=True ) self.fields['assigned_locations'].help_text = ExpandedMobileWorkerFilter.location_search_help self.fields['primary_location'].widget = PrimaryLocationWidget( @@ -1178,7 +1177,7 @@ def clean_assigned_locations(self): locations = get_locations_from_ids(location_ids, self.domain) except SQLLocation.DoesNotExist: raise forms.ValidationError(_('One or more of the locations was not found.')) - if LOCATION_HAS_USERS.enabled(self.domain) and locations.filter(location_type__has_users=False).exists(): + if locations.filter(location_type__has_users=False).exists(): raise forms.ValidationError( _('One or more of the locations you specified cannot have users assigned.')) return [location.location_id for location in locations] @@ -1635,7 +1634,7 @@ def __init__(self, *args, **kwargs): id='id_location_id', placeholder=_("All Locations"), attrs={'data-bind': 'value: location_id'}, - include_locations_with_no_users_allowed=False + for_user_location_selection=True ) self.fields['location_id'].widget.query_url = "{url}?show_all=true".format( url=self.fields['location_id'].widget.query_url diff --git a/corehq/toggles/__init__.py b/corehq/toggles/__init__.py index 08bdceb3c276..1de3ab96ad02 100644 --- a/corehq/toggles/__init__.py +++ b/corehq/toggles/__init__.py @@ -2904,14 +2904,16 @@ def domain_has_privilege_from_toggle(privilege_slug, domain): USH_RESTORE_FILE_LOCATION_CASE_SYNC_RESTRICTION = StaticToggle( 'ush_restore_file_location_case_sync_restriction', - 'USH: Limit the location-owned cases that show up in a user\'s restore file', + 'USH: Limit the location-owned cases in a user\'s restore file, and allow marking whether a ' + 'location can have users assigned or not.', TAG_CUSTOM, namespaces=[NAMESPACE_DOMAIN], help_link='https://dimagi.atlassian.net/wiki/spaces/USH/pages/2252210196/Prevent+Syncing+of+Lower+Level+Locations', # noqa: E501 description=""" In the 'Organizational Level' section of location management, web admins can specify which org level to expand to when syncing the location-owned cases included in a user's restore file. Limits cases in a user's - restore file and thus can improve performance. + restore file and thus can improve performance. Also allows marking whether a location can have users assigned + to it. """ ) @@ -2937,10 +2939,3 @@ def domain_has_privilege_from_toggle(privilege_slug, domain): tag=TAG_CUSTOM, namespaces=[NAMESPACE_DOMAIN], ) - -LOCATION_HAS_USERS = StaticToggle( - slug='location_has_users', - label='USH Dev: Allows marking whether a location should have users assigned or not.', - tag=TAG_PRODUCT, - namespaces=[NAMESPACE_DOMAIN], -)