diff --git a/AUTHORS b/AUTHORS index 9ba3b6cdd5..47bcffea3e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -222,3 +222,6 @@ Xiaolu Xiong Tim Krones Linda Liu Alessandro Verdura +Sven Marnach +Richard Moch + diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f9d007c62e..5d6383562c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -47,6 +47,8 @@ LMS: Support adding students to a cohort via the instructor dashboard. TNL-163 LMS: Show cohorts on the new instructor dashboard. TNL-161 +LMS: Extended hints feature + LMS: Mobile API available for courses that opt in using the Course Advanced Setting "Mobile Course Available" (only used in limited closed beta). diff --git a/cms/djangoapps/contentstore/features/component_settings_editor_helpers.py b/cms/djangoapps/contentstore/features/component_settings_editor_helpers.py index 743f54f829..f295a3517d 100644 --- a/cms/djangoapps/contentstore/features/component_settings_editor_helpers.py +++ b/cms/djangoapps/contentstore/features/component_settings_editor_helpers.py @@ -59,12 +59,12 @@ def click_new_component_button(step, component_button_css): def _click_advanced(): - css = 'ul.problem-type-tabs a[href="#tab2"]' + css = 'ul.problem-type-tabs a[href="#tab3"]' world.css_click(css) # Wait for the advanced tab items to be displayed - tab2_css = 'div.ui-tabs-panel#tab2' - world.wait_for_visible(tab2_css) + tab3_css = 'div.ui-tabs-panel#tab3' + world.wait_for_visible(tab3_css) def _find_matching_link(category, component_type): diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index 549cfc985a..1b1aaee4c9 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -1109,6 +1109,17 @@ class ContentStoreTest(ContentStoreTestCase): self.assertFalse(instructor_role.has_user(self.user)) self.assertEqual(len(instructor_role.users_with_role()), 0) + def test_create_course_after_delete(self): + """ + Test that course creation works after deleting a course with the same URL + """ + test_course_data = self.assert_created_course() + course_id = _get_course_id(self.store, test_course_data) + + delete_course_and_groups(course_id, self.user.id) + + self.assert_created_course() + def test_create_course_duplicate_course(self): """Test new course creation - error path""" self.client.ajax_post('/course/', self.course_data) diff --git a/cms/djangoapps/contentstore/tests/utils.py b/cms/djangoapps/contentstore/tests/utils.py index 73f3541441..4c9cd57841 100644 --- a/cms/djangoapps/contentstore/tests/utils.py +++ b/cms/djangoapps/contentstore/tests/utils.py @@ -11,15 +11,16 @@ from django.contrib.auth.models import User from django.test.client import Client from opaque_keys.edx.locations import SlashSeparatedCourseKey, AssetLocation -from contentstore.utils import reverse_url -from student.models import Registration +from contentstore.utils import reverse_url # pylint: disable=import-error +from student.models import Registration # pylint: disable=import-error from xmodule.modulestore.split_mongo.split import SplitMongoModuleStore from xmodule.contentstore.django import contentstore from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.inheritance import own_metadata from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase -from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory +from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.xml_importer import import_course_from_xml +from xmodule.modulestore.tests.utils import ProceduralCourseTestMixin TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT @@ -67,7 +68,7 @@ class AjaxEnabledTestClient(Client): return self.get(path, data or {}, follow, HTTP_ACCEPT="application/json", **extra) -class CourseTestCase(ModuleStoreTestCase): +class CourseTestCase(ProceduralCourseTestMixin, ModuleStoreTestCase): """ Base class for Studio tests that require a logged in user and a course. Also provides helper methods for manipulating and verifying the course. @@ -100,26 +101,6 @@ class CourseTestCase(ModuleStoreTestCase): nonstaff.is_authenticated = lambda: authenticate return client, nonstaff - def populate_course(self, branching=2): - """ - Add k chapters, k^2 sections, k^3 verticals, k^4 problems to self.course (where k = branching) - """ - user_id = self.user.id - self.populated_usage_keys = {} - - def descend(parent, stack): - if not stack: - return - - xblock_type = stack[0] - for _ in range(branching): - child = ItemFactory.create(category=xblock_type, parent_location=parent.location, user_id=user_id) - print child.location - self.populated_usage_keys.setdefault(xblock_type, []).append(child.location) - descend(child, stack[1:]) - - descend(self.course, ['chapter', 'sequential', 'vertical', 'problem']) - def reload_course(self): """ Reloads the course object from the database diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index a3fadaad2a..8169f5499e 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -58,9 +58,9 @@ ADVANCED_COMPONENT_POLICY_KEY = 'advanced_modules' ADVANCED_PROBLEM_TYPES = settings.ADVANCED_PROBLEM_TYPES -CONTAINER_TEMPATES = [ +CONTAINER_TEMPLATES = [ "basic-modal", "modal-button", "edit-xblock-modal", - "editor-mode-button", "upload-dialog", "image-modal", + "editor-mode-button", "upload-dialog", "add-xblock-component", "add-xblock-component-button", "add-xblock-component-menu", "add-xblock-component-menu-problem", "xblock-string-field-editor", "publish-xblock", "publish-history", "unit-outline", "container-message", "license-selector", @@ -217,7 +217,7 @@ def container_handler(request, usage_key_string): 'xblock_info': xblock_info, 'draft_preview_link': preview_lms_link, 'published_preview_link': lms_link, - 'templates': CONTAINER_TEMPATES + 'templates': CONTAINER_TEMPLATES }) else: return HttpResponseBadRequest("Only supports HTML requests") @@ -227,7 +227,7 @@ def get_component_templates(courselike, library=False): """ Returns the applicable component templates that can be used by the specified course or library. """ - def create_template_dict(name, cat, boilerplate_name=None, is_common=False): + def create_template_dict(name, cat, boilerplate_name=None, tab="common"): """ Creates a component template dict. @@ -235,14 +235,14 @@ def get_component_templates(courselike, library=False): display_name: the user-visible name of the component category: the type of component (problem, html, etc.) boilerplate_name: name of boilerplate for filling in default values. May be None. - is_common: True if "common" problem, False if "advanced". May be None, as it is only used for problems. + tab: common(default)/advanced/hint, which tab it goes in """ return { "display_name": name, "category": cat, "boilerplate_name": boilerplate_name, - "is_common": is_common + "tab": tab } component_display_names = { @@ -268,8 +268,8 @@ def get_component_templates(courselike, library=False): # add the default template with localized display name # TODO: Once mixins are defined per-application, rather than per-runtime, # this should use a cms mixed-in class. (cpennington) - display_name = xblock_type_display_name(category, _('Blank')) - templates_for_category.append(create_template_dict(display_name, category)) + display_name = xblock_type_display_name(category, _('Blank')) # this is the Blank Advanced problem + templates_for_category.append(create_template_dict(display_name, category, None, 'advanced')) categories.add(category) # add boilerplates @@ -277,12 +277,20 @@ def get_component_templates(courselike, library=False): for template in component_class.templates(): filter_templates = getattr(component_class, 'filter_templates', None) if not filter_templates or filter_templates(template, courselike): + # Tab can be 'common' 'advanced' 'hint' + # Default setting is common/advanced depending on the presence of markdown + tab = 'common' + if template['metadata'].get('markdown') is None: + tab = 'advanced' + # Then the problem can override that with a tab: setting + tab = template['metadata'].get('tab', tab) + templates_for_category.append( create_template_dict( _(template['metadata'].get('display_name')), # pylint: disable=translation-of-non-string category, template.get('template_id'), - template['metadata'].get('markdown') is not None + tab ) ) @@ -297,7 +305,7 @@ def get_component_templates(courselike, library=False): log.warning('Unable to load xblock type %s to read display_name', component, exc_info=True) else: templates_for_category.append( - create_template_dict(component_display_name, component, boilerplate_name) + create_template_dict(component_display_name, component, boilerplate_name, 'advanced') ) categories.add(component) diff --git a/cms/djangoapps/contentstore/views/library.py b/cms/djangoapps/contentstore/views/library.py index 7bb3cc1e1a..12987bfe0b 100644 --- a/cms/djangoapps/contentstore/views/library.py +++ b/cms/djangoapps/contentstore/views/library.py @@ -26,7 +26,7 @@ from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from .user import user_with_role -from .component import get_component_templates, CONTAINER_TEMPATES +from .component import get_component_templates, CONTAINER_TEMPLATES from student.auth import ( STUDIO_VIEW_USERS, STUDIO_EDIT_ROLES, get_user_permissions, has_studio_read_access, has_studio_write_access ) @@ -197,7 +197,7 @@ def library_blocks_view(library, user, response_format): 'context_library': library, 'component_templates': json.dumps(component_templates), 'xblock_info': xblock_info, - 'templates': CONTAINER_TEMPATES, + 'templates': CONTAINER_TEMPLATES, }) diff --git a/cms/djangoapps/contentstore/views/tests/test_assets.py b/cms/djangoapps/contentstore/views/tests/test_assets.py index 9bd55c9d50..3fc5646ac7 100644 --- a/cms/djangoapps/contentstore/views/tests/test_assets.py +++ b/cms/djangoapps/contentstore/views/tests/test_assets.py @@ -8,7 +8,6 @@ from PIL import Image import json from django.conf import settings -from django.test.utils import override_settings from contentstore.tests.utils import CourseTestCase from contentstore.views import assets @@ -56,18 +55,19 @@ class AssetsTestCase(CourseTestCase): """ Returns an in-memory file of the specified type with the given name for testing """ + sample_asset = BytesIO() + sample_file_contents = "This file is generated by python unit test" if asset_type == 'text': - sample_asset = BytesIO(name) sample_asset.name = '{name}.txt'.format(name=name) + sample_asset.write(sample_file_contents) elif asset_type == 'image': image = Image.new("RGB", size=(50, 50), color=(256, 0, 0)) - sample_asset = BytesIO() - image.save(unicode(sample_asset), 'jpeg') + image.save(sample_asset, 'jpeg') sample_asset.name = '{name}.jpg'.format(name=name) - sample_asset.seek(0) elif asset_type == 'opendoc': - sample_asset = BytesIO(name) sample_asset.name = '{name}.odt'.format(name=name) + sample_asset.write(sample_file_contents) + sample_asset.seek(0) return sample_asset @@ -324,7 +324,7 @@ class DownloadTestCase(AssetsTestCase): # Now, download it. resp = self.client.get(self.uploaded_url, HTTP_ACCEPT='text/html') self.assertEquals(resp.status_code, 200) - self.assertEquals(resp.content, self.asset_name) + self.assertContains(resp, 'This file is generated by python unit test') def test_download_not_found_throw(self): url = self.uploaded_url.replace(self.asset_name, 'not_the_asset_name') diff --git a/cms/djangoapps/contentstore/views/tests/test_videos.py b/cms/djangoapps/contentstore/views/tests/test_videos.py index 24b504305b..e8bed3c2b2 100644 --- a/cms/djangoapps/contentstore/views/tests/test_videos.py +++ b/cms/djangoapps/contentstore/views/tests/test_videos.py @@ -16,11 +16,9 @@ from mock import Mock, patch from edxval.api import create_profile, create_video, get_video_info from contentstore.models import VideoUploadConfig -from contentstore.views.videos import KEY_EXPIRATION_IN_SECONDS, VIDEO_ASSET_TYPE, StatusDisplayStrings +from contentstore.views.videos import KEY_EXPIRATION_IN_SECONDS, StatusDisplayStrings from contentstore.tests.utils import CourseTestCase from contentstore.utils import reverse_course_url -from xmodule.assetstore import AssetMetadata -from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.factories import CourseFactory @@ -47,6 +45,7 @@ class VideoUploadTestMixin(object): "client_video_id": "test1.mp4", "duration": 42.0, "status": "upload", + "courses": [unicode(self.course.id)], "encoded_videos": [], }, { @@ -54,6 +53,7 @@ class VideoUploadTestMixin(object): "client_video_id": "test2.mp4", "duration": 128.0, "status": "file_complete", + "courses": [unicode(self.course.id)], "encoded_videos": [ { "profile": "profile1", @@ -74,6 +74,7 @@ class VideoUploadTestMixin(object): "client_video_id": u"nón-ascii-näme.mp4", "duration": 256.0, "status": "transcode_active", + "courses": [unicode(self.course.id)], "encoded_videos": [ { "profile": "profile1", @@ -91,6 +92,7 @@ class VideoUploadTestMixin(object): "client_video_id": "status_test.mp4", "duration": 3.14, "status": status, + "courses": [unicode(self.course.id)], "encoded_videos": [], } for status in ( @@ -102,12 +104,6 @@ class VideoUploadTestMixin(object): create_profile(profile) for video in self.previous_uploads: create_video(video) - modulestore().save_asset_metadata( - AssetMetadata( - self.course.id.make_asset_key(VIDEO_ASSET_TYPE, video["edx_video_id"]) - ), - self.user.id - ) def _get_previous_upload(self, edx_video_id): """Returns the previous upload with the given video id.""" @@ -289,13 +285,6 @@ class VideosHandlerTestCase(VideoUploadTestMixin, CourseTestCase): headers={"Content-Type": file_info["content_type"]} ) - # Ensure asset store was updated and the created_by field was set - asset_metadata = modulestore().find_asset_metadata( - self.course.id.make_asset_key(VIDEO_ASSET_TYPE, video_id) - ) - self.assertIsNotNone(asset_metadata) - self.assertEquals(asset_metadata.created_by, self.user.id) - # Ensure VAL was updated val_info = get_video_info(video_id) self.assertEqual(val_info["status"], "upload") diff --git a/cms/djangoapps/contentstore/views/videos.py b/cms/djangoapps/contentstore/views/videos.py index 0c981c06f3..0c047f8ba1 100644 --- a/cms/djangoapps/contentstore/views/videos.py +++ b/cms/djangoapps/contentstore/views/videos.py @@ -12,15 +12,13 @@ from django.utils.translation import ugettext as _, ugettext_noop from django.views.decorators.http import require_GET, require_http_methods import rfc6266 -from edxval.api import create_video, get_videos_for_ids, SortDirection, VideoSortField +from edxval.api import create_video, get_videos_for_course, SortDirection, VideoSortField from opaque_keys.edx.keys import CourseKey from contentstore.models import VideoUploadConfig from contentstore.utils import reverse_course_url from edxmako.shortcuts import render_to_response from util.json_request import expect_json, JsonResponse -from xmodule.assetstore import AssetMetadata -from xmodule.modulestore.django import modulestore from .course import get_course_and_check_access @@ -28,9 +26,6 @@ from .course import get_course_and_check_access __all__ = ["videos_handler", "video_encodings_download"] -# String constant used in asset keys to identify video assets. -VIDEO_ASSET_TYPE = "video" - # Default expiration, in seconds, of one-time URLs used for uploading videos. KEY_EXPIRATION_IN_SECONDS = 86400 @@ -217,15 +212,9 @@ def _get_and_validate_course(course_key_string, user): def _get_videos(course): """ - Retrieves the list of videos from VAL corresponding to the videos listed in - the asset metadata store. + Retrieves the list of videos from VAL corresponding to this course. """ - edx_videos_ids = [ - v.asset_id.path - for v in modulestore().get_all_asset_metadata(course.id, VIDEO_ASSET_TYPE) - ] - - videos = list(get_videos_for_ids(edx_videos_ids, VideoSortField.created, SortDirection.desc)) + videos = list(get_videos_for_course(course.id, VideoSortField.created, SortDirection.desc)) # convert VAL's status to studio's Video Upload feature status. for video in videos: @@ -333,11 +322,6 @@ def videos_post(course, request): headers={"Content-Type": req_file["content_type"]} ) - # persist edx_video_id as uploaded through this course - user_id = request.user.id - video_meta_data = AssetMetadata(course.id.make_asset_key(VIDEO_ASSET_TYPE, edx_video_id), created_by=user_id) - modulestore().save_asset_metadata(video_meta_data, user_id) - # persist edx_video_id in VAL create_video({ "edx_video_id": edx_video_id, diff --git a/cms/envs/aws.py b/cms/envs/aws.py index bae10699c1..b14c726e40 100644 --- a/cms/envs/aws.py +++ b/cms/envs/aws.py @@ -332,10 +332,6 @@ VIDEO_UPLOAD_PIPELINE = ENV_TOKENS.get('VIDEO_UPLOAD_PIPELINE', VIDEO_UPLOAD_PIP PARSE_KEYS = AUTH_TOKENS.get("PARSE_KEYS", {}) -#date format the api will be formatting the datetime values -API_DATE_FORMAT = '%Y-%m-%d' -API_DATE_FORMAT = ENV_TOKENS.get('API_DATE_FORMAT', API_DATE_FORMAT) - # Video Caching. Pairing country codes with CDN URLs. # Example: {'CN': 'http://api.xuetangx.com/edx/video?s3_url='} VIDEO_CDN_URL = ENV_TOKENS.get('VIDEO_CDN_URL', {}) diff --git a/cms/envs/common.py b/cms/envs/common.py index bb47f87384..eb8ec6e78c 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -175,6 +175,9 @@ FEATURES = { # Enable credit eligibility feature 'ENABLE_CREDIT_ELIGIBILITY': False, + + # Can the visibility of the discussion tab be configured on a per-course basis? + 'ALLOW_HIDING_DISCUSSION_TAB': False, } ENABLE_JASMINE = False @@ -207,6 +210,7 @@ MAKO_TEMPLATES['main'] = [ COMMON_ROOT / 'templates', COMMON_ROOT / 'djangoapps' / 'pipeline_mako' / 'templates', COMMON_ROOT / 'djangoapps' / 'pipeline_js' / 'templates', + COMMON_ROOT / 'static', # required to statically include common Underscore templates ] for namespace, template_dirs in lms.envs.common.MAKO_TEMPLATES.iteritems(): @@ -946,8 +950,6 @@ ADVANCED_PROBLEM_TYPES = [ } ] -#date format the api will be formatting the datetime values -API_DATE_FORMAT = '%Y-%m-%d' # Files and Uploads type filter values diff --git a/cms/envs/devstack.py b/cms/envs/devstack.py index 0f0028d9a3..0260b5311f 100644 --- a/cms/envs/devstack.py +++ b/cms/envs/devstack.py @@ -30,6 +30,11 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' LMS_BASE = "localhost:8000" FEATURES['PREVIEW_LMS_BASE'] = "preview." + LMS_BASE +########################### PIPELINE ################################# + +# Skip RequireJS optimizer in development +STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage' + ############################# ADVANCED COMPONENTS ############################# # Make it easier to test advanced components in local dev @@ -92,6 +97,11 @@ FEATURES['ENABLE_COURSEWARE_INDEX'] = True FEATURES['ENABLE_LIBRARY_INDEX'] = True SEARCH_ENGINE = "search.elastic.ElasticSearchEngine" +################################# DJANGO-REQUIRE ############################### + +# Whether to run django-require in debug mode. +REQUIRE_DEBUG = DEBUG + ############################################################################### # See if the developer has any local overrides. try: diff --git a/cms/static/build.js b/cms/static/build.js index 6103b8afe7..b01b4a5720 100644 --- a/cms/static/build.js +++ b/cms/static/build.js @@ -39,6 +39,7 @@ 'js/certificates/factories/certificates_page_factory', 'js/factories/import', 'js/factories/index', + 'js/factories/library', 'js/factories/login', 'js/factories/manage_users', 'js/factories/outline', @@ -118,7 +119,7 @@ * As of 1.0.3, this value can also be a string that is converted to a * RegExp via new RegExp(). */ - fileExclusionRegExp: /^\.|spec/, + fileExclusionRegExp: /^\.|spec|spec_helpers/, /** * Allow CSS optimizations. Allowed values: * - "standard": @import inlining and removal of comments, unnecessary @@ -153,6 +154,6 @@ * SILENT: 4 * Default is 0. */ - logLevel: 4 + logLevel: 1 }; } ()) diff --git a/cms/static/coffee/spec/main.coffee b/cms/static/coffee/spec/main.coffee index 6052f95412..7051007949 100644 --- a/cms/static/coffee/spec/main.coffee +++ b/cms/static/coffee/spec/main.coffee @@ -23,6 +23,7 @@ requirejs.config({ "jquery.simulate": "xmodule_js/common_static/js/vendor/jquery.simulate", "datepair": "xmodule_js/common_static/js/vendor/timepicker/datepair", "date": "xmodule_js/common_static/js/vendor/date", + "text": "xmodule_js/common_static/js/vendor/requirejs/text", "underscore": "xmodule_js/common_static/js/vendor/underscore-min", "underscore.string": "xmodule_js/common_static/js/vendor/underscore.string.min", "backbone": "xmodule_js/common_static/js/vendor/backbone-min", @@ -240,13 +241,11 @@ define([ "js/spec/views/active_video_upload_list_spec", "js/spec/views/previous_video_upload_spec", "js/spec/views/previous_video_upload_list_spec", - "js/spec/views/paging_spec", "js/spec/views/assets_spec", "js/spec/views/baseview_spec", "js/spec/views/container_spec", "js/spec/views/paged_container_spec", "js/spec/views/group_configuration_spec", - "js/spec/views/paging_spec", "js/spec/views/unit_outline_spec", "js/spec/views/xblock_spec", "js/spec/views/xblock_editor_spec", @@ -279,6 +278,7 @@ define([ "js/certificates/spec/views/certificate_details_spec", "js/certificates/spec/views/certificate_editor_spec", "js/certificates/spec/views/certificates_list_spec", + "js/certificates/spec/views/certificate_preview_spec", # these tests are run separately in the cms-squire suite, due to process # isolation issues with Squire.js diff --git a/cms/static/coffee/spec/main_spec.coffee b/cms/static/coffee/spec/main_spec.coffee index db442b15ed..2de4869dbe 100644 --- a/cms/static/coffee/spec/main_spec.coffee +++ b/cms/static/coffee/spec/main_spec.coffee @@ -1,4 +1,4 @@ -require ["jquery", "backbone", "coffee/src/main", "js/common_helpers/ajax_helpers", "jasmine-stealth", "jquery.cookie"], +require ["jquery", "backbone", "coffee/src/main", "common/js/spec_helpers/ajax_helpers", "jasmine-stealth", "jquery.cookie"], ($, Backbone, main, AjaxHelpers) -> describe "CMS", -> it "should initialize URL", -> diff --git a/cms/static/coffee/spec/main_squire.coffee b/cms/static/coffee/spec/main_squire.coffee index 7698e8b34b..1feee91414 100644 --- a/cms/static/coffee/spec/main_squire.coffee +++ b/cms/static/coffee/spec/main_squire.coffee @@ -21,6 +21,7 @@ requirejs.config({ "jquery.immediateDescendents": "xmodule_js/common_static/coffee/src/jquery.immediateDescendents", "datepair": "xmodule_js/common_static/js/vendor/timepicker/datepair", "date": "xmodule_js/common_static/js/vendor/date", + "text": "xmodule_js/common_static/js/vendor/requirejs/text", "underscore": "xmodule_js/common_static/js/vendor/underscore-min", "underscore.string": "xmodule_js/common_static/js/vendor/underscore.string.min", "backbone": "xmodule_js/common_static/js/vendor/backbone-min", diff --git a/cms/static/coffee/spec/models/section_spec.coffee b/cms/static/coffee/spec/models/section_spec.coffee index 491cb340ec..95d26e43d4 100644 --- a/cms/static/coffee/spec/models/section_spec.coffee +++ b/cms/static/coffee/spec/models/section_spec.coffee @@ -1,4 +1,4 @@ -define ["js/models/section", "js/common_helpers/ajax_helpers", "js/utils/module"], (Section, AjaxHelpers, ModuleUtils) -> +define ["js/models/section", "common/js/spec_helpers/ajax_helpers", "js/utils/module"], (Section, AjaxHelpers, ModuleUtils) -> describe "Section", -> describe "basic", -> beforeEach -> diff --git a/cms/static/coffee/spec/views/assets_spec.coffee b/cms/static/coffee/spec/views/assets_spec.coffee index 038ad63b73..72bccbe396 100644 --- a/cms/static/coffee/spec/views/assets_spec.coffee +++ b/cms/static/coffee/spec/views/assets_spec.coffee @@ -1,11 +1,9 @@ -define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"], +define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"], ($, jasmine, AjaxHelpers, Squire) -> feedbackTpl = readFixtures('system-feedback.underscore') assetLibraryTpl = readFixtures('asset-library.underscore') assetTpl = readFixtures('asset.underscore') - pagingHeaderTpl = readFixtures('paging-header.underscore') - pagingFooterTpl = readFixtures('paging-footer.underscore') describe "Asset view", -> beforeEach -> @@ -141,8 +139,6 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"], beforeEach -> setFixtures($(" diff --git a/cms/templates/container.html b/cms/templates/container.html index b93ada88c9..4f2cc15da9 100644 --- a/cms/templates/container.html +++ b/cms/templates/container.html @@ -24,6 +24,9 @@ from django.utils.translation import ugettext as _ <%static:include path="js/${template_name}.underscore" /> % endfor + diff --git a/cms/templates/js/add-xblock-component-menu-problem.underscore b/cms/templates/js/add-xblock-component-menu-problem.underscore index aca3c34e79..d3324d1b62 100644 --- a/cms/templates/js/add-xblock-component-menu-problem.underscore +++ b/cms/templates/js/add-xblock-component-menu-problem.underscore @@ -4,13 +4,13 @@ <%= gettext("Common Problem Types") %>
  • - <%= gettext("Advanced") %> + <%= gettext("Advanced") %>
  • % endif -
    + % for item, course in shoppingcart_items: % if loop.index > 0 :
    @@ -294,8 +294,13 @@ from microsite_configuration import microsite alt="${course.display_number_with_default | h} ${get_course_about_section(course, 'title')} Image"/>
    -

    ${_("Registration for")}: - + +

    + ${_('Registration for:')} + ${ course.display_name } +

    +

    + <% course_start_time = course.start_datetime_text() course_end_time = course.end_datetime_text() @@ -304,23 +309,21 @@ from microsite_configuration import microsite ${_("Course Dates")}: %endif -

    - -

    ${course.display_name}

    - - % if course_start_time: - ${course_start_time} - %endif - - - % if course_end_time: - ${course_end_time} - %endif - -
    + + % if course_start_time: + ${course_start_time} + %endif + - + % if course_end_time: + ${course_end_time} + %endif + +

    +
    % if item.status == "purchased":
    - % if item.list_price != None: + % if item.is_discounted:
    ${_('Price per student:')} ${currency_symbol}${"{0:0.2f}".format(item.list_price)}
    ${_('Discount Applied:')} ${currency_symbol}${"{0:0.2f}".format(item.unit_cost)}
    @@ -338,7 +341,7 @@ from microsite_configuration import microsite
    % elif item.status == "refunded":
    - % if item.list_price != None: + % if item.is_discounted:
    ${_('Price per student:')} ${currency_symbol}${"{0:0.2f}".format(item.list_price)}
    ${_('Discount Applied:')} ${currency_symbol}${"{0:0.2f}".format(item.unit_cost)} diff --git a/lms/templates/shoppingcart/shopping_cart.html b/lms/templates/shoppingcart/shopping_cart.html index 8df06c14ed..14ceab73c4 100644 --- a/lms/templates/shoppingcart/shopping_cart.html +++ b/lms/templates/shoppingcart/shopping_cart.html @@ -71,14 +71,18 @@ from django.utils.translation import ungettext
    ## Translators: "Registration for:" is followed by a course name -

    ${_('Registration for:')} - ${_('Course Dates:')} +

    + ${_('Registration for:')} + ${ course.display_name }

    -

    ${ course.display_name }

    ${course.start_datetime_text()} - ${course.end_datetime_text()} +

    + ${_('Course Dates:')} + ${ course.start_datetime_text() } - ${ course.end_datetime_text() } +


    - % if item.list_price != None: + % if item.is_discounted: <% discount_applied = True %>
    ${_('Price per student:')} @@ -98,19 +102,29 @@ from django.utils.translation import ungettext % endif
    -
    - +
    +
    - +
    - + + - +
    - +
    @@ -122,10 +136,11 @@ from django.utils.translation import ungettext
    % if not discount_applied: -
    +
    + - +
    % else:
    @@ -147,31 +162,37 @@ from django.utils.translation import ungettext
    % if order_type == 'business':
    - -

    - ${_('After this purchase is complete, a receipt is generated with relative billing details and registration codes for students.')} -

    + +

    + ${_('After this purchase is complete, a receipt is generated with relative billing details and registration codes for students.')} +

    diff --git a/lms/templates/student_account/finish_auth.html b/lms/templates/student_account/finish_auth.html new file mode 100644 index 0000000000..99bddc2dff --- /dev/null +++ b/lms/templates/student_account/finish_auth.html @@ -0,0 +1,51 @@ +<%! from django.utils.translation import ugettext as _ %> +<%namespace name='static' file='/static_content.html'/> +<%inherit file="/main.html" /> + +<%block name="pagetitle">${_("Please Wait")} + +<%block name="js_extra"> + + <%static:js group='utility'/> + + +<%block name="headextra"> + + + + + +
    +
    +

    ${_('Please wait')}

    + +
    +
    +
    + +## This overwrites the "footer" block declared in main.html +## with an empty block, effectively hiding the footer. +<%block name="footer"/> diff --git a/lms/templates/student_account/login.underscore b/lms/templates/student_account/login.underscore index 2a7294adfa..5420e769c4 100644 --- a/lms/templates/student_account/login.underscore +++ b/lms/templates/student_account/login.underscore @@ -1,7 +1,7 @@ @@ -20,6 +20,13 @@
      +<% if (context.errorMessage) { %> +
      +

      <%- _.sprintf( gettext("An error occurred when signing you in to %(platformName)s."), context ) %>

      +
        <%- context.errorMessage %>
      +
      +<% } %> +
      diff --git a/lms/templates/student_account/login_and_register.html b/lms/templates/student_account/login_and_register.html index c81c1f5196..5bddd0b0c7 100644 --- a/lms/templates/student_account/login_and_register.html +++ b/lms/templates/student_account/login_and_register.html @@ -27,6 +27,7 @@ class="login-register" data-initial-mode="${initial_mode}" data-third-party-auth='${third_party_auth|h}' + data-next-url='${login_redirect_url|h}' data-platform-name='${platform_name}' data-login-form-desc='${login_form_desc|h}' data-registration-form-desc='${registration_form_desc|h}' diff --git a/lms/templates/student_account/register.underscore b/lms/templates/student_account/register.underscore index a5de5b5f29..f3e2c5c2a9 100644 --- a/lms/templates/student_account/register.underscore +++ b/lms/templates/student_account/register.underscore @@ -4,6 +4,14 @@
      + + <% if (context.errorMessage) { %> +
      +

      <%- gettext("An error occurred.") %>

      +
        <%- context.errorMessage %>
      +
      + <% } %> + <% if (context.currentProvider) { %>

      diff --git a/lms/templates/studio_render_paged_children_view.html b/lms/templates/studio_render_paged_children_view.html index 7d6dff0d9a..46921168fc 100644 --- a/lms/templates/studio_render_paged_children_view.html +++ b/lms/templates/studio_render_paged_children_view.html @@ -2,12 +2,6 @@ <%namespace name='static' file='static_content.html'/> -% for template_name in ["paging-header", "paging-footer"]: - -% endfor -

      {% block pagetitle %}{% endblock %} | {% trans "Wiki" %} | {% platform_name %}{% endblock %} +{% block bodyclass %}view-incourse view-wiki{% endblock %} + {% block headextra %} {% compressed_css 'course' %} - +