Merge pull request #2334 from edx/ali123/course-short-description
Changes to make the course short description editable in cms
This commit is contained in:
1
AUTHORS
1
AUTHORS
@@ -129,3 +129,4 @@ Alison Hodges <ahodges@edx.org>
|
||||
Jane Manning <jmanning@gmail.com>
|
||||
Toddi Norum <toddi@edx.org>
|
||||
Xavier Antoviaque <xavier@antoviaque.org>
|
||||
Ali Reza Sharafat <ali.sharafat@gmail.com>
|
||||
|
||||
@@ -14,6 +14,8 @@ Blades: Add view for field type Dict in Studio. BLD-658.
|
||||
|
||||
Blades: Refactor stub implementation of LTI Provider. BLD-601.
|
||||
|
||||
Studio: Added ability to edit course short descriptions that appear on the course catalog page.
|
||||
|
||||
LMS: In left accordion and progress page, due dates are now displayed in time
|
||||
zone specified by settings.TIME_ZONE, instead of UTC always
|
||||
|
||||
|
||||
@@ -76,6 +76,11 @@ class CourseDetailsTestCase(CourseTestCase):
|
||||
CourseDetails.update_from_json(self.course_locator, jsondetails.__dict__, self.user).syllabus,
|
||||
jsondetails.syllabus, "After set syllabus"
|
||||
)
|
||||
jsondetails.short_description = "Short Description"
|
||||
self.assertEqual(
|
||||
CourseDetails.update_from_json(self.course_locator, jsondetails.__dict__, self.user).short_description,
|
||||
jsondetails.short_description, "After set short_description"
|
||||
)
|
||||
jsondetails.overview = "Overview"
|
||||
self.assertEqual(
|
||||
CourseDetails.update_from_json(self.course_locator, jsondetails.__dict__, self.user).overview,
|
||||
@@ -120,10 +125,19 @@ class CourseDetailsTestCase(CourseTestCase):
|
||||
|
||||
self.assertContains(response, "Introducing Your Course")
|
||||
self.assertContains(response, "Course Image")
|
||||
self.assertContains(response, "Course Short Description")
|
||||
self.assertNotContains(response, "Course Overview")
|
||||
self.assertNotContains(response, "Course Introduction Video")
|
||||
self.assertNotContains(response, "Requirements")
|
||||
|
||||
def test_editable_short_description_fetch(self):
|
||||
settings_details_url = self.course_locator.url_reverse('settings/details/')
|
||||
|
||||
with mock.patch.dict('django.conf.settings.FEATURES', {'EDITABLE_SHORT_DESCRIPTION': False}):
|
||||
response = self.client.get_html(settings_details_url)
|
||||
self.assertNotContains(response, "Course Short Description")
|
||||
|
||||
|
||||
def test_regular_site_fetch(self):
|
||||
settings_details_url = self.course_locator.url_reverse('settings/details/')
|
||||
|
||||
@@ -141,6 +155,7 @@ class CourseDetailsTestCase(CourseTestCase):
|
||||
|
||||
self.assertContains(response, "Introducing Your Course")
|
||||
self.assertContains(response, "Course Image")
|
||||
self.assertContains(response, "Course Short Description")
|
||||
self.assertContains(response, "Course Overview")
|
||||
self.assertContains(response, "Course Introduction Video")
|
||||
self.assertContains(response, "Requirements")
|
||||
@@ -186,6 +201,7 @@ class CourseDetailsViewTest(CourseTestCase):
|
||||
self.alter_field(url, details, 'enrollment_start', datetime.datetime(2012, 10, 12, 1, 30, tzinfo=utc))
|
||||
|
||||
self.alter_field(url, details, 'enrollment_end', datetime.datetime(2012, 11, 15, 1, 30, tzinfo=utc))
|
||||
self.alter_field(url, details, 'short_description', "Short Description")
|
||||
self.alter_field(url, details, 'overview', "Overview")
|
||||
self.alter_field(url, details, 'intro_video', "intro_video")
|
||||
self.alter_field(url, details, 'effort', "effort")
|
||||
@@ -199,6 +215,7 @@ class CourseDetailsViewTest(CourseTestCase):
|
||||
self.compare_date_fields(details, encoded, context, 'end_date')
|
||||
self.compare_date_fields(details, encoded, context, 'enrollment_start')
|
||||
self.compare_date_fields(details, encoded, context, 'enrollment_end')
|
||||
self.assertEqual(details['short_description'], encoded['short_description'], context + " short_description not ==")
|
||||
self.assertEqual(details['overview'], encoded['overview'], context + " overviews not ==")
|
||||
self.assertEqual(details['intro_video'], encoded.get('intro_video', None), context + " intro_video not ==")
|
||||
self.assertEqual(details['effort'], encoded['effort'], context + " efforts not ==")
|
||||
|
||||
@@ -459,6 +459,8 @@ def settings_handler(request, tag=None, package_id=None, branch=None, version_gu
|
||||
settings.FEATURES.get('ENABLE_MKTG_SITE', False)
|
||||
)
|
||||
|
||||
short_description_editable = settings.FEATURES.get('EDITABLE_SHORT_DESCRIPTION', True)
|
||||
|
||||
return render_to_response('settings.html', {
|
||||
'context_course': course_module,
|
||||
'course_locator': locator,
|
||||
@@ -466,6 +468,7 @@ def settings_handler(request, tag=None, package_id=None, branch=None, version_gu
|
||||
'course_image_url': utils.course_image_url(course_module),
|
||||
'details_url': locator.url_reverse('/settings/details/'),
|
||||
'about_page_editable': about_page_editable,
|
||||
'short_description_editable': short_description_editable,
|
||||
'upload_asset_url': upload_asset_url
|
||||
})
|
||||
elif 'application/json' in request.META.get('HTTP_ACCEPT', ''):
|
||||
|
||||
@@ -23,6 +23,7 @@ class CourseDetails(object):
|
||||
self.enrollment_start = None
|
||||
self.enrollment_end = None
|
||||
self.syllabus = None # a pdf file asset
|
||||
self.short_description = ""
|
||||
self.overview = "" # html to render as the overview
|
||||
self.intro_video = None # a video pointer
|
||||
self.effort = None # int hours/week
|
||||
@@ -51,6 +52,12 @@ class CourseDetails(object):
|
||||
except ItemNotFoundError:
|
||||
pass
|
||||
|
||||
temploc = course_old_location.replace(category='about', name='short_description')
|
||||
try:
|
||||
course.short_description = get_modulestore(temploc).get_item(temploc).data
|
||||
except ItemNotFoundError:
|
||||
pass
|
||||
|
||||
temploc = temploc.replace(name='overview')
|
||||
try:
|
||||
course.overview = get_modulestore(temploc).get_item(temploc).data
|
||||
@@ -150,7 +157,7 @@ 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']:
|
||||
for about_type in ['syllabus', 'overview', 'effort', 'short_description']:
|
||||
cls.update_about_item(course_old_location, about_type, jsondict[about_type], descriptor, user)
|
||||
|
||||
recomposed_video_tag = CourseDetails.recompose_video_tag(jsondict['intro_video'])
|
||||
|
||||
@@ -73,6 +73,9 @@ FEATURES = {
|
||||
|
||||
# Turn off account locking if failed login attempts exceeds a limit
|
||||
'ENABLE_MAX_FAILED_LOGIN_ATTEMPTS': False,
|
||||
|
||||
# Allow editing of short description in course settings in cms
|
||||
'EDITABLE_SHORT_DESCRIPTION': True,
|
||||
}
|
||||
ENABLE_JASMINE = False
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ var CourseDetails = Backbone.Model.extend({
|
||||
enrollment_start: null,
|
||||
enrollment_end: null,
|
||||
syllabus: null,
|
||||
short_description: "",
|
||||
overview: "",
|
||||
intro_video: null,
|
||||
effort: null, // an int or null,
|
||||
|
||||
@@ -50,6 +50,8 @@ var DetailsView = ValidatingView.extend({
|
||||
this.$el.find('#' + this.fieldToSelectorMap['overview']).val(this.model.get('overview'));
|
||||
this.codeMirrorize(null, $('#course-overview')[0]);
|
||||
|
||||
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());
|
||||
this.$el.find('#' + this.fieldToSelectorMap['intro_video']).val(this.model.get('intro_video') || '');
|
||||
if (this.model.has('intro_video')) {
|
||||
@@ -71,6 +73,7 @@ var DetailsView = ValidatingView.extend({
|
||||
'enrollment_start' : 'enrollment-start',
|
||||
'enrollment_end' : 'enrollment-end',
|
||||
'overview' : 'course-overview',
|
||||
'short_description' : 'course-short-description',
|
||||
'intro_video' : 'course-introduction-video',
|
||||
'effort' : "course-effort",
|
||||
'course_image_asset_path': 'course-image-url'
|
||||
@@ -148,6 +151,9 @@ var DetailsView = ValidatingView.extend({
|
||||
case 'course-effort':
|
||||
this.setField(event);
|
||||
break;
|
||||
case 'course-short-description':
|
||||
this.setField(event);
|
||||
break;
|
||||
// Don't make the user reload the page to check the Youtube ID.
|
||||
// Wait for a second to load the video, avoiding egregious AJAX calls.
|
||||
case 'course-introduction-video':
|
||||
|
||||
@@ -67,19 +67,19 @@ require(["domReady!", "jquery", "js/models/settings/course_details", "js/views/s
|
||||
<ol class="list-input">
|
||||
<li class="field text is-not-editable" id="field-course-organization">
|
||||
<label for="course-organization">${_("Organization")}</label>
|
||||
<input title="${_('This field is disabled: this information cannot be changed.')}" type="text"
|
||||
<input title="${_('This field is disabled: this information cannot be changed.')}" type="text"
|
||||
class="long" id="course-organization" readonly />
|
||||
</li>
|
||||
|
||||
<li class="field text is-not-editable" id="field-course-number">
|
||||
<label for="course-number">${_("Course Number")}</label>
|
||||
<input title="${_('This field is disabled: this information cannot be changed.')}" type="text"
|
||||
<input title="${_('This field is disabled: this information cannot be changed.')}" type="text"
|
||||
class="short" id="course-number" readonly>
|
||||
</li>
|
||||
|
||||
<li class="field text is-not-editable" id="field-course-name">
|
||||
<label for="course-name">${_("Course Name")}</label>
|
||||
<input title="${_('This field is disabled: this information cannot be changed.')}" type="text"
|
||||
<input title="${_('This field is disabled: this information cannot be changed.')}" type="text"
|
||||
class="long" id="course-name" readonly />
|
||||
</li>
|
||||
</ol>
|
||||
@@ -93,7 +93,7 @@ require(["domReady!", "jquery", "js/models/settings/course_details", "js/views/s
|
||||
|
||||
<ul class="list-actions">
|
||||
<li class="action-item">
|
||||
<a title="${_('Send a note to students via email')}"
|
||||
<a title="${_('Send a note to students via email')}"
|
||||
href="mailto:someone@domain.com?Subject=Enroll%20in%20${context_course.display_name_with_default}&body=The%20course%20"${context_course.display_name_with_default}",%20provided%20by%20edX,%20is%20open%20for%20enrollment.%20Please%20navigate%20to%20this%20course%20at%20https:${lms_link_for_about_page}%20to%20enroll." class="action action-primary">
|
||||
<i class="icon-envelope-alt icon-inline"></i>${_("Invite your students")}</a>
|
||||
</li>
|
||||
@@ -198,6 +198,14 @@ require(["domReady!", "jquery", "js/models/settings/course_details", "js/views/s
|
||||
<span class="tip">${_("Information for prospective students")}</span>
|
||||
</header>
|
||||
<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>
|
||||
<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>
|
||||
% endif
|
||||
|
||||
% if about_page_editable:
|
||||
<li class="field text" id="field-course-overview">
|
||||
<label for="course-overview">${_("Course Overview")}</label>
|
||||
|
||||
Reference in New Issue
Block a user