Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
dd4d13af
Unverified
Commit
dd4d13af
authored
Dec 13, 2017
by
Bill Filler
Committed by
GitHub
Dec 13, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #16611 from edx/afzaledx/WL-1219
Added extended profile fields to the Account settings page.
parents
773b622e
f589dc9d
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
200 additions
and
13 deletions
+200
-13
lms/djangoapps/student_account/views.py
+62
-3
lms/envs/common.py
+1
-0
lms/static/js/student_account/models/user_account_model.js
+2
-1
lms/static/js/student_account/views/account_settings_factory.js
+34
-2
lms/static/js/student_account/views/account_settings_fields.js
+58
-2
lms/templates/student_account/account_settings.html
+4
-2
openedx/core/djangoapps/user_api/accounts/api.py
+11
-0
openedx/core/djangoapps/user_api/accounts/serializers.py
+24
-0
openedx/core/djangoapps/user_api/accounts/tests/test_api.py
+1
-0
openedx/core/djangoapps/user_api/accounts/tests/test_views.py
+3
-3
No files found.
lms/djangoapps/student_account/views.py
View file @
dd4d13af
...
...
@@ -2,9 +2,9 @@
import
json
import
logging
import
urlparse
from
datetime
import
datetime
import
urlparse
from
django.conf
import
settings
from
django.contrib
import
messages
from
django.contrib.auth
import
get_user_model
...
...
@@ -16,7 +16,6 @@ from django.utils.translation import ugettext as _
from
django.views.decorators.csrf
import
ensure_csrf_cookie
from
django.views.decorators.http
import
require_http_methods
from
django_countries
import
countries
import
third_party_auth
from
edxmako.shortcuts
import
render_to_response
from
lms.djangoapps.commerce.models
import
CommerceConfiguration
...
...
@@ -403,6 +402,65 @@ def _get_form_descriptions(request):
}
def
_get_extended_profile_fields
():
"""Retrieve the extended profile fields from site configuration to be shown on the
Account Settings page
Returns:
A list of dicts. Each dict corresponds to a single field. The keys per field are:
"field_name" : name of the field stored in user_profile.meta
"field_label" : The label of the field.
"field_type" : TextField or ListField
"field_options": a list of tuples for options in the dropdown in case of ListField
"""
extended_profile_fields
=
[]
fields_already_showing
=
[
'username'
,
'name'
,
'email'
,
'pref-lang'
,
'country'
,
'time_zone'
,
'level_of_education'
,
'gender'
,
'year_of_birth'
,
'language_proficiencies'
,
'social_links'
]
field_labels_map
=
{
"first_name"
:
_
(
u"First Name"
),
"last_name"
:
_
(
u"Last Name"
),
"city"
:
_
(
u"City"
),
"state"
:
_
(
u"State/Province/Region"
),
"company"
:
_
(
u"Company"
),
"title"
:
_
(
u"Title"
),
"mailing_address"
:
_
(
u"Mailing address"
),
"goals"
:
_
(
u"Tell us why you're interested in {platform_name}"
)
.
format
(
platform_name
=
configuration_helpers
.
get_value
(
"PLATFORM_NAME"
,
settings
.
PLATFORM_NAME
)
),
"profession"
:
_
(
"Profession"
),
"specialty"
:
_
(
"Specialty"
)
}
extended_profile_field_names
=
configuration_helpers
.
get_value
(
'extended_profile_fields'
,
[])
for
field_to_exclude
in
fields_already_showing
:
if
field_to_exclude
in
extended_profile_field_names
:
extended_profile_field_names
.
remove
(
field_to_exclude
)
# pylint: disable=no-member
extended_profile_field_options
=
configuration_helpers
.
get_value
(
'EXTRA_FIELD_OPTIONS'
,
[])
extended_profile_field_option_tuples
=
{}
for
field
in
extended_profile_field_options
.
keys
():
field_options
=
extended_profile_field_options
[
field
]
extended_profile_field_option_tuples
[
field
]
=
[(
option
.
lower
(),
option
)
for
option
in
field_options
]
for
field
in
extended_profile_field_names
:
field_dict
=
{
"field_name"
:
field
,
"field_label"
:
field_labels_map
.
get
(
field
,
field
),
}
field_options
=
extended_profile_field_option_tuples
.
get
(
field
)
if
field_options
:
field_dict
[
"field_type"
]
=
"ListField"
field_dict
[
"field_options"
]
=
field_options
else
:
field_dict
[
"field_type"
]
=
"TextField"
extended_profile_fields
.
append
(
field_dict
)
return
extended_profile_fields
def
_external_auth_intercept
(
request
,
mode
):
"""Allow external auth to intercept a login/registration request.
...
...
@@ -564,7 +622,8 @@ def account_settings_context(request):
'disable_courseware_js'
:
True
,
'show_program_listing'
:
ProgramsApiConfig
.
is_enabled
(),
'show_dashboard_tabs'
:
True
,
'order_history'
:
user_orders
'order_history'
:
user_orders
,
'extended_profile_fields'
:
_get_extended_profile_fields
(),
}
enterprise_customer_name
=
None
...
...
lms/envs/common.py
View file @
dd4d13af
...
...
@@ -3096,6 +3096,7 @@ ACCOUNT_VISIBILITY_CONFIGURATION = {
"requires_parental_consent"
,
"account_privacy"
,
"accomplishments_shared"
,
"extended_profile"
,
]
}
...
...
lms/static/js/student_account/models/user_account_model.js
View file @
dd4d13af
...
...
@@ -24,7 +24,8 @@
requires_parental_consent
:
true
,
profile_image
:
null
,
accomplishments_shared
:
false
,
default_public_account_fields
:
[]
default_public_account_fields
:
[],
extended_profile
:
[]
},
parse
:
function
(
response
)
{
...
...
lms/static/js/student_account/views/account_settings_factory.js
View file @
dd4d13af
...
...
@@ -26,14 +26,15 @@
syncLearnerProfileData
,
enterpriseName
,
enterpriseReadonlyAccountFields
,
edxSupportUrl
edxSupportUrl
,
extendedProfileFields
)
{
var
$accountSettingsElement
,
userAccountModel
,
userPreferencesModel
,
aboutSectionsData
,
accountsSectionData
,
ordersSectionData
,
accountSettingsView
,
showAccountSettingsPage
,
showLoadingError
,
orderNumber
,
getUserField
,
userFields
,
timeZoneDropdownField
,
countryDropdownField
,
emailFieldView
,
socialFields
,
platformData
,
aboutSectionMessageType
,
aboutSectionMessage
,
fullnameFieldView
,
countryFieldView
,
fullNameFieldData
,
emailFieldData
,
countryFieldData
;
fullNameFieldData
,
emailFieldData
,
countryFieldData
,
additionalFields
,
fieldItem
;
$accountSettingsElement
=
$
(
'.wrapper-account-settings'
);
...
...
@@ -231,6 +232,37 @@
}
];
// Add the extended profile fields
additionalFields
=
aboutSectionsData
[
1
];
for
(
var
field
in
extendedProfileFields
)
{
// eslint-disable-line guard-for-in, no-restricted-syntax, vars-on-top, max-len
fieldItem
=
extendedProfileFields
[
field
];
if
(
fieldItem
.
field_type
===
'TextField'
)
{
additionalFields
.
fields
.
push
({
view
:
new
AccountSettingsFieldViews
.
ExtendedFieldTextFieldView
({
model
:
userAccountModel
,
title
:
fieldItem
.
field_label
,
fieldName
:
fieldItem
.
field_name
,
valueAttribute
:
'extended_profile'
,
persistChanges
:
true
})
});
}
else
{
if
(
fieldItem
.
field_type
===
'ListField'
)
{
additionalFields
.
fields
.
push
({
view
:
new
AccountSettingsFieldViews
.
ExtendedFieldListFieldView
({
model
:
userAccountModel
,
title
:
fieldItem
.
field_label
,
fieldName
:
fieldItem
.
field_name
,
options
:
fieldItem
.
field_options
,
valueAttribute
:
'extended_profile'
,
persistChanges
:
true
})
});
}
}
}
// Add the social link fields
socialFields
=
{
title
:
gettext
(
'Social Media Links'
),
...
...
lms/static/js/student_account/views/account_settings_fields.js
View file @
dd4d13af
...
...
@@ -217,9 +217,10 @@
}
},
saveValue
:
function
()
{
var
attributes
=
{},
value
=
''
;
if
(
this
.
persistChanges
===
true
)
{
var
attributes
=
{},
value
=
this
.
fieldValue
()
?
[{
code
:
this
.
fieldValue
()}]
:
[];
value
=
this
.
fieldValue
()
?
[{
code
:
this
.
fieldValue
()}]
:
[];
attributes
[
this
.
options
.
valueAttribute
]
=
value
;
this
.
saveAttributes
(
attributes
);
}
...
...
@@ -258,6 +259,61 @@
}
}
}),
ExtendedFieldTextFieldView
:
FieldViews
.
TextFieldView
.
extend
({
render
:
function
()
{
HtmlUtils
.
setHtml
(
this
.
$el
,
HtmlUtils
.
template
(
field_text_account_template
)({
id
:
this
.
options
.
valueAttribute
+
'_'
+
this
.
options
.
field_name
,
title
:
this
.
options
.
title
,
value
:
this
.
modelValue
(),
message
:
this
.
options
.
helpMessage
,
placeholder
:
this
.
options
.
placeholder
||
''
}));
this
.
delegateEvents
();
return
this
;
},
modelValue
:
function
()
{
var
extendedProfileFields
=
this
.
model
.
get
(
this
.
options
.
valueAttribute
);
for
(
var
i
=
0
;
i
<
extendedProfileFields
.
length
;
i
++
)
{
// eslint-disable-line vars-on-top
if
(
extendedProfileFields
[
i
].
field_name
===
this
.
options
.
fieldName
)
{
return
extendedProfileFields
[
i
].
field_value
;
}
}
return
null
;
},
saveValue
:
function
()
{
var
attributes
,
value
;
if
(
this
.
persistChanges
===
true
)
{
attributes
=
{};
value
=
this
.
fieldValue
()
!=
null
?
[{
field_name
:
this
.
options
.
fieldName
,
field_value
:
this
.
fieldValue
()}]
:
[];
attributes
[
this
.
options
.
valueAttribute
]
=
value
;
this
.
saveAttributes
(
attributes
);
}
}
}),
ExtendedFieldListFieldView
:
FieldViews
.
DropdownFieldView
.
extend
({
fieldTemplate
:
field_dropdown_account_template
,
modelValue
:
function
()
{
var
extendedProfileFields
=
this
.
model
.
get
(
this
.
options
.
valueAttribute
);
for
(
var
i
=
0
;
i
<
extendedProfileFields
.
length
;
i
++
)
{
// eslint-disable-line vars-on-top
if
(
extendedProfileFields
[
i
].
field_name
===
this
.
options
.
fieldName
)
{
return
extendedProfileFields
[
i
].
field_value
;
}
}
return
null
;
},
saveValue
:
function
()
{
var
attributes
=
{},
value
;
if
(
this
.
persistChanges
===
true
)
{
value
=
this
.
fieldValue
()
?
[{
field_name
:
this
.
options
.
fieldName
,
field_value
:
this
.
fieldValue
()}]
:
[];
attributes
[
this
.
options
.
valueAttribute
]
=
value
;
this
.
saveAttributes
(
attributes
);
}
}
}),
AuthFieldView
:
FieldViews
.
LinkFieldView
.
extend
({
fieldTemplate
:
field_social_link_template
,
className
:
function
()
{
...
...
lms/templates/student_account/account_settings.html
View file @
dd4d13af
...
...
@@ -43,7 +43,8 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
syncLearnerProfileData = ${ bool(sync_learner_profile_data) | n, dump_js_escaped_json },
enterpriseName = '${ enterprise_name | n, js_escaped_string }',
enterpriseReadonlyAccountFields = ${ enterprise_readonly_account_fields | n, dump_js_escaped_json },
edxSupportUrl = '${ edx_support_url | n, js_escaped_string }';
edxSupportUrl = '${ edx_support_url | n, js_escaped_string }',
extendedProfileFields = ${ extended_profile_fields | n, dump_js_escaped_json };
AccountSettingsFactory(
fieldsData,
...
...
@@ -61,7 +62,8 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
syncLearnerProfileData,
enterpriseName,
enterpriseReadonlyAccountFields,
edxSupportUrl
edxSupportUrl,
extendedProfileFields
);
</
%
static:require
_module
>
</
%
block>
openedx/core/djangoapps/user_api/accounts/api.py
View file @
dd4d13af
...
...
@@ -229,6 +229,17 @@ def update_account_settings(requesting_user, update, username=None):
existing_user_profile
.
set_meta
(
meta
)
existing_user_profile
.
save
()
# updating extended user profile info
if
'extended_profile'
in
update
:
meta
=
existing_user_profile
.
get_meta
()
new_extended_profile
=
update
[
'extended_profile'
]
for
field
in
new_extended_profile
:
field_name
=
field
[
'field_name'
]
new_value
=
field
[
'field_value'
]
meta
[
field_name
]
=
new_value
existing_user_profile
.
set_meta
(
meta
)
existing_user_profile
.
save
()
except
PreferenceValidationError
as
err
:
raise
AccountValidationError
(
err
.
preference_errors
)
except
AccountValidationError
as
err
:
...
...
openedx/core/djangoapps/user_api/accounts/serializers.py
View file @
dd4d13af
"""
Django REST Framework serializers for the User API Accounts sub-application
"""
import
json
import
logging
from
rest_framework
import
serializers
...
...
@@ -10,6 +11,7 @@ from django.core.exceptions import ObjectDoesNotExist
from
django.core.urlresolvers
import
reverse
from
lms.djangoapps.badges.utils
import
badges_enabled
from
openedx.core.djangoapps.site_configuration
import
helpers
as
configuration_helpers
from
openedx.core.djangoapps.user_api
import
errors
from
openedx.core.djangoapps.user_api.models
import
UserPreference
from
openedx.core.djangoapps.user_api.serializers
import
ReadOnlyFieldsSerializerMixin
...
...
@@ -112,6 +114,7 @@ class UserReadOnlySerializer(serializers.Serializer):
"accomplishments_shared"
:
accomplishments_shared
,
"account_privacy"
:
self
.
configuration
.
get
(
'default_visibility'
),
"social_links"
:
None
,
"extended_profile_fields"
:
None
,
}
if
user_profile
:
...
...
@@ -138,6 +141,7 @@ class UserReadOnlySerializer(serializers.Serializer):
"social_links"
:
SocialLinkSerializer
(
user_profile
.
social_links
.
all
(),
many
=
True
)
.
data
,
"extended_profile"
:
get_extended_profile
(
user_profile
),
}
)
...
...
@@ -327,6 +331,26 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea
return
instance
def
get_extended_profile
(
user_profile
):
"""Returns the extended user profile fields stored in user_profile.meta"""
# pick the keys from the site configuration
extended_profile_field_names
=
configuration_helpers
.
get_value
(
'extended_profile_fields'
,
[])
try
:
extended_profile_fields_data
=
json
.
loads
(
user_profile
.
meta
)
except
ValueError
:
extended_profile_fields_data
=
{}
extended_profile
=
[]
for
field_name
in
extended_profile_field_names
:
extended_profile
.
append
({
"field_name"
:
field_name
,
"field_value"
:
extended_profile_fields_data
.
get
(
field_name
,
""
)
})
return
extended_profile
def
get_profile_visibility
(
user_profile
,
user
,
configuration
=
None
):
"""Returns the visibility level for the specified user profile."""
if
user_profile
.
requires_parental_consent
():
...
...
openedx/core/djangoapps/user_api/accounts/tests/test_api.py
View file @
dd4d13af
...
...
@@ -318,6 +318,7 @@ class AccountSettingsOnCreationTest(TestCase):
'language_proficiencies'
:
[],
'account_privacy'
:
PRIVATE_VISIBILITY
,
'accomplishments_shared'
:
False
,
'extended_profile'
:
[],
})
...
...
openedx/core/djangoapps/user_api/accounts/tests/test_views.py
View file @
dd4d13af
...
...
@@ -250,7 +250,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
Verify that all account fields are returned (even those that are not shareable).
"""
data
=
response
.
data
self
.
assertEqual
(
1
8
,
len
(
data
))
self
.
assertEqual
(
1
9
,
len
(
data
))
self
.
assertEqual
(
self
.
user
.
username
,
data
[
"username"
])
self
.
assertEqual
(
self
.
user
.
first_name
+
" "
+
self
.
user
.
last_name
,
data
[
"name"
])
self
.
assertEqual
(
"US"
,
data
[
"country"
])
...
...
@@ -382,7 +382,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
with
self
.
assertNumQueries
(
queries
):
response
=
self
.
send_get
(
self
.
client
)
data
=
response
.
data
self
.
assertEqual
(
1
8
,
len
(
data
))
self
.
assertEqual
(
1
9
,
len
(
data
))
self
.
assertEqual
(
self
.
user
.
username
,
data
[
"username"
])
self
.
assertEqual
(
self
.
user
.
first_name
+
" "
+
self
.
user
.
last_name
,
data
[
"name"
])
for
empty_field
in
(
"year_of_birth"
,
"level_of_education"
,
"mailing_address"
,
"bio"
):
...
...
@@ -776,7 +776,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
response
=
self
.
send_get
(
client
)
if
has_full_access
:
data
=
response
.
data
self
.
assertEqual
(
1
8
,
len
(
data
))
self
.
assertEqual
(
1
9
,
len
(
data
))
self
.
assertEqual
(
self
.
user
.
username
,
data
[
"username"
])
self
.
assertEqual
(
self
.
user
.
first_name
+
" "
+
self
.
user
.
last_name
,
data
[
"name"
])
self
.
assertEqual
(
self
.
user
.
email
,
data
[
"email"
])
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment