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
aac28d08
Commit
aac28d08
authored
Jul 14, 2014
by
sjang92
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #74 from Stanford-Online/sjang92/origin/enrollment-email
Sjang92/origin/enrollment email
parents
44129cc4
496eff88
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
347 additions
and
14 deletions
+347
-14
AUTHORS
+3
-2
cms/djangoapps/contentstore/tests/test_course_settings.py
+2
-0
cms/djangoapps/models/settings/course_details.py
+43
-1
cms/static/js/models/settings/course_details.js
+5
-4
cms/static/js/views/settings/main.js
+92
-1
cms/static/sass/views/_settings.scss
+43
-0
cms/templates/settings.html
+48
-1
common/djangoapps/student/models.py
+3
-0
common/djangoapps/student/tests/test_email.py
+45
-2
common/djangoapps/student/views.py
+54
-2
common/lib/xmodule/xmodule/course_module.py
+2
-0
lms/djangoapps/courseware/courses.py
+7
-1
No files found.
AUTHORS
View file @
aac28d08
...
...
@@ -154,4 +154,6 @@ Abdallah Nassif <abdoosh00@gmail.com>
Johnny Brown <johnnybrown7@gmail.com>
Ben McMorran <bmcmorran@edx.org>
Mat Peterson <mpeterson@edx.org>
Tim Babych <tim.babych@gmail.com>
\ No newline at end of file
Tim Babych <tim.babych@gmail.com>
Se Won Jang <swjang@stanford.edu>
cms/djangoapps/contentstore/tests/test_course_settings.py
View file @
aac28d08
...
...
@@ -42,6 +42,8 @@ class CourseDetailsTestCase(CourseTestCase):
self
.
assertIsNone
(
details
.
syllabus
,
"syllabus somehow initialized"
+
str
(
details
.
syllabus
))
self
.
assertIsNone
(
details
.
intro_video
,
"intro_video somehow initialized"
+
str
(
details
.
intro_video
))
self
.
assertIsNone
(
details
.
effort
,
"effort somehow initialized"
+
str
(
details
.
effort
))
self
.
assertFalse
(
details
.
enable_enrollment_email
,
"Enrollment Email should be initialized as false"
)
self
.
assertTrue
(
details
.
enable_default_enrollment_email
,
"Default Template option for enrollment email should be initialized as true"
)
def
test_encoder
(
self
):
details
=
CourseDetails
.
fetch
(
self
.
course
.
id
)
...
...
cms/djangoapps/models/settings/course_details.py
View file @
aac28d08
...
...
@@ -24,10 +24,16 @@ class CourseDetails(object):
self
.
syllabus
=
None
# a pdf file asset
self
.
short_description
=
""
self
.
overview
=
""
# html to render as the overview
self
.
pre_enrollment_email
=
""
# html to render as the pre-enrollment email
self
.
post_enrollment_email
=
""
# html to render as the post-enrollment email
self
.
pre_enrollment_email_subject
=
""
# header of the pre_enrollment_email
self
.
post_enrollment_email_subject
=
""
# header of the post_enrollment_email
self
.
intro_video
=
None
# a video pointer
self
.
effort
=
None
# int hours/week
self
.
course_image_name
=
""
self
.
course_image_asset_path
=
""
# URL of the course image
self
.
enable_enrollment_email
=
False
self
.
enable_default_enrollment_email
=
True
@classmethod
def
fetch
(
cls
,
course_key
):
...
...
@@ -43,6 +49,8 @@ class CourseDetails(object):
course_details
.
enrollment_end
=
descriptor
.
enrollment_end
course_details
.
course_image_name
=
descriptor
.
course_image
course_details
.
course_image_asset_path
=
course_image_url
(
descriptor
)
course_details
.
enable_enrollment_email
=
descriptor
.
enable_enrollment_email
course_details
.
enable_default_enrollment_email
=
descriptor
.
enable_default_enrollment_email
temploc
=
course_key
.
make_usage_key
(
'about'
,
'syllabus'
)
try
:
...
...
@@ -61,6 +69,28 @@ class CourseDetails(object):
course_details
.
overview
=
modulestore
()
.
get_item
(
temploc
)
.
data
except
ItemNotFoundError
:
pass
temploc
=
course_key
.
make_usage_key
(
'about'
,
'pre_enrollment_email_subject'
)
try
:
course_details
.
pre_enrollment_email_subject
=
modulestore
()
.
get_item
(
temploc
)
.
data
except
ItemNotFoundError
:
pass
temploc
=
course_key
.
make_usage_key
(
'about'
,
'post_enrollment_email_subject'
)
try
:
course_details
.
post_enrollment_email_subject
=
modulestore
()
.
get_item
(
temploc
)
.
data
except
ItemNotFoundError
:
pass
temploc
=
course_key
.
make_usage_key
(
'about'
,
'pre_enrollment_email'
)
try
:
course_details
.
pre_enrollment_email
=
modulestore
()
.
get_item
(
temploc
)
.
data
except
ItemNotFoundError
:
pass
temploc
=
course_key
.
make_usage_key
(
'about'
,
'post_enrollment_email'
)
try
:
course_details
.
post_enrollment_email
=
modulestore
()
.
get_item
(
temploc
)
.
data
except
ItemNotFoundError
:
pass
temploc
=
course_key
.
make_usage_key
(
'about'
,
'effort'
)
try
:
...
...
@@ -110,6 +140,16 @@ class CourseDetails(object):
# into the model is nasty, convert the JSON Date to a Python date, which is what the
# setter expects as input.
date
=
Date
()
# Added to allow admins to enable/disable default enrollment emails
if
'enable_default_enrollment_email'
in
jsondict
:
descriptor
.
enable_default_enrollment_email
=
jsondict
[
'enable_default_enrollment_email'
]
dirty
=
True
# Added to allow admins to enable/disable enrollment emails
if
'enable_enrollment_email'
in
jsondict
:
descriptor
.
enable_enrollment_email
=
jsondict
[
'enable_enrollment_email'
]
dirty
=
True
if
'start_date'
in
jsondict
:
converted
=
date
.
from_json
(
jsondict
[
'start_date'
])
...
...
@@ -155,7 +195,9 @@ class CourseDetails(object):
# NOTE: below auto writes to the db w/o verifying that any of the fields actually changed
# to make faster, could compare against db or could have client send over a list of which fields changed.
for
about_type
in
[
'syllabus'
,
'overview'
,
'effort'
,
'short_description'
]:
for
about_type
in
[
'syllabus'
,
'overview'
,
'effort'
,
'short_description'
,
'pre_enrollment_email'
,
'post_enrollment_email'
,
'pre_enrollment_email_subject'
,
'post_enrollment_email_subject'
]:
cls
.
update_about_item
(
course_key
,
about_type
,
jsondict
[
about_type
],
descriptor
,
user
)
recomposed_video_tag
=
CourseDetails
.
recompose_video_tag
(
jsondict
[
'intro_video'
])
...
...
cms/static/js/models/settings/course_details.js
View file @
aac28d08
...
...
@@ -5,17 +5,18 @@ var CourseDetails = Backbone.Model.extend({
org
:
''
,
course_id
:
''
,
run
:
''
,
start_date
:
null
,
// maps to 'start'
end_date
:
null
,
// maps to 'end'
start_date
:
null
,
// maps to 'start'
end_date
:
null
,
// maps to 'end'
enrollment_start
:
null
,
enrollment_end
:
null
,
syllabus
:
null
,
short_description
:
""
,
overview
:
""
,
intro_video
:
null
,
effort
:
null
,
// an int or null,
effort
:
null
,
// an int or null,
course_image_name
:
''
,
// the filename
course_image_asset_path
:
''
// the full URL (/c4x/org/course/num/asset/filename)
course_image_asset_path
:
''
,
// the full URL (/c4x/org/course/num/asset/filename)
enable_enrollment_email
:
false
},
// When init'g from html script, ensure you pass {parse: true} as an option (2nd arg to reset)
...
...
cms/static/js/views/settings/main.js
View file @
aac28d08
...
...
@@ -12,6 +12,10 @@ var DetailsView = ValidatingView.extend({
"change textarea"
:
"updateModel"
,
'click .remove-course-introduction-video'
:
"removeVideo"
,
'focus #course-overview'
:
"codeMirrorize"
,
'click #enable-enrollment-email'
:
"toggleEnrollmentEmails"
,
'click #enable-default-enrollment-email'
:
"toggleDefaultEnrollmentEmails"
,
'focus #pre-enrollment-email'
:
"codeMirrorize"
,
'focus #post-enrollment-email'
:
"codeMirrorize"
,
'mouseover #timezone'
:
"updateTime"
,
// would love to move to a general superclass, but event hashes don't inherit in backbone :-(
'focus :input'
:
"inputFocus"
,
...
...
@@ -40,6 +44,21 @@ var DetailsView = ValidatingView.extend({
this
.
listenTo
(
this
.
model
,
'invalid'
,
this
.
handleValidationError
);
this
.
listenTo
(
this
.
model
,
'change'
,
this
.
showNotificationBar
);
this
.
selectorToField
=
_
.
invert
(
this
.
fieldToSelectorMap
);
/* Memoize html elements for enrollment emails */
this
.
pre_enrollment_email_elem
=
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
'pre_enrollment_email'
]);
this
.
pre_enrollment_email_subject_elem
=
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
'pre_enrollment_email_subject'
]);
this
.
pre_enrollment_email_field
=
this
.
$el
.
find
(
'#field-pre-enrollment-email'
);
this
.
pre_enrollment_email_subject_field
=
this
.
$el
.
find
(
'#field-pre-enrollment-email-subject'
);
this
.
post_enrollment_email_elem
=
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
'post_enrollment_email'
]);
this
.
post_enrollment_email_subject_elem
=
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
'post_enrollment_email_subject'
]);
this
.
post_enrollment_email_field
=
this
.
$el
.
find
(
'#field-post-enrollment-email'
);
this
.
post_enrollment_email_subject_field
=
this
.
$el
.
find
(
'#field-post-enrollment-email-subject'
);
this
.
enable_enrollment_email_box
=
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
'enable_enrollment_email'
])[
0
];
this
.
enable_default_enrollment_email_box
=
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
'enable_default_enrollment_email'
])[
0
];
},
render
:
function
()
{
...
...
@@ -51,6 +70,31 @@ var DetailsView = ValidatingView.extend({
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
'overview'
]).
val
(
this
.
model
.
get
(
'overview'
));
this
.
codeMirrorize
(
null
,
$
(
'#course-overview'
)[
0
]);
this
.
pre_enrollment_email_subject_elem
.
val
(
this
.
model
.
get
(
'pre_enrollment_email_subject'
));
this
.
post_enrollment_email_subject_elem
.
val
(
this
.
model
.
get
(
'post_enrollment_email_subject'
));
this
.
pre_enrollment_email_elem
.
val
(
this
.
model
.
get
(
'pre_enrollment_email'
));
this
.
codeMirrorize
(
null
,
$
(
'#pre-enrollment-email'
)[
0
]);
this
.
post_enrollment_email_elem
.
val
(
this
.
model
.
get
(
'post_enrollment_email'
));
this
.
codeMirrorize
(
null
,
$
(
'#post-enrollment-email'
)[
0
]);
this
.
enable_enrollment_email_box
.
checked
=
this
.
model
.
get
(
'enable_enrollment_email'
);
this
.
enable_default_enrollment_email_box
.
checked
=
this
.
model
.
get
(
'enable_default_enrollment_email'
);
if
(
this
.
model
.
get
(
'enable_enrollment_email'
))
{
this
.
pre_enrollment_email_field
.
show
();
this
.
post_enrollment_email_field
.
show
();
this
.
pre_enrollment_email_subject_field
.
show
();
this
.
post_enrollment_email_subject_field
.
show
();
}
else
{
this
.
pre_enrollment_email_field
.
hide
();
this
.
post_enrollment_email_field
.
hide
();
this
.
pre_enrollment_email_subject_field
.
hide
();
this
.
post_enrollment_email_subject_field
.
hide
();
}
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
'short_description'
]).
val
(
this
.
model
.
get
(
'short_description'
));
this
.
$el
.
find
(
'.current-course-introduction-video iframe'
).
attr
(
'src'
,
this
.
model
.
videosourceSample
());
...
...
@@ -74,10 +118,16 @@ var DetailsView = ValidatingView.extend({
'enrollment_start'
:
'enrollment-start'
,
'enrollment_end'
:
'enrollment-end'
,
'overview'
:
'course-overview'
,
'pre_enrollment_email'
:
'pre-enrollment-email'
,
'post_enrollment_email'
:
'post-enrollment-email'
,
'short_description'
:
'course-short-description'
,
'intro_video'
:
'course-introduction-video'
,
'effort'
:
"course-effort"
,
'course_image_asset_path'
:
'course-image-url'
'course_image_asset_path'
:
'course-image-url'
,
'enable_enrollment_email'
:
'enable-enrollment-email'
,
'pre_enrollment_email_subject'
:
'pre-enrollment-email-subject'
,
'post_enrollment_email_subject'
:
'post-enrollment-email-subject'
,
'enable_default_enrollment_email'
:
'enable-default-enrollment-email'
},
updateTime
:
function
(
e
)
{
...
...
@@ -152,6 +202,12 @@ var DetailsView = ValidatingView.extend({
case
'course-effort'
:
this
.
setField
(
event
);
break
;
case
'pre-enrollment-email-subject'
:
this
.
setField
(
event
);
break
;
case
'post-enrollment-email-subject'
:
this
.
setField
(
event
);
break
;
case
'course-short-description'
:
this
.
setField
(
event
);
break
;
...
...
@@ -185,6 +241,41 @@ var DetailsView = ValidatingView.extend({
this
.
$el
.
find
(
'.remove-course-introduction-video'
).
hide
();
}
},
toggleEnrollmentEmails
:
function
(
event
)
{
var
isChecked
=
this
.
enable_enrollment_email_box
.
checked
;
if
(
isChecked
)
{
this
.
pre_enrollment_email_field
.
show
();
this
.
post_enrollment_email_field
.
show
();
this
.
pre_enrollment_email_subject_field
.
show
();
this
.
post_enrollment_email_subject_field
.
show
();
}
else
{
this
.
pre_enrollment_email_field
.
hide
();
this
.
post_enrollment_email_field
.
hide
();
this
.
pre_enrollment_email_subject_field
.
hide
();
this
.
post_enrollment_email_subject_field
.
hide
();
/* If enrollment email sending option is turned off, default email option should be turned off as well. */
this
.
enable_default_enrollment_email_box
.
checked
=
false
;
this
.
setAndValidate
(
this
.
selectorToField
[
'enable-default-enrollment-email'
],
false
);
}
var
field
=
this
.
selectorToField
[
'enable-enrollment-email'
];
if
(
this
.
model
.
get
(
field
)
!=
isChecked
)
{
this
.
setAndValidate
(
field
,
isChecked
);
}
},
toggleDefaultEnrollmentEmails
:
function
(
event
)
{
var
isChecked
=
this
.
enable_default_enrollment_email_box
.
checked
;
var
field
=
this
.
selectorToField
[
'enable-default-enrollment-email'
];
if
(
this
.
model
.
get
(
field
)
!=
isChecked
)
{
this
.
setAndValidate
(
field
,
isChecked
);
}
},
codeMirrors
:
{},
codeMirrorize
:
function
(
e
,
forcedTarget
)
{
var
thisTarget
;
...
...
cms/static/sass/views/_settings.scss
View file @
aac28d08
...
...
@@ -202,6 +202,23 @@
display
:
inline-block
;
}
}
.list-actions
{
padding-top
:
(
$baseline
/
2
);
.action-primary
{
@include
blue-button
();
@extend
%t-action3
;
font-weight
:
600
;
[
class
^=
"icon-"
]
{
@extend
%t-icon5
;
display
:
inline-block
;
vertical-align
:
middle
;
margin-top
:
-3px
;
}
}
}
}
.field-group
{
...
...
@@ -404,6 +421,32 @@
}
}
// specific fields - pre-enrollment email
#field-pre-enrollment-email
{
#pre-enrollment-email
{
height
:
(
$baseline
*
20
);
}
//adds back in CodeMirror border removed due to Unit page styling of component editors
.CodeMirror
{
border
:
1px
solid
$gray-l2
;
}
}
// specific fields - post-enrollment email
#field-post-enrollment-email
{
#post-enrollment-email
{
height
:
(
$baseline
*
20
);
}
//adds back in CodeMirror border removed due to Unit page styling of component editors
.CodeMirror
{
border
:
1px
solid
$gray-l2
;
}
}
// specific fields - video
#field-course-introduction-video
{
...
...
cms/templates/settings.html
View file @
aac28d08
...
...
@@ -205,7 +205,7 @@ require(["domReady!", "jquery", "js/models/settings/course_details", "js/views/s
<ol
class=
"list-input"
>
% if short_description_editable:
<li
class=
"field text"
id=
"field-course-short-description"
>
<label
for=
"course-
overview
"
>
${_("Course Short Description")}
</label>
<label
for=
"course-
short-description
"
>
${_("Course Short Description")}
</label>
<textarea
class=
"text"
id=
"course-short-description"
></textarea>
<span
class=
"tip tip-stacked"
>
${_("Appears on the course catalog page when students roll over the course name. Limit to ~150 characters")}
</span>
</li>
...
...
@@ -225,6 +225,53 @@ require(["domReady!", "jquery", "js/models/settings/course_details", "js/views/s
</li>
% endif
<li
class=
"field text"
id=
"field-enable-enrollment-email"
>
<label
for=
"course-email"
>
${_("Course Enrollment Email")}
</label>
<input
type=
"checkbox"
id=
"enable-enrollment-email"
/>
<label
for=
"enable-enrollment-email"
>
${_("Enable enrollment emails")}
</label>
</li>
<li
class=
"field text"
id=
"field-enable-default-enrollment-email"
>
<input
type=
"checkbox"
id=
"enable-default-enrollment-email"
/>
<label
for=
"enable-enrollment-email"
>
${_("Use default enrollment emails")}
</label>
</li>
<li
class=
"field text"
id=
"field-pre-enrollment-email-subject"
>
<label
for=
"pre-enrollment-email-subject"
>
${_("Subject of the Email sent to students who enroll before the course starts")}
</label>
<textarea
class =
'text'
id=
"pre-enrollment-email-subject"
></textarea>
</li>
<li
class=
"field text"
id=
"field-pre-enrollment-email"
>
<label
for=
"pre-enrollment-email"
>
${_("Email sent to students who enroll before the course starts")}
</label>
<textarea
class=
"tinymce text-editor"
id=
"pre-enrollment-email"
></textarea>
<span
class=
"tip tip-stacked"
>
${_("This email will be sent to any student who enrolls in the course before its start date")}
</span>
<ul
class=
"list-actions"
>
<li
class=
"action-item"
>
<a
title=
"${_('Send me a copy of this via email')}"
href=
""
class=
"action action-primary"
>
${_("Send me a test email")}
</a>
</li>
</ul>
</li>
<li
class=
"field text"
id=
"field-post-enrollment-email-subject"
>
<label
for=
"post-enrollment-email-subject"
>
${_("Subject of the Email sent to students who enroll after the course starts")}
</label>
<textarea
class =
'text'
id=
"post-enrollment-email-subject"
></textarea>
</li>
<li
class=
"field text"
id=
"field-post-enrollment-email"
>
<label
for=
"post-enrollment-email"
>
${_("Email sent to students who enroll after the course starts")}
</label>
<textarea
class=
"tinymce text-editor"
id=
"post-enrollment-email"
></textarea>
<span
class=
"tip tip-stacked"
>
${_("This email will be sent to any student who enrolls in the course after its start date")}
</span>
<ul
class=
"list-actions"
>
<li
class=
"action-item"
>
<a
title=
"${_('Send me a copy of this via email')}"
href=
""
class=
"action action-primary"
>
${_("Send me a test email")}
</a>
</li>
</ul>
</li>
<li
class=
"field image"
id=
"field-course-image"
>
<label>
${_("Course Image")}
</label>
<div
class=
"current current-course-image"
>
...
...
common/djangoapps/student/models.py
View file @
aac28d08
...
...
@@ -791,6 +791,9 @@ class CourseEnrollment(models.Model):
may include "audit", "verified_id", etc. Please don't use it
until we have these mapped out.
'should_send_email' is a boolean that specifies if a course enrollment
email should be sent to the given user.
It is expected that this method is called from a method which has already
verified the user authentication and access.
"""
...
...
common/djangoapps/student/tests/test_email.py
View file @
aac28d08
...
...
@@ -3,7 +3,7 @@ import django.db
import
unittest
from
student.tests.factories
import
UserFactory
,
RegistrationFactory
,
PendingEmailChangeFactory
from
student.views
import
reactivation_email_for_user
,
change_email_request
,
confirm_email_change
from
student.views
import
reactivation_email_for_user
,
change_email_request
,
confirm_email_change
,
notify_enrollment_by_email
from
student.models
import
UserProfile
,
PendingEmailChange
from
django.contrib.auth.models
import
User
,
AnonymousUser
from
django.test
import
TestCase
,
TransactionTestCase
...
...
@@ -14,7 +14,8 @@ from django.conf import settings
from
edxmako.shortcuts
import
render_to_string
from
util.request
import
safe_get_host
from
textwrap
import
dedent
from
microsite_configuration
import
microsite
from
xmodule.modulestore.tests.factories
import
CourseFactory
class
TestException
(
Exception
):
"""Exception used for testing that nothing will catch explicitly"""
...
...
@@ -58,6 +59,48 @@ class EmailTestMixin(object):
settings
.
ALLOWED_HOSTS
.
append
(
hostname
)
self
.
addCleanup
(
settings
.
ALLOWED_HOSTS
.
pop
)
class
EnrollmentEmailTests
(
TestCase
):
""" Test senging automated emails to users upon course enrollment. """
def
setUp
(
self
):
# Test Contstants
COURSE_SLUG
=
"100"
COURSE_NAME
=
"test_course"
COURSE_ORG
=
"EDX"
self
.
user
=
UserFactory
.
create
(
username
=
"tester"
,
email
=
"tester@gmail.com"
,
password
=
"test"
)
self
.
course
=
CourseFactory
.
create
(
org
=
COURSE_ORG
,
display_name
=
COURSE_NAME
,
number
=
COURSE_SLUG
)
self
.
assertIsNotNone
(
self
.
course
)
self
.
request
=
RequestFactory
()
.
post
(
'random_url'
)
self
.
request
.
user
=
self
.
user
def
send_enrollment_email
(
self
):
""" Send enrollment email to the user and return the Json response data. """
return
json
.
loads
(
notify_enrollment_by_email
(
self
.
course
,
self
.
user
,
self
.
request
)
.
content
)
def
test_disabled_email_case
(
self
):
""" Make sure emails don't fire when enable_enrollment_email setting is disabled. """
self
.
course
.
enable_enrollment_email
=
False
email_result
=
self
.
send_enrollment_email
()
self
.
assertIn
(
'email_did_fire'
,
email_result
)
self
.
assertFalse
(
email_result
[
'email_did_fire'
])
def
test_custom_enrollment_email_sent
(
self
):
""" Test sending of enrollment emails when enable_default_enrollment_email setting is disabled. """
self
.
course
.
enable_enrollment_email
=
True
self
.
course
.
enable_default_enrollment_email
=
False
email_result
=
self
.
send_enrollment_email
()
self
.
assertNotIn
(
'email_did_fire'
,
email_result
)
self
.
assertIn
(
'is_success'
,
email_result
)
def
test_default_enrollment_email_sent
(
self
):
""" Test sending of enrollment emails when enable_default_enrollment_email setting is enabled. """
self
.
course
.
enable_enrollment_email
=
True
self
.
course
.
enable_default_enrollment_email
=
True
email_result
=
self
.
send_enrollment_email
()
self
.
assertNotIn
(
'email_did_fire'
,
email_result
)
self
.
assertIn
(
'is_success'
,
email_result
)
@patch
(
'student.views.render_to_string'
,
Mock
(
side_effect
=
mock_render_to_string
,
autospec
=
True
))
@patch
(
'django.contrib.auth.models.User.email_user'
)
...
...
common/djangoapps/student/views.py
View file @
aac28d08
...
...
@@ -8,6 +8,8 @@ import uuid
import
time
from
collections
import
defaultdict
from
pytz
import
UTC
from
pytz
import
timezone
import
json
from
django.conf
import
settings
from
django.contrib.auth
import
logout
,
authenticate
,
login
...
...
@@ -57,7 +59,7 @@ from xmodule.modulestore import ModuleStoreEnum
from
collections
import
namedtuple
from
courseware.courses
import
get_courses
,
sort_by_announcement
from
courseware.courses
import
get_courses
,
sort_by_announcement
,
get_course_about_section
from
courseware.access
import
has_access
from
courseware.models
import
CoursePreference
...
...
@@ -682,7 +684,11 @@ def change_enrollment(request):
current_mode
=
available_modes
[
0
]
CourseEnrollment
.
enroll
(
user
,
course
.
id
,
mode
=
current_mode
.
slug
)
# notify the user of the enrollment via email
enrollment_email_result
=
json
.
loads
(
notify_enrollment_by_email
(
course
,
user
,
request
)
.
content
)
if
(
'is_success'
in
enrollment_email_result
and
not
enrollment_email_result
[
'is_success'
]):
return
HttpResponseBadRequest
(
_
(
enrollment_email_result
[
'error'
]))
return
HttpResponse
()
elif
action
==
"add_to_cart"
:
...
...
@@ -706,6 +712,52 @@ def change_enrollment(request):
else
:
return
HttpResponseBadRequest
(
_
(
"Enrollment action is invalid"
))
def
notify_enrollment_by_email
(
course
,
user
,
request
):
"""
Updates the user about the course enrollment by email.
If the Course has already started, use post_enrollment_email
If the Course has not yet started, use pre_enrollment_email
"""
if
(
not
(
settings
.
FEATURES
.
get
(
'AUTOMATIC_AUTH_FOR_TESTING'
))
and
course
.
enable_enrollment_email
):
from_address
=
microsite
.
get_value
(
'email_from_address'
,
settings
.
DEFAULT_FROM_EMAIL
)
try
:
if
(
not
course
.
enable_default_enrollment_email
):
# Check if the course has already started and set subject & message accordingly
if
(
course
.
has_started
):
subject
=
get_course_about_section
(
course
,
'post_enrollment_email_subject'
)
message
=
get_course_about_section
(
course
,
'post_enrollment_email'
)
else
:
subject
=
get_course_about_section
(
course
,
'pre_enrollment_email_subject'
)
message
=
get_course_about_section
(
course
,
'pre_enrollment_email'
)
else
:
# If not default, use the default emailing template
course_url
=
reverse
(
'info'
,
args
=
(
course
.
id
.
to_deprecated_string
(),))
context
=
{
'course'
:
course
,
'full_name'
:
user
.
profile
.
name
,
'site_name'
:
microsite
.
get_value
(
'SITE_NAME'
,
settings
.
SITE_NAME
),
'course_url'
:
request
.
build_absolute_uri
(
course_url
),
}
subject
=
render_to_string
(
'emails/enroll_email_enrolledsubject.txt'
,
context
)
message
=
render_to_string
(
'emails/enroll_email_enrolledmessage.txt'
,
context
)
subject
=
''
.
join
(
subject
.
splitlines
())
user
.
email_user
(
subject
,
message
,
from_address
)
except
Exception
:
log
.
error
(
'unable to send course enrollment verification email to user from "{from_address}"'
.
format
(
from_address
=
from_address
),
exc_info
=
True
)
return
JsonResponse
({
"is_success"
:
False
,
"error"
:
_
(
"Could not send enrollment email to the user"
),})
return
JsonResponse
({
"is_success"
:
True
,
"subject"
:
subject
,
"message"
:
message
})
else
:
return
JsonResponse
({
"email_did_fire"
:
False
})
def
_check_can_enroll_in_course
(
user
,
course_key
,
access_type
=
"enroll"
):
"""
...
...
common/lib/xmodule/xmodule/course_module.py
View file @
aac28d08
...
...
@@ -169,6 +169,8 @@ class CourseFields(object):
default
=
[],
scope
=
Scope
.
content
)
wiki_slug
=
String
(
help
=
"Slug that points to the wiki for this course"
,
scope
=
Scope
.
content
)
enable_enrollment_email
=
Boolean
(
help
=
"Whether to send notification email upon enrollment or not"
,
default
=
False
,
scope
=
Scope
.
settings
)
enable_default_enrollment_email
=
Boolean
(
help
=
"Whether to use default enrollment email for enrollment notification"
,
default
=
True
,
scope
=
Scope
.
settings
)
enrollment_start
=
Date
(
help
=
"Date that enrollment for this class is opened"
,
scope
=
Scope
.
settings
)
enrollment_end
=
Date
(
help
=
"Date that enrollment for this class is closed"
,
scope
=
Scope
.
settings
)
start
=
Date
(
help
=
"Start time when this module is visible"
,
...
...
lms/djangoapps/courseware/courses.py
View file @
aac28d08
...
...
@@ -162,6 +162,10 @@ def get_course_about_section(course, section_key):
- faq
- more_info
- ocw_links
- pre_enrollment_email
- post_enrollment_email
- pre_enrollment_email_subject
- post_enrollment_email_subject
"""
# Many of these are stored as html files instead of some semantic
...
...
@@ -173,7 +177,9 @@ def get_course_about_section(course, section_key):
'course_staff_short'
,
'course_staff_extended'
,
'requirements'
,
'syllabus'
,
'textbook'
,
'faq'
,
'more_info'
,
'number'
,
'instructors'
,
'overview'
,
'effort'
,
'end_date'
,
'prerequisites'
,
'ocw_links'
]:
'effort'
,
'end_date'
,
'prerequisites'
,
'ocw_links'
,
'pre_enrollment_email'
,
'post_enrollment_email'
,
'pre_enrollment_email_subject'
,
'post_enrollment_email_subject'
]:
try
:
...
...
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