diff --git a/AUTHORS b/AUTHORS index 7d6397629f..9bb4ede121 100644 --- a/AUTHORS +++ b/AUTHORS @@ -75,4 +75,6 @@ Frances Botsford Jonah Stanley Slater Victoroff Peter Fogg -Renzo Lucioni \ No newline at end of file +Bethany LaPenta +Renzo Lucioni +Felix Sun diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bbaf3f3a6b..0e161e4f72 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,27 @@ These are notable changes in edx-platform. This is a rolling list of changes, in roughly chronological order, most recent first. Add your entries at or near the top. Include a label indicating the component affected. +Studio: Remove XML from the video component editor. All settings are +moved to be edited as metadata. + +XModule: Only write out assets files if the contents have changed. + +XModule: Don't delete generated xmodule asset files when compiling (for +instance, when XModule provides a coffeescript file, don't delete +the associated javascript) + +Studio: For courses running on edx.org (marketing site), disable fields in +Course Settings that do not apply. + +Common: Make asset watchers run as singletons (so they won't start if the +watcher is already running in another shell). + +Common: Use coffee directly when watching for coffeescript file changes. + +Common: Make rake provide better error messages if packages are missing. + +Common: Repairs development documentation generation by sphinx. + LMS: Problem rescoring. Added options on the Grades tab of the Instructor Dashboard to allow all students' submissions for a particular problem to be rescored. Also supports resetting all @@ -12,6 +33,8 @@ students' number of attempts to zero. Provides a list of background tasks that are currently running for the course, and an option to see a history of background tasks for a given problem. +LMS: Fixed the preferences scope for storing data in xmodules. + LMS: Forums. Added handling for case where discussion module can get `None` as value of lms.start in `lms/djangoapps/django_comment_client/utils.py` diff --git a/Gemfile b/Gemfile index 7f7b146978..1ad685c34d 100644 --- a/Gemfile +++ b/Gemfile @@ -4,3 +4,4 @@ gem 'sass', '3.1.15' gem 'bourbon', '~> 1.3.6' gem 'colorize', '~> 0.5.8' gem 'launchy', '~> 2.1.2' +gem 'sys-proctable', '~> 0.9.3' diff --git a/cms/djangoapps/contentstore/features/advanced-settings.py b/cms/djangoapps/contentstore/features/advanced-settings.py index 4995f3505d..2360baea5a 100644 --- a/cms/djangoapps/contentstore/features/advanced-settings.py +++ b/cms/djangoapps/contentstore/features/advanced-settings.py @@ -2,7 +2,7 @@ #pylint: disable=W0621 from lettuce import world, step -from nose.tools import assert_false, assert_equal, assert_regexp_matches +from nose.tools import assert_false, assert_equal, assert_regexp_matches, assert_true from common import type_in_codemirror KEY_CSS = '.key input.policy-key' @@ -28,7 +28,15 @@ def i_am_on_advanced_course_settings(step): @step(u'I press the "([^"]*)" notification button$') def press_the_notification_button(step, name): css = 'a.%s-button' % name.lower() - world.css_click(css) + + # Save was clicked if either the save notification bar is gone, or we have a error notification + # overlaying it (expected in the case of typing Object into display_name). + def save_clicked(): + confirmation_dismissed = world.is_css_not_present('.is-shown.wrapper-notification-warning') + error_showing = world.is_css_present('.is-shown.wrapper-notification-error') + return confirmation_dismissed or error_showing + + assert_true(world.css_click(css, success_condition=save_clicked), 'Save button not clicked after 5 attempts.') @step(u'I edit the value of a policy key$') diff --git a/cms/djangoapps/contentstore/features/common.py b/cms/djangoapps/contentstore/features/common.py index e126b746c5..bdf07fc5ae 100644 --- a/cms/djangoapps/contentstore/features/common.py +++ b/cms/djangoapps/contentstore/features/common.py @@ -174,6 +174,16 @@ def open_new_unit(step): world.css_click('a.new-unit-item') +@step('when I view the video it (.*) show the captions') +def shows_captions(step, show_captions): + # Prevent cookies from overriding course settings + world.browser.cookies.delete('hide_captions') + if show_captions == 'does not': + assert world.css_find('.video')[0].has_class('closed') + else: + assert world.is_css_not_present('.video.closed') + + def type_in_codemirror(index, text): world.css_click(".CodeMirror", index=index) g = world.css_find("div.CodeMirror.CodeMirror-focused > div > textarea") diff --git a/cms/djangoapps/contentstore/features/grading.feature b/cms/djangoapps/contentstore/features/grading.feature new file mode 100644 index 0000000000..78634cb964 --- /dev/null +++ b/cms/djangoapps/contentstore/features/grading.feature @@ -0,0 +1,53 @@ +Feature: Course Grading + As a course author, I want to be able to configure how my course is graded + + Scenario: Users can add grading ranges + Given I have opened a new course in Studio + And I am viewing the grading settings + When I add "1" new grade + Then I see I now have "3" grades + + Scenario: Users can only have up to 5 grading ranges + Given I have opened a new course in Studio + And I am viewing the grading settings + When I add "6" new grades + Then I see I now have "5" grades + + #Cannot reliably make the delete button appear so using javascript instead + Scenario: Users can delete grading ranges + Given I have opened a new course in Studio + And I am viewing the grading settings + When I add "1" new grade + And I delete a grade + Then I see I now have "2" grades + + Scenario: Users can move grading ranges + Given I have opened a new course in Studio + And I am viewing the grading settings + When I move a grading section + Then I see that the grade range has changed + + Scenario: Users can modify Assignment types + Given I have opened a new course in Studio + And I have populated the course + And I am viewing the grading settings + When I change assignment type "Homework" to "New Type" + And I go back to the main course page + Then I do see the assignment name "New Type" + And I do not see the assignment name "Homework" + + Scenario: Users can delete Assignment types + Given I have opened a new course in Studio + And I have populated the course + And I am viewing the grading settings + When I delete the assignment type "Homework" + And I go back to the main course page + Then I do not see the assignment name "Homework" + + Scenario: Users can add Assignment types + Given I have opened a new course in Studio + And I have populated the course + And I am viewing the grading settings + When I add a new assignment type "New Type" + And I go back to the main course page + Then I do see the assignment name "New Type" diff --git a/cms/djangoapps/contentstore/features/grading.py b/cms/djangoapps/contentstore/features/grading.py new file mode 100644 index 0000000000..4e59897c1c --- /dev/null +++ b/cms/djangoapps/contentstore/features/grading.py @@ -0,0 +1,108 @@ +#pylint: disable=C0111 +#pylint: disable=W0621 + +from lettuce import world, step +from common import * + + +@step(u'I am viewing the grading settings') +def view_grading_settings(step): + world.click_course_settings() + link_css = 'li.nav-course-settings-grading a' + world.css_click(link_css) + + +@step(u'I add "([^"]*)" new grade') +def add_grade(step, many): + grade_css = '.new-grade-button' + for i in range(int(many)): + world.css_click(grade_css) + + +@step(u'I delete a grade') +def delete_grade(step): + #grade_css = 'li.grade-specific-bar > a.remove-button' + #range_css = '.grade-specific-bar' + #world.css_find(range_css)[1].mouseover() + #world.css_click(grade_css) + world.browser.execute_script('document.getElementsByClassName("remove-button")[0].click()') + + +@step(u'I see I now have "([^"]*)" grades$') +def view_grade_slider(step, how_many): + grade_slider_css = '.grade-specific-bar' + all_grades = world.css_find(grade_slider_css) + assert len(all_grades) == int(how_many) + + +@step(u'I move a grading section') +def move_grade_slider(step): + moveable_css = '.ui-resizable-e' + f = world.css_find(moveable_css).first + f.action_chains.drag_and_drop_by_offset(f._element, 100, 0).perform() + + +@step(u'I see that the grade range has changed') +def confirm_change(step): + range_css = '.range' + all_ranges = world.css_find(range_css) + for i in range(len(all_ranges)): + assert all_ranges[i].html != '0-50' + + +@step(u'I change assignment type "([^"]*)" to "([^"]*)"$') +def change_assignment_name(step, old_name, new_name): + name_id = '#course-grading-assignment-name' + index = get_type_index(old_name) + f = world.css_find(name_id)[index] + assert index != -1 + for count in range(len(old_name)): + f._element.send_keys(Keys.END, Keys.BACK_SPACE) + f._element.send_keys(new_name) + + +@step(u'I go back to the main course page') +def main_course_page(step): + main_page_link_css = 'a[href="/MITx/999/course/Robot_Super_Course"]' + world.css_click(main_page_link_css) + + +@step(u'I do( not)? see the assignment name "([^"]*)"$') +def see_assignment_name(step, do_not, name): + assignment_menu_css = 'ul.menu > li > a' + assignment_menu = world.css_find(assignment_menu_css) + allnames = [item.html for item in assignment_menu] + if do_not: + assert not name in allnames + else: + assert name in allnames + + +@step(u'I delete the assignment type "([^"]*)"$') +def delete_assignment_type(step, to_delete): + delete_css = '.remove-grading-data' + world.css_click(delete_css, index=get_type_index(to_delete)) + + +@step(u'I add a new assignment type "([^"]*)"$') +def add_assignment_type(step, new_name): + add_button_css = '.add-grading-data' + world.css_click(add_button_css) + name_id = '#course-grading-assignment-name' + f = world.css_find(name_id)[4] + f._element.send_keys(new_name) + + +@step(u'I have populated the course') +def populate_course(step): + step.given('I have added a new section') + step.given('I have added a new subsection') + + +def get_type_index(name): + name_id = '#course-grading-assignment-name' + f = world.css_find(name_id) + for i in range(len(f)): + if f[i].value == name: + return i + return -1 diff --git a/cms/djangoapps/contentstore/features/video-editor.feature b/cms/djangoapps/contentstore/features/video-editor.feature index 4c2a460042..f28ee568dc 100644 --- a/cms/djangoapps/contentstore/features/video-editor.feature +++ b/cms/djangoapps/contentstore/features/video-editor.feature @@ -4,10 +4,20 @@ Feature: Video Component Editor Scenario: User can view metadata Given I have created a Video component And I edit and select Settings - Then I see only the Video display name setting + Then I see the correct settings and default values Scenario: User can modify display name Given I have created a Video component And I edit and select Settings Then I can modify the display name And my display name change is persisted on save + + Scenario: Captions are hidden when "show captions" is false + Given I have created a Video component + And I have set "show captions" to False + Then when I view the video it does not show the captions + + Scenario: Captions are shown when "show captions" is true + Given I have created a Video component + And I have set "show captions" to True + Then when I view the video it does show the captions diff --git a/cms/djangoapps/contentstore/features/video-editor.py b/cms/djangoapps/contentstore/features/video-editor.py index 27423575c3..987b4959b8 100644 --- a/cms/djangoapps/contentstore/features/video-editor.py +++ b/cms/djangoapps/contentstore/features/video-editor.py @@ -4,6 +4,20 @@ from lettuce import world, step -@step('I see only the video display name setting$') -def i_see_only_the_video_display_name(step): - world.verify_all_setting_entries([['Display Name', "default", True]]) +@step('I see the correct settings and default values$') +def i_see_the_correct_settings_and_values(step): + world.verify_all_setting_entries([['Default Speed', 'OEoXaMPEzfM', False], + ['Display Name', 'default', True], + ['Download Track', '', False], + ['Download Video', '', False], + ['Show Captions', 'True', False], + ['Speed: .75x', '', False], + ['Speed: 1.25x', '', False], + ['Speed: 1.5x', '', False]]) + + +@step('I have set "show captions" to (.*)') +def set_show_captions(step, setting): + world.css_click('a.edit-button') + world.browser.select('Show Captions', setting) + world.css_click('a.save-button') diff --git a/cms/djangoapps/contentstore/features/video.feature b/cms/djangoapps/contentstore/features/video.feature index 0129732d30..e4caa70ef6 100644 --- a/cms/djangoapps/contentstore/features/video.feature +++ b/cms/djangoapps/contentstore/features/video.feature @@ -9,7 +9,16 @@ Feature: Video Component Given I have clicked the new unit button Then creating a video takes a single click - Scenario: Captions are shown correctly + Scenario: Captions are hidden correctly Given I have created a Video component And I have hidden captions Then when I view the video it does not show the captions + + Scenario: Captions are shown correctly + Given I have created a Video component + Then when I view the video it does show the captions + + Scenario: Captions are toggled correctly + Given I have created a Video component + And I have toggled captions + Then when I view the video it does show the captions diff --git a/cms/djangoapps/contentstore/features/video.py b/cms/djangoapps/contentstore/features/video.py index c48b36a5aa..190f8e9f1e 100644 --- a/cms/djangoapps/contentstore/features/video.py +++ b/cms/djangoapps/contentstore/features/video.py @@ -18,11 +18,16 @@ def video_takes_a_single_click(_step): assert(world.is_css_present('.xmodule_VideoModule')) -@step('I have hidden captions') -def set_show_captions_false(step): - world.css_click('a.hide-subtitles') - - -@step('when I view the video it does not show the captions') -def does_not_show_captions(step): - assert world.css_find('.video')[0].has_class('closed') +@step('I have (hidden|toggled) captions') +def hide_or_show_captions(step, shown): + button_css = 'a.hide-subtitles' + if shown == 'hidden': + world.css_click(button_css) + if shown == 'toggled': + world.css_click(button_css) + # When we click the first time, a tooltip shows up. We want to + # click the button rather than the tooltip, so move the mouse + # away to make it disappear. + button = world.css_find(button_css) + button.mouse_out() + world.css_click(button_css) diff --git a/cms/djangoapps/contentstore/tests/test_checklists.py b/cms/djangoapps/contentstore/tests/test_checklists.py index 0e5cd9b884..52e9ba14fe 100644 --- a/cms/djangoapps/contentstore/tests/test_checklists.py +++ b/cms/djangoapps/contentstore/tests/test_checklists.py @@ -19,7 +19,6 @@ class ChecklistTestCase(CourseTestCase): modulestore = get_modulestore(self.course.location) return modulestore.get_item(self.course.location).checklists - def compare_checklists(self, persisted, request): """ Handles url expansion as possible difference and descends into guts @@ -99,7 +98,6 @@ class ChecklistTestCase(CourseTestCase): 'name': self.course.location.name, 'checklist_index': 2}) - def get_first_item(checklist): return checklist['items'][0] diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index 8c15b1ae95..5c2a15ac87 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -1,11 +1,16 @@ +""" +Tests for Studio Course Settings. +""" import datetime import json import copy +import mock from django.contrib.auth.models import User from django.test.client import Client from django.core.urlresolvers import reverse from django.utils.timezone import UTC +from django.test.utils import override_settings from xmodule.modulestore import Location from models.settings.course_details import (CourseDetails, CourseSettingsEncoder) @@ -21,6 +26,9 @@ from xmodule.fields import Date class CourseTestCase(ModuleStoreTestCase): + """ + Base class for test classes below. + """ def setUp(self): """ These tests need a user in the DB so that the django Test Client @@ -51,6 +59,9 @@ class CourseTestCase(ModuleStoreTestCase): class CourseDetailsTestCase(CourseTestCase): + """ + Tests the first course settings page (course dates, overview, etc.). + """ def test_virgin_fetch(self): details = CourseDetails.fetch(self.course_location) self.assertEqual(details.course_location, self.course_location, "Location not copied into") @@ -81,9 +92,9 @@ class CourseDetailsTestCase(CourseTestCase): Test the encoder out of its original constrained purpose to see if it functions for general use """ details = {'location': Location(['tag', 'org', 'course', 'category', 'name']), - 'number': 1, - 'string': 'string', - 'datetime': datetime.datetime.now(UTC())} + 'number': 1, + 'string': 'string', + 'datetime': datetime.datetime.now(UTC())} jsondetails = json.dumps(details, cls=CourseSettingsEncoder) jsondetails = json.loads(jsondetails) @@ -118,8 +129,60 @@ class CourseDetailsTestCase(CourseTestCase): jsondetails.effort, "After set effort" ) + @override_settings(MKTG_URLS={'ROOT': 'dummy-root'}) + def test_marketing_site_fetch(self): + settings_details_url = reverse( + 'settings_details', + kwargs={ + 'org': self.course_location.org, + 'name': self.course_location.name, + 'course': self.course_location.course + } + ) + + with mock.patch.dict('django.conf.settings.MITX_FEATURES', {'ENABLE_MKTG_SITE': True}): + response = self.client.get(settings_details_url) + self.assertContains(response, "Course Summary Page") + self.assertContains(response, "course summary page will not be viewable") + + self.assertContains(response, "Course Start Date") + self.assertContains(response, "Course End Date") + self.assertNotContains(response, "Enrollment Start Date") + self.assertNotContains(response, "Enrollment End Date") + self.assertContains(response, "not the dates shown on your course summary page") + + self.assertNotContains(response, "Introducing Your Course") + self.assertNotContains(response, "Requirements") + + def test_regular_site_fetch(self): + settings_details_url = reverse( + 'settings_details', + kwargs={ + 'org': self.course_location.org, + 'name': self.course_location.name, + 'course': self.course_location.course + } + ) + + with mock.patch.dict('django.conf.settings.MITX_FEATURES', {'ENABLE_MKTG_SITE': False}): + response = self.client.get(settings_details_url) + self.assertContains(response, "Course Summary Page") + self.assertNotContains(response, "course summary page will not be viewable") + + self.assertContains(response, "Course Start Date") + self.assertContains(response, "Course End Date") + self.assertContains(response, "Enrollment Start Date") + self.assertContains(response, "Enrollment End Date") + self.assertNotContains(response, "not the dates shown on your course summary page") + + self.assertContains(response, "Introducing Your Course") + self.assertContains(response, "Requirements") + class CourseDetailsViewTest(CourseTestCase): + """ + Tests for modifying content on the first course settings page (course dates, overview, etc.). + """ def alter_field(self, url, details, field, val): setattr(details, field, val) # Need to partially serialize payload b/c the mock doesn't handle it correctly @@ -181,6 +244,9 @@ class CourseDetailsViewTest(CourseTestCase): class CourseGradingTest(CourseTestCase): + """ + Tests for the course settings grading page. + """ def test_initial_grader(self): descriptor = get_modulestore(self.course_location).get_item(self.course_location) test_grader = CourseGradingModel(descriptor) @@ -256,6 +322,9 @@ class CourseGradingTest(CourseTestCase): class CourseMetadataEditingTest(CourseTestCase): + """ + Tests for CourseMetadata. + """ def setUp(self): CourseTestCase.setUp(self) # add in the full class too diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 8a29a637b8..dd7573bad5 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -227,7 +227,8 @@ def get_course_settings(request, org, course, name): kwargs={"org": org, "course": course, "name": name, - "section": "details"}) + "section": "details"}), + 'about_page_editable': not settings.MITX_FEATURES.get('ENABLE_MKTG_SITE', False) }) diff --git a/cms/static/js/views/assets.js b/cms/static/js/views/assets.js index 9eb521dcb6..18ef131f52 100644 --- a/cms/static/js/views/assets.js +++ b/cms/static/js/views/assets.js @@ -9,7 +9,7 @@ function removeAsset(e){ e.preventDefault(); var that = this; - var msg = new CMS.Models.ConfirmAssetDeleteMessage({ + var msg = new CMS.Views.Prompt.Confirmation({ title: gettext("Delete File Confirmation"), message: gettext("Are you sure you wish to delete this item. It cannot be reversed!\n\nAlso any content that links/refers to this item will no longer work (e.g. broken images and/or links)"), actions: { @@ -17,15 +17,17 @@ function removeAsset(e){ text: gettext("OK"), click: function(view) { // call the back-end to actually remove the asset - $.post(view.model.get('remove_asset_url'), - { 'location': view.model.get('asset_location') }, + var url = $('.asset-library').data('remove-asset-callback-url'); + var row = $(that).closest('tr'); + $.post(url, + { 'location': row.data('id') }, function() { // show the post-commit confirmation $(".wrapper-alert-confirmation").addClass("is-shown").attr('aria-hidden','false'); - view.model.get('row_to_remove').remove(); + row.remove(); analytics.track('Deleted Asset', { 'course': course_location_analytics, - 'id': view.model.get('asset_location') + 'id': row.data('id') }); } ); @@ -38,24 +40,9 @@ function removeAsset(e){ view.hide(); } }] - }, - remove_asset_url: $('.asset-library').data('remove-asset-callback-url'), - asset_location: $(this).closest('tr').data('id'), - row_to_remove: $(this).closest('tr') + } }); - - // workaround for now. We can't spawn multiple instances of the Prompt View - // so for now, a bit of hackery to just make sure we have a single instance - // note: confirm_delete_prompt is in asset_index.html - if (confirm_delete_prompt === null) - confirm_delete_prompt = new CMS.Views.Prompt({model: msg}); - else - { - confirm_delete_prompt.model = msg; - confirm_delete_prompt.show(); - } - - return; + return msg.show(); } function showUploadModal(e) { @@ -125,4 +112,4 @@ function displayFinishedUpload(xhr) { 'course': course_location_analytics, 'asset_url': resp.url }); -} \ No newline at end of file +} diff --git a/cms/static/js/views/feedback.js b/cms/static/js/views/feedback.js index b04fb6e3d1..0cfd6fa4ef 100644 --- a/cms/static/js/views/feedback.js +++ b/cms/static/js/views/feedback.js @@ -90,6 +90,7 @@ CMS.Views.SystemFeedback = Backbone.View.extend({ var parent = CMS.Views[_.str.capitalize(this.options.type)]; if(parent && parent.active && parent.active !== this) { parent.active.stopListening(); + parent.active.undelegateEvents(); } this.$el.html(this.template(this.options)); parent.active = this; diff --git a/cms/static/sass/elements/_system-help.scss b/cms/static/sass/elements/_system-help.scss index 7fcb218282..a9a3e16128 100644 --- a/cms/static/sass/elements/_system-help.scss +++ b/cms/static/sass/elements/_system-help.scss @@ -1,2 +1,40 @@ // studio - elements - system help // ==================== + +// notices - in-context: to be used as notices to users within the context of a form/action +.notice-incontext { + @extend .ui-well; + @include border-radius(($baseline/10)); + + .title { + @extend .t-title7; + margin-bottom: ($baseline/4); + font-weight: 600; + } + + .copy { + @extend .t-copy-sub1; + @include transition(opacity 0.25s ease-in-out 0); + opacity: 0.75; + } + + strong { + font-weight: 600; + } + + &:hover { + + .copy { + opacity: 1.0; + } + } +} + +// particular warnings around a workflow for something +.notice-workflow { + background: $yellow-l5; + + .copy { + color: $gray-d1; + } +} diff --git a/cms/static/sass/views/_settings.scss b/cms/static/sass/views/_settings.scss index 735774511f..cbb1034626 100644 --- a/cms/static/sass/views/_settings.scss +++ b/cms/static/sass/views/_settings.scss @@ -21,7 +21,7 @@ body.course.settings { font-size: 14px; } - .message-status { + .message-status { display: none; @include border-top-radius(2px); @include box-sizing(border-box); @@ -52,6 +52,12 @@ body.course.settings { } } + // notices - used currently for edx mktg + .notice-workflow { + margin-top: ($baseline); + } + + // in form - elements .group-settings { margin: 0 0 ($baseline*2) 0; diff --git a/cms/templates/asset_index.html b/cms/templates/asset_index.html index 0006d29d38..abbc5bb1b4 100644 --- a/cms/templates/asset_index.html +++ b/cms/templates/asset_index.html @@ -8,11 +8,6 @@ <%block name="jsextra"> - - <%block name="content"> @@ -98,7 +93,7 @@ - + % endfor diff --git a/cms/templates/emails/activation_email.txt b/cms/templates/emails/activation_email.txt index 5a1d63b670..4badb4ca88 100644 --- a/cms/templates/emails/activation_email.txt +++ b/cms/templates/emails/activation_email.txt @@ -1,4 +1,4 @@ -Thank you for signing up for edX edge! To activate your account, +Thank you for signing up for edX Studio! To activate your account, please copy and paste this address into your web browser's address bar: diff --git a/cms/templates/settings.html b/cms/templates/settings.html index 2adc0cd980..14c79e586a 100644 --- a/cms/templates/settings.html +++ b/cms/templates/settings.html @@ -1,3 +1,5 @@ +<%! from django.utils.translation import ugettext as _ %> + <%inherit file="base.html" /> <%block name="title">Schedule & Details Settings <%block name="bodyclass">is-signedin course schedule settings @@ -50,8 +52,8 @@ from contentstore import utils

- Settings - > Schedule & Details + ${_("Settings")} + > ${_("Schedule & Details")}

@@ -62,59 +64,68 @@ from contentstore import utils
-

Basic Information

- The nuts and bolts of your course +

${_("Basic Information")}

+ ${_("The nuts and bolts of your course")}
  1. - - + +
  2. - - + +
  3. - - + +
-

Course Summary Page (for student enrollment and access)

+

${_("Course Summary Page")} ${_("(for student enrollment and access)")}

+ + % if not about_page_editable: +
+

${_("Promoting Your Course with edX")}

+
+

${_('Your course summary page will not be viewable until your course has been announced. To provide content for the page and preview it, follow the instructions provided by your PM or Conrad Warre (conrad@edx.org).')}

+
+
+ % endif

-

Course Schedule

- Important steps and segments of your course +

${_('Course Schedule')}

+ ${_('Dates that control when your course can be viewed.')}
  1. - + - First day the course begins + ${_("First day the course begins")}
    - +
    @@ -122,29 +133,30 @@ from contentstore import utils
  2. - + - Last day your course is active + ${_("Last day your course is active")}
    - +
+ % if about_page_editable:
  1. - + - First day students can enroll + ${_("First day students can enroll")}
    - +
    @@ -152,91 +164,106 @@ from contentstore import utils
  2. - + - Last day students can enroll + ${_("Last day students can enroll")}
    - +
-
+ % endif + % if not about_page_editable: +
+

${_("These Dates Are Not Used When Promoting Your Course")}

+
+

${_('These dates impact when your courseware can be viewed, but they are not the dates shown on your course summary page. To provide the course start and registration dates as shown on your course summary page, follow the instructions provided by your PM or Conrad Warre (conrad@edx.org).')}

+
+
+ % endif +
+ % if about_page_editable: +
+
+

${_("Introducing Your Course")}

+ ${_("Information for prospective students")} +
-
-
-

Introducing Your Course

- Information for prospective students -
+
    +
  1. + + + <%def name='overview_text()'><% + a_link_start = '' + _("your course summary page") + '' + a_link = a_link_start + utils.get_lms_link_for_about_page(course_location) + a_link_end + text = _("Introductions, prerequisites, FAQs that are used on %s (formatted in HTML)") % a_link + %>${text} + ${overview_text()} +
  2. -
      -
    1. - - - Introductions, prerequisites, FAQs that are used on your course summary page (formatted in HTML) -
    2. +
    3. + + -
    4. - -
      -
      - -
      - -
      +
      + + ${_("Enter your YouTube video's ID (along with any restriction parameters)")} +
      +
    5. +
    +
-
- - Enter your YouTube video's ID (along with any restriction parameters) -
- - -
+
-
+
+
+

${_("Requirements")}

+ ${_("Expectations of the students taking this course")} +
-
-
-

Requirements

- Expectations of the students taking this course -
- -
    -
  1. - - - Time spent on all course work -
  2. -
-
+
    +
  1. + + + ${_("Time spent on all course work")} +
  2. +
+
+ % endif
-