diff --git a/openedx/core/djangoapps/user_api/models.py b/openedx/core/djangoapps/user_api/models.py index ca2e28f7c8..64f45cbdcd 100644 --- a/openedx/core/djangoapps/user_api/models.py +++ b/openedx/core/djangoapps/user_api/models.py @@ -83,6 +83,24 @@ class UserPreference(models.Model): except cls.DoesNotExist: return default + @classmethod + def has_value(cls, user, preference_key): + """Checks if the user has preference value for a given key. + + Note: + This method provides no authorization of access to the user preference. + Consider using user_api.preferences.api.has_user_preference instead if + this is part of a REST API request. + + Arguments: + user (User): The user whose preference should be checked. + preference_key (str): The key for the user preference. + + Returns: + (bool): True if user preference for the given key is set and False otherwise. + """ + return cls.objects.filter(user=user, key=preference_key).exists() + @receiver(pre_save, sender=UserPreference) def pre_save_callback(sender, **kwargs): diff --git a/openedx/core/djangoapps/user_api/preferences/api.py b/openedx/core/djangoapps/user_api/preferences/api.py index c52b198b31..b38f2536a9 100644 --- a/openedx/core/djangoapps/user_api/preferences/api.py +++ b/openedx/core/djangoapps/user_api/preferences/api.py @@ -34,6 +34,31 @@ from ..serializers import RawUserPreferenceSerializer log = logging.getLogger(__name__) +@intercept_errors(UserAPIInternalError, ignore_errors=[UserAPIRequestError]) +def has_user_preference(requesting_user, preference_key, username=None): + """ + Returns True if the user has preference with the specified key. + + Args: + requesting_user (User): The user requesting the user preference check. Only the user with username + `username` or users with "is_staff" privileges can access the preferences. + preference_key (str): The key for the user preference. + username (str): Optional username for which to look up the preferences. If not specified, + `requesting_user.username` is assumed. + + Returns: + (bool): Returns True if the user has preference with the specified key and False otherwise. + + Raises: + UserNotFound: no user with username `username` exists (or `requesting_user.username` if + `username` is not specified) + UserNotAuthorized: the requesting_user does not have access to the user preference. + UserAPIInternalError: the operation failed due to an unexpected error. + """ + existing_user = _get_authorized_user(requesting_user, username, allow_staff=True) + return UserPreference.has_value(existing_user, preference_key) + + @intercept_errors(UserAPIInternalError, ignore_errors=[UserAPIRequestError]) def get_user_preference(requesting_user, preference_key, username=None): """Returns the value of the user preference with the specified key. diff --git a/openedx/core/djangoapps/user_authn/views/register.py b/openedx/core/djangoapps/user_authn/views/register.py index db72b48542..162b83a2a9 100644 --- a/openedx/core/djangoapps/user_authn/views/register.py +++ b/openedx/core/djangoapps/user_authn/views/register.py @@ -215,7 +215,10 @@ def create_account_with_params(request, params): # Sites using multiple languages need to record the language used during registration. # If not, compose_and_send_activation_email will be sent in site's default language only. create_or_set_user_attribute_created_on_site(user, request.site) - preferences_api.set_user_preference(user, LANGUAGE_KEY, get_language()) + + # Only add a default user preference if user does not already has one. + if not preferences_api.has_user_preference(user, LANGUAGE_KEY): + preferences_api.set_user_preference(user, LANGUAGE_KEY, get_language()) # Check if system is configured to skip activation email for the current user. skip_email = _skip_activation_email( diff --git a/requirements/constraints.txt b/requirements/constraints.txt index f8133f0fcc..32f69d4c87 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -35,7 +35,7 @@ drf-yasg<1.17.1 # The team that owns this package will manually bump this package rather than having it pulled in automatically. # This is to allow them to better control its deployment and to do it in a process that works better # for them. -edx-enterprise==3.10.5 +edx-enterprise==3.11.1 # v2 requires the ES7 upgrade work to be complete edx-search<2.0.0 diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 13071d0a75..45d144b02f 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -98,7 +98,7 @@ edx-django-release-util==0.4.4 # via -r requirements/edx/base.in edx-django-sites-extensions==2.5.1 # via -r requirements/edx/base.in edx-django-utils==3.11.0 # via -r requirements/edx/base.in, django-config-models, edx-drf-extensions, edx-enterprise, edx-rest-api-client, edx-toggles, edx-when edx-drf-extensions==6.2.0 # via -r requirements/edx/base.in, edx-completion, edx-enterprise, edx-organizations, edx-proctoring, edx-rbac, edx-when, edxval -edx-enterprise==3.10.5 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.in +edx-enterprise==3.11.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.in edx-i18n-tools==0.5.3 # via ora2 edx-milestones==0.3.0 # via -r requirements/edx/base.in edx-opaque-keys[django]==2.1.1 # via -r requirements/edx/paver.txt, edx-bulk-grades, edx-ccx-keys, edx-completion, edx-drf-extensions, edx-enterprise, edx-milestones, edx-organizations, edx-proctoring, edx-user-state-client, edx-when, lti-consumer-xblock, xmodule diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 9d4aab10ec..7e256d571b 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -109,7 +109,7 @@ edx-django-release-util==0.4.4 # via -r requirements/edx/testing.txt edx-django-sites-extensions==2.5.1 # via -r requirements/edx/testing.txt edx-django-utils==3.11.0 # via -r requirements/edx/testing.txt, django-config-models, edx-drf-extensions, edx-enterprise, edx-rest-api-client, edx-toggles, edx-when edx-drf-extensions==6.2.0 # via -r requirements/edx/testing.txt, edx-completion, edx-enterprise, edx-organizations, edx-proctoring, edx-rbac, edx-when, edxval -edx-enterprise==3.10.5 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt +edx-enterprise==3.11.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt edx-i18n-tools==0.5.3 # via -r requirements/edx/testing.txt, ora2 edx-lint==1.5.2 # via -r requirements/edx/testing.txt edx-milestones==0.3.0 # via -r requirements/edx/testing.txt diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 8f22b76632..7014bd549e 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -106,7 +106,7 @@ edx-django-release-util==0.4.4 # via -r requirements/edx/base.txt edx-django-sites-extensions==2.5.1 # via -r requirements/edx/base.txt edx-django-utils==3.11.0 # via -r requirements/edx/base.txt, django-config-models, edx-drf-extensions, edx-enterprise, edx-rest-api-client, edx-toggles, edx-when edx-drf-extensions==6.2.0 # via -r requirements/edx/base.txt, edx-completion, edx-enterprise, edx-organizations, edx-proctoring, edx-rbac, edx-when, edxval -edx-enterprise==3.10.5 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt +edx-enterprise==3.11.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt edx-i18n-tools==0.5.3 # via -r requirements/edx/base.txt, -r requirements/edx/testing.in, ora2 edx-lint==1.5.2 # via -r requirements/edx/testing.in edx-milestones==0.3.0 # via -r requirements/edx/base.txt