diff --git a/cms/.coveragerc b/cms/.coveragerc index b7ae181e99..4f0dbebe79 100644 --- a/cms/.coveragerc +++ b/cms/.coveragerc @@ -2,7 +2,7 @@ [run] data_file = reports/cms/.coverage source = cms,common/djangoapps -omit = cms/envs/*, cms/manage.py +omit = cms/envs/*, cms/manage.py, common/djangoapps/terrain/*, common/djangoapps/*/migrations/* [report] ignore_errors = True diff --git a/cms/djangoapps/contentstore/features/common.py b/cms/djangoapps/contentstore/features/common.py index f868b598a8..925bb101f3 100644 --- a/cms/djangoapps/contentstore/features/common.py +++ b/cms/djangoapps/contentstore/features/common.py @@ -20,7 +20,8 @@ def i_visit_the_studio_homepage(step): # LETTUCE_SERVER_PORT = 8001 # in your settings.py file. world.browser.visit(django_url('/')) - assert world.browser.is_element_present_by_css('body.no-header', 10) + signin_css = 'a.action-signin' + assert world.browser.is_element_present_by_css(signin_css, 10) @step('I am logged into Studio$') @@ -113,7 +114,11 @@ def log_into_studio( create_studio_user(uname=uname, email=email, is_staff=is_staff) world.browser.cookies.delete() world.browser.visit(django_url('/')) - world.browser.is_element_present_by_css('body.no-header', 10) + signin_css = 'a.action-signin' + world.browser.is_element_present_by_css(signin_css, 10) + + # click the signin button + css_click(signin_css) login_form = world.browser.find_by_css('form#login_form') login_form.find_by_name('email').fill(email) @@ -127,16 +132,19 @@ def create_a_course(): css_click('a.new-course-button') fill_in_course_info() css_click('input.new-course-save') - assert_true(world.browser.is_element_present_by_css('a#courseware-tab', 5)) + course_title_css = 'span.course-title' + assert_true(world.browser.is_element_present_by_css(course_title_css, 5)) def add_section(name='My Section'): link_css = 'a.new-courseware-section-button' css_click(link_css) - name_css = '.new-section-name' - save_css = '.new-section-name-save' + name_css = 'input.new-section-name' + save_css = 'input.new-section-name-save' css_fill(name_css, name) css_click(save_css) + span_css = 'span.section-name-span' + assert_true(world.browser.is_element_present_by_css(span_css, 5)) def add_subsection(name='Subsection One'): diff --git a/cms/djangoapps/contentstore/features/courses.py b/cms/djangoapps/contentstore/features/courses.py index d2d038a928..e394165f08 100644 --- a/cms/djangoapps/contentstore/features/courses.py +++ b/cms/djangoapps/contentstore/features/courses.py @@ -34,8 +34,8 @@ def i_click_the_course_link_in_my_courses(step): @step('the Courseware page has loaded in Studio$') def courseware_page_has_loaded_in_studio(step): - courseware_css = 'a#courseware-tab' - assert world.browser.is_element_present_by_css(courseware_css) + course_title_css = 'span.course-title' + assert world.browser.is_element_present_by_css(course_title_css) @step('I see the course listed in My Courses$') @@ -59,4 +59,4 @@ def i_am_on_tab(step, tab_name): @step('I see a link for adding a new section$') def i_see_new_section_link(step): link_css = 'a.new-courseware-section-button' - assert_css_with_text(link_css, 'New Section') + assert_css_with_text(link_css, '+ New Section') diff --git a/cms/djangoapps/contentstore/features/signup.feature b/cms/djangoapps/contentstore/features/signup.feature index 8a6f93d33b..03a1c9524a 100644 --- a/cms/djangoapps/contentstore/features/signup.feature +++ b/cms/djangoapps/contentstore/features/signup.feature @@ -5,8 +5,8 @@ Feature: Sign in Scenario: Sign up from the homepage Given I visit the Studio homepage - When I click the link with the text "Sign up" + When I click the link with the text "Sign Up" And I fill in the registration form - And I press the "Create My Account" button on the registration form + And I press the Create My Account button on the registration form Then I should see be on the studio home page - And I should see the message "please click on the activation link in your email." \ No newline at end of file + And I should see the message "please click on the activation link in your email." diff --git a/cms/djangoapps/contentstore/features/signup.py b/cms/djangoapps/contentstore/features/signup.py index e105b674f7..a786225ead 100644 --- a/cms/djangoapps/contentstore/features/signup.py +++ b/cms/djangoapps/contentstore/features/signup.py @@ -11,10 +11,11 @@ def i_fill_in_the_registration_form(step): register_form.find_by_name('terms_of_service').check() -@step('I press the "([^"]*)" button on the registration form$') -def i_press_the_button_on_the_registration_form(step, button): +@step('I press the Create My Account button on the registration form$') +def i_press_the_button_on_the_registration_form(step): register_form = world.browser.find_by_css('form#register_form') - register_form.find_by_value(button).click() + submit_css = 'button#submit' + register_form.find_by_css(submit_css).click() @step('I should see be on the studio home page$') diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index 7622ee7661..b79d86b52f 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -342,7 +342,7 @@ class ContentStoreTest(ModuleStoreTestCase): # Create a course so there is something to view resp = self.client.get(reverse('index')) self.assertContains(resp, - '

My Courses

', + '

My Courses

', status_code=200, html=True) @@ -378,7 +378,7 @@ class ContentStoreTest(ModuleStoreTestCase): resp = self.client.get(reverse('course_index', kwargs=data)) self.assertContains(resp, - 'Robot Super Course', + '
', status_code=200, html=True) @@ -401,11 +401,11 @@ class ContentStoreTest(ModuleStoreTestCase): def test_capa_module(self): """Test that a problem treats markdown specially.""" - CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course') + course = CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course') problem_data = { 'parent_location': 'i4x://MITx/999/course/Robot_Super_Course', - 'template': 'i4x://edx/templates/problem/Empty' + 'template': 'i4x://edx/templates/problem/Blank_Common_Problem' } resp = self.client.post(reverse('clone_item'), problem_data) @@ -429,7 +429,7 @@ class TemplateTestCase(ModuleStoreTestCase): # insert a bogus template in the store bogus_template_location = Location('i4x', 'edx', 'templates', 'html', 'bogus') - source_template_location = Location('i4x', 'edx', 'templates', 'html', 'Empty') + source_template_location = Location('i4x', 'edx', 'templates', 'html', 'Blank_HTML_Page') ms.clone_item(source_template_location, bogus_template_location) diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index 925b2431b9..86503d2136 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -143,10 +143,6 @@ class CourseDetailsViewTest(CourseTestCase): def test_update_and_fetch(self): details = CourseDetails.fetch(self.course_location) - resp = self.client.get(reverse('course_settings', kwargs={'org': self.course_location.org, 'course': self.course_location.course, - 'name': self.course_location.name})) - self.assertContains(resp, '
  • Course Details
  • ', status_code=200, html=True) - # resp s/b json from here on url = reverse('course_settings', kwargs={'org': self.course_location.org, 'course': self.course_location.course, 'name': self.course_location.name, 'section': 'details'}) diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 87a2943773..6d5905afe7 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -59,6 +59,7 @@ from cms.djangoapps.models.settings.course_details import CourseDetails,\ from cms.djangoapps.models.settings.course_grading import CourseGradingModel from cms.djangoapps.contentstore.utils import get_modulestore from lxml import etree +from django.shortcuts import redirect # to install PIL on MacOSX: 'easy_install http://dist.repoze.org/PIL-1.1.6.tar.gz' @@ -81,6 +82,11 @@ def signup(request): csrf_token = csrf(request)['csrf_token'] return render_to_response('signup.html', {'csrf': csrf_token}) +def old_login_redirect(request): + ''' + Redirect to the active login url. + ''' + return redirect('login', permanent=True) @ssl_login_shortcut @ensure_csrf_cookie @@ -94,6 +100,11 @@ def login_page(request): 'forgot_password_link': "//{base}/#forgot-password-modal".format(base=settings.LMS_BASE), }) +def howitworks(request): + if request.user.is_authenticated(): + return index(request) + else: + return render_to_response('howitworks.html', {}) # ==== Views for any logged-in user ================================== @@ -120,7 +131,8 @@ def index(request): reverse('course_index', args=[ course.location.org, course.location.course, - course.location.name])) + course.location.name]), + get_lms_link_for_item(course.location)) for course in courses], 'user': request.user, 'disable_course_creation': settings.MITX_FEATURES.get('DISABLE_COURSE_CREATION', False) and not request.user.is_staff @@ -161,6 +173,8 @@ def course_index(request, org, course, name): if not has_access(request.user, location): raise PermissionDenied() + lms_link = get_lms_link_for_item(location) + upload_asset_callback_url = reverse('upload_asset', kwargs={ 'org': org, 'course': course, @@ -173,6 +187,7 @@ def course_index(request, org, course, name): return render_to_response('overview.html', { 'active_tab': 'courseware', 'context_course': course, + 'lms_link': lms_link, 'sections': sections, 'course_graders': json.dumps(CourseGradingModel.fetch(course.location).graders), 'parent_location': course.location, @@ -273,7 +288,7 @@ def edit_unit(request, location): template.display_name, template.location.url(), 'markdown' in template.metadata, - template.location.name == 'Empty' + 'empty' in template.metadata )) components = [ @@ -730,8 +745,6 @@ def clone_item(request): #@login_required #@ensure_csrf_cookie - - def upload_asset(request, org, course, coursename): ''' cdodge: this method allows for POST uploading of files into the course asset library, which will @@ -796,8 +809,6 @@ def upload_asset(request, org, course, coursename): ''' This view will return all CMS users who are editors for the specified course ''' - - @login_required @ensure_csrf_cookie def manage_users(request, location): @@ -819,7 +830,7 @@ def manage_users(request, location): }) -def create_json_response(errmsg=None): +def create_json_response(errmsg = None): if errmsg is not None: resp = HttpResponse(json.dumps({'Status': 'Failed', 'ErrMsg': errmsg})) else: @@ -831,8 +842,6 @@ def create_json_response(errmsg=None): This POST-back view will add a user - specified by email - to the list of editors for the specified course ''' - - @expect_json @login_required @ensure_csrf_cookie @@ -865,8 +874,6 @@ def add_user(request, location): This POST-back view will remove a user - specified by email - from the list of editors for the specified course ''' - - @expect_json @login_required @ensure_csrf_cookie @@ -1124,8 +1131,31 @@ def get_course_settings(request, org, course, name): course_details = CourseDetails.fetch(location) return render_to_response('settings.html', { - 'active_tab': 'settings', 'context_course': course_module, + 'course_location' : location, + 'course_details' : json.dumps(course_details, cls=CourseSettingsEncoder) + }) + +@login_required +@ensure_csrf_cookie +def course_config_graders_page(request, org, course, name): + """ + Send models and views as well as html for editing the course settings to the client. + + org, course, name: Attributes of the Location for the item to edit + """ + location = ['i4x', org, course, 'course', name] + + # check that logged in user has permissions to this item + if not has_access(request.user, location): + raise PermissionDenied() + + course_module = modulestore().get_item(location) + course_details = CourseGradingModel.fetch(location) + + return render_to_response('settings_graders.html', { + 'context_course': course_module, + 'course_location' : location, 'course_details': json.dumps(course_details, cls=CourseSettingsEncoder) }) diff --git a/cms/envs/common.py b/cms/envs/common.py index 30aac6ea01..281dd97f20 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -74,8 +74,8 @@ TEMPLATE_DIRS = MAKO_TEMPLATES['main'] MITX_ROOT_URL = '' -LOGIN_REDIRECT_URL = MITX_ROOT_URL + '/login' -LOGIN_URL = MITX_ROOT_URL + '/login' +LOGIN_REDIRECT_URL = MITX_ROOT_URL + '/signin' +LOGIN_URL = MITX_ROOT_URL + '/signin' TEMPLATE_CONTEXT_PROCESSORS = ( diff --git a/cms/static/client_templates/course_grade_policy.html b/cms/static/client_templates/course_grade_policy.html index c9a21280dd..db129614f6 100644 --- a/cms/static/client_templates/course_grade_policy.html +++ b/cms/static/client_templates/course_grade_policy.html @@ -1,69 +1,37 @@ -
  • -
    - +
  • +
    + + + e.g. Homework, Midterm Exams +
    -
    -
    - - e.g. Homework, Labs, Midterm Exams, Final Exam -
    -
    - - -
    - - -
    -
    - - e.g. HW, Midterm, Final -
    -
    -
    - -
    - - -
    -
    - - e.g. 25% -
    -
    -
    - -
    - - -
    -
    - - total exercises assigned -
    -
    -
    - -
    - - -
    -
    - - total exercises that won't be graded -
    -
    -
    - Delete +
    + + + e.g. HW, Midterm +
    + +
    + + + e.g. 25% +
    + +
    + + + total exercises assigned +
    + +
    + + + total exercises that won't be graded +
    + +
    + Delete +
  • diff --git a/cms/static/coffee/src/views/tabs.coffee b/cms/static/coffee/src/views/tabs.coffee index 5a826c1794..9fbe4e5789 100644 --- a/cms/static/coffee/src/views/tabs.coffee +++ b/cms/static/coffee/src/views/tabs.coffee @@ -1,6 +1,4 @@ class CMS.Views.TabsEdit extends Backbone.View - events: - 'click .new-tab': 'addNewTab' initialize: => @$('.component').each((idx, element) => @@ -13,6 +11,7 @@ class CMS.Views.TabsEdit extends Backbone.View ) ) + @options.mast.find('.new-tab').on('click', @addNewTab) @$('.components').sortable( handle: '.drag-handle' update: @tabMoved diff --git a/cms/static/img/hiw-feature1.png b/cms/static/img/hiw-feature1.png new file mode 100644 index 0000000000..3cfd48d066 Binary files /dev/null and b/cms/static/img/hiw-feature1.png differ diff --git a/cms/static/img/hiw-feature2.png b/cms/static/img/hiw-feature2.png new file mode 100644 index 0000000000..9442325dd5 Binary files /dev/null and b/cms/static/img/hiw-feature2.png differ diff --git a/cms/static/img/hiw-feature3.png b/cms/static/img/hiw-feature3.png new file mode 100644 index 0000000000..fa6b81ae89 Binary files /dev/null and b/cms/static/img/hiw-feature3.png differ diff --git a/cms/static/img/html-icon.png b/cms/static/img/html-icon.png index e739f2fc11..8f576178b2 100644 Binary files a/cms/static/img/html-icon.png and b/cms/static/img/html-icon.png differ diff --git a/cms/static/img/large-discussion-icon.png b/cms/static/img/large-discussion-icon.png index 2f0bfea98f..cebf332769 100644 Binary files a/cms/static/img/large-discussion-icon.png and b/cms/static/img/large-discussion-icon.png differ diff --git a/cms/static/img/large-freeform-icon.png b/cms/static/img/large-freeform-icon.png index b1d195a7ca..0d5e454f58 100644 Binary files a/cms/static/img/large-freeform-icon.png and b/cms/static/img/large-freeform-icon.png differ diff --git a/cms/static/img/large-problem-icon.png b/cms/static/img/large-problem-icon.png index b962d42b14..a30ab8eac8 100644 Binary files a/cms/static/img/large-problem-icon.png and b/cms/static/img/large-problem-icon.png differ diff --git a/cms/static/img/large-video-icon.png b/cms/static/img/large-video-icon.png index 392851324c..f1ab048b4c 100644 Binary files a/cms/static/img/large-video-icon.png and b/cms/static/img/large-video-icon.png differ diff --git a/cms/static/img/logo-edx-studio-white.png b/cms/static/img/logo-edx-studio-white.png new file mode 100644 index 0000000000..3e3ee63622 Binary files /dev/null and b/cms/static/img/logo-edx-studio-white.png differ diff --git a/cms/static/img/logo-edx-studio.png b/cms/static/img/logo-edx-studio.png new file mode 100644 index 0000000000..006194a195 Binary files /dev/null and b/cms/static/img/logo-edx-studio.png differ diff --git a/cms/static/img/pl-1x1-000.png b/cms/static/img/pl-1x1-000.png new file mode 100644 index 0000000000..b94b7a9746 Binary files /dev/null and b/cms/static/img/pl-1x1-000.png differ diff --git a/cms/static/img/pl-1x1-fff.png b/cms/static/img/pl-1x1-fff.png new file mode 100644 index 0000000000..7081c75d36 Binary files /dev/null and b/cms/static/img/pl-1x1-fff.png differ diff --git a/cms/static/img/preview-lms-staticpages.png b/cms/static/img/preview-lms-staticpages.png new file mode 100644 index 0000000000..05a62f7c7f Binary files /dev/null and b/cms/static/img/preview-lms-staticpages.png differ diff --git a/cms/static/img/thumb-hiw-feature1.png b/cms/static/img/thumb-hiw-feature1.png new file mode 100644 index 0000000000..b2dc0c00ee Binary files /dev/null and b/cms/static/img/thumb-hiw-feature1.png differ diff --git a/cms/static/img/thumb-hiw-feature2.png b/cms/static/img/thumb-hiw-feature2.png new file mode 100644 index 0000000000..e96bcad1aa Binary files /dev/null and b/cms/static/img/thumb-hiw-feature2.png differ diff --git a/cms/static/img/thumb-hiw-feature3.png b/cms/static/img/thumb-hiw-feature3.png new file mode 100644 index 0000000000..f694fca516 Binary files /dev/null and b/cms/static/img/thumb-hiw-feature3.png differ diff --git a/cms/static/js/base.js b/cms/static/js/base.js index 7e55d2b8d8..d8b32cb0e8 100644 --- a/cms/static/js/base.js +++ b/cms/static/js/base.js @@ -5,7 +5,7 @@ var $newComponentItem; var $changedInput; var $spinner; -$(document).ready(function() { +$(document).ready(function () { $body = $('body'); $modal = $('.history-modal'); $modalCover = $(' diff --git a/cms/templates/activation_complete.html b/cms/templates/activation_complete.html index 5d9437ccb3..1e195a632c 100644 --- a/cms/templates/activation_complete.html +++ b/cms/templates/activation_complete.html @@ -5,7 +5,7 @@

    Activation Complete!

    -

    Thanks for activating your account. Log in here.

    +

    Thanks for activating your account. Log in here.

    diff --git a/cms/templates/asset_index.html b/cms/templates/asset_index.html index 01766e2dac..5ace98df56 100644 --- a/cms/templates/asset_index.html +++ b/cms/templates/asset_index.html @@ -1,7 +1,7 @@ <%inherit file="base.html" /> <%! from django.core.urlresolvers import reverse %> -<%block name="bodyclass">assets -<%block name="title">Courseware Assets +<%block name="bodyclass">is-signedin course uploads +<%block name="title">Uploads & Files <%namespace name='static' file='static_content.html'/> @@ -33,12 +33,27 @@ +
    +
    +
    + Course Content +

    Files & Uploads

    +
    + + +
    +
    +
    diff --git a/cms/templates/base.html b/cms/templates/base.html index 84f10fc2d1..498897bd11 100644 --- a/cms/templates/base.html +++ b/cms/templates/base.html @@ -5,23 +5,29 @@ + + <%block name="title"></%block> | + % if context_course: + <% ctx_loc = context_course.location %> + ${context_course.display_name} | + % endif + edX Studio + + + + <%static:css group='base-style'/> - + - <%block name="title"></%block> - - - - <%block name="header_extras"> - <%include file="widgets/header.html" args="active_tab=active_tab"/> + <%include file="widgets/header.html" /> <%include file="courseware_vendor_js.html"/> @@ -47,9 +53,9 @@ <%block name="content"> + <%include file="widgets/footer.html" /> <%block name="jsextra"> - diff --git a/cms/templates/course_index.html b/cms/templates/course_index.html index e490ad7817..5c8772c1ed 100644 --- a/cms/templates/course_index.html +++ b/cms/templates/course_index.html @@ -1,5 +1,5 @@ <%inherit file="base.html" /> -<%block name="title">Course Manager + <%include file="widgets/header.html"/> <%block name="content"> diff --git a/cms/templates/course_info.html b/cms/templates/course_info.html index 83d829efa0..a68a0da76a 100644 --- a/cms/templates/course_info.html +++ b/cms/templates/course_info.html @@ -2,8 +2,9 @@ <%namespace name='static' file='static_content.html'/> -<%block name="title">Course Info -<%block name="bodyclass">course-info +<%block name="title">Updates +<%block name="bodyclass">is-signedin course course-info updates + <%block name="jsextra"> @@ -41,16 +42,38 @@ <%block name="content"> +
    +
    +
    + Course Content +

    Course Updates

    +
    + + +
    +
    + +
    +
    +
    +

    Course updates are announcements or notifications you want to share with your class. Other course authors have used them for important exam/date reminders, change in schedules, and to call out any important steps students need to be aware of.

    +
    +
    +
    +
    -

    Course Info

    diff --git a/cms/templates/edit-static-page.html b/cms/templates/edit-static-page.html index 02fe2308fa..f1b2374b46 100644 --- a/cms/templates/edit-static-page.html +++ b/cms/templates/edit-static-page.html @@ -1,7 +1,7 @@ <%inherit file="base.html" /> <%! from django.core.urlresolvers import reverse %> -<%block name="title">Edit Static Page -<%block name="bodyclass">edit-static-page +<%block name="title">Editing Static Page +<%block name="bodyclass">is-signedin course pages edit-static-page <%block name="content">
    diff --git a/cms/templates/edit-tabs.html b/cms/templates/edit-tabs.html index c6ffb14124..1a44de60c1 100644 --- a/cms/templates/edit-tabs.html +++ b/cms/templates/edit-tabs.html @@ -1,7 +1,7 @@ <%inherit file="base.html" /> <%! from django.core.urlresolvers import reverse %> -<%block name="title">Tabs -<%block name="bodyclass">static-pages +<%block name="title">Static Pages +<%block name="bodyclass">is-signedin course pages static-pages <%block name="jsextra"> <%block name="content"> +
    +
    +
    + Course Content +

    Static Pages

    +
    + + +
    +
    + +
    +
    + +
    +
    +
    -
    -

    Here you can add and manage additional pages for your course

    -

    These pages will be added to the primary navigation menu alongside Courseware, Course Info, Discussion, etc.

    -
    - -
      @@ -43,4 +67,17 @@
    + +
    +

    How Static Pages are Used in Your Course

    +
    + Preview of how Static Pages are used in your course +
    These pages will be presented in your course's main navigation alongside Courseware, Course Info, Discussion, etc.
    +
    + + + + close modal + +
    \ No newline at end of file diff --git a/cms/templates/edit_subsection.html b/cms/templates/edit_subsection.html index d81f577940..00780eab3b 100644 --- a/cms/templates/edit_subsection.html +++ b/cms/templates/edit_subsection.html @@ -7,8 +7,9 @@ %> <%! from django.core.urlresolvers import reverse %> -<%block name="bodyclass">subsection <%block name="title">CMS Subsection +<%block name="bodyclass">is-signedin course subsection + <%namespace name="units" file="widgets/units.html" /> <%namespace name='static' file='static_content.html'/> @@ -97,6 +98,7 @@
    +
    <%block name="jsextra"> diff --git a/cms/templates/export.html b/cms/templates/export.html index fcdd26458a..27045d82ce 100644 --- a/cms/templates/export.html +++ b/cms/templates/export.html @@ -2,10 +2,19 @@ <%namespace name='static' file='static_content.html'/> <%! from django.core.urlresolvers import reverse %> -<%block name="title">Export -<%block name="bodyclass">export +<%block name="title">Export Course +<%block name="bodyclass">is-signedin course tools export <%block name="content"> +
    +
    +
    + Tools +

    Course Export

    +
    +
    +
    +
    diff --git a/cms/templates/howitworks.html b/cms/templates/howitworks.html new file mode 100644 index 0000000000..1cf9b17710 --- /dev/null +++ b/cms/templates/howitworks.html @@ -0,0 +1,185 @@ +<%inherit file="base.html" /> +<%! from django.core.urlresolvers import reverse %> + +<%block name="title">Welcome +<%block name="bodyclass">not-signedin index howitworks + +<%block name="content"> + +
    +
    +
    +

    Welcome to

    +

    Studio helps manage your courses online, so you can focus on teaching them

    +
    +
    +
    + +
    +
    +
    +

    Studio's Many Features

    +
    + +
      +
    1. +
      + + Studio Helps You Keep Your Courses Organized +
      Studio Helps You Keep Your Courses Organized
      + + + +
      +
      + +
      +

      Keeping Your Course Organized

      +

      The backbone of your course is how it is organized. Studio offers an Outline editor, providing a simple hierarchy and easy drag and drop to help you and your students stay organized.

      + +
        +
      • +

        Simple Organization For Content

        +

        Studio uses a simple hierarchy of sections and subsections to organize your content.

        +
      • + +
      • +

        Change Your Mind Anytime

        +

        Draft your outline and build content anywhere. Simple drag and drop tools let your reorganize quickly.

        +
      • + +
      • +

        Go A Week Or A Semester At A Time

        +

        Build and release sections to your students incrementally. You don't have to have it all done at once.

        +
      • +
      +
      +
    2. + +
    3. +
      + + Learning is More than Just Lectures +
      Learning is More than Just Lectures
      + + + +
      +
      + +
      +

      Learning is More than Just Lectures

      +

      Studio lets you weave your content together in a way that reinforces learning — short video lectures interleaved with exercises and more. Insert videos and author a wide variety of exercise types with just a few clicks.

      + +
        +
      • +

        Create Learning Pathways

        +

        Help your students understand a small interactive piece at a time with multimedia, HTML, and exercises.

        +
      • + +
      • +

        Work Visually, Organize Quickly

        +

        Work visually and see exactly what your students will see. Reorganize all your content with drag and drop.

        +
      • + +
      • +

        A Broad Library of Problem Types

        +

        It's more than just multiple choice. Studio has nearly a dozen types of problems to challenge your learners.

        +
      • +
      +
      +
    4. + +
    5. +
      + + Studio Gives You Simple, Fast, and Incremental Publishing. With Friends. +
      Studio Gives You Simple, Fast, and Incremental Publishing. With Friends.
      + + + +
      +
      + +
      +

      Simple, Fast, and Incremental Publishing. With Friends.

      +

      Studio works like web applications you already know, yet understands how you build curriculum. Instant publishing to the web when you want it, incremental release when it makes sense. And with co-authors, you can have a whole team building a course, together.

      + +
        +
      • +

        Instant Changes

        +

        Caught a bug? No problem. When you want, your changes to live when you hit Save.

        +
      • + +
      • +

        Release-On Date Publishing

        +

        When you've finished a section, pick when you want it to go live and Studio takes care of the rest. Build your course incrementally.

        +
      • + +
      • +

        Work in Teams

        +

        Co-authors have full access to all the same authoring tools. Make your course better through a team effort.

        +
      • +
      +
      +
    6. +
    +
    +
    + +
    +
    +
    +

    Sign Up for Studio Today!

    +
    + + +
    +
    + +
    +

    Outlining Your Course

    +
    + +
    Simple two-level outline to organize your couse. Drag and drop, and see your course at a glance.
    +
    + + + + close modal + +
    + +
    +

    More than Just Lectures

    +
    + +
    Quickly create videos, text snippets, inline discussions, and a variety of problem types.
    +
    + + + + close modal + +
    + +
    +

    Publishing on Date

    +
    + +
    Simply set the date of a section or subsection, and Studio will publish it to your students for you.
    +
    + + + + close modal + +
    + \ No newline at end of file diff --git a/cms/templates/import.html b/cms/templates/import.html index e4f8019714..b0a9f04903 100644 --- a/cms/templates/import.html +++ b/cms/templates/import.html @@ -2,10 +2,19 @@ <%namespace name='static' file='static_content.html'/> <%! from django.core.urlresolvers import reverse %> -<%block name="title">Import -<%block name="bodyclass">import +<%block name="title">Import Course +<%block name="bodyclass">is-signedin course tools import <%block name="content"> +
    +
    +
    + Tools +

    Course Import

    +
    +
    +
    +
    diff --git a/cms/templates/index.html b/cms/templates/index.html index 45c4edc176..fdb46612a0 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -1,6 +1,7 @@ <%inherit file="base.html" /> -<%block name="bodyclass">index + <%block name="title">Courses +<%block name="bodyclass">is-signedin index dashboard <%block name="header_extras"> - - + \ No newline at end of file diff --git a/cms/templates/manage_users.html b/cms/templates/manage_users.html index 99ac279bfb..722e756203 100644 --- a/cms/templates/manage_users.html +++ b/cms/templates/manage_users.html @@ -1,17 +1,31 @@ <%inherit file="base.html" /> <%block name="title">Course Staff Manager -<%block name="bodyclass">users +<%block name="bodyclass">is-signedin course users settings team + <%block name="content"> +
    +
    +
    + Course Settings +

    Course Team

    +
    + + +
    +
    +
    -
    - %if allow_actions: - - New User - - %endif -

    The following list of users have been designated as course staff. This means that these users will have permissions to modify course content. You may add additional course staff below, if you are the course instructor. Please note that they must have already registered and verified their account.

    diff --git a/cms/templates/overview.html b/cms/templates/overview.html index 20ddcead01..91a1107726 100644 --- a/cms/templates/overview.html +++ b/cms/templates/overview.html @@ -6,7 +6,8 @@ from datetime import datetime %> <%! from django.core.urlresolvers import reverse %> -<%block name="title">CMS Courseware Overview +<%block name="title">Course Outline +<%block name="bodyclass">is-signedin course outline <%namespace name='static' file='static_content.html'/> <%namespace name="units" file="widgets/units.html" /> @@ -119,12 +120,32 @@
    +
    +
    +
    + Course Content +

    Course Outline

    +
    + + +
    +
    +
    -
    % for section in sections:
    diff --git a/cms/templates/settings.html b/cms/templates/settings.html index c96d5686fd..32d24b77e6 100644 --- a/cms/templates/settings.html +++ b/cms/templates/settings.html @@ -1,6 +1,6 @@ <%inherit file="base.html" /> -<%block name="bodyclass">settings -<%block name="title">Settings +<%block name="title">Schedule & Details +<%block name="bodyclass">is-signedin course schedule settings <%namespace name='static' file='static_content.html'/> <%! @@ -15,24 +15,24 @@ from contentstore import utils - - - - - + + + + + + + + + + + +<%block name="content"> + +
    +
    +

    Settings

    +
    +
    + +
    +

    Faculty

    + +
    +
    +

    Faculty Members

    + Individuals instructing and help with this course +
    + +
    +
    +
      +
    • +
      + +
      + +
      +
      + +
      + +
      + +
      +
      + +
      + + +
      + +
      + +
      + + A brief description of your education, experience, and expertise +
      +
      + + Delete Faculty Member +
    • + +
    • +
      + +
      + +
      +
      + +
      + +
      + +
      +
      + +
      + +
      +
      + + Upload Faculty Photo + + Max size: 30KB +
      +
      +
      + +
      + +
      +
      + + A brief description of your education, experience, and expertise +
      +
      +
      +
    • +
    + + + New Faculty Member + +
    +
    +
    + +
    + +
    +

    Problems

    + +
    +
    +

    General Settings

    + Course-wide settings for all problems +
    + +
    +

    Problem Randomization:

    + +
    +
    + + +
    + + randomize all problems +
    +
    + +
    + + +
    + + do not randomize problems +
    +
    + +
    + + +
    + + randomize problems per student +
    +
    +
    +
    + +
    +

    Show Answers:

    + +
    +
    + + +
    + + Answers will be shown after the number of attempts has been met +
    +
    + +
    + + +
    + + Answers will never be shown, regardless of attempts +
    +
    +
    +
    + +
    + + +
    +
    + + Students will this have this number of chances to answer a problem. To set infinite atttempts, use "0" +
    +
    +
    +
    + +
    +
    +

    [Assignment Type Name]

    +
    + +
    +

    Problem Randomization:

    + +
    +
    + + +
    + + randomize all problems +
    +
    + +
    + + +
    + + do not randomize problems +
    +
    + +
    + + +
    + + randomize problems per student +
    +
    +
    +
    + +
    +

    Show Answers:

    + +
    +
    + + +
    + + Answers will be shown after the number of attempts has been met +
    +
    + +
    + + +
    + + Answers will never be shown, regardless of attempts +
    +
    +
    +
    + +
    + + +
    +
    + + Students will this have this number of chances to answer a problem. To set infinite atttempts, use "0" +
    +
    +
    +
    +
    + +
    +

    Discussions

    + +
    +
    +

    General Settings

    + Course-wide settings for online discussion +
    + +
    +

    Anonymous Discussions:

    + +
    +
    + + +
    + + Students and faculty will be able to post anonymously +
    +
    + +
    + + +
    + + Posting anonymously is not allowed. Any previous anonymous posts will be reverted to non-anonymous +
    +
    +
    +
    + +
    +

    Anonymous Discussions:

    + +
    +
    + + +
    + + Students and faculty will be able to post anonymously +
    +
    + +
    + + +
    + + This option is disabled since there are previous discussions that are anonymous. +
    +
    +
    +
    + +
    +

    Discussion Categories

    + +
    + + + + New Discussion Category + +
    +
    +
    +
    +
    +
    +
    +
    +
    + diff --git a/cms/templates/settings_graders.html b/cms/templates/settings_graders.html new file mode 100644 index 0000000000..61cb59e995 --- /dev/null +++ b/cms/templates/settings_graders.html @@ -0,0 +1,151 @@ +<%inherit file="base.html" /> +<%block name="title">Grading +<%block name="bodyclass">is-signedin course grading settings + +<%namespace name='static' file='static_content.html'/> +<%! +from contentstore import utils +%> + +<%block name="jsextra"> + + + + + + + + + + + + + +<%block name="content"> +
    +
    +
    + Settings +

    Grading

    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    Overall Grade Range

    + Your overall grading scale for student final grades +
    + +
      +
    1. +
      + +
      +
      +
        +
      1. 0
      2. +
      3. 10
      4. +
      5. 20
      6. +
      7. 30
      8. +
      9. 40
      10. +
      11. 50
      12. +
      13. 60
      14. +
      15. 70
      16. +
      17. 80
      18. +
      19. 90
      20. +
      21. 100
      22. +
      +
        +
      +
      +
      +
      +
    2. +
    +
    + +
    + +
    +
    +

    Grading Rules & Policies

    + Deadlines, requirements, and logistics around grading student work +
    + +
      +
    1. + + + Leeway on due dates +
    2. +
    +
    + +
    + +
    +
    +

    Assignment Types

    + Categories and labels for any exercises that are gradable +
    + +
      + +
    + + +
    +
    +
    + + +
    +
    + diff --git a/cms/templates/signup.html b/cms/templates/signup.html index 2c60b758e6..30c5c1cf2b 100644 --- a/cms/templates/signup.html +++ b/cms/templates/signup.html @@ -1,94 +1,141 @@ <%inherit file="base.html" /> <%! from django.core.urlresolvers import reverse %> -<%block name="title">Sign up -<%block name="bodyclass">no-header +<%block name="title">Sign Up +<%block name="bodyclass">not-signedin signup <%block name="content"> -
    +
    +
    +
    +

    Sign Up for edX Studio

    + +
    - +
    +

    I've never authored a course online before. Is there help?

    +

    Absolutely. We have created an online course, edX101, that describes some best practices: from filming video, creating exercises, to the basics of running an online course. Additionally, we're always here to help, just drop us a note.

    +
    + +
    +
    + - + ); + }); + })(this) + \ No newline at end of file diff --git a/cms/templates/unit.html b/cms/templates/unit.html index f3a779604e..c529f5863a 100644 --- a/cms/templates/unit.html +++ b/cms/templates/unit.html @@ -1,8 +1,9 @@ <%inherit file="base.html" /> <%! from django.core.urlresolvers import reverse %> <%namespace name="units" file="widgets/units.html" /> -<%block name="bodyclass">unit -<%block name="title">CMS Unit +<%block name="title">Individual Unit +<%block name="bodyclass">is-signedin course unit + <%block name="jsextra"> @@ -56,38 +65,66 @@
    % for type, templates in sorted(component_templates.items()):
    -

    Select ${type} component type:

    - - + % if type == "problem": +
    + + % endif +
    +
      + % for name, location, has_markdown, is_empty in templates: + % if has_markdown or type != "problem": + % if is_empty: +
    • + + ${name} + +
    • + + % else: +
    • + + ${name} + +
    • + % endif + % endif + + %endfor +
    +
    + % if type == "problem": +
    +
      + % for name, location, has_markdown, is_empty in templates: + % if not has_markdown: + % if is_empty: +
    • + + ${name} + +
    • + + % else: +
    • + + ${name} + + +
    • + % endif + % endif + % endfor +
    +
    +
    + % endif Cancel
    % endfor diff --git a/cms/templates/widgets/footer.html b/cms/templates/widgets/footer.html new file mode 100644 index 0000000000..0f265dfc2c --- /dev/null +++ b/cms/templates/widgets/footer.html @@ -0,0 +1,30 @@ +<%! from django.core.urlresolvers import reverse %> + + \ No newline at end of file diff --git a/cms/templates/widgets/header.html b/cms/templates/widgets/header.html index 5f41452339..7b516ececd 100644 --- a/cms/templates/widgets/header.html +++ b/cms/templates/widgets/header.html @@ -1,40 +1,117 @@ <%! from django.core.urlresolvers import reverse %> -<% active_tab_class = 'active-tab-' + active_tab if active_tab else '' %> -
    -
    -
    -
    - % if context_course: - <% ctx_loc = context_course.location %> - › - ${context_course.display_name} › - % endif -
    +
    + + +
    + % if user.is_authenticated(): + + % else: + + % endif +
    +
    +
    \ No newline at end of file diff --git a/cms/templates/widgets/problem-edit.html b/cms/templates/widgets/problem-edit.html index 4ff9d299ab..8ca07a7928 100644 --- a/cms/templates/widgets/problem-edit.html +++ b/cms/templates/widgets/problem-edit.html @@ -1,20 +1,20 @@ <%include file="metadata-edit.html" />
    - %if markdown != '' or data == '\n\n': + %if enable_markdown:
    • -
    • -
    • -
    • -
    • @@ -56,7 +56,7 @@
    -
    Check Multiple
    +
    Checkboxes
    @@ -67,7 +67,7 @@
    -
    String Response
    +
    Text Input
    @@ -76,7 +76,7 @@
    -
    Numerical Response
    +
    Numerical Input
    @@ -85,7 +85,7 @@
    -
    Option Response
    +
    Dropdown
    diff --git a/cms/urls.py b/cms/urls.py index ad4dd87d74..35b2707241 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -6,7 +6,8 @@ from django.conf.urls import patterns, include, url # admin.autodiscover() urlpatterns = ('', - url(r'^$', 'contentstore.views.index', name='index'), + url(r'^$', 'contentstore.views.howitworks', name='homepage'), + url(r'^listing', 'contentstore.views.index', name='index'), url(r'^edit/(?P.*?)$', 'contentstore.views.edit_unit', name='edit_unit'), url(r'^subsection/(?P.*?)$', 'contentstore.views.edit_subsection', name='edit_subsection'), url(r'^preview_component/(?P.*?)$', 'contentstore.views.preview_component', name='preview_component'), @@ -42,9 +43,10 @@ urlpatterns = ('', 'contentstore.views.remove_user', name='remove_user'), url(r'^(?P[^/]+)/(?P[^/]+)/info/(?P[^/]+)$', 'contentstore.views.course_info', name='course_info'), url(r'^(?P[^/]+)/(?P[^/]+)/course_info/updates/(?P.*)$', 'contentstore.views.course_info_updates', name='course_info'), - url(r'^(?P[^/]+)/(?P[^/]+)/settings/(?P[^/]+)$', 'contentstore.views.get_course_settings', name='course_settings'), - url(r'^(?P[^/]+)/(?P[^/]+)/settings/(?P[^/]+)/section/(?P
    [^/]+).*$', 'contentstore.views.course_settings_updates', name='course_settings'), - url(r'^(?P[^/]+)/(?P[^/]+)/grades/(?P[^/]+)/(?P.*)$', 'contentstore.views.course_grader_updates', name='course_settings'), + url(r'^(?P[^/]+)/(?P[^/]+)/settings-details/(?P[^/]+)$', 'contentstore.views.get_course_settings', name='course_settings'), + url(r'^(?P[^/]+)/(?P[^/]+)/settings-grading/(?P[^/]+)$', 'contentstore.views.course_config_graders_page', name='course_settings'), + url(r'^(?P[^/]+)/(?P[^/]+)/settings-details/(?P[^/]+)/section/(?P
    [^/]+).*$', 'contentstore.views.course_settings_updates', name='course_settings'), + url(r'^(?P[^/]+)/(?P[^/]+)/settings-grading/(?P[^/]+)/(?P.*)$', 'contentstore.views.course_grader_updates', name='course_settings'), url(r'^(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/gradeas.*$', 'contentstore.views.assignment_type_update', name='assignment_type_update'), @@ -76,13 +78,15 @@ urlpatterns = ('', # User creation and updating views urlpatterns += ( + url(r'^howitworks$', 'contentstore.views.howitworks', name='howitworks'), url(r'^signup$', 'contentstore.views.signup', name='signup'), url(r'^create_account$', 'student.views.create_account'), url(r'^activate/(?P[^/]*)$', 'student.views.activate_account', name='activate'), # form page - url(r'^login$', 'contentstore.views.login_page', name='login'), + url(r'^login$', 'contentstore.views.old_login_redirect', name='old_login'), + url(r'^signin$', 'contentstore.views.login_page', name='login'), # ajax view that actually does the work url(r'^login_post$', 'student.views.login_user', name='login_post'), diff --git a/lms/djangoapps/terrain/__init__.py b/common/djangoapps/terrain/__init__.py similarity index 100% rename from lms/djangoapps/terrain/__init__.py rename to common/djangoapps/terrain/__init__.py diff --git a/lms/djangoapps/terrain/browser.py b/common/djangoapps/terrain/browser.py similarity index 87% rename from lms/djangoapps/terrain/browser.py rename to common/djangoapps/terrain/browser.py index e1925bde0b..8c2a8ba7a5 100644 --- a/lms/djangoapps/terrain/browser.py +++ b/common/djangoapps/terrain/browser.py @@ -11,8 +11,9 @@ from django.core.management import call_command @before.harvest def initial_setup(server): - # Launch firefox + # Launch the browser app (choose one of these below) world.browser = Browser('chrome') + # world.browser = Browser('firefox') @before.each_scenario diff --git a/lms/djangoapps/terrain/factories.py b/common/djangoapps/terrain/factories.py similarity index 100% rename from lms/djangoapps/terrain/factories.py rename to common/djangoapps/terrain/factories.py diff --git a/lms/djangoapps/terrain/steps.py b/common/djangoapps/terrain/steps.py similarity index 100% rename from lms/djangoapps/terrain/steps.py rename to common/djangoapps/terrain/steps.py diff --git a/common/lib/logsettings.py b/common/lib/logsettings.py index 2ed20a0bad..8fc2bb9db1 100644 --- a/common/lib/logsettings.py +++ b/common/lib/logsettings.py @@ -53,7 +53,7 @@ def get_logger_config(log_dir, logging_env=logging_env, hostname=hostname) - handlers = ['console', 'local', 'null'] if debug else ['console', + handlers = ['console', 'local'] if debug else ['console', 'syslogger-remote', 'local'] logger_config = { @@ -84,12 +84,6 @@ def get_logger_config(log_dir, 'level': 'ERROR', 'class': 'newrelic_logging.NewRelicHandler', 'formatter': 'raw', - }, - 'null' : { - 'level': 'CRITICAL', - 'class': 'logging.handlers.SysLogHandler', - 'address': syslog_addr, - 'formatter': 'syslog_format', } }, 'loggers': { @@ -98,26 +92,11 @@ def get_logger_config(log_dir, 'level': 'DEBUG', 'propagate': False, }, - 'django.db.backends': { - 'handlers': ['null'], - 'propagate': False, - 'level':'DEBUG', - }, - 'django_comment_client.utils' : { - 'handlers': ['null'], - 'propagate': False, - 'level':'DEBUG', - }, - 'pipeline.compilers' : { - 'handlers': ['null'], - 'propagate': False, - 'level':'DEBUG', - }, '': { 'handlers': handlers, 'level': 'DEBUG', 'propagate': False - } + }, } } diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index d806ec7913..4635cc6871 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -703,15 +703,15 @@ class CapaDescriptor(RawDescriptor): def get_context(self): _context = RawDescriptor.get_context(self) - _context.update({'markdown': self.metadata.get('markdown', '')}) + _context.update({'markdown': self.metadata.get('markdown', ''), + 'enable_markdown' : 'markdown' in self.metadata}) return _context @property def editable_metadata_fields(self): - """Remove metadata from the editable fields since it has its own editor""" - subset = super(CapaDescriptor, self).editable_metadata_fields - if 'markdown' in subset: - subset.remove('markdown') + """Remove any metadata from the editable fields which have their own editor or shouldn't be edited by user.""" + subset = [field for field in super(CapaDescriptor,self).editable_metadata_fields + if field not in ['markdown', 'empty']] return subset diff --git a/common/lib/xmodule/xmodule/css/combinedopenended/display.scss b/common/lib/xmodule/xmodule/css/combinedopenended/display.scss index 4cff477127..20700ab092 100644 --- a/common/lib/xmodule/xmodule/css/combinedopenended/display.scss +++ b/common/lib/xmodule/xmodule/css/combinedopenended/display.scss @@ -120,7 +120,7 @@ div.combined-rubric-container { } } - b.rubric-category { + span.rubric-category { font-size: .9em; } padding-bottom: 5px; diff --git a/common/lib/xmodule/xmodule/css/html/display.scss b/common/lib/xmodule/xmodule/css/html/display.scss index 956923c6d0..93138ac5a9 100644 --- a/common/lib/xmodule/xmodule/css/html/display.scss +++ b/common/lib/xmodule/xmodule/css/html/display.scss @@ -49,10 +49,18 @@ p { em, i { font-style: italic; + + span { + font-style: italic; + } } strong, b { font-weight: bold; + + span { + font-weight: bold; + } } p + p, ul + p, ol + p { diff --git a/common/lib/xmodule/xmodule/html_module.py b/common/lib/xmodule/xmodule/html_module.py index af1ce0ad80..456ea3cf10 100644 --- a/common/lib/xmodule/xmodule/html_module.py +++ b/common/lib/xmodule/xmodule/html_module.py @@ -172,6 +172,13 @@ class HtmlDescriptor(XmlDescriptor, EditingDescriptor): elt.set("filename", relname) return elt + @property + def editable_metadata_fields(self): + """Remove any metadata from the editable fields which have their own editor or shouldn't be edited by user.""" + subset = [field for field in super(HtmlDescriptor,self).editable_metadata_fields + if field not in ['empty']] + return subset + class AboutDescriptor(HtmlDescriptor): """ diff --git a/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee b/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee index 159821b618..224b33169a 100644 --- a/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee +++ b/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee @@ -19,7 +19,7 @@ class @Rubric # finds the scores for each rubric category @get_score_list: () => # find the number of categories: - num_categories = $('b.rubric-category').length + num_categories = $('.rubric-category').length score_lst = [] # get the score for each one @@ -38,7 +38,7 @@ class @Rubric @check_complete: () -> # check to see whether or not any categories have not been scored - num_categories = $('b.rubric-category').length + num_categories = $('.rubric-category').length for i in [0..(num_categories-1)] score = $("input[name='score-selection-#{i}']:checked").val() if score == undefined diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/display/html5_video.js b/common/lib/xmodule/xmodule/js/src/videoalpha/display/html5_video.js index acdc03932c..c3cc462ab8 100644 --- a/common/lib/xmodule/xmodule/js/src/videoalpha/display/html5_video.js +++ b/common/lib/xmodule/xmodule/js/src/videoalpha/display/html5_video.js @@ -221,6 +221,15 @@ this.HTML5Video = (function () { // and end playing at the specified end time. After it was paused, or when a seek operation happeded, // the starting time and ending time will reset to the beginning and the end of the video respectively. this.video.addEventListener('canplay', function () { + // Because firefox triggers 'canplay' event every time when 'currentTime' property + // changes, we must make sure that this block of code runs only once. Otherwise, + // this will be an endless loop ('currentTime' property is changed below). + // + // Chrome is immune to this behavior. + if (_this.playerState !== HTML5Video.PlayerState.UNSTARTED) { + return; + } + _this.playerState = HTML5Video.PlayerState.PAUSED; if (_this.start > _this.video.duration) { diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py index 62f173e3d3..6c7ff66c73 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py @@ -532,9 +532,6 @@ class CombinedOpenEndedV1Module(): rubric_scores = [all_responses[i]['rubric_scores'] for i in xrange(0,len(all_responses)) if len(all_responses[i]['rubric_scores'])>0 and all_responses[i]['grader_types'][0] in HUMAN_GRADER_TYPE.keys()] grader_types = [all_responses[i]['grader_types'] for i in xrange(0,len(all_responses)) if len(all_responses[i]['grader_types'])>0 and all_responses[i]['grader_types'][0] in HUMAN_GRADER_TYPE.keys()] feedback_items = [all_responses[i]['feedback_items'] for i in xrange(0,len(all_responses)) if len(all_responses[i]['feedback_items'])>0 and all_responses[i]['grader_types'][0] in HUMAN_GRADER_TYPE.keys()] - log.debug(rubric_scores) - log.debug(grader_types) - log.debug(feedback_items) rubric_html = self.rubric_renderer.render_combined_rubric(stringify_children(self.static_data['rubric']), rubric_scores, grader_types, feedback_items) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py index 817d0293e7..7c00c5f029 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py @@ -50,7 +50,7 @@ class CombinedOpenEndedRubric(object): success = False try: rubric_categories = self.extract_categories(rubric_xml) - if score_list: + if score_list and len(score_list)==len(rubric_categories): for i in xrange(0,len(rubric_categories)): category = rubric_categories[i] for j in xrange(0,len(category['options'])): @@ -231,6 +231,14 @@ class CombinedOpenEndedRubric(object): @staticmethod def reformat_scores_for_rendering(scores, score_types, feedback_types): + """ + Takes in a list of rubric scores, the types of those scores, and feedback associated with them + Outputs a reformatted list of score tuples (count, rubric category, rubric score, [graders that gave this score], [feedback types]) + @param scores: + @param score_types: + @param feedback_types: + @return: + """ success = False if len(scores)==0: log.error("Score length is 0.") @@ -271,6 +279,13 @@ class CombinedOpenEndedRubric(object): @staticmethod def check_for_tuple_matches(tuples, tuple): + """ + Checks to see if a tuple in a list of tuples is a match for tuple. + If not match, creates a new tuple matching tuple. + @param tuples: list of tuples + @param tuple: tuples to match + @return: a new list of tuples, and the index of the tuple that matches tuple + """ category = tuple[1] score = tuple[2] tup_ind = -1 diff --git a/common/lib/xmodule/xmodule/templates/html/empty.yaml b/common/lib/xmodule/xmodule/templates/html/empty.yaml index 1262ed37cf..b6d867d7d6 100644 --- a/common/lib/xmodule/xmodule/templates/html/empty.yaml +++ b/common/lib/xmodule/xmodule/templates/html/empty.yaml @@ -1,6 +1,7 @@ --- metadata: - display_name: Empty + display_name: Blank HTML Page + empty: True data: | diff --git a/common/lib/xmodule/xmodule/templates/problem/circuitschematic.yaml b/common/lib/xmodule/xmodule/templates/problem/circuitschematic.yaml index f56b17b1b9..a94b824cfb 100644 --- a/common/lib/xmodule/xmodule/templates/problem/circuitschematic.yaml +++ b/common/lib/xmodule/xmodule/templates/problem/circuitschematic.yaml @@ -1,6 +1,7 @@ + --- metadata: - display_name: Circuit Schematic + display_name: Circuit Schematic Builder rerandomize: never showanswer: always weight: "" diff --git a/common/lib/xmodule/xmodule/templates/problem/customgrader.yaml b/common/lib/xmodule/xmodule/templates/problem/customgrader.yaml index 6ada6f97f3..aadbe4075a 100644 --- a/common/lib/xmodule/xmodule/templates/problem/customgrader.yaml +++ b/common/lib/xmodule/xmodule/templates/problem/customgrader.yaml @@ -1,6 +1,6 @@ --- metadata: - display_name: Custom Grader + display_name: Custom Python-Evaluated Input rerandomize: never showanswer: always weight: "" @@ -8,7 +8,7 @@ metadata: data: |

    - A custom response problem accepts one or more lines of text input from the + A custom python-evaluated input problem accepts one or more lines of text input from the student, and evaluates the inputs for correctness based on evaluation using a python script embedded within the problem.

    diff --git a/common/lib/xmodule/xmodule/templates/problem/empty.yaml b/common/lib/xmodule/xmodule/templates/problem/empty.yaml index 346f49609c..39c9e7671c 100644 --- a/common/lib/xmodule/xmodule/templates/problem/empty.yaml +++ b/common/lib/xmodule/xmodule/templates/problem/empty.yaml @@ -1,10 +1,11 @@ --- metadata: - display_name: Empty + display_name: Blank Common Problem rerandomize: never showanswer: always markdown: "" weight: "" + empty: True attempts: "" data: | diff --git a/common/lib/xmodule/xmodule/templates/problem/emptyadvanced.yaml b/common/lib/xmodule/xmodule/templates/problem/emptyadvanced.yaml new file mode 100644 index 0000000000..bba7b3a8ac --- /dev/null +++ b/common/lib/xmodule/xmodule/templates/problem/emptyadvanced.yaml @@ -0,0 +1,13 @@ +--- +metadata: + display_name: Blank Advanced Problem + rerandomize: never + showanswer: always + weight: "" + attempts: "" + empty: True +data: | + + + +children: [] diff --git a/common/lib/xmodule/xmodule/templates/problem/forumularesponse.yaml b/common/lib/xmodule/xmodule/templates/problem/forumularesponse.yaml index 5b30a0497d..b4c53a107b 100644 --- a/common/lib/xmodule/xmodule/templates/problem/forumularesponse.yaml +++ b/common/lib/xmodule/xmodule/templates/problem/forumularesponse.yaml @@ -1,6 +1,6 @@ --- metadata: - display_name: Formula Response + display_name: Math Expression Input rerandomize: never showanswer: always weight: "" @@ -8,7 +8,7 @@ metadata: data: |

    - A formula response problem accepts a line of text representing a mathematical expression from the + A math expression input problem accepts a line of text representing a mathematical expression from the student, and evaluates the input for equivalence to a mathematical expression provided by the grader. Correctness is based on numerical sampling of the symbolic expressions.

    diff --git a/common/lib/xmodule/xmodule/templates/problem/imageresponse.yaml b/common/lib/xmodule/xmodule/templates/problem/imageresponse.yaml index 069c157852..3ef619d54b 100644 --- a/common/lib/xmodule/xmodule/templates/problem/imageresponse.yaml +++ b/common/lib/xmodule/xmodule/templates/problem/imageresponse.yaml @@ -1,6 +1,6 @@ --- metadata: - display_name: Image Response + display_name: Image Mapped Input rerandomize: never showanswer: always weight: "" @@ -8,7 +8,7 @@ metadata: data: |

    - An image response problem presents an image for the student. Input is + An image mapped input problem presents an image for the student. Input is given by the location of mouse clicks on the image. Correctness of input can be evaluated based on expected dimensions of a rectangle.

    diff --git a/common/lib/xmodule/xmodule/templates/problem/multiplechoice.yaml b/common/lib/xmodule/xmodule/templates/problem/multiplechoice.yaml index 9e61324ae1..3a35a35199 100644 --- a/common/lib/xmodule/xmodule/templates/problem/multiplechoice.yaml +++ b/common/lib/xmodule/xmodule/templates/problem/multiplechoice.yaml @@ -26,10 +26,6 @@ metadata: ( ) The vegetable peeler - ( ) Android - - ( ) The Beatles - [explanation] The release of the iPod allowed consumers to carry their entire music library with them in a @@ -51,8 +47,6 @@ data: | Napster The iPod The vegetable peeler - Android - The Beatles diff --git a/common/lib/xmodule/xmodule/templates/problem/numericalresponse.yaml b/common/lib/xmodule/xmodule/templates/problem/numericalresponse.yaml index e0a5776222..1dc46f5f51 100644 --- a/common/lib/xmodule/xmodule/templates/problem/numericalresponse.yaml +++ b/common/lib/xmodule/xmodule/templates/problem/numericalresponse.yaml @@ -1,12 +1,12 @@ --- metadata: - display_name: Numerical Response + display_name: Numerical Input rerandomize: never showanswer: always weight: "" attempts: "" markdown: - "A numerical response problem accepts a line of text input from the + "A numerical input problem accepts a line of text input from the student, and evaluates the input for correctness based on its numerical value. @@ -45,7 +45,7 @@ metadata: data: |

    - A numerical response problem accepts a line of text input from the + A numerical input problem accepts a line of text input from the student, and evaluates the input for correctness based on its numerical value.

    diff --git a/common/lib/xmodule/xmodule/templates/problem/optionresponse.yaml b/common/lib/xmodule/xmodule/templates/problem/optionresponse.yaml index 1a42a5a009..f523c7fdc5 100644 --- a/common/lib/xmodule/xmodule/templates/problem/optionresponse.yaml +++ b/common/lib/xmodule/xmodule/templates/problem/optionresponse.yaml @@ -1,12 +1,12 @@ --- metadata: - display_name: Option Response + display_name: Dropdown rerandomize: never showanswer: always weight: "" attempts: "" markdown: - "OptionResponse gives a limited set of options for students to respond with, and presents those options + "Dropdown problems give a limited set of options for students to respond with, and present those options in a format that encourages them to search for a specific answer rather than being immediately presented with options from which to recognize the correct answer. @@ -14,30 +14,30 @@ metadata: The answer options and the identification of the correct answer is defined in the optioninput tag. - Translation between Option Response and __________ is extremely straightforward: + Translation between Dropdown and __________ is extremely straightforward: - [[(Multiple Choice), String Response, Numerical Response, External Response, Image Response]] + [[(Multiple Choice), Text Input, Numerical Input, External Response, Image Response]] [explanation] Multiple Choice also allows students to select from a variety of pre-written responses, although the - format makes it easier for students to read very long response options. Optionresponse also differs + format makes it easier for students to read very long response options. Dropdowns also differ slightly because students are more likely to think of an answer and then search for it rather than relying purely on recognition to answer the question. [explanation] " data: | -

    OptionResponse gives a limited set of options for students to respond with, and presents those options +

    Dropdown problems give a limited set of options for students to respond with, and present those options in a format that encourages them to search for a specific answer rather than being immediately presented with options from which to recognize the correct answer.

    The answer options and the identification of the correct answer is defined in the optioninput tag.

    -

    Translation between Option Response and __________ is extremely straightforward: +

    Translation between Dropdown and __________ is extremely straightforward: - +

    diff --git a/common/lib/xmodule/xmodule/templates/problem/string_response.yaml b/common/lib/xmodule/xmodule/templates/problem/string_response.yaml index 1761ea8f67..c018d3f6cf 100644 --- a/common/lib/xmodule/xmodule/templates/problem/string_response.yaml +++ b/common/lib/xmodule/xmodule/templates/problem/string_response.yaml @@ -1,15 +1,15 @@ --- metadata: - display_name: String Response + display_name: Text Input rerandomize: never showanswer: always weight: "" attempts: "" # Note, the extra newlines are needed to make the yaml parser add blank lines instead of folding markdown: - "A string response problem accepts a line of text input from the + "A text input problem accepts a line of text from the student, and evaluates the input for correctness based on an expected - answer within each input box. + answer. The answer is correct if it matches every character of the expected answer. This can be a problem with @@ -30,9 +30,9 @@ data: |

    - A string response problem accepts a line of text input from the + A text input problem accepts a line of text from the student, and evaluates the input for correctness based on an expected - answer within each input box. + answer. The answer is correct if it matches every character of the expected answer. This can be a problem with international spelling, dates, or anything where the format of the answer is not clear.

    diff --git a/common/static/sass/_mixins.scss b/common/static/sass/_mixins.scss index 58a92d1ee6..76d52ed930 100644 --- a/common/static/sass/_mixins.scss +++ b/common/static/sass/_mixins.scss @@ -1,19 +1,39 @@ +// font-sizing @function em($pxval, $base: 16) { @return #{$pxval / $base}em; } -// Line-height +@mixin font-size($sizeValue: 1.6){ + font-size: $sizeValue + px; + font-size: ($sizeValue/10) + rem; +} + +// line-height @function lh($amount: 1) { @return $body-line-height * $amount; } -@mixin hide-text(){ - text-indent: -9999px; +// image-replacement hidden text +@mixin text-hide() { + text-indent: 100%; + white-space: nowrap; overflow: hidden; - display: block; } -@mixin vertically-and-horizontally-centered ( $height, $width ) { +// hidden elems - screenreaders +@mixin text-sr() { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +// vertical and horizontal centering +@mixin vertically-and-horizontally-centered ($height, $width) { left: 50%; margin-left: -$width / 2; //margin-top: -$height / 2; @@ -22,3 +42,26 @@ position: absolute; top: 150px; } + +// sizing +@mixin size($width: $baseline, $height: $baseline) { + height: $height; + width: $width; +} + +@mixin square($size: $baseline) { + @include size($size); +} + +// placeholder styling +@mixin placeholder($color) { + :-moz-placeholder { + color: $color; + } + ::-webkit-input-placeholder { + color: $color; + } + :-ms-input-placeholder { + color: $color; + } +} \ No newline at end of file diff --git a/lms/.coveragerc b/lms/.coveragerc index 35aa7a3851..72b7b037ef 100644 --- a/lms/.coveragerc +++ b/lms/.coveragerc @@ -2,7 +2,7 @@ [run] data_file = reports/lms/.coverage source = lms,common/djangoapps -omit = lms/envs/* +omit = lms/envs/*, common/djangoapps/terrain/*, common/djangoapps/*/migrations/* [report] ignore_errors = True diff --git a/lms/djangoapps/portal/features/common.py b/lms/djangoapps/courseware/features/common.py similarity index 97% rename from lms/djangoapps/portal/features/common.py rename to lms/djangoapps/courseware/features/common.py index 8bfb548367..2e19696ad4 100644 --- a/lms/djangoapps/portal/features/common.py +++ b/lms/djangoapps/courseware/features/common.py @@ -1,5 +1,4 @@ -from lettuce import world, step # , before, after -from factories import * +from lettuce import world, step from django.core.management import call_command from nose.tools import assert_equals, assert_in from lettuce.django import django_url diff --git a/lms/djangoapps/courseware/features/courses.py b/lms/djangoapps/courseware/features/courses.py index 9b1316b00d..ba0bcd359b 100644 --- a/lms/djangoapps/courseware/features/courses.py +++ b/lms/djangoapps/courseware/features/courses.py @@ -9,7 +9,6 @@ logger = getLogger(__name__) ## support functions - def get_courses(): ''' Returns dict of lists of courses available, keyed by course.org (ie university). @@ -20,29 +19,6 @@ def get_courses(): courses = sorted(courses, key=lambda course: course.number) return courses -# def get_courseware(course_id): -# """ -# Given a course_id (string), return a courseware array of dictionaries for the -# top two levels of navigation. Example: - -# [ -# {'chapter_name': 'Overview', -# 'sections': ['Welcome', 'System Usage Sequence', 'Lab0: Using the tools', 'Circuit Sandbox'] -# }, -# {'chapter_name': 'Week 1', -# 'sections': ['Administrivia and Circuit Elements', 'Basic Circuit Analysis', 'Resistor Divider', 'Week 1 Tutorials'] -# }, -# {'chapter_name': 'Midterm Exam', -# 'sections': ['Midterm Exam'] -# } -# ] -# """ - -# course = get_course_by_id(course_id) -# chapters = course.get_children() -# courseware = [ {'chapter_name':c.display_name, 'sections':[s.display_name for s in c.get_children()]} for c in chapters] -# return courseware - def get_courseware_with_tabs(course_id): """ @@ -106,8 +82,8 @@ def get_courseware_with_tabs(course_id): course = get_course_by_id(course_id) chapters = [chapter for chapter in course.get_children() if chapter.metadata.get('hide_from_toc', 'false').lower() != 'true'] courseware = [{'chapter_name': c.display_name, - 'sections': [{'section_name': s.display_name, - 'clickable_tab_count': len(s.get_children()) if (type(s) == seq_module.SequenceDescriptor) else 0, + 'sections': [{'section_name': s.display_name, + 'clickable_tab_count': len(s.get_children()) if (type(s) == seq_module.SequenceDescriptor) else 0, 'tabs': [{'children_count': len(t.get_children()) if (type(t) == vertical_module.VerticalDescriptor) else 0, 'class': t.__class__.__name__} for t in s.get_children()]} diff --git a/lms/djangoapps/courseware/features/courseware.feature b/lms/djangoapps/courseware/features/courseware.feature index 21c7e84541..279e5732c9 100644 --- a/lms/djangoapps/courseware/features/courseware.feature +++ b/lms/djangoapps/courseware/features/courseware.feature @@ -9,10 +9,3 @@ Feature: View the Courseware Tab And I click on View Courseware When I click on the "Courseware" tab Then the "Courseware" tab is active - - # TODO: fix this one? Not sure whether you should get a 404. - # Scenario: I cannot get to the courseware tab when not logged in - # Given I am not logged in - # And I visit the homepage - # When I visit the courseware URL - # Then the login dialog is visible diff --git a/lms/djangoapps/courseware/features/courseware_common.py b/lms/djangoapps/courseware/features/courseware_common.py index 5ee21da906..96304e016f 100644 --- a/lms/djangoapps/courseware/features/courseware_common.py +++ b/lms/djangoapps/courseware/features/courseware_common.py @@ -4,13 +4,13 @@ from lettuce.django import django_url @step('I click on View Courseware') def i_click_on_view_courseware(step): - css = 'p.enter-course' + css = 'a.enter-course' world.browser.find_by_css(css).first.click() @step('I click on the "([^"]*)" tab$') def i_click_on_the_tab(step, tab): - world.browser.find_link_by_text(tab).first.click() + world.browser.find_link_by_partial_text(tab).first.click() world.save_the_html() diff --git a/lms/djangoapps/portal/features/homepage.feature b/lms/djangoapps/courseware/features/homepage.feature similarity index 100% rename from lms/djangoapps/portal/features/homepage.feature rename to lms/djangoapps/courseware/features/homepage.feature diff --git a/lms/djangoapps/portal/features/homepage.py b/lms/djangoapps/courseware/features/homepage.py similarity index 100% rename from lms/djangoapps/portal/features/homepage.py rename to lms/djangoapps/courseware/features/homepage.py diff --git a/lms/djangoapps/portal/features/login.feature b/lms/djangoapps/courseware/features/login.feature similarity index 100% rename from lms/djangoapps/portal/features/login.feature rename to lms/djangoapps/courseware/features/login.feature diff --git a/lms/djangoapps/portal/features/login.py b/lms/djangoapps/courseware/features/login.py similarity index 99% rename from lms/djangoapps/portal/features/login.py rename to lms/djangoapps/courseware/features/login.py index 094db078ca..ca7d710c61 100644 --- a/lms/djangoapps/portal/features/login.py +++ b/lms/djangoapps/courseware/features/login.py @@ -34,7 +34,6 @@ def click_the_dropdown(step): #### helper functions - def user_is_an_unactivated_user(uname): u = User.objects.get(username=uname) u.is_active = False diff --git a/lms/djangoapps/courseware/features/openended.feature b/lms/djangoapps/courseware/features/openended.feature index 3c7043ba54..cc9f6e1c5f 100644 --- a/lms/djangoapps/courseware/features/openended.feature +++ b/lms/djangoapps/courseware/features/openended.feature @@ -3,31 +3,35 @@ Feature: Open ended grading In order to complete the courseware questions I want the machine learning grading to be functional - Scenario: An answer that is too short is rejected - Given I navigate to an openended question - And I enter the answer "z" - When I press the "Check" button - And I wait for "8" seconds - And I see the grader status "Submitted for grading" - And I press the "Recheck for Feedback" button - Then I see the red X - And I see the grader score "0" + # Commenting these all out right now until we can + # make a reference implementation for a course with + # an open ended grading problem that is always available + # + # Scenario: An answer that is too short is rejected + # Given I navigate to an openended question + # And I enter the answer "z" + # When I press the "Check" button + # And I wait for "8" seconds + # And I see the grader status "Submitted for grading" + # And I press the "Recheck for Feedback" button + # Then I see the red X + # And I see the grader score "0" - Scenario: An answer with too many spelling errors is rejected - Given I navigate to an openended question - And I enter the answer "az" - When I press the "Check" button - And I wait for "8" seconds - And I see the grader status "Submitted for grading" - And I press the "Recheck for Feedback" button - Then I see the red X - And I see the grader score "0" - When I click the link for full output - Then I see the spelling grading message "More spelling errors than average." + # Scenario: An answer with too many spelling errors is rejected + # Given I navigate to an openended question + # And I enter the answer "az" + # When I press the "Check" button + # And I wait for "8" seconds + # And I see the grader status "Submitted for grading" + # And I press the "Recheck for Feedback" button + # Then I see the red X + # And I see the grader score "0" + # When I click the link for full output + # Then I see the spelling grading message "More spelling errors than average." - Scenario: An answer makes its way to the instructor dashboard - Given I navigate to an openended question as staff - When I submit the answer "I love Chemistry." - And I wait for "8" seconds - And I visit the staff grading page - Then my answer is queued for instructor grading \ No newline at end of file + # Scenario: An answer makes its way to the instructor dashboard + # Given I navigate to an openended question as staff + # When I submit the answer "I love Chemistry." + # And I wait for "8" seconds + # And I visit the staff grading page + # Then my answer is queued for instructor grading diff --git a/lms/djangoapps/portal/features/registration.feature b/lms/djangoapps/courseware/features/registration.feature similarity index 94% rename from lms/djangoapps/portal/features/registration.feature rename to lms/djangoapps/courseware/features/registration.feature index d8a6796ee3..d9b588534b 100644 --- a/lms/djangoapps/portal/features/registration.feature +++ b/lms/djangoapps/courseware/features/registration.feature @@ -14,4 +14,4 @@ Feature: Register for a course And I visit the dashboard When I click the link with the text "Unregister" And I press the "Unregister" button in the Unenroll dialog - Then I should see "Looks like you haven't registered for any courses yet." somewhere in the page \ No newline at end of file + Then I should see "Looks like you haven't registered for any courses yet." somewhere in the page diff --git a/lms/djangoapps/portal/features/registration.py b/lms/djangoapps/courseware/features/registration.py similarity index 88% rename from lms/djangoapps/portal/features/registration.py rename to lms/djangoapps/courseware/features/registration.py index b2b4c4bd8d..f585136412 100644 --- a/lms/djangoapps/portal/features/registration.py +++ b/lms/djangoapps/courseware/features/registration.py @@ -4,7 +4,7 @@ from lettuce import world, step @step('I register for the course numbered "([^"]*)"$') def i_register_for_the_course(step, course): courses_section = world.browser.find_by_css('section.courses') - course_link_css = 'article[id*="%s"] a' % course + course_link_css = 'article[id*="%s"] > div' % course course_link = courses_section.find_by_css(course_link_css).first course_link.click() @@ -25,3 +25,4 @@ def i_should_see_that_course_in_my_dashboard(step, course): def i_press_the_button_in_the_unenroll_dialog(step, value): button_css = 'section#unenroll-modal input[value="%s"]' % value world.browser.find_by_css(button_css).click() + assert world.browser.is_element_present_by_css('section.container.dashboard') diff --git a/lms/djangoapps/portal/features/signup.feature b/lms/djangoapps/courseware/features/signup.feature similarity index 100% rename from lms/djangoapps/portal/features/signup.feature rename to lms/djangoapps/courseware/features/signup.feature diff --git a/lms/djangoapps/portal/features/signup.py b/lms/djangoapps/courseware/features/signup.py similarity index 100% rename from lms/djangoapps/portal/features/signup.py rename to lms/djangoapps/courseware/features/signup.py diff --git a/lms/djangoapps/courseware/features/smart-accordion.feature b/lms/djangoapps/courseware/features/smart-accordion.feature index 90d097144a..ccf1d45601 100644 --- a/lms/djangoapps/courseware/features/smart-accordion.feature +++ b/lms/djangoapps/courseware/features/smart-accordion.feature @@ -23,37 +23,41 @@ Feature: There are courses on the homepage As an acceptance test I want to count all the chapters, sections, and tabs for each course - Scenario: Navigate through course MITx/3.091x/2012_Fall - Given I am registered for course "MITx/3.091x/2012_Fall" - And I log in - Then I verify all the content of each course + # Commenting these all out for now because they don't always run, + # they have too many prerequesites, e.g. the course exists, and + # is within the start and end dates, etc. - Scenario: Navigate through course MITx/6.002x/2012_Fall - Given I am registered for course "MITx/6.002x/2012_Fall" - And I log in - Then I verify all the content of each course + # Scenario: Navigate through course MITx/3.091x/2012_Fall + # Given I am registered for course "MITx/3.091x/2012_Fall" + # And I log in + # Then I verify all the content of each course - Scenario: Navigate through course MITx/6.00x/2012_Fall - Given I am registered for course "MITx/6.00x/2012_Fall" - And I log in - Then I verify all the content of each course + # Scenario: Navigate through course MITx/6.002x/2012_Fall + # Given I am registered for course "MITx/6.002x/2012_Fall" + # And I log in + # Then I verify all the content of each course - Scenario: Navigate through course HarvardX/PH207x/2012_Fall - Given I am registered for course "HarvardX/PH207x/2012_Fall" - And I log in - Then I verify all the content of each course + # Scenario: Navigate through course MITx/6.00x/2012_Fall + # Given I am registered for course "MITx/6.00x/2012_Fall" + # And I log in + # Then I verify all the content of each course - Scenario: Navigate through course BerkeleyX/CS169.1x/2012_Fall - Given I am registered for course "BerkeleyX/CS169.1x/2012_Fall" - And I log in - Then I verify all the content of each course + # Scenario: Navigate through course HarvardX/PH207x/2012_Fall + # Given I am registered for course "HarvardX/PH207x/2012_Fall" + # And I log in + # Then I verify all the content of each course - Scenario: Navigate through course BerkeleyX/CS169.2x/2012_Fall - Given I am registered for course "BerkeleyX/CS169.2x/2012_Fall" - And I log in - Then I verify all the content of each course + # Scenario: Navigate through course BerkeleyX/CS169.1x/2012_Fall + # Given I am registered for course "BerkeleyX/CS169.1x/2012_Fall" + # And I log in + # Then I verify all the content of each course - Scenario: Navigate through course BerkeleyX/CS184.1x/2012_Fall - Given I am registered for course "BerkeleyX/CS184.1x/2012_Fall" - And I log in - Then I verify all the content of each course \ No newline at end of file + # Scenario: Navigate through course BerkeleyX/CS169.2x/2012_Fall + # Given I am registered for course "BerkeleyX/CS169.2x/2012_Fall" + # And I log in + # Then I verify all the content of each course + + # Scenario: Navigate through course BerkeleyX/CS184.1x/2012_Fall + # Given I am registered for course "BerkeleyX/CS184.1x/2012_Fall" + # And I log in + # Then I verify all the content of each course \ No newline at end of file diff --git a/lms/djangoapps/instructor/views.py b/lms/djangoapps/instructor/views.py index 2bb4f00724..75146b833f 100644 --- a/lms/djangoapps/instructor/views.py +++ b/lms/djangoapps/instructor/views.py @@ -413,7 +413,7 @@ def instructor_dashboard(request, course_id): smdat = StudentModule.objects.filter(course_id=course_id, module_state_key=module_state_key) smdat = smdat.order_by('student') - msg+="Found module to reset. " + msg += "Found %d records to dump " % len(smdat) except Exception as err: msg+="Couldn't find module with that urlname. " msg += "
    %s
    " % escape(err) diff --git a/lms/djangoapps/open_ended_grading/staff_grading_service.py b/lms/djangoapps/open_ended_grading/staff_grading_service.py index 5bde7a2bc8..0ead7aa364 100644 --- a/lms/djangoapps/open_ended_grading/staff_grading_service.py +++ b/lms/djangoapps/open_ended_grading/staff_grading_service.py @@ -50,7 +50,7 @@ class MockStaffGradingService(object): ]}) - def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores): + def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores, submission_flagged): return self.get_next(course_id, 'fake location', grader_id) @@ -113,7 +113,7 @@ class StaffGradingService(GradingService): return json.dumps(self._render_rubric(response)) - def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores): + def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores, submission_flagged): """ Save a score and feedback for a submission. @@ -132,7 +132,8 @@ class StaffGradingService(GradingService): 'grader_id': grader_id, 'skipped': skipped, 'rubric_scores': rubric_scores, - 'rubric_scores_complete': True} + 'rubric_scores_complete': True, + 'submission_flagged': submission_flagged} return self.post(self.save_grade_url, data=data) @@ -291,7 +292,7 @@ def save_grade(request, course_id): if request.method != 'POST': raise Http404 - required = set(['score', 'feedback', 'submission_id', 'location', 'rubric_scores[]']) + required = set(['score', 'feedback', 'submission_id', 'location','submission_flagged', 'rubric_scores[]']) actual = set(request.POST.keys()) missing = required - actual if len(missing) > 0: @@ -312,7 +313,8 @@ def save_grade(request, course_id): p['score'], p['feedback'], skipped, - p.getlist('rubric_scores[]')) + p.getlist('rubric_scores[]'), + p['submission_flagged']) except GradingServiceError: log.exception("Error saving grade") return _err_response('Could not connect to grading service') diff --git a/lms/djangoapps/open_ended_grading/tests.py b/lms/djangoapps/open_ended_grading/tests.py index de5a7a2100..63048c78d4 100644 --- a/lms/djangoapps/open_ended_grading/tests.py +++ b/lms/djangoapps/open_ended_grading/tests.py @@ -103,6 +103,7 @@ class TestStaffGradingService(ct.PageLoader): 'feedback': 'great!', 'submission_id': '123', 'location': self.location, + 'submission_flagged': "true", 'rubric_scores[]': ['1', '2']} r = self.check_for_post_code(200, url, data) diff --git a/lms/djangoapps/portal/README.md b/lms/djangoapps/portal/README.md deleted file mode 100644 index 09930ec8fb..0000000000 --- a/lms/djangoapps/portal/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## acceptance_testing - -This fake django app is here to support acceptance testing using lettuce + -splinter (which wraps selenium). - -First you need to make sure that you've installed the requirements. -This includes lettuce, selenium, splinter, etc. -Do this with: -```pip install -r test-requirements.txt``` - -The settings.py environment file used is named acceptance.py. -It uses a test SQLite database defined as ../db/test-mitx.db. -You need to first start up the server separately, then run the lettuce scenarios. - -Full documentation can be found on the wiki at this link. diff --git a/lms/djangoapps/portal/__init__.py b/lms/djangoapps/portal/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lms/djangoapps/portal/features/factories.py b/lms/djangoapps/portal/features/factories.py deleted file mode 100644 index 71781ea3d6..0000000000 --- a/lms/djangoapps/portal/features/factories.py +++ /dev/null @@ -1,37 +0,0 @@ -import factory -from student.models import User, UserProfile, Registration -from datetime import datetime -import uuid - - -class UserProfileFactory(factory.Factory): - FACTORY_FOR = UserProfile - - user = None - name = 'Jack Foo' - level_of_education = None - gender = 'm' - mailing_address = None - goals = 'World domination' - - -class RegistrationFactory(factory.Factory): - FACTORY_FOR = Registration - - user = None - activation_key = uuid.uuid4().hex - - -class UserFactory(factory.Factory): - FACTORY_FOR = User - - username = 'robot' - email = 'robot+test@edx.org' - password = 'test' - first_name = 'Robot' - last_name = 'Test' - is_staff = False - is_active = True - is_superuser = False - last_login = datetime(2012, 1, 1) - date_joined = datetime(2011, 1, 1) diff --git a/lms/envs/acceptance.py b/lms/envs/acceptance.py index 412815a402..b6941f4a70 100644 --- a/lms/envs/acceptance.py +++ b/lms/envs/acceptance.py @@ -38,4 +38,4 @@ MITX_FEATURES['STUB_VIDEO_FOR_TESTING'] = True # Include the lettuce app for acceptance testing, including the 'harvest' django-admin command INSTALLED_APPS += ('lettuce.django',) -LETTUCE_APPS = ('portal',) # dummy app covers the home page, login, registration, and course enrollment +LETTUCE_APPS = ('courseware',) diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index 983ec42eb4..f7768de01e 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -171,6 +171,7 @@ class @StaffGrading @feedback_area = $('.feedback-area') @score_selection_container = $('.score-selection-container') @grade_selection_container = $('.grade-selection-container') + @flag_submission_checkbox = $('.flag-checkbox') @submit_button = $('.submit-button') @action_button = $('.action-button') @@ -261,6 +262,7 @@ class @StaffGrading submission_id: @submission_id location: @location skipped: true + submission_flagged: false @backend.post('save_grade', data, @ajax_callback) get_problem_list: () -> @@ -274,6 +276,7 @@ class @StaffGrading feedback: @feedback_area.val() submission_id: @submission_id location: @location + submission_flagged: @flag_submission_checkbox.is(':checked') @backend.post('save_grade', data, @ajax_callback) @@ -331,6 +334,7 @@ class @StaffGrading @error_container.html(@error_msg) @message_container.toggle(@message != "") @error_container.toggle(@error_msg != "") + @flag_submission_checkbox.prop('checked', false) # only show the grading elements when we are not in list view or the state diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index 6b2e6a8136..bd9dde7117 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -75,7 +75,9 @@

    Written Feedback

    -
    +

    + Flag as inappropriate content for later review +

    diff --git a/lms/templates/open_ended_combined_rubric.html b/lms/templates/open_ended_combined_rubric.html index 7f988a38dc..61393cdc95 100644 --- a/lms/templates/open_ended_combined_rubric.html +++ b/lms/templates/open_ended_combined_rubric.html @@ -1,7 +1,7 @@
    % for i in range(len(categories)): <% category = categories[i] %> - ${category['description']}
    + ${category['description']}
      % for j in range(len(category['options'])): <% option = category['options'][j] %> diff --git a/lms/templates/open_ended_rubric.html b/lms/templates/open_ended_rubric.html index da2be513d9..2cbab3ab3b 100644 --- a/lms/templates/open_ended_rubric.html +++ b/lms/templates/open_ended_rubric.html @@ -4,7 +4,7 @@
      % for i in range(len(categories)): <% category = categories[i] %> - ${category['description']}
      + ${category['description']}
        % for j in range(len(category['options'])): <% option = category['options'][j] %>