diff --git a/openedx/core/djangoapps/user_api/models.py b/openedx/core/djangoapps/user_api/models.py index ca2e28f7c80c3ae1f940318f6b959b3216ef2772..64f45cbdcd72c59cc75d32560e6ded793c791d14 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 c52b198b31bc9c3edf9ef8d8ab3e59e18c9d73bb..b38f2536a98e11f65f7da1ddba09c2a26a2656a2 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 db72b4854214339642c5dd1d3178843f2dd93674..162b83a2a93ed5e67d03ff027dad37c996d2cd0f 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 f8133f0fcc38293a292c1faaccfd9c09e513028d..32f69d4c8765b7216088bf0db8c9d31ae55ee04e 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 13071d0a7562216a9a923f876f3d59b5280bdebc..45d144b02fcf86840975c6a686d4ed96547650fb 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 9d4aab10ecf7d8df2984ad25e58bc0dfc3009f4b..7e256d571b0f9b0f3a290eb95c1f653b1ea16ee6 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 8f22b76632f1b07f69bbd7a33afc355ef02c85a2..7014bd549ed2a40acf7cafa466770be39b17e364 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