diff --git a/AUTHORS b/AUTHORS index 944d143cba..80c2acc58e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -239,3 +239,4 @@ Mirjam Škarica Saleem Latif Julien Paillé Michael Frey +Hasnain Naveed diff --git a/cms/djangoapps/contentstore/features/help.feature b/cms/djangoapps/contentstore/features/help.feature deleted file mode 100644 index 567a2f2526..0000000000 --- a/cms/djangoapps/contentstore/features/help.feature +++ /dev/null @@ -1,53 +0,0 @@ -@shard_1 -Feature: CMS.Help - As a course author, I am able to access online help - - Scenario: Users can access online help on course listing page - Given There are no courses - And I am logged into Studio - Then I should see online help for "get_started" - - - Scenario: Users can access online help within a course - Given I have opened a new course in Studio - - And I click the course link in Studio Home - Then I should see online help for "outline" - - And I go to the course updates page - Then I should see online help for "updates" - - And I go to the pages page - Then I should see online help for "pages" - - And I go to the files and uploads page - Then I should see online help for "files" - - And I go to the textbooks page - Then I should see online help for "textbooks" - - And I select Schedule and Details - Then I should see online help for "setting_up" - - And I am viewing the grading settings - Then I should see online help for "grading" - - And I am viewing the course team settings - Then I should see online help for "course-team" - - And I select the Advanced Settings - Then I should see online help for "index" - - And I select Checklists from the Tools menu - Then I should see online help for "checklist" - - And I go to the import page - Then I should see online help for "import" - - And I go to the export page - Then I should see online help for "export" - - - Scenario: Users can access online help on the unit page - Given I am in Studio editing a new unit - Then I should see online help for "units" diff --git a/cms/djangoapps/contentstore/features/help.py b/cms/djangoapps/contentstore/features/help.py deleted file mode 100644 index f169e72057..0000000000 --- a/cms/djangoapps/contentstore/features/help.py +++ /dev/null @@ -1,24 +0,0 @@ -# pylint: disable=missing-docstring -# pylint: disable=redefined-outer-name -# pylint: disable=unused-argument - -from nose.tools import assert_false # pylint: disable=no-name-in-module -from lettuce import step, world - - -@step(u'I should see online help for "([^"]*)"$') -def see_online_help_for(step, page_name): - # make sure the online Help link exists on this page and contains the expected page name - elements_found = world.browser.find_by_xpath( - '//li[contains(@class, "nav-account-help")]//a[contains(@href, "{page_name}")]'.format( - page_name=page_name - ) - ) - assert_false(elements_found.is_empty()) - - # make sure the PDF link on the sock of this page exists - # for now, the PDF link stays constant for all the pages so we just check for "pdf" - elements_found = world.browser.find_by_xpath( - '//section[contains(@class, "sock")]//li[contains(@class, "js-help-pdf")]//a[contains(@href, "pdf")]' - ) - assert_false(elements_found.is_empty()) diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index a9dd9ee8bd..57a9718cf5 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -16,6 +16,9 @@ from models.settings.course_details import (CourseDetails, CourseSettingsEncoder from models.settings.course_grading import CourseGradingModel from contentstore.utils import reverse_course_url, reverse_usage_url from xmodule.modulestore.tests.factories import CourseFactory +from student.roles import CourseInstructorRole +from student.tests.factories import UserFactory + from models.settings.course_metadata import CourseMetadata from xmodule.fields import Date @@ -1106,3 +1109,116 @@ class CourseGraderUpdatesTest(CourseTestCase): self.assertEqual(obj, grader) current_graders = CourseGradingModel.fetch(self.course.id).graders self.assertEqual(len(self.starting_graders) + 1, len(current_graders)) + + +class CourseEnrollmentEndFieldTest(CourseTestCase): + """ + Base class to test the enrollment end fields in the course settings details view in Studio + when using marketing site flag and global vs non-global staff to access the page. + """ + NOT_EDITABLE_HELPER_MESSAGE = "Contact your edX Partner Manager to update these settings." + NOT_EDITABLE_DATE_WRAPPER = "
" + NOT_EDITABLE_TIME_WRAPPER = "
" + NOT_EDITABLE_DATE_FIELD = "" + NOT_EDITABLE_TIME_FIELD = "" + + EDITABLE_DATE_WRAPPER = "
" + EDITABLE_TIME_WRAPPER = "
" + EDITABLE_DATE_FIELD = "" + EDITABLE_TIME_FIELD = "" + + EDITABLE_ELEMENTS = [ + EDITABLE_DATE_WRAPPER, + EDITABLE_TIME_WRAPPER, + EDITABLE_DATE_FIELD, + EDITABLE_TIME_FIELD, + ] + + NOT_EDITABLE_ELEMENTS = [ + NOT_EDITABLE_HELPER_MESSAGE, + NOT_EDITABLE_DATE_WRAPPER, + NOT_EDITABLE_TIME_WRAPPER, + NOT_EDITABLE_DATE_FIELD, + NOT_EDITABLE_TIME_FIELD, + ] + + def setUp(self): + """ Initialize course used to test enrollment fields. """ + super(CourseEnrollmentEndFieldTest, self).setUp() + self.course = CourseFactory.create(org='edX', number='dummy', display_name='Marketing Site Course') + self.course_details_url = reverse_course_url('settings_handler', unicode(self.course.id)) + + def _get_course_details_response(self, global_staff): + """ Return the course details page as either global or non-global staff""" + user = UserFactory(is_staff=global_staff) + CourseInstructorRole(self.course.id).add_users(user) + + self.client.login(username=user.username, password='test') + + return self.client.get_html(self.course_details_url) + + def _verify_editable(self, response): + """ Verify that the response has expected editable fields. + + Assert that all editable field content exists and no + uneditable field content exists for enrollment end fields. + """ + self.assertEqual(response.status_code, 200) + for element in self.NOT_EDITABLE_ELEMENTS: + self.assertNotContains(response, element) + + for element in self.EDITABLE_ELEMENTS: + self.assertContains(response, element) + + def _verify_not_editable(self, response): + """ Verify that the response has expected non-editable fields. + + Assert that all uneditable field content exists and no + editable field content exists for enrollment end fields. + """ + self.assertEqual(response.status_code, 200) + for element in self.NOT_EDITABLE_ELEMENTS: + self.assertContains(response, element) + + for element in self.EDITABLE_ELEMENTS: + self.assertNotContains(response, element) + + @mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_MKTG_SITE': False}) + def test_course_details_with_disabled_setting_global_staff(self): + """ Test that user enrollment end date is editable in response. + + Feature flag 'ENABLE_MKTG_SITE' is not enabled. + User is global staff. + """ + self._verify_editable(self._get_course_details_response(True)) + + @mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_MKTG_SITE': False}) + def test_course_details_with_disabled_setting_non_global_staff(self): + """ Test that user enrollment end date is editable in response. + + Feature flag 'ENABLE_MKTG_SITE' is not enabled. + User is non-global staff. + """ + self._verify_editable(self._get_course_details_response(False)) + + @mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_MKTG_SITE': True}) + def test_course_details_with_enabled_setting_global_staff(self): + """ Test that user enrollment end date is editable in response. + + Feature flag 'ENABLE_MKTG_SITE' is enabled. + User is global staff. + """ + self._verify_editable(self._get_course_details_response(True)) + + @mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_MKTG_SITE': True}) + def test_course_details_with_enabled_setting_non_global_staff(self): + """ Test that user enrollment end date is not editable in response. + + Feature flag 'ENABLE_MKTG_SITE' is enabled. + User is non-global staff. + """ + self._verify_not_editable(self._get_course_details_response(False)) diff --git a/cms/djangoapps/contentstore/tests/test_crud.py b/cms/djangoapps/contentstore/tests/test_crud.py index 92d6c88c77..512667fa02 100644 --- a/cms/djangoapps/contentstore/tests/test_crud.py +++ b/cms/djangoapps/contentstore/tests/test_crud.py @@ -1,42 +1,21 @@ import unittest -from opaque_keys.edx.locator import LocalId - from xmodule import templates from xmodule.modulestore import ModuleStoreEnum -from xmodule.modulestore.tests import persistent_factories +from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, TEST_DATA_SPLIT_MODULESTORE from xmodule.course_module import CourseDescriptor -from xmodule.modulestore.django import modulestore, clear_existing_modulestores from xmodule.seq_module import SequenceDescriptor from xmodule.capa_module import CapaDescriptor -from xmodule.contentstore.django import _CONTENTSTORE -from xmodule.modulestore.exceptions import ItemNotFoundError, DuplicateCourseError from xmodule.html_module import HtmlDescriptor +from xmodule.modulestore.exceptions import DuplicateCourseError -class TemplateTests(unittest.TestCase): +class TemplateTests(ModuleStoreTestCase): """ Test finding and using the templates (boilerplates) for xblocks. """ - - def setUp(self): - super(TemplateTests, self).setUp() - clear_existing_modulestores() # redundant w/ cleanup but someone was getting errors - self.addCleanup(self._drop_mongo_collections) - self.addCleanup(clear_existing_modulestores) - self.split_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.split) - - @staticmethod - def _drop_mongo_collections(): - """ - If using a Mongo-backed modulestore & contentstore, drop the collections. - """ - module_store = modulestore() - if hasattr(module_store, '_drop_database'): - module_store._drop_database() # pylint: disable=protected-access - _CONTENTSTORE.clear() - if hasattr(module_store, 'close_connections'): - module_store.close_connections() + MODULESTORE = TEST_DATA_SPLIT_MODULESTORE def test_get_templates(self): found = templates.all_templates() @@ -69,42 +48,49 @@ class TemplateTests(unittest.TestCase): self.assertIsNotNone(HtmlDescriptor.get_template('announcement.yaml')) def test_factories(self): - test_course = persistent_factories.PersistentCourseFactory.create( - course='course', run='2014', org='testx', - display_name='fun test course', user_id='testbot' + test_course = CourseFactory.create( + org='testx', + course='course', + run='2014', + display_name='fun test course', + user_id='testbot' ) self.assertIsInstance(test_course, CourseDescriptor) self.assertEqual(test_course.display_name, 'fun test course') - index_info = self.split_store.get_course_index_info(test_course.id) - self.assertEqual(index_info['org'], 'testx') - self.assertEqual(index_info['course'], 'course') - self.assertEqual(index_info['run'], '2014') + course_from_store = self.store.get_course(test_course.id) + self.assertEqual(course_from_store.id.org, 'testx') + self.assertEqual(course_from_store.id.course, 'course') + self.assertEqual(course_from_store.id.run, '2014') - test_chapter = persistent_factories.ItemFactory.create( - display_name='chapter 1', - parent_location=test_course.location + test_chapter = ItemFactory.create( + parent_location=test_course.location, + category='chapter', + display_name='chapter 1' ) self.assertIsInstance(test_chapter, SequenceDescriptor) # refetch parent which should now point to child - test_course = self.split_store.get_course(test_course.id.version_agnostic()) + test_course = self.store.get_course(test_course.id.version_agnostic()) self.assertIn(test_chapter.location, test_course.children) with self.assertRaises(DuplicateCourseError): - persistent_factories.PersistentCourseFactory.create( - course='course', run='2014', org='testx', - display_name='fun test course', user_id='testbot' + CourseFactory.create( + org='testx', + course='course', + run='2014', + display_name='fun test course', + user_id='testbot' ) def test_temporary_xblocks(self): """ Test create_xblock to create non persisted xblocks """ - test_course = persistent_factories.PersistentCourseFactory.create( + test_course = CourseFactory.create( course='course', run='2014', org='testx', display_name='fun test course', user_id='testbot' ) - test_chapter = self.split_store.create_xblock( + test_chapter = self.store.create_xblock( test_course.system, test_course.id, 'chapter', fields={'display_name': 'chapter n'}, parent_xblock=test_course ) @@ -114,7 +100,7 @@ class TemplateTests(unittest.TestCase): # test w/ a definition (e.g., a problem) test_def_content = 'boo' - test_problem = self.split_store.create_xblock( + test_problem = self.store.create_xblock( test_course.system, test_course.id, 'problem', fields={'data': test_def_content}, parent_xblock=test_chapter ) @@ -124,131 +110,28 @@ class TemplateTests(unittest.TestCase): test_problem.display_name = 'test problem' self.assertEqual(test_problem.display_name, 'test problem') - def test_persist_dag(self): - """ - try saving temporary xblocks - """ - test_course = persistent_factories.PersistentCourseFactory.create( - course='course', run='2014', org='testx', - display_name='fun test course', user_id='testbot' - ) - test_chapter = self.split_store.create_xblock( - test_course.system, test_course.id, 'chapter', fields={'display_name': 'chapter n'}, - parent_xblock=test_course - ) - self.assertEqual(test_chapter.display_name, 'chapter n') - test_def_content = 'boo' - # create child - new_block = self.split_store.create_xblock( - test_course.system, test_course.id, - 'problem', - fields={ - 'data': test_def_content, - 'display_name': 'problem' - }, - parent_xblock=test_chapter - ) - self.assertIsNotNone(new_block.definition_locator) - self.assertTrue(isinstance(new_block.definition_locator.definition_id, LocalId)) - # better to pass in persisted parent over the subdag so - # subdag gets the parent pointer (otherwise 2 ops, persist dag, update parent children, - # persist parent - persisted_course = self.split_store.persist_xblock_dag(test_course, 'testbot') - self.assertEqual(len(persisted_course.children), 1) - persisted_chapter = persisted_course.get_children()[0] - self.assertEqual(persisted_chapter.category, 'chapter') - self.assertEqual(persisted_chapter.display_name, 'chapter n') - self.assertEqual(len(persisted_chapter.children), 1) - persisted_problem = persisted_chapter.get_children()[0] - self.assertEqual(persisted_problem.category, 'problem') - self.assertEqual(persisted_problem.data, test_def_content) - # update it - persisted_problem.display_name = 'altered problem' - persisted_problem = self.split_store.persist_xblock_dag(persisted_problem, 'testbot') - self.assertEqual(persisted_problem.display_name, 'altered problem') - def test_delete_course(self): - test_course = persistent_factories.PersistentCourseFactory.create( - course='history', run='doomed', org='edu.harvard', + test_course = CourseFactory.create( + org='edu.harvard', + course='history', + run='doomed', display_name='doomed test course', user_id='testbot') - persistent_factories.ItemFactory.create( - display_name='chapter 1', - parent_location=test_course.location + ItemFactory.create( + parent_location=test_course.location, + category='chapter', + display_name='chapter 1' ) id_locator = test_course.id.for_branch(ModuleStoreEnum.BranchName.draft) - guid_locator = test_course.location.course_agnostic() # verify it can be retrieved by id - self.assertIsInstance(self.split_store.get_course(id_locator), CourseDescriptor) - # and by guid -- TODO reenable when split_draft supports getting specific versions -# self.assertIsInstance(self.split_store.get_item(guid_locator), CourseDescriptor) - self.split_store.delete_course(id_locator, 'testbot') - # test can no longer retrieve by id - self.assertRaises(ItemNotFoundError, self.split_store.get_course, id_locator) - # but can by guid -- same TODO as above -# self.assertIsInstance(self.split_store.get_item(guid_locator), CourseDescriptor) - - def test_block_generations(self): - """ - Test get_block_generations - """ - test_course = persistent_factories.PersistentCourseFactory.create( - course='history', run='hist101', org='edu.harvard', - display_name='history test course', - user_id='testbot' - ) - chapter = persistent_factories.ItemFactory.create( - display_name='chapter 1', - parent_location=test_course.location, - user_id='testbot' - ) - sub = persistent_factories.ItemFactory.create( - display_name='subsection 1', - parent_location=chapter.location, - user_id='testbot', - category='vertical' - ) - first_problem = persistent_factories.ItemFactory.create( - display_name='problem 1', parent_location=sub.location, user_id='testbot', category='problem', - data="" - ) - first_problem.max_attempts = 3 - first_problem.save() # decache the above into the kvs - updated_problem = self.split_store.update_item(first_problem, 'testbot') - self.assertIsNotNone(updated_problem.previous_version) - self.assertEqual(updated_problem.previous_version, first_problem.update_version) - self.assertNotEqual(updated_problem.update_version, first_problem.update_version) - self.split_store.delete_item(updated_problem.location, 'testbot') - - second_problem = persistent_factories.ItemFactory.create( - display_name='problem 2', - parent_location=sub.location.version_agnostic(), - user_id='testbot', category='problem', - data="" - ) - - # The draft course root has 2 revisions: the published revision, and then the subsequent - # changes to the draft revision - version_history = self.split_store.get_block_generations(test_course.location) - self.assertIsNotNone(version_history) - self.assertEqual(version_history.locator.version_guid, test_course.location.version_guid) - self.assertEqual(len(version_history.children), 1) - self.assertEqual(version_history.children[0].children, []) - self.assertEqual(version_history.children[0].locator.version_guid, chapter.location.version_guid) - - # sub changed on add, add problem, delete problem, add problem in strict linear seq - version_history = self.split_store.get_block_generations(sub.location) - self.assertEqual(len(version_history.children), 1) - self.assertEqual(len(version_history.children[0].children), 1) - self.assertEqual(len(version_history.children[0].children[0].children), 1) - self.assertEqual(len(version_history.children[0].children[0].children[0].children), 0) - - # first and second problem may show as same usage_id; so, need to ensure their histories are right - version_history = self.split_store.get_block_generations(updated_problem.location) - self.assertEqual(version_history.locator.version_guid, first_problem.location.version_guid) - self.assertEqual(len(version_history.children), 1) # updated max_attempts - self.assertEqual(len(version_history.children[0].children), 0) - - version_history = self.split_store.get_block_generations(second_problem.location) - self.assertNotEqual(version_history.locator.version_guid, first_problem.location.version_guid) + self.assertIsInstance(self.store.get_course(id_locator), CourseDescriptor) + # TODO reenable when split_draft supports getting specific versions + # guid_locator = test_course.location.course_agnostic() + # Verify it can be retrieved by guid + # self.assertIsInstance(self.store.get_item(guid_locator), CourseDescriptor) + self.store.delete_course(id_locator, 'testbot') + # Test can no longer retrieve by id. + self.assertIsNone(self.store.get_course(id_locator)) + # But can retrieve by guid -- same TODO as above + # self.assertIsInstance(self.store.get_item(guid_locator), CourseDescriptor) diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 5c230c13d7..113a543ee4 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -903,12 +903,15 @@ def settings_handler(request, course_key_string): # see if the ORG of this course can be attributed to a 'Microsite'. In that case, the # course about page should be editable in Studio - about_page_editable = not microsite.get_value_for_org( + marketing_site_enabled = microsite.get_value_for_org( course_module.location.org, 'ENABLE_MKTG_SITE', settings.FEATURES.get('ENABLE_MKTG_SITE', False) ) + about_page_editable = not marketing_site_enabled + enrollment_end_editable = GlobalStaff().has_user(request.user) or not marketing_site_enabled + short_description_editable = settings.FEATURES.get('EDITABLE_SHORT_DESCRIPTION', True) settings_context = { 'context_course': course_module, @@ -924,6 +927,7 @@ def settings_handler(request, course_key_string): 'credit_eligibility_enabled': credit_eligibility_enabled, 'is_credit_course': False, 'show_min_grade_warning': False, + 'enrollment_end_editable': enrollment_end_editable, } if prerequisite_course_enabled: courses, in_process_course_actions = get_courses_accessible_to_user(request) diff --git a/cms/envs/aws.py b/cms/envs/aws.py index 84d8947fbf..cc3d974377 100644 --- a/cms/envs/aws.py +++ b/cms/envs/aws.py @@ -204,8 +204,6 @@ LOGGING = get_logger_config(LOG_DIR, PLATFORM_NAME = ENV_TOKENS.get('PLATFORM_NAME', 'edX') STUDIO_NAME = ENV_TOKENS.get('STUDIO_NAME', 'edX Studio') STUDIO_SHORT_NAME = ENV_TOKENS.get('STUDIO_SHORT_NAME', 'Studio') -TENDER_DOMAIN = ENV_TOKENS.get('TENDER_DOMAIN', TENDER_DOMAIN) -TENDER_SUBDOMAIN = ENV_TOKENS.get('TENDER_SUBDOMAIN', TENDER_SUBDOMAIN) # Event Tracking if "TRACKING_IGNORE_URL_PATTERNS" in ENV_TOKENS: diff --git a/cms/envs/bok_choy.py b/cms/envs/bok_choy.py index ee671b084c..94f90d46ad 100644 --- a/cms/envs/bok_choy.py +++ b/cms/envs/bok_choy.py @@ -98,6 +98,9 @@ FEATURES['LICENSING'] = True FEATURES['ENABLE_MOBILE_REST_API'] = True # Enable video bumper in Studio FEATURES['ENABLE_VIDEO_BUMPER'] = True # Enable video bumper in Studio settings +# Enable partner support link in Studio footer +FEATURES['PARTNER_SUPPORT_EMAIL'] = 'partner-support@example.com' + ########################### Entrance Exams ################################# FEATURES['ENTRANCE_EXAMS'] = True diff --git a/cms/envs/common.py b/cms/envs/common.py index 304ac08b1a..56899b8807 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -619,18 +619,6 @@ REQUIRE_ENVIRONMENT = "node" DEBUG_TOOLBAR_PATCH_SETTINGS = False -################################# TENDER ###################################### - -# If you want to enable Tender integration (http://tenderapp.com/), -# put in the subdomain where Tender hosts tender_widget.js. For example, -# if you want to use the URL https://example.tenderapp.com/tender_widget.js, -# you should use "example". -TENDER_SUBDOMAIN = None -# If you want to have a vanity domain that points to Tender, put that here. -# For example, "help.myapp.com". Otherwise, should should be your full -# tenderapp domain name: for example, "example.tenderapp.com". -TENDER_DOMAIN = None - ################################# CELERY ###################################### # Message configuration @@ -1004,6 +992,9 @@ ADVANCED_COMPONENT_TYPES = [ # In-course reverification checkpoint 'edx-reverification-block', + + # Peer instruction tool + 'ubcpi', ] # Adding components in this list will disable the creation of new problem for diff --git a/cms/envs/test.py b/cms/envs/test.py index 16133ac8ef..1d7e585f22 100644 --- a/cms/envs/test.py +++ b/cms/envs/test.py @@ -94,9 +94,6 @@ STATICFILES_STORAGE = 'pipeline.storage.NonPackagingPipelineStorage' STATIC_URL = "/static/" PIPELINE_ENABLED = False -TENDER_DOMAIN = "help.edge.edx.org" -TENDER_SUBDOMAIN = "edxedge" - # Update module store settings per defaults for tests update_module_store_settings( MODULESTORE, diff --git a/cms/static/cms/js/build.js b/cms/static/cms/js/build.js index dc9abfdc5b..1b290554b5 100644 --- a/cms/static/cms/js/build.js +++ b/cms/static/cms/js/build.js @@ -83,7 +83,6 @@ 'gettext': 'empty:', 'xmodule': 'empty:', 'mathjax': 'empty:', - 'tender': 'empty:', 'youtube': 'empty:' }, diff --git a/cms/static/cms/js/require-config.js b/cms/static/cms/js/require-config.js index c64ac7f9c1..4e5be98dfc 100644 --- a/cms/static/cms/js/require-config.js +++ b/cms/static/cms/js/require-config.js @@ -70,14 +70,6 @@ require.config({ // end of Annotation tool files // externally hosted files - "tender": [ - // if TENDER_SUBDOMAIN is defined, use that; otherwise, use a dummy value - // (the application JS will never `require(['tender'])` if it's not defined) - "//" + (typeof TENDER_SUBDOMAIN === "string" ? TENDER_SUBDOMAIN : "example") + ".tenderapp.com/tender_widget", - // if tender fails to load, fallback on a local file - // so that require doesn't fall over - "js/src/tender_fallback" - ], "mathjax": "//cdn.mathjax.org/mathjax/2.4-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured", "youtube": [ // youtube URL does not end in ".js". We add "?noext" to the path so @@ -172,9 +164,6 @@ require.config({ deps: ["backbone"], exports: "Backbone.Paginator" }, - "tender": { - exports: 'Tender' - }, "youtube": { exports: "YT" }, diff --git a/cms/static/coffee/spec/main.coffee b/cms/static/coffee/spec/main.coffee index 0fcf7f6e2e..5454bea202 100644 --- a/cms/static/coffee/spec/main.coffee +++ b/cms/static/coffee/spec/main.coffee @@ -53,7 +53,6 @@ requirejs.config({ "mathjax": "//cdn.mathjax.org/mathjax/2.4-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured", "youtube": "//www.youtube.com/player_api?noext", - "tender": "//api.tenderapp.com/tender_widget", "coffee/src/ajax_prefix": "xmodule_js/common_static/coffee/src/ajax_prefix", "js/spec/test_utils": "js/spec/test_utils", diff --git a/cms/static/coffee/spec/main_squire.coffee b/cms/static/coffee/spec/main_squire.coffee index 1feee91414..866e8d1a12 100644 --- a/cms/static/coffee/spec/main_squire.coffee +++ b/cms/static/coffee/spec/main_squire.coffee @@ -44,7 +44,6 @@ requirejs.config({ "mathjax": "//cdn.mathjax.org/mathjax/2.4-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured", "youtube": "//www.youtube.com/player_api?noext", - "tender": "//api.tenderapp.com/tender_widget.js" "coffee/src/ajax_prefix": "xmodule_js/common_static/coffee/src/ajax_prefix" } diff --git a/cms/static/js/base.js b/cms/static/js/base.js index 5d937ef83f..4ab1c9a505 100644 --- a/cms/static/js/base.js +++ b/cms/static/js/base.js @@ -61,9 +61,6 @@ domReady(function() { // general link management - smooth scrolling page links $('a[rel*="view"][href^="#"]').bind('click', smoothScrollLink); - // tender feedback window scrolling - $('a.show-tender').bind('click', smoothScrollTop); - IframeUtils.iframeBinding(); // disable ajax caching in IE so that backbone fetches work diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index b8ab6e6dc9..0bfc824143 100644 --- a/cms/static/sass/_base.scss +++ b/cms/static/sass/_base.scss @@ -1,8 +1,8 @@ // studio - base styling // ==================== -// Table of Contents -// * +Basic Setup +// Table of Contents +// * +Basic Setup // * +Typography - Basic // * +Typography - Primary Content // * +Typography - Secondary Content @@ -19,7 +19,7 @@ // * +JS Dependent // +Basic Setup -// ==================== +// ==================== html { font-size: 62.5%; height: 102%; // force scrollbar to prevent jump when scroll appears, cannot use overflow because it breaks drag @@ -66,7 +66,7 @@ h1 { } // +Typography - Basic -// ==================== +// ==================== .page-header { @extend %t-title3; @extend %t-strong; @@ -111,7 +111,7 @@ h1 { } // +Typography - Primary Content -// ==================== +// ==================== .content-primary { .section-header { @@ -148,7 +148,7 @@ h1 { } // +Typography - Secondary Content -// ==================== +// ==================== .content-secondary { .section-header { @@ -177,7 +177,7 @@ h1 { } // +Typography - Loose Headings (BT: needs to be removed once html is clean) -// ==================== +// ==================== .title-1, .title-2, .title-3, .title-4, .title-5, .title-6 { @extend %t-strong; } @@ -226,13 +226,13 @@ p, ul, ol, dl { } // +Layout - Basic -// ==================== +// ==================== .wrapper-view { } // +Layout - Basic Page Header -// ==================== +// ==================== .wrapper-mast { margin: ($baseline*1.5) 0 0 0; padding: 0 $baseline; @@ -375,7 +375,7 @@ p, ul, ol, dl { } // +Layout - Basic Page Content -// ==================== +// ==================== .wrapper-content { margin: 0; padding: 0 $baseline; @@ -419,7 +419,7 @@ p, ul, ol, dl { } // +Layout - Primary Content -// ==================== +// ==================== .content-primary { .title-1 { @@ -457,7 +457,7 @@ p, ul, ol, dl { } // +Layout - Supplemental Content -// ==================== +// ==================== .content-supplementary { > section { @@ -466,7 +466,7 @@ p, ul, ol, dl { } // +Layout - Grandfathered -// ==================== +// ==================== .main-wrapper { position: relative; margin: 0 ($baseline*2); @@ -503,7 +503,7 @@ p, ul, ol, dl { } // +UI - Actions -// ==================== +// ==================== .new-unit-item, .new-subsection-item, .new-policy-item { @@ -535,7 +535,7 @@ p, ul, ol, dl { } // +UI - Misc -// ==================== +// ==================== hr.divide { @extend %cont-text-sr; } @@ -623,7 +623,7 @@ hr.divide { // +Utility - Basic -// ==================== +// ==================== // UI - semantically hide text .sr { diff --git a/cms/static/sass/_build.scss b/cms/static/sass/_build.scss index 303861279e..2253926702 100644 --- a/cms/static/sass/_build.scss +++ b/cms/static/sass/_build.scss @@ -37,7 +37,6 @@ @import 'elements/header'; @import 'elements/footer'; @import 'elements/sock'; -@import 'elements/tender-widget'; @import 'elements/system-feedback'; // alerts, notifications, states @import 'elements/system-help'; // help UI @import 'elements/modal'; // interstitial UI, dialogs, modal windows diff --git a/cms/static/sass/_variables.scss b/cms/static/sass/_variables.scss index b68e0399f7..acdc7e31b3 100644 --- a/cms/static/sass/_variables.scss +++ b/cms/static/sass/_variables.scss @@ -1,7 +1,7 @@ // studio - utilities - variables // ==================== -// Table of Contents +// Table of Contents // * +Grid // * +Fonts // * +Colors - Utility @@ -16,7 +16,7 @@ $baseline: 20px; // +Grid -// ==================== +// ==================== $gw-column: ($baseline*3); $gw-gutter: $baseline; $fg-column: $gw-column; @@ -26,16 +26,16 @@ $fg-max-width: 1280px; $fg-min-width: 900px; // +Fonts -// ==================== +// ==================== $f-sans-serif: 'Open Sans','Helvetica Neue', Helvetica, Arial, sans-serif; $f-monospace: 'Bitstream Vera Sans Mono', Consolas, Courier, monospace; // +Colors - Utility -// ==================== +// ==================== $transparent: rgba(0,0,0,0); // used when color value is needed for UI width/transitions but element is transparent // +Colors - Primary -// ==================== +// ==================== $black: rgb(0,0,0); $black-t0: rgba($black, 0.125); $black-t1: rgba($black, 0.25); @@ -168,7 +168,7 @@ $orange-u2: desaturate($orange,30%); $orange-u3: desaturate($orange,45%); // +Colors - Shadows -// ==================== +// ==================== $shadow: rgba($black, 0.2); $shadow-l1: rgba($black, 0.1); $shadow-l2: rgba($black, 0.05); @@ -176,7 +176,7 @@ $shadow-d1: rgba($black, 0.4); $shadow-d2: rgba($black, 0.6); // +Colors - Application -// ==================== +// ==================== $color-draft: $gray-l3; $color-live: $blue; $color-ready: $green; @@ -190,7 +190,7 @@ $color-copy-base: $gray-l1; $color-copy-emphasized: $gray-d2; // +Timing -// ==================== +// ==================== // used for animation/transition mixin syncing $tmg-s3: 3.0s; $tmg-s2: 2.0s; @@ -201,7 +201,7 @@ $tmg-f2: 0.25s; $tmg-f3: 0.125s; // +Archetype UI -// ==================== +// ==================== $ui-action-primary-color: $blue-u2; $ui-action-primary-color-focus: $blue-s1; @@ -209,12 +209,12 @@ $ui-link-color: $blue-u2; $ui-link-color-focus: $blue-s1; // +Specific UI -// ==================== +// ==================== $ui-notification-height: ($baseline*10); $ui-update-color: $blue-l4; -// +Deprecated -// ==================== +// +Deprecated +// ==================== // do not use, future clean up will use updated styles $baseFontColor: $gray-d2; $lighter-base-font-color: rgb(100,100,100); diff --git a/cms/static/sass/assets/_anims.scss b/cms/static/sass/assets/_anims.scss index 1841430fad..6223ab95f9 100644 --- a/cms/static/sass/assets/_anims.scss +++ b/cms/static/sass/assets/_anims.scss @@ -1,7 +1,7 @@ // studio animations & keyframes // ==================== -// Table of Contents +// Table of Contents // * +Fade In - Extend // * +Fade Out - Extend // * +Rotate Up - Extend @@ -16,7 +16,7 @@ // * +Dropped - Extend // +Fade In - Extend -// ==================== +// ==================== // fade in keyframes @include keyframes(fadeIn) { 0% { @@ -38,9 +38,9 @@ } // +Fade Out - Extend -// ==================== +// ==================== // fade out keyframes -@include keyframes(fadeOut) { +@include keyframes(fadeOut) { 0% { opacity: 1.0; } @@ -60,7 +60,7 @@ } // +Rotate Up - Extend -// ==================== +// ==================== // rotate up keyframes @include keyframes(rotateUp) { 0% { @@ -262,7 +262,7 @@ } } -// flash double animation +// flash double animation %anim-flashDouble { @include animation(flashDouble $tmg-f1 ease-in-out 1); } diff --git a/cms/static/sass/elements/_controls.scss b/cms/static/sass/elements/_controls.scss index 278c1a6478..45df38f5ec 100644 --- a/cms/static/sass/elements/_controls.scss +++ b/cms/static/sass/elements/_controls.scss @@ -1,7 +1,7 @@ // studio - elements - UI controls // ==================== -// Table of Contents +// Table of Contents // * +General Action - Extend // * +General Type and Size - Extend // * +Primary Button - Extends @@ -16,7 +16,7 @@ // +General Action - Extend -// ==================== +// ==================== %action { @extend %ui-fake-link; @@ -28,7 +28,7 @@ } // +General Type and Size - Extend -// ==================== +// ==================== %sizing { @extend %t-action4; padding: ($baseline/4) ($baseline/2) ($baseline/3) ($baseline/2); @@ -36,7 +36,7 @@ // +Primary Button - Extends -// ==================== +// ==================== // gray primary button %btn-primary-gray { @extend %ui-btn-primary; @@ -106,7 +106,7 @@ } // +Secondary Button - Extends -// ==================== +// ==================== // gray secondary button %btn-secondary-gray { @extend %ui-btn-secondary; @@ -193,7 +193,7 @@ } // +Button Element -// ==================== +// ==================== .button { .icon { @@ -385,7 +385,7 @@ cursor: move; } } - + // UI: is draggable .is-draggable { @include transition(border-color $tmg-f2 ease-in-out 0, box-shadow $tmg-f2 ease-in-out 0, margin $tmg-f2 ease-in-out 0); diff --git a/cms/static/sass/elements/_sock.scss b/cms/static/sass/elements/_sock.scss index 92e2fa9bb9..4ed0e06f69 100644 --- a/cms/static/sass/elements/_sock.scss +++ b/cms/static/sass/elements/_sock.scss @@ -86,6 +86,7 @@ .action-item { @include float(left); @include margin-right($baseline/2); + margin-bottom: ($baseline/2); &:last-child { @include margin-right(0); diff --git a/cms/static/sass/elements/_tender-widget.scss b/cms/static/sass/elements/_tender-widget.scss deleted file mode 100644 index 61d968c88e..0000000000 --- a/cms/static/sass/elements/_tender-widget.scss +++ /dev/null @@ -1,273 +0,0 @@ -// tender help/support widget -// ==================== - -// UI: hiding the default tender help "tag" element -#tender_toggler { - display: none; -} - -#tender_frame, #tender_window { - background-image: none !important; - background: none; -} - -#tender_window { - border-radius: 3px; - box-shadow: 0 2px 3px $shadow; - height: ($baseline*35) !important; - background: $white !important; - border: 2px solid $blue; -} - -#tender_window { - padding: 0 !important; -} - -#tender_frame { - background: $white; -} - -#tender_closer { - color: $white-t2 !important; - text-transform: uppercase; - top: 16px !important; - - &:hover { - color: $white !important; - } -} - -// ==================== - -// tender style overrides - not rendered through here, but an archive is needed -#tender_frame iframe html { - font-size: 62.5%; -} - -.widget-layout { - font-family: 'Open Sans', sans-serif; -} - -.widget-layout .search, -.widget-layout .tabs, -.widget-layout .footer, -.widget-layout .header h1 a { - display: none; -} - -.widget-layout .header { - background: rgb(0, 159, 230); - padding: ($baseline/2) $baseline; -} - -.widget-layout h1, .widget-layout h2, .widget-layout h3, .widget-layout h4, .widget-layout h5, .widget-layout h6, .widget-layout label { - @extend %t-strong; -} - -.widget-layout .header h1 { - @extend %t-title4; -} - -.widget-layout .content { - overflow: auto; - height: auto !important; - padding: 20px; -} - -.widget-layout .flash { - margin: -10px 0 15px 0; - padding: 10px 20px !important; - background-image: none !important; -} - -.widget-layout .flash-error { - background: rgb(178, 6, 16) !important; - color: rgb(255,255,255) !important; -} - -.widget-layout label { - @extend %t-copy-sub1; - @extend %t-strong; - margin-bottom: ($baseline/4); - color: #4c4c4c; -} - -.widget-layout input[type="text"], .widget-layout textarea { - @extend %t-copy-base; - padding: 10px; - color: rgb(0,0,0) !important; - border: 1px solid #b0b6c2; - border-radius: 2px; - background-color: #edf1f5; - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #edf1f5),color-stop(100%, #fdfdfe)); - background-image: -webkit-linear-gradient(top, #edf1f5,#fdfdfe); - background-image: -moz-linear-gradient(top, #edf1f5,#fdfdfe); - background-image: -ms-linear-gradient(top, #edf1f5,#fdfdfe); - background-image: -o-linear-gradient(top, #edf1f5,#fdfdfe); - background-image: linear-gradient(top, #edf1f5,#fdfdfe); - background-color: #edf1f5; - -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.1) inset; - -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.1) inset; - box-shadow: 0 1px 2px rgba(0,0,0,0.1) inset; -} - -.widget-layout input[type="text"]:focus, .widget-layout textarea:focus { - background-color: #fffcf1; - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fffcf1),color-stop(100%, #fffefd)); - background-image: -webkit-linear-gradient(top, #fffcf1,#fffefd); - background-image: -moz-linear-gradient(top, #fffcf1,#fffefd); - background-image: -ms-linear-gradient(top, #fffcf1,#fffefd); - background-image: -o-linear-gradient(top, #fffcf1,#fffefd); - background-image: linear-gradient(top, #fffcf1,#fffefd); - outline: 0; -} - -.widget-layout textarea { - width: 97%; -} - -.widget-layout p.note { - text-align: right !important; - display: inline-block !important; - position: absolute !important; - right: -130px !important; - top: -5px !important; - font-size: 13px !important; - opacity: 0.80; -} - -.widget-layout .form-actions { - margin: 15px 0; - border: none; - padding: 0; -} - -.widget-layout dl.form { - float: none; - width: 100%; - border-bottom: 1px solid $gray-l5; - margin-bottom: ($baseline/2); - padding-bottom: ($baseline/2); -} - -.widget-layout dl.form:last-child { - border: none; - padding-bottom: 0; - margin-bottom: $baseline; -} - -.widget-layout dl.form dt, .widget-layout dl.form dd { - display: inline-block; - vertical-align: middle; -} - -.widget-layout dl.form dt { - margin-right: ($baseline*0.75); - width: 70px; -} - -.widget-layout dl.form dd { - width: 65%; - position: relative; -} - -// specific elements -.widget-layout #discussion_body { - -} - -.widget-layout #discussion_body:before { - @extend %t-copy-sub1; - @extend %t-strong; - content: "What Question or Feedback Would You Like to Share?"; - display: block; - margin-bottom: ($baseline/4); - color: #4c4c4c; -} - - -.widget-layout dl#brain_buster_captcha { - float: none; - width: 100%; - border-top: 1px solid $gray-l5; - margin-top: ($baseline/2); - padding-top: ($baseline/2); -} - -.widget-layout dl#brain_buster_captcha dd { - display: block !important; -} - -.widget-layout dl#brain_buster_captcha #captcha_answer { - border-color: #333; -} - -.widget-layout dl#brain_buster_captcha dd label { - @extend %t-strong; - display: block; - margin: 0 15px 5px 0 !important; -} - -.widget-layout dl#brain_buster_captcha dd #captcha_answer { - display: block; - width: 97%; -} - -.widget-layout .form-actions .btn-post_topic { - @extend %t-copy-base; - @extend %t-strong; - display: block; - width: 100%; - height: auto !important; - -webkit-box-shadow: 0 1px 0 rgba(255,255,255,0.3) inset,0 0 0 $transparent; - -moz-box-shadow: 0 1px 0 rgba(255,255,255,0.3) inset,0 0 0 $transparent; - box-shadow: 0 1px 0 rgba(255,255,255,0.3) inset,0 0 0 $transparent; - -webkit-transition-property: background-color,0.15s; - -moz-transition-property: background-color,0.15s; - -ms-transition-property: background-color,0.15s; - -o-transition-property: background-color,0.15s; - transition-property: background-color,0.15s; - -webkit-transition-duration: box-shadow,0.15s; - -moz-transition-duration: box-shadow,0.15s; - -ms-transition-duration: box-shadow,0.15s; - -o-transition-duration: box-shadow,0.15s; - transition-duration: box-shadow,0.15s; - -webkit-transition-timing-function: ease-out; - -moz-transition-timing-function: ease-out; - -ms-transition-timing-function: ease-out; - -o-transition-timing-function: ease-out; - transition-timing-function: ease-out; - -webkit-transition-delay: 0; - -moz-transition-delay: 0; - -ms-transition-delay: 0; - -o-transition-delay: 0; - transition-delay: 0; - border: 1px solid #34854c; - border-radius: 3px; - background-color: rgba(255,255,255,0.3); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(255,255,255,0.3)),color-stop(100%, rgba(255,255,255,0))); - background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.3),rgba(255,255,255,0)); - background-image: -moz-linear-gradient(top, rgba(255,255,255,0.3),rgba(255,255,255,0)); - background-image: -ms-linear-gradient(top, rgba(255,255,255,0.3),rgba(255,255,255,0)); - background-image: -o-linear-gradient(top, rgba(255,255,255,0.3),rgba(255,255,255,0)); - background-image: linear-gradient(top, rgba(255,255,255,0.3),rgba(255,255,255,0)); - background-color: #25b85a; - -webkit-box-shadow: 0 1px 0 rgba(255,255,255,0.3) inset; - -moz-box-shadow: 0 1px 0 rgba(255,255,255,0.3) inset; - box-shadow: 0 1px 0 rgba(255,255,255,0.3) inset; - color: $white; - text-align: center; - margin-top: $baseline; - padding: ($baseline/2) $baseline; -} - -.widget-layout .form-actions #private-discussion-opt { - float: none; - text-align: left; - margin: 0 0 15px 0; -} - -.widget-layout .form-actions .btn-post_topic:hover, .widget-layout .form-actions .btn-post_topic:active { - background-color: #16ca57; - color: $white; -} diff --git a/cms/static/sass/elements/_xblocks.scss b/cms/static/sass/elements/_xblocks.scss index 15f71d4d4c..bcea673d8a 100644 --- a/cms/static/sass/elements/_xblocks.scss +++ b/cms/static/sass/elements/_xblocks.scss @@ -4,7 +4,7 @@ // Table of Contents // * +Layout - Xblocks // * +Licensing - Xblocks -// * +Pagination - Xblocks +// * +Pagination - Xblocks // * +Messaging - Xblocks // * +Case: Page Level // * +Case: Nesting Level @@ -14,7 +14,7 @@ // * +Case - Special Xblock Type Overrides -// +Layout - Xblocks +// +Layout - Xblocks // ==================== // styling for xblocks at various levels of nesting: page level, .wrapper-xblock { @@ -116,7 +116,7 @@ } } - // +Licensing - Xblocks + // +Licensing - Xblocks // ==================== .xblock-license, .xmodule_display.xmodule_HtmlModule .xblock-license, @@ -157,7 +157,7 @@ } - // +Pagination - Xblocks + // +Pagination - Xblocks .container-paging-header { .meta-wrap { margin: $baseline ($baseline/2); @@ -910,7 +910,7 @@ div.wrapper-comp-editor.is-inactive ~ div.launch-latex-compiler { @extend %t-copy-sub2; } } - + .wrapper-license-options { margin-bottom: ($baseline/2); diff --git a/cms/static/sass/vendor/bi-app/_bi-app-ltr.scss b/cms/static/sass/vendor/bi-app/_bi-app-ltr.scss index 6278a31380..3b5dfab593 100755 --- a/cms/static/sass/vendor/bi-app/_bi-app-ltr.scss +++ b/cms/static/sass/vendor/bi-app/_bi-app-ltr.scss @@ -1,11 +1,11 @@ // ------------------------------------------ // left to right module -// authors: +// authors: // twitter.com/anasnakawa // twitter.com/victorzamfir -// licensed under the MIT license +// licensed under the MIT license // http://www.opensource.org/licenses/mit-license.php // ------------------------------------------ @import 'variables-ltr'; -@import 'mixins'; \ No newline at end of file +@import 'mixins'; diff --git a/cms/static/sass/vendor/bi-app/_bi-app-rtl.scss b/cms/static/sass/vendor/bi-app/_bi-app-rtl.scss index 17b7f2e90f..2e4b8271d8 100755 --- a/cms/static/sass/vendor/bi-app/_bi-app-rtl.scss +++ b/cms/static/sass/vendor/bi-app/_bi-app-rtl.scss @@ -1,11 +1,11 @@ // ------------------------------------------ // right to left module -// authors: +// authors: // twitter.com/anasnakawa // twitter.com/victorzamfir -// licensed under the MIT license +// licensed under the MIT license // http://www.opensource.org/licenses/mit-license.php // ------------------------------------------ @import 'variables-rtl'; -@import 'mixins'; \ No newline at end of file +@import 'mixins'; diff --git a/cms/static/sass/vendor/bi-app/_variables-ltr.scss b/cms/static/sass/vendor/bi-app/_variables-ltr.scss index 36d5a7b06e..12273051a3 100755 --- a/cms/static/sass/vendor/bi-app/_variables-ltr.scss +++ b/cms/static/sass/vendor/bi-app/_variables-ltr.scss @@ -1,15 +1,15 @@ // ------------------------------------------ // left to right variables to be used by bi-app mixins -// authors: +// authors: // twitter.com/anasnakawa // twitter.com/victorzamfir -// licensed under the MIT license +// licensed under the MIT license // http://www.opensource.org/licenses/mit-license.php // ------------------------------------------ // namespacing variables with bi-app to // avoid conflicting with other global variables -$bi-app-left : left; -$bi-app-right : right; -$bi-app-direction : ltr; -$bi-app-invert-direction: rtl; \ No newline at end of file +$bi-app-left : left; +$bi-app-right : right; +$bi-app-direction : ltr; +$bi-app-invert-direction: rtl; diff --git a/cms/static/sass/vendor/bi-app/_variables-rtl.scss b/cms/static/sass/vendor/bi-app/_variables-rtl.scss index 7300f17863..6b8da0bdbf 100755 --- a/cms/static/sass/vendor/bi-app/_variables-rtl.scss +++ b/cms/static/sass/vendor/bi-app/_variables-rtl.scss @@ -1,15 +1,15 @@ // ------------------------------------------ // right to left variables to be used by bi-app mixins -// authors: +// authors: // twitter.com/anasnakawa // twitter.com/victorzamfir -// licensed under the MIT license +// licensed under the MIT license // http://www.opensource.org/licenses/mit-license.php // ------------------------------------------ // namespacing variables with bi-app to // avoid conflicting with other global variables -$bi-app-left : right; +$bi-app-left : right; $bi-app-right : left; -$bi-app-direction : rtl; -$bi-app-invert-direction: ltr; \ No newline at end of file +$bi-app-direction : rtl; +$bi-app-invert-direction: ltr; diff --git a/cms/static/sass/views/_certificates.scss b/cms/static/sass/views/_certificates.scss index ee0ad2205c..0b231664d6 100644 --- a/cms/static/sass/views/_certificates.scss +++ b/cms/static/sass/views/_certificates.scss @@ -1,7 +1,7 @@ // studio - views - certificates // ==================== -// Table of Contents +// Table of Contents // * +Layout - Certificates // * +Main - Collection // * +Main - Certificate @@ -510,7 +510,7 @@ // * +Signatories -Certificate // ==================== -// TO-DO: refactor to use collection styling where possible. +// TO-DO: refactor to use collection styling where possible. .view-certificates .certificates { .signatory-details, .signatory-edit { @@ -694,4 +694,4 @@ } } } -} \ No newline at end of file +} diff --git a/cms/static/sass/views/_settings.scss b/cms/static/sass/views/_settings.scss index 467a2b464e..8be7e82dd5 100644 --- a/cms/static/sass/views/_settings.scss +++ b/cms/static/sass/views/_settings.scss @@ -62,6 +62,27 @@ margin-top: ($baseline); } + // specific fields - settings details + .settings-details { + + // course details that should appear more like content than elements to change + .is-not-editable { + + label { + + } + + input, textarea { + @extend %t-copy-lead1; + @extend %t-strong; + box-shadow: none; + border: none; + background: none; + margin: 0; + } + } + } + // in form - elements .group-settings { @@ -225,7 +246,7 @@ } } } - + .input-minimum-grade { @include float(left); @include size(92%,100%); @@ -305,21 +326,10 @@ } } - // course details that should appear more like content than elements to change - .field.is-not-editable { - - label { - - } + .is-not-editable { input, textarea { - @extend %t-copy-lead1; - @extend %t-strong; - box-shadow: none; - border: none; - background: none; padding: 0; - margin: 0; } } @@ -438,6 +448,13 @@ padding-bottom: 0; } + .is-not-editable { + + input, textarea { + padding: 10px; + } + } + .field { @include float(left); width: flex-grid(3, 9); diff --git a/cms/templates/activation_invalid.html b/cms/templates/activation_invalid.html index 60a7954a56..fd9e5a9e5f 100644 --- a/cms/templates/activation_invalid.html +++ b/cms/templates/activation_invalid.html @@ -24,16 +24,6 @@ )}

- -
diff --git a/cms/templates/base.html b/cms/templates/base.html index 7d8cda30da..eca989ad37 100644 --- a/cms/templates/base.html +++ b/cms/templates/base.html @@ -42,7 +42,6 @@ import json @@ -65,7 +64,6 @@ import json <%include file="widgets/sock.html" args="online_help_token=online_help_token" /> % endif <%include file="widgets/footer.html" /> - <%include file="widgets/tender.html" />
diff --git a/cms/templates/course_info.html b/cms/templates/course_info.html index bbb5e12d72..a770fd9049 100644 --- a/cms/templates/course_info.html +++ b/cms/templates/course_info.html @@ -7,7 +7,7 @@ from django.utils.translation import ugettext as _ from django.template.defaultfilters import escapejs %> - +## TODO decode course # from context_course into title. <%block name="title">${_("Course Updates")} <%block name="bodyclass">is-signedin course course-info updates view-updates diff --git a/cms/templates/error.html b/cms/templates/error.html index 99191a3d4d..cebe726544 100644 --- a/cms/templates/error.html +++ b/cms/templates/error.html @@ -14,15 +14,8 @@ from django.conf import settings <%! -if settings.TENDER_DOMAIN: - help_link_start = ''.format( - domain=settings.TENDER_DOMAIN, - title=_("Use our feedback tool, Tender, to share your feedback") - ), - help_link_end = '' -else: - help_link_start = ''.format(email=settings.TECH_SUPPORT_EMAIL) - help_link_end = '' +help_link_start = ''.format(email=settings.TECH_SUPPORT_EMAIL) +help_link_end = '' %> <%block name="content"> diff --git a/cms/templates/index.html b/cms/templates/index.html index 92efdd0d81..472156038c 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -61,7 +61,8 @@
  1. - ## Translators: This is an example name for a new course, seen when filling out the form to create a new course. + ## Translators: This is an example name for a new course, seen when + ## filling out the form to create a new course. ${_("The public display name for your course. This cannot be changed, but you can set a different display name in Advanced Settings later.")} @@ -77,7 +78,9 @@
  2. - ## Translators: This is an example for the number used to identify a course, seen when filling out the form to create a new course. The number here is short for "Computer Science 101". It can contain letters but cannot contain spaces. + ## Translators: This is an example for the number used to identify a course, + ## seen when filling out the form to create a new course. The number here is + ## short for "Computer Science 101". It can contain letters but cannot contain spaces. ${_("The unique number that identifies your course within your organization.")} ${_("Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.")} @@ -85,7 +88,8 @@
  3. - ## Translators: This is an example for the "run" used to identify different instances of a course, seen when filling out the form to create a new course. + ## Translators: This is an example for the "run" used to identify different + ## instances of a course, seen when filling out the form to create a new course. ${_("The term in which your course will run.")} ${_("Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.")} @@ -123,7 +127,9 @@
    1. - ## Translators: This is an example name for a new content library, seen when filling out the form to create a new library. (A library is a collection of content or problems.) + ## Translators: This is an example name for a new content library, seen when + ## filling out the form to create a new library. + ## (A library is a collection of content or problems.) ${_("The public display name for your library.")} @@ -137,7 +143,10 @@
    2. - ## Translators: This is an example for the "code" used to identify a library, seen when filling out the form to create a new library. This example is short for "Computer Science Problems". The example number may contain letters but must not contain spaces. + ## Translators: This is an example for the "code" used to identify a library, + ## seen when filling out the form to create a new library. This example is short + ## for "Computer Science Problems". The example number may contain letters + ## but must not contain spaces. ${_("The unique code that identifies this library.")} ${_("Note: This is part of your library URL, so no spaces or special characters are allowed.")} ${_("This cannot be changed.")} @@ -188,7 +197,11 @@
      ${_("This course run is currently being created.")}
      - ## Translators: This is a status message, used to inform the user of what the system is doing. This status means that the user has requested to re-run an existing course, and the system is currently in the process of duplicating and configuring the existing course so that it can be re-run. + ## Translators: This is a status message, used to inform the user of + ## what the system is doing. This status means that the user has + ## requested to re-run an existing course, and the system is currently + ## in the process of duplicating and configuring the existing course + ## so that it can be re-run. ${_("Configuring as re-run")}
      @@ -227,10 +240,14 @@
      -
      This re-run processing status:
      + ## Translators: This is a status message for the course re-runs feature. + ## When a course admin indicates that a course should be re-run, the system + ## needs to process the request and prepare the new course. The status of + ## the process will follow this text. +
      ${_("This re-run processing status:")}
      - Configuration Error + ${_("Configuration Error")}
      @@ -479,13 +496,6 @@ ${_("Getting Started with {studio_name}").format(studio_name=settings.STUDIO_NAME)}
    3. - % if settings.TENDER_DOMAIN: -
    4. - - ${_("Request help with {studio_name}").format(studio_name=settings.STUDIO_NAME)} - -
    5. - % endif
    @@ -513,14 +523,8 @@ <%! from django.conf import settings -if settings.TENDER_DOMAIN: - help_link_start = ''.format( - domain=settings.TENDER_DOMAIN, - ) - help_link_end = '' -else: - help_link_start = ''.format(email=settings.TECH_SUPPORT_EMAIL) - help_link_end = '' +help_link_start = ''.format(email=settings.TECH_SUPPORT_EMAIL) +help_link_end = '' %>

    ${_("Your request to author courses in {studio_name} has been denied. Please {link_start}contact {platform_name} Staff with further questions{link_end}.").format( studio_name=settings.STUDIO_NAME, @@ -556,16 +560,6 @@ else:

    ${_('Need help?')}

    ${_('Please check your Junk or Spam folders in case our email isn\'t in your INBOX. Still can\'t find the verification email? Request help via the link below.')}

    - -
      - % if settings.TENDER_DOMAIN: -
    1. - - ${_("Request help with your {studio_name} account").format(studio_name=settings.STUDIO_NAME)} - -
    2. - % endif -
    diff --git a/cms/templates/login.html b/cms/templates/login.html index 76da01e65b..5e99961f63 100644 --- a/cms/templates/login.html +++ b/cms/templates/login.html @@ -30,9 +30,9 @@ from django.utils.translation import ugettext as _
  4. - ${_("Forgot password?")} + ${_("Forgot password?")}
@@ -45,20 +45,6 @@ from django.utils.translation import ugettext as _ - - % if settings.TENDER_DOMAIN: - - % endif diff --git a/cms/templates/settings.html b/cms/templates/settings.html index 4f0769e080..9cccd92ecb 100644 --- a/cms/templates/settings.html +++ b/cms/templates/settings.html @@ -220,17 +220,25 @@ CMS.URL.UPLOAD_ASSET = '${upload_asset_url}'; ${_("(UTC)")} - + <% + enrollment_end_readonly = "readonly aria-readonly=\"true\"" if not enrollment_end_editable else "" + enrollment_end_editable_class = "is-not-editable" if not enrollment_end_editable else "" + %>
  • -
    +
    - - ${_("Last day students can enroll")} + + + ${_("Last day students can enroll.")} + % if not enrollment_end_editable: + ${_("Contact your edX Partner Manager to update these settings.")} + % endif +
    -
    +
    - + ${_("(UTC)")}
  • diff --git a/cms/templates/widgets/footer.html b/cms/templates/widgets/footer.html index 4ddfd3763b..8c1e21fbbf 100644 --- a/cms/templates/widgets/footer.html +++ b/cms/templates/widgets/footer.html @@ -19,11 +19,6 @@ from django.core.urlresolvers import reverse - % if settings.TENDER_DOMAIN and user.is_authenticated(): - - % endif diff --git a/cms/templates/widgets/sock.html b/cms/templates/widgets/sock.html index 55f588d4e2..3d222fd9ed 100644 --- a/cms/templates/widgets/sock.html +++ b/cms/templates/widgets/sock.html @@ -20,46 +20,56 @@ from django.core.urlresolvers import reverse
    -

    ${_("{studio_name} Documentation").format(studio_name=settings.STUDIO_NAME)}

    + <%! + from django.conf import settings -
    -

    ${_("You can click Help in the upper right corner of any page to get more information about the page you're on. You can also use the links below to download the Building and Running an {platform_name} Course PDF file, to go to the {platform_name} Author Support site, or to enroll in edX101.").format(platform_name=settings.PLATFORM_NAME)}

    -
    + is_edx_domain = settings.FEATURES.get('IS_EDX_DOMAIN', False) + partner_email = settings.FEATURES.get('PARTNER_SUPPORT_EMAIL', '') - -
    - - % if settings.TENDER_DOMAIN: - - % endif diff --git a/cms/templates/widgets/tender.html b/cms/templates/widgets/tender.html deleted file mode 100644 index bf8b9e20de..0000000000 --- a/cms/templates/widgets/tender.html +++ /dev/null @@ -1,20 +0,0 @@ -% if settings.TENDER_SUBDOMAIN and user.is_authenticated(): - -% endif diff --git a/common/djangoapps/course_modes/tests/factories.py b/common/djangoapps/course_modes/tests/factories.py index bbe1ca5c2b..853922bb32 100644 --- a/common/djangoapps/course_modes/tests/factories.py +++ b/common/djangoapps/course_modes/tests/factories.py @@ -6,7 +6,8 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey # Factories are self documenting # pylint: disable=missing-docstring class CourseModeFactory(DjangoModelFactory): - FACTORY_FOR = CourseMode + class Meta(object): + model = CourseMode course_id = SlashSeparatedCourseKey('MITx', '999', 'Robot_Super_Course') mode_slug = 'audit' diff --git a/common/djangoapps/student/admin.py b/common/djangoapps/student/admin.py index f3227dc93e..6000ff4d58 100644 --- a/common/djangoapps/student/admin.py +++ b/common/djangoapps/student/admin.py @@ -1,25 +1,22 @@ -''' -django admin pages for courseware model -''' +""" Django admin pages for student app """ from django import forms -from config_models.admin import ConfigurationModelAdmin from django.contrib.auth.models import User - -from student.models import UserProfile, UserTestGroup, CourseEnrollmentAllowed, DashboardConfiguration -from student.models import ( - CourseEnrollment, Registration, PendingNameChange, CourseAccessRole, LinkedInAddToProfileConfiguration -) from ratelimitbackend import admin -from student.roles import REGISTERED_ACCESS_ROLES - from xmodule.modulestore.django import modulestore - -from opaque_keys.edx.keys import CourseKey from opaque_keys import InvalidKeyError +from opaque_keys.edx.keys import CourseKey + +from config_models.admin import ConfigurationModelAdmin +from student.models import ( + UserProfile, UserTestGroup, CourseEnrollmentAllowed, DashboardConfiguration, CourseEnrollment, Registration, + PendingNameChange, CourseAccessRole, LinkedInAddToProfileConfiguration +) +from student.roles import REGISTERED_ACCESS_ROLES class CourseAccessRoleForm(forms.ModelForm): """Form for adding new Course Access Roles view the Django Admin Panel.""" + class Meta(object): # pylint: disable=missing-docstring model = CourseAccessRole @@ -135,12 +132,21 @@ class LinkedInAddToProfileConfigurationAdmin(admin.ModelAdmin): exclude = ('dashboard_tracking_code',) +class CourseEnrollmentAdmin(admin.ModelAdmin): + """ Admin interface for the CourseEnrollment model. """ + list_display = ('id', 'course_id', 'mode', 'user', 'is_active',) + list_filter = ('mode', 'is_active',) + search_fields = ('course_id', 'mode', 'user__username',) + readonly_fields = ('course_id', 'mode', 'user',) + + class Meta(object): # pylint: disable=missing-docstring + model = CourseEnrollment + + admin.site.register(UserProfile) admin.site.register(UserTestGroup) -admin.site.register(CourseEnrollment) - admin.site.register(CourseEnrollmentAllowed) admin.site.register(Registration) @@ -152,3 +158,5 @@ admin.site.register(CourseAccessRole, CourseAccessRoleAdmin) admin.site.register(DashboardConfiguration, ConfigurationModelAdmin) admin.site.register(LinkedInAddToProfileConfiguration, LinkedInAddToProfileConfigurationAdmin) + +admin.site.register(CourseEnrollment, CourseEnrollmentAdmin) diff --git a/common/djangoapps/student/management/commands/set_superuser.py b/common/djangoapps/student/management/commands/set_superuser.py new file mode 100644 index 0000000000..068742bc8c --- /dev/null +++ b/common/djangoapps/student/management/commands/set_superuser.py @@ -0,0 +1,46 @@ +"""Management command to grant or revoke superuser access for one or more users""" + +from optparse import make_option +from django.contrib.auth.models import User +from django.core.management.base import BaseCommand, CommandError + + +class Command(BaseCommand): + """Management command to grant or revoke superuser access for one or more users""" + option_list = BaseCommand.option_list + ( + make_option('--unset', + action='store_true', + dest='unset', + default=False, + help='Set is_superuser to False instead of True'), + ) + + args = ' [user|email ...]>' + help = """ + This command will set is_superuser to true for one or more users. + Lookup by username or email address, assumes usernames + do not look like email addresses. + """ + + def handle(self, *args, **options): + if len(args) < 1: + raise CommandError('Usage is set_superuser {0}'.format(self.args)) + + for user in args: + try: + if '@' in user: + userobj = User.objects.get(email=user) + else: + userobj = User.objects.get(username=user) + + if options['unset']: + userobj.is_superuser = False + else: + userobj.is_superuser = True + + userobj.save() + + except Exception as err: # pylint: disable=broad-except + print "Error modifying user with identifier {}: {}: {}".format(user, type(err).__name__, err.message) + + print 'Success!' diff --git a/common/djangoapps/student/tests/factories.py b/common/djangoapps/student/tests/factories.py index 7965cdd186..f59750eb40 100644 --- a/common/djangoapps/student/tests/factories.py +++ b/common/djangoapps/student/tests/factories.py @@ -17,14 +17,16 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey class GroupFactory(DjangoModelFactory): - FACTORY_FOR = Group - FACTORY_DJANGO_GET_OR_CREATE = ('name', ) + class Meta(object): + model = Group + django_get_or_create = ('name', ) name = factory.Sequence(u'group{0}'.format) class UserStandingFactory(DjangoModelFactory): - FACTORY_FOR = UserStanding + class Meta(object): + model = UserStanding user = None account_status = None @@ -32,8 +34,9 @@ class UserStandingFactory(DjangoModelFactory): class UserProfileFactory(DjangoModelFactory): - FACTORY_FOR = UserProfile - FACTORY_DJANGO_GET_OR_CREATE = ('user', ) + class Meta(object): + model = UserProfile + django_get_or_create = ('user', ) user = None name = factory.LazyAttribute(u'{0.user.first_name} {0.user.last_name}'.format) @@ -45,7 +48,8 @@ class UserProfileFactory(DjangoModelFactory): class CourseModeFactory(DjangoModelFactory): - FACTORY_FOR = CourseMode + class Meta(object): + model = CourseMode course_id = None mode_display_name = u'Honor Code', @@ -57,15 +61,17 @@ class CourseModeFactory(DjangoModelFactory): class RegistrationFactory(DjangoModelFactory): - FACTORY_FOR = Registration + class Meta(object): + model = Registration user = None activation_key = uuid4().hex.decode('ascii') class UserFactory(DjangoModelFactory): - FACTORY_FOR = User - FACTORY_DJANGO_GET_OR_CREATE = ('email', 'username') + class Meta(object): + model = User + django_get_or_create = ('email', 'username') username = factory.Sequence(u'robot{0}'.format) email = factory.Sequence(u'robot+test+{0}@edx.org'.format) @@ -101,7 +107,8 @@ class UserFactory(DjangoModelFactory): class AnonymousUserFactory(factory.Factory): - FACTORY_FOR = AnonymousUser + class Meta(object): + model = AnonymousUser class AdminFactory(UserFactory): @@ -109,14 +116,16 @@ class AdminFactory(UserFactory): class CourseEnrollmentFactory(DjangoModelFactory): - FACTORY_FOR = CourseEnrollment + class Meta(object): + model = CourseEnrollment user = factory.SubFactory(UserFactory) course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') class CourseAccessRoleFactory(DjangoModelFactory): - FACTORY_FOR = CourseAccessRole + class Meta(object): + model = CourseAccessRole user = factory.SubFactory(UserFactory) course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') @@ -124,7 +133,8 @@ class CourseAccessRoleFactory(DjangoModelFactory): class CourseEnrollmentAllowedFactory(DjangoModelFactory): - FACTORY_FOR = CourseEnrollmentAllowed + class Meta(object): + model = CourseEnrollmentAllowed email = 'test@edx.org' course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') @@ -137,7 +147,8 @@ class PendingEmailChangeFactory(DjangoModelFactory): new_email: sequence of new+email+{}@edx.org activation_key: sequence of integers, padded to 30 characters """ - FACTORY_FOR = PendingEmailChange + class Meta(object): + model = PendingEmailChange user = factory.SubFactory(UserFactory) new_email = factory.Sequence(u'new+email+{0}@edx.org'.format) diff --git a/common/djangoapps/student/tests/test_login.py b/common/djangoapps/student/tests/test_login.py index e57e81b732..9ac21c17f5 100644 --- a/common/djangoapps/student/tests/test_login.py +++ b/common/djangoapps/student/tests/test_login.py @@ -8,6 +8,7 @@ from django.test import TestCase from django.test.client import Client from django.test.utils import override_settings from django.conf import settings +from django.contrib.auth.models import User from django.core.cache import cache from django.core.urlresolvers import reverse, NoReverseMatch from django.http import HttpResponseBadRequest, HttpResponse @@ -252,7 +253,7 @@ class LoginTest(TestCase): self._assert_response(response, success=True) # Reload the user from the database - self.user = UserFactory.FACTORY_FOR.objects.get(pk=self.user.pk) + self.user = User.objects.get(pk=self.user.pk) self.assertEqual(self.user.profile.get_meta()['session_id'], client1.session.session_key) diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py index e4ffb58121..05ab444bf8 100644 --- a/common/djangoapps/student/tests/tests.py +++ b/common/djangoapps/student/tests/tests.py @@ -488,21 +488,18 @@ class DashboardTest(ModuleStoreTestCase): self.assertContains(response, expected_url) @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') - @ddt.data((ModuleStoreEnum.Type.mongo, 1), (ModuleStoreEnum.Type.split, 3)) - @ddt.unpack - def test_dashboard_metadata_caching(self, modulestore_type, expected_mongo_calls): + @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split) + def test_dashboard_metadata_caching(self, modulestore_type): """ Check that the student dashboard makes use of course metadata caching. - After enrolling a student in a course, that course's metadata should be - cached as a CourseOverview. The student dashboard should never have to make - calls to the modulestore. + After creating a course, that course's metadata should be cached as a + CourseOverview. The student dashboard should never have to make calls to + the modulestore. Arguments: modulestore_type (ModuleStoreEnum.Type): Type of modulestore to create test course in. - expected_mongo_calls (int >=0): Number of MongoDB queries expected for - a single call to the module store. Note to future developers: If you break this test so that the "check_mongo_calls(0)" fails, @@ -512,11 +509,11 @@ class DashboardTest(ModuleStoreTestCase): CourseDescriptor isn't necessary. """ # Create a course and log in the user. - test_course = CourseFactory.create(default_store=modulestore_type) + # Creating a new course will trigger a publish event and the course will be cached + test_course = CourseFactory.create(default_store=modulestore_type, emit_signals=True) self.client.login(username="jack", password="test") - # Enrolling the user in the course will result in a modulestore query. - with check_mongo_calls(expected_mongo_calls): + with check_mongo_calls(0): CourseEnrollment.enroll(self.user, test_course.id) # Subsequent requests will only result in SQL queries to load the diff --git a/common/lib/xmodule/xmodule/css/capa/display.scss b/common/lib/xmodule/xmodule/css/capa/display.scss index 77d9225f3d..14ded8073d 100644 --- a/common/lib/xmodule/xmodule/css/capa/display.scss +++ b/common/lib/xmodule/xmodule/css/capa/display.scss @@ -379,7 +379,7 @@ div.problem { } > span { - display: block; + display: inline-block; margin-bottom: lh(0.5); } diff --git a/common/lib/xmodule/xmodule/lti_module.py b/common/lib/xmodule/xmodule/lti_module.py index c559e1d985..7d3a895eb3 100644 --- a/common/lib/xmodule/xmodule/lti_module.py +++ b/common/lib/xmodule/xmodule/lti_module.py @@ -206,9 +206,8 @@ class LTIFields(object): ask_to_send_username = Boolean( display_name=_("Request user's username"), # Translators: This is used to request the user's username for a third party service. - # Usernames can only be requested if "Open in New Page" is set to True. help=_( - "Select True to request the user's username. You must also set Open in New Page to True to get the user's information." + "Select True to request the user's username." ), default=False, scope=Scope.settings @@ -216,9 +215,8 @@ class LTIFields(object): ask_to_send_email = Boolean( display_name=_("Request user's email"), # Translators: This is used to request the user's email for a third party service. - # Emails can only be requested if "Open in New Page" is set to True. help=_( - "Select True to request the user's email address. You must also set Open in New Page to True to get the user's information." + "Select True to request the user's email address." ), default=False, scope=Scope.settings @@ -603,11 +601,10 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule): except AttributeError: self.user_username = "" - if self.open_in_a_new_page: - if self.ask_to_send_username and self.user_username: - body["lis_person_sourcedid"] = self.user_username - if self.ask_to_send_email and self.user_email: - body["lis_person_contact_email_primary"] = self.user_email + if self.ask_to_send_username and self.user_username: + body["lis_person_sourcedid"] = self.user_username + if self.ask_to_send_email and self.user_email: + body["lis_person_contact_email_primary"] = self.user_email # Appending custom parameter for signing. body.update(custom_parameters) diff --git a/common/lib/xmodule/xmodule/modulestore/tests/factories.py b/common/lib/xmodule/xmodule/modulestore/tests/factories.py index 8748591c55..42221b6c4b 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/factories.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/factories.py @@ -71,10 +71,11 @@ class XModuleFactory(Factory): Factory for XModules """ - # We have to give a Factory a FACTORY_FOR. + # We have to give a model for Factory. # However, the class that we create is actually determined by the category # specified in the factory - FACTORY_FOR = Dummy + class Meta(object): # pylint: disable=missing-docstring + model = Dummy @lazy_attribute def modulestore(self): @@ -114,7 +115,7 @@ class CourseFactory(XModuleFactory): name = kwargs.get('name', kwargs.get('run', Location.clean(kwargs.get('display_name')))) run = kwargs.pop('run', name) user_id = kwargs.pop('user_id', ModuleStoreEnum.UserID.test) - emit_signals = kwargs.get('emit_signals', False) + emit_signals = kwargs.pop('emit_signals', False) # Pass the metadata just as field=value pairs kwargs.update(kwargs.pop('metadata', {})) diff --git a/common/lib/xmodule/xmodule/modulestore/tests/persistent_factories.py b/common/lib/xmodule/xmodule/modulestore/tests/persistent_factories.py deleted file mode 100644 index 2eee09740f..0000000000 --- a/common/lib/xmodule/xmodule/modulestore/tests/persistent_factories.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Provides factories for Split.""" -from xmodule.modulestore import ModuleStoreEnum -from xmodule.course_module import CourseDescriptor -from xmodule.x_module import XModuleDescriptor -import factory -from factory.helpers import lazy_attribute -from opaque_keys.edx.keys import UsageKey -# Factories are self documenting -# pylint: disable=missing-docstring - - -class SplitFactory(factory.Factory): - """ - Abstracted superclass which defines modulestore so that there's no dependency on django - if the caller passes modulestore in kwargs - """ - @lazy_attribute - def modulestore(self): - # Delayed import so that we only depend on django if the caller - # hasn't provided their own modulestore - from xmodule.modulestore.django import modulestore - return modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.split) - - -class PersistentCourseFactory(SplitFactory): - """ - Create a new course (not a new version of a course, but a whole new index entry). - - keywords: any xblock field plus (note, the below are filtered out; so, if they - become legitimate xblock fields, they won't be settable via this factory) - * org: defaults to textX - * master_branch: (optional) defaults to ModuleStoreEnum.BranchName.draft - * user_id: (optional) defaults to 'test_user' - * display_name (xblock field): will default to 'Robot Super Course' unless provided - """ - FACTORY_FOR = CourseDescriptor - - # pylint: disable=unused-argument - @classmethod - def _create(cls, target_class, course='999', run='run', org='testX', user_id=ModuleStoreEnum.UserID.test, - master_branch=ModuleStoreEnum.BranchName.draft, **kwargs): - - modulestore = kwargs.pop('modulestore') - root_block_id = kwargs.pop('root_block_id', 'course') - # Write the data to the mongo datastore - new_course = modulestore.create_course( - org, course, run, user_id, fields=kwargs, - master_branch=master_branch, root_block_id=root_block_id - ) - - return new_course - - @classmethod - def _build(cls, target_class, *args, **kwargs): - raise NotImplementedError() - - -class ItemFactory(SplitFactory): - FACTORY_FOR = XModuleDescriptor - - display_name = factory.LazyAttributeSequence(lambda o, n: "{} {}".format(o.category, n)) - - # pylint: disable=unused-argument - @classmethod - def _create(cls, target_class, parent_location, category='chapter', - user_id=ModuleStoreEnum.UserID.test, definition_locator=None, force=False, - continue_version=False, **kwargs): - """ - passes *kwargs* as the new item's field values: - - :param parent_location: (required) the location of the course & possibly parent - - :param category: (defaults to 'chapter') - - :param definition_locator (optional): the DescriptorLocator for the definition this uses or branches - """ - modulestore = kwargs.pop('modulestore') - if isinstance(parent_location, UsageKey): - return modulestore.create_child( - user_id, parent_location, category, defintion_locator=definition_locator, - force=force, continue_version=continue_version, **kwargs - ) - else: - return modulestore.create_item( - user_id, parent_location, category, defintion_locator=definition_locator, - force=force, continue_version=continue_version, **kwargs - ) - - @classmethod - def _build(cls, target_class, *args, **kwargs): - raise NotImplementedError() diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py b/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py index 25e2dc7741..88da3c71aa 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py @@ -772,6 +772,122 @@ class SplitModuleCourseTests(SplitModuleTest): self.assertEqual(len(result.children[0].children), 1) self.assertEqual(result.children[0].children[0].locator.version_guid, versions[0]) + @patch('xmodule.tabs.CourseTab.from_json', side_effect=mock_tab_from_json) + def test_persist_dag(self, _from_json): + """ + try saving temporary xblocks + """ + test_course = modulestore().create_course( + course='course', run='2014', org='testx', + display_name='fun test course', user_id='testbot', + master_branch=ModuleStoreEnum.BranchName.draft + ) + test_chapter = modulestore().create_xblock( + test_course.system, test_course.id, 'chapter', fields={'display_name': 'chapter n'}, + parent_xblock=test_course + ) + self.assertEqual(test_chapter.display_name, 'chapter n') + test_def_content = 'boo' + # create child + new_block = modulestore().create_xblock( + test_course.system, test_course.id, + 'problem', + fields={ + 'data': test_def_content, + 'display_name': 'problem' + }, + parent_xblock=test_chapter + ) + self.assertIsNotNone(new_block.definition_locator) + self.assertTrue(isinstance(new_block.definition_locator.definition_id, LocalId)) + # better to pass in persisted parent over the subdag so + # subdag gets the parent pointer (otherwise 2 ops, persist dag, update parent children, + # persist parent + persisted_course = modulestore().persist_xblock_dag(test_course, 'testbot') + self.assertEqual(len(persisted_course.children), 1) + persisted_chapter = persisted_course.get_children()[0] + self.assertEqual(persisted_chapter.category, 'chapter') + self.assertEqual(persisted_chapter.display_name, 'chapter n') + self.assertEqual(len(persisted_chapter.children), 1) + persisted_problem = persisted_chapter.get_children()[0] + self.assertEqual(persisted_problem.category, 'problem') + self.assertEqual(persisted_problem.data, test_def_content) + # update it + persisted_problem.display_name = 'altered problem' + persisted_problem = modulestore().update_item(persisted_problem, 'testbot') + self.assertEqual(persisted_problem.display_name, 'altered problem') + + @patch('xmodule.tabs.CourseTab.from_json', side_effect=mock_tab_from_json) + def test_block_generations(self, _from_json): + """ + Test get_block_generations + """ + test_course = modulestore().create_course( + org='edu.harvard', + course='history', + run='hist101', + display_name='history test course', + user_id='testbot', + master_branch=ModuleStoreEnum.BranchName.draft + ) + chapter = modulestore().create_child( + None, test_course.location, + block_type='chapter', + block_id='chapter1', + fields={'display_name': 'chapter 1'} + ) + sub = modulestore().create_child( + None, chapter.location, + block_type='vertical', + block_id='subsection1', + fields={'display_name': 'subsection 1'} + ) + first_problem = modulestore().create_child( + None, sub.location, + block_type='problem', + block_id='problem1', + fields={'display_name': 'problem 1', 'data': ''} + ) + first_problem.max_attempts = 3 + first_problem.save() # decache the above into the kvs + updated_problem = modulestore().update_item(first_problem, 'testbot') + self.assertIsNotNone(updated_problem.previous_version) + self.assertEqual(updated_problem.previous_version, first_problem.update_version) + self.assertNotEqual(updated_problem.update_version, first_problem.update_version) + modulestore().delete_item(updated_problem.location, 'testbot') + + second_problem = modulestore().create_child( + None, sub.location.version_agnostic(), + block_type='problem', + block_id='problem2', + fields={'display_name': 'problem 2', 'data': ''} + ) + + # The draft course root has 2 revisions: the published revision, and then the subsequent + # changes to the draft revision + version_history = modulestore().get_block_generations(test_course.location) + self.assertIsNotNone(version_history) + self.assertEqual(version_history.locator.version_guid, test_course.location.version_guid) + self.assertEqual(len(version_history.children), 1) + self.assertEqual(version_history.children[0].children, []) + self.assertEqual(version_history.children[0].locator.version_guid, chapter.location.version_guid) + + # sub changed on add, add problem, delete problem, add problem in strict linear seq + version_history = modulestore().get_block_generations(sub.location) + self.assertEqual(len(version_history.children), 1) + self.assertEqual(len(version_history.children[0].children), 1) + self.assertEqual(len(version_history.children[0].children[0].children), 1) + self.assertEqual(len(version_history.children[0].children[0].children[0].children), 0) + + # first and second problem may show as same usage_id; so, need to ensure their histories are right + version_history = modulestore().get_block_generations(updated_problem.location) + self.assertEqual(version_history.locator.version_guid, first_problem.location.version_guid) + self.assertEqual(len(version_history.children), 1) # updated max_attempts + self.assertEqual(len(version_history.children[0].children), 0) + + version_history = modulestore().get_block_generations(second_problem.location) + self.assertNotEqual(version_history.locator.version_guid, first_problem.location.version_guid) + class TestCourseStructureCache(SplitModuleTest): """Tests for the CourseStructureCache""" diff --git a/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py b/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py index bd1fdddb8f..7ddf4a7004 100644 --- a/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py +++ b/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py @@ -104,7 +104,8 @@ class ModuleSystemFactory(Factory): performed by :func:`xmodule.tests.get_test_system`, so arguments for that function are valid factory attributes. """ - FACTORY_FOR = ModuleSystem + class Meta(object): # pylint: disable=missing-docstring + model = ModuleSystem @classmethod def _build(cls, target_class, *args, **kwargs): # pylint: disable=unused-argument @@ -119,7 +120,8 @@ class DescriptorSystemFactory(Factory): performed by :func:`xmodule.tests.get_test_descriptor_system`, so arguments for that function are valid factory attributes. """ - FACTORY_FOR = DescriptorSystem + class Meta(object): # pylint: disable=missing-docstring + model = DescriptorSystem @classmethod def _build(cls, target_class, *args, **kwargs): # pylint: disable=unused-argument @@ -190,7 +192,8 @@ class LeafDescriptorFactory(Factory): """ # pylint: disable=missing-docstring - FACTORY_FOR = XModuleDescriptor + class Meta(object): + model = XModuleDescriptor runtime = SubFactory(DescriptorSystemFactory) url_name = LazyAttributeSequence('{.block_type}_{}'.format) diff --git a/common/lib/xmodule/xmodule/tests/xml/factories.py b/common/lib/xmodule/xmodule/tests/xml/factories.py index ab844735be..946e115f1b 100644 --- a/common/lib/xmodule/xmodule/tests/xml/factories.py +++ b/common/lib/xmodule/xmodule/tests/xml/factories.py @@ -64,7 +64,8 @@ class XmlImportFactory(Factory): Factory for generating XmlImportData's, which can hold all the data needed to run an XModule XML import """ - FACTORY_FOR = XmlImportData + class Meta(object): # pylint: disable=missing-docstring + model = XmlImportData filesystem = MemoryFS() xblock_mixins = (InheritanceMixin, XModuleMixin) diff --git a/common/static/common/js/components/views/search_field.js b/common/static/common/js/components/views/search_field.js index 7599edee35..955133fe1c 100644 --- a/common/static/common/js/components/views/search_field.js +++ b/common/static/common/js/components/views/search_field.js @@ -14,12 +14,15 @@ 'submit .search-form': 'performSearch', 'blur .search-form': 'onFocusOut', 'keyup .search-field': 'refreshState', - 'click .action-clear': 'clearSearch' + 'click .action-clear': 'clearSearch', + 'mouseover .action-clear': 'setMouseOverState', + 'mouseout .action-clear': 'setMouseOutState', }, initialize: function(options) { this.type = options.type; this.label = options.label; + this.mouseOverClear = false; }, refreshState: function() { @@ -43,10 +46,18 @@ return this; }, + setMouseOverState: function(event) { + this.mouseOverClear = true; + }, + + setMouseOutState: function(event) { + this.mouseOverClear = false; + }, + onFocusOut: function(event) { // If the focus is going anywhere but the clear search // button then treat it as a request to search. - if (!$(event.relatedTarget).hasClass('action-clear')) { + if (!this.mouseOverClear) { this.performSearch(event); } }, diff --git a/common/static/js/src/tender_fallback.js b/common/static/js/src/tender_fallback.js deleted file mode 100644 index 1df3b2f106..0000000000 --- a/common/static/js/src/tender_fallback.js +++ /dev/null @@ -1 +0,0 @@ -console.error("Can't load Tender -- anything that relies on it will fail"); diff --git a/common/static/sass/_mixins.scss b/common/static/sass/_mixins.scss index 2b85aa0186..e326dec790 100644 --- a/common/static/sass/_mixins.scss +++ b/common/static/sass/_mixins.scss @@ -1,7 +1,7 @@ // common - utilities - mixins and extends // ==================== -// Table of Contents +// Table of Contents // * +Font Sizing - Mixin // * +Line Height - Mixin // * +Sizing - Mixin @@ -26,34 +26,34 @@ // * +Icon - Font-Awesome - Extend // +Font Sizing - Mixin -// ==================== +// ==================== @mixin font-size($sizeValue: 16){ font-size: $sizeValue + px; font-size: ($sizeValue/10) + rem; } // +Line Height - Mixin -// ==================== +// ==================== @mixin line-height($fontSize: auto){ line-height: ($fontSize*1.48) + px; line-height: (($fontSize/10)*1.48) + rem; } // +Sizing - Mixin -// ==================== +// ==================== @mixin size($width: $baseline, $height: $baseline) { height: $height; width: $width; } // +Square - Mixin -// ==================== +// ==================== @mixin square($size: $baseline) { @include size($size); } // +Placeholder Styling - Mixin -// ==================== +// ==================== @mixin placeholder($color) { :-moz-placeholder { color: $color; @@ -67,7 +67,7 @@ } // +Flex Support - Mixin -// ==================== +// ==================== @mixin ui-flexbox() { display: -webkit-box; display: -moz-box; @@ -77,7 +77,7 @@ } // +Flex PolyFill - Extends -// ==================== +// ==================== // justify-content right for display:flex alignment in older browsers %ui-justify-right-flex { @@ -107,7 +107,7 @@ // +UI - Wrapper - Extends -// ==================== +// ==================== // used for page/view-level wrappers (for centering/grids) %ui-wrapper { @include clearfix(); @@ -128,7 +128,7 @@ } // +UI - Window - Extends -// ==================== +// ==================== %ui-window { @include clearfix(); border-radius: ($baseline/10); @@ -144,13 +144,13 @@ } // +UI - Visual Link - Extends -// ==================== +// ==================== %ui-fake-link { cursor: pointer; } // +UI - Functional Disable - Extends -// ==================== +// ==================== %ui-disabled { pointer-events: none; outline: none; @@ -158,7 +158,7 @@ } // +UI - Depth Levels - Extends -// ==================== +// ==================== %ui-depth0 { z-index: 0; } %ui-depth1 { z-index: 10; } %ui-depth2 { z-index: 100; } @@ -168,7 +168,7 @@ // +UI - Clear Children - Extends -// ==================== +// ==================== // extends - UI - utility - first child clearing %wipe-first-child { @@ -190,7 +190,7 @@ } // +UI - Buttons - Extends -// ==================== +// ==================== %ui-btn { @include box-sizing(border-box); @include transition(color $tmg-f2 ease-in-out 0s, border-color $tmg-f2 ease-in-out 0s, background $tmg-f2 ease-in-out 0s, box-shadow $tmg-f2 ease-in-out 0s); @@ -329,7 +329,7 @@ } // +UI - Well Archetype - Extends -// ==================== +// ==================== %ui-well { box-shadow: inset 0 1px 2px 1px $shadow; padding: ($baseline*0.75) $baseline; @@ -378,7 +378,7 @@ } // +Content - No List - Extends -// ==================== +// ==================== // removes list styling/spacing when using uls, ols for navigation and less content-centric cases %cont-no-list { list-style: none; @@ -393,7 +393,7 @@ } // +Content - Hidden Image Text - Extend -// ==================== +// ==================== // image-replacement hidden text %cont-text-hide { text-indent: 100%; @@ -402,7 +402,7 @@ } // +Content - Screenreader Text - Extend -// ==================== +// ==================== %cont-text-sr { border: 0; clip: rect(0 0 0 0); @@ -415,13 +415,13 @@ } // +Content - Text Wrap - Extend -// ==================== +// ==================== %cont-text-wrap { word-wrap: break-word; } // +Content - Text Truncate - Extend -// ==================== +// ==================== %cont-truncated { @include box-sizing(border-box); overflow: hidden; @@ -430,7 +430,7 @@ } // * +Icon - Font-Awesome - Extend -// ==================== +// ==================== %use-font-awesome { display: inline-block; font-family: FontAwesome; diff --git a/common/templates/course_modes/choose.html b/common/templates/course_modes/choose.html index 97499d8f87..0def3743fd 100644 --- a/common/templates/course_modes/choose.html +++ b/common/templates/course_modes/choose.html @@ -68,6 +68,9 @@ from django.core.urlresolvers import reverse
    + <% + b_tag_kwargs = {'b_start': '', 'b_end': ''} + %> % if "verified" in modes:
    @@ -82,9 +85,9 @@ from django.core.urlresolvers import reverse

    ${_("Benefits of a Verified Certificate")}

      -
    • ${_("{b_start}Eligible for credit:{b_end} Receive academic credit after successfully completing the course").format(b_start='', b_end='')}
    • -
    • ${_("{b_start}Official:{b_end} Receive an instructor-signed certificate with the institution's logo").format(b_start='', b_end='')}
    • -
    • ${_("{b_start}Easily shareable:{b_end} Add the certificate to your CV or resume, or post it directly on LinkedIn").format(b_start='', b_end='')}
    • +
    • ${_("{b_start}Eligible for credit:{b_end} Receive academic credit after successfully completing the course").format(**b_tag_kwargs)}
    • +
    • ${_("{b_start}Official:{b_end} Receive an instructor-signed certificate with the institution's logo").format(**b_tag_kwargs)}
    • +
    • ${_("{b_start}Easily shareable:{b_end} Add the certificate to your CV or resume, or post it directly on LinkedIn").format(**b_tag_kwargs)}
    @@ -108,9 +111,12 @@ from django.core.urlresolvers import reverse

    ${_("Benefits of a Verified Certificate")}

      -
    • ${_("{b_start}Official: {b_end}Receive an instructor-signed certificate with the institution's logo").format(b_start='', b_end='')}
    • -
    • ${_("{b_start}Easily shareable: {b_end}Add the certificate to your CV or resume, or post it directly on LinkedIn").format(b_start='', b_end='')}
    • -
    • ${_("{b_start}Motivating: {b_end}Give yourself an additional incentive to complete the course").format(b_start='', b_end='')}
    • +
    • ${_("{b_start}Official: {b_end}Receive an instructor-signed certificate with the institution's logo").format(**b_tag_kwargs)}
    • +
    • ${_("{b_start}Easily shareable: {b_end}Add the certificate to your CV or resume, or post it directly on LinkedIn").format(**b_tag_kwargs)}
    • +
    • ${_("{b_start}Motivating: {b_end}Give yourself an additional incentive to complete the course").format(**b_tag_kwargs)}
    • + % if settings.FEATURES.get('IS_EDX_DOMAIN', False): +
    • ${_("{b_start}Support our Mission: {b_end} EdX, a non-profit, relies on verified certificates to help fund free education for everyone globally").format(**b_tag_kwargs)}
    • + % endif
    diff --git a/common/test/acceptance/fixtures/discussion.py b/common/test/acceptance/fixtures/discussion.py index 25cc034f6a..8655be5c1e 100644 --- a/common/test/acceptance/fixtures/discussion.py +++ b/common/test/acceptance/fixtures/discussion.py @@ -12,7 +12,8 @@ from . import COMMENTS_STUB_URL class ContentFactory(factory.Factory): - FACTORY_FOR = dict + class Meta(object): # pylint: disable=missing-docstring + model = dict id = None user_id = "1234" username = "dummy-username" @@ -63,7 +64,8 @@ class Response(Comment): class SearchResult(factory.Factory): - FACTORY_FOR = dict + class Meta(object): # pylint: disable=missing-docstring + model = dict discussion_data = [] annotated_content_info = {} num_pages = 1 diff --git a/common/test/acceptance/fixtures/edxnotes.py b/common/test/acceptance/fixtures/edxnotes.py index e64c40abec..1bc1f928d0 100644 --- a/common/test/acceptance/fixtures/edxnotes.py +++ b/common/test/acceptance/fixtures/edxnotes.py @@ -10,7 +10,8 @@ from . import EDXNOTES_STUB_URL class Range(factory.Factory): - FACTORY_FOR = dict + class Meta(object): # pylint: disable=missing-docstring + model = dict start = "/div[1]/p[1]" end = "/div[1]/p[1]" startOffset = 0 @@ -18,7 +19,8 @@ class Range(factory.Factory): class Note(factory.Factory): - FACTORY_FOR = dict + class Meta(object): # pylint: disable=missing-docstring + model = dict user = "dummy-user" usage_id = "dummy-usage-id" course_id = "dummy-course-id" diff --git a/common/test/acceptance/pages/lms/instructor_dashboard.py b/common/test/acceptance/pages/lms/instructor_dashboard.py index fe79cd2aae..7826e3f2de 100644 --- a/common/test/acceptance/pages/lms/instructor_dashboard.py +++ b/common/test/acceptance/pages/lms/instructor_dashboard.py @@ -72,7 +72,7 @@ class InstructorDashboardPage(CoursePage): """ self.q(css='a[data-section=proctoring]').first.click() proctoring_section = ProctoringPage(self.browser) - proctoring_section.wait_for_ajax() + proctoring_section.wait_for_page() return proctoring_section @staticmethod diff --git a/common/test/acceptance/pages/studio/settings.py b/common/test/acceptance/pages/studio/settings.py index 1f5b2fc10d..9e0c3d90d7 100644 --- a/common/test/acceptance/pages/studio/settings.py +++ b/common/test/acceptance/pages/studio/settings.py @@ -4,11 +4,13 @@ Course Schedule and Details Settings page. """ from __future__ import unicode_literals from bok_choy.promise import EmptyPromise +from bok_choy.javascript import requirejs from .course_page import CoursePage from .utils import press_the_notification_button +@requirejs('js/factories/settings') class SettingsPage(CoursePage): """ Course Schedule and Details Settings page. @@ -22,6 +24,13 @@ class SettingsPage(CoursePage): def is_browser_on_page(self): return self.q(css='body.view-settings').present + def wait_for_require_js(self): + """ + Wait for require-js to load javascript files. + """ + if hasattr(self, 'wait_for_js'): + self.wait_for_js() # pylint: disable=no-member + def refresh_and_wait_for_load(self): """ Refresh the page and wait for all resources to load. @@ -182,4 +191,5 @@ class SettingsPage(CoursePage): lambda: self.q(css='body.view-settings').present, 'Page is refreshed' ).fulfill() + self.wait_for_require_js() self.wait_for_ajax() diff --git a/common/test/acceptance/pages/studio/settings_certificates.py b/common/test/acceptance/pages/studio/settings_certificates.py index a5d40941bc..c285193d2a 100644 --- a/common/test/acceptance/pages/studio/settings_certificates.py +++ b/common/test/acceptance/pages/studio/settings_certificates.py @@ -12,6 +12,7 @@ import os from bok_choy.promise import EmptyPromise from .course_page import CoursePage +from common.test.acceptance.tests.helpers import disable_animations class CertificatesPage(CoursePage): @@ -138,8 +139,10 @@ class CertificatesPage(CoursePage): """ Clicks the main action presented by the prompt (such as 'Delete') """ + disable_animations(self) self.wait_for_confirmation_prompt() - self.q(css='button.action-primary').first.click() + self.q(css='.prompt button.action-primary').first.click() + self.wait_for_element_invisibility('.prompt', 'wait for pop up to disappear') self.wait_for_ajax() @@ -263,7 +266,7 @@ class Certificate(object): Returns whether or not the certificate delete icon is present. """ EmptyPromise( - lambda: self.find_css('.actions .delete').present, + lambda: self.find_css('.actions .delete.action-icon').present, 'Certificate delete button is displayed' ).fulfill() @@ -323,8 +326,7 @@ class Certificate(object): Remove the first (possibly the only) certificate from the set """ self.wait_for_certificate_delete_button() - self.find_css('.actions .delete').first.click() - self.page.wait_for_ajax() + self.find_css('.actions .delete.action-icon').first.click() class Signatory(object): diff --git a/common/test/acceptance/pages/studio/utils.py b/common/test/acceptance/pages/studio/utils.py index cd2607af35..f3c28712dd 100644 --- a/common/test/acceptance/pages/studio/utils.py +++ b/common/test/acceptance/pages/studio/utils.py @@ -4,6 +4,7 @@ Utility methods useful for Studio page tests. from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from bok_choy.javascript import js_defined +from bok_choy.promise import EmptyPromise from ..common.utils import click_css, wait_for_notification @@ -199,3 +200,17 @@ def verify_ordering(test_class, page, expected_orderings): blocks_checked.add(expected) break test_class.assertEqual(len(blocks_checked), len(xblocks)) + + +def click_studio_help(page): + """Click the Studio help link in the page footer.""" + page.q(css='.cta-show-sock').click() + EmptyPromise( + lambda: page.q(css='.support .list-actions a').results[0].text != '', + 'Support section opened' + ).fulfill() + + +def studio_help_links(page): + """Return the list of Studio help links in the page footer.""" + return page.q(css='.support .list-actions a').results diff --git a/common/test/acceptance/tests/lms/test_lms.py b/common/test/acceptance/tests/lms/test_lms.py index cd9cedd386..a0ee88c801 100644 --- a/common/test/acceptance/tests/lms/test_lms.py +++ b/common/test/acceptance/tests/lms/test_lms.py @@ -480,7 +480,7 @@ class PayAndVerifyTest(EventsTestMixin, UniqueCourseTest): self.assertEqual(enrollment_mode, 'verified') -@attr('shard_5') +@attr('shard_1') class CourseWikiTest(UniqueCourseTest): """ Tests that verify the course wiki. @@ -534,7 +534,7 @@ class CourseWikiTest(UniqueCourseTest): self.assertEqual(content, actual_content) -@attr('shard_5') +@attr('shard_1') class HighLevelTabTest(UniqueCourseTest): """ Tests that verify each of the high-level tabs available within a course. @@ -720,7 +720,7 @@ class PDFTextBooksTabTest(UniqueCourseTest): self.tab_nav.go_to_tab("PDF Book {}".format(i)) -@attr('shard_5') +@attr('shard_1') class VideoTest(UniqueCourseTest): """ Navigate to a video in the courseware and play it. @@ -791,7 +791,7 @@ class VideoTest(UniqueCourseTest): self.assertGreaterEqual(self.video.duration, self.video.elapsed_time) -@attr('shard_5') +@attr('shard_1') class VisibleToStaffOnlyTest(UniqueCourseTest): """ Tests that content with visible_to_staff_only set to True cannot be viewed by students. @@ -876,7 +876,7 @@ class VisibleToStaffOnlyTest(UniqueCourseTest): self.assertEqual(["Html Child in visible unit"], self.course_nav.sequence_items) -@attr('shard_5') +@attr('shard_1') class TooltipTest(UniqueCourseTest): """ Tests that tooltips are displayed @@ -921,7 +921,7 @@ class TooltipTest(UniqueCourseTest): self.assertTrue(self.courseware_page.tooltips_displayed()) -@attr('shard_5') +@attr('shard_1') class PreRequisiteCourseTest(UniqueCourseTest): """ Tests that pre-requisite course messages are displayed @@ -1006,7 +1006,7 @@ class PreRequisiteCourseTest(UniqueCourseTest): self.settings_page.save_changes() -@attr('shard_5') +@attr('shard_1') class ProblemExecutionTest(UniqueCourseTest): """ Tests of problems. @@ -1085,7 +1085,7 @@ class ProblemExecutionTest(UniqueCourseTest): self.assertFalse(problem_page.is_correct()) -@attr('shard_5') +@attr('shard_1') class EntranceExamTest(UniqueCourseTest): """ Tests that course has an entrance exam. @@ -1156,7 +1156,7 @@ class EntranceExamTest(UniqueCourseTest): )) -@attr('shard_5') +@attr('shard_1') class NotLiveRedirectTest(UniqueCourseTest): """ Test that a banner is shown when the user is redirected to diff --git a/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py b/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py index ea9ab01afc..b3e7c4d852 100644 --- a/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py +++ b/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py @@ -44,7 +44,7 @@ class BaseInstructorDashboardTest(EventsTestMixin, UniqueCourseTest): return instructor_dashboard_page -@attr('shard_5') +@attr('shard_1') class AutoEnrollmentWithCSVTest(BaseInstructorDashboardTest): """ End-to-end tests for Auto-Registration and enrollment functionality via CSV file. @@ -209,7 +209,7 @@ class ProctoredExamsTest(BaseInstructorDashboardTest): self._auto_auth("STAFF_TESTER", "staff101@example.com", True) self.course_outline.visit() - #open the exam settings to make it a proctored exam. + # open the exam settings to make it a proctored exam. self.course_outline.open_exam_settings_dialog() self.course_outline.make_exam_timed() time.sleep(2) # Wait for 2 seconds to save the settings. @@ -222,14 +222,12 @@ class ProctoredExamsTest(BaseInstructorDashboardTest): # Start the proctored exam. self.courseware_page.start_timed_exam() - @flaky # TODO fix this, see SOL-1183 def test_can_add_remove_allowance(self): """ Make sure that allowances can be added and removed. """ - - # Given that an exam has been configured to be a proctored exam. - self._create_a_proctored_exam_and_attempt() + # Given that an exam has been configured to be a timed exam. + self._create_a_timed_exam_and_attempt() # When I log in as an instructor, self.log_in_as_instructor() @@ -267,7 +265,7 @@ class ProctoredExamsTest(BaseInstructorDashboardTest): self.assertFalse(exam_attempts_section.is_student_attempt_visible) -@attr('shard_5') +@attr('shard_1') class EntranceExamGradeTest(BaseInstructorDashboardTest): """ Tests for Entrance exam specific student grading tasks. @@ -559,7 +557,7 @@ class DataDownloadsTest(BaseInstructorDashboardTest): self.verify_report_download(report_name) -@attr('shard_5') +@attr('shard_1') class CertificatesTest(BaseInstructorDashboardTest): """ Tests for Certificates functionality on instructor dashboard. diff --git a/common/test/acceptance/tests/studio/test_studio_help.py b/common/test/acceptance/tests/studio/test_studio_help.py new file mode 100644 index 0000000000..4561019396 --- /dev/null +++ b/common/test/acceptance/tests/studio/test_studio_help.py @@ -0,0 +1,46 @@ +""" +Test the Studio help links. +""" + +from .base_studio_test import StudioCourseTest +from ...pages.studio.index import DashboardPage +from ...pages.studio.utils import click_studio_help, studio_help_links + + +class StudioHelpTest(StudioCourseTest): + """Tests for Studio help.""" + + def test_studio_help_links(self): + """Test that the help links are present and have the correct content.""" + page = DashboardPage(self.browser) + page.visit() + click_studio_help(page) + links = studio_help_links(page) + expected_links = [{ + 'href': u'http://docs.edx.org/', + 'text': u'edX Documentation', + 'sr_text': u'Access documentation on http://docs.edx.org' + }, { + 'href': u'https://open.edx.org/', + 'text': u'Open edX Portal', + 'sr_text': u'Access the Open edX Portal' + }, { + 'href': u'https://www.edx.org/course/overview-creating-edx-course-edx-edx101#.VO4eaLPF-n1', + 'text': u'Enroll in edX101', + 'sr_text': u'Enroll in edX101: Overview of Creating an edX Course' + }, { + 'href': u'https://www.edx.org/course/creating-course-edx-studio-edx-studiox', + 'text': u'Enroll in StudioX', + 'sr_text': u'Enroll in StudioX: Creating a Course with edX Studio' + }, { + 'href': u'mailto:partner-support@example.com', + 'text': u'Contact Us', + 'sr_text': 'Send an email to partner-support@example.com' + }] + for expected, actual in zip(expected_links, links): + self.assertEqual(expected['href'], actual.get_attribute('href')) + self.assertEqual(expected['text'], actual.text) + self.assertEqual( + expected['sr_text'], + actual.find_element_by_xpath('following-sibling::span').text + ) diff --git a/common/test/acceptance/tests/studio/test_studio_library.py b/common/test/acceptance/tests/studio/test_studio_library.py index ca3f0a0150..3094499285 100644 --- a/common/test/acceptance/tests/studio/test_studio_library.py +++ b/common/test/acceptance/tests/studio/test_studio_library.py @@ -186,7 +186,7 @@ class LibraryEditPageTest(StudioLibraryTest): self.assertIn("Checkboxes", problem_block.name) -@attr('shard_5') +@attr('shard_2') @ddt class LibraryNavigationTest(StudioLibraryTest): """ diff --git a/common/test/acceptance/tests/studio/test_studio_settings_certificates.py b/common/test/acceptance/tests/studio/test_studio_settings_certificates.py index ea879b1f4b..d4b0dd7a47 100644 --- a/common/test/acceptance/tests/studio/test_studio_settings_certificates.py +++ b/common/test/acceptance/tests/studio/test_studio_settings_certificates.py @@ -107,7 +107,6 @@ class CertificatesTest(StudioCourseTest): self.assertIn("Updated Course Title Override 2", certificate.course_title) - @flaky # TODO fix this, see SOL-1199 def test_can_delete_certificate(self): """ Scenario: Ensure that the user can delete certificate. diff --git a/common/test/acceptance/tests/studio/test_studio_settings_details.py b/common/test/acceptance/tests/studio/test_studio_settings_details.py index 5faa002e43..0e68e69708 100644 --- a/common/test/acceptance/tests/studio/test_studio_settings_details.py +++ b/common/test/acceptance/tests/studio/test_studio_settings_details.py @@ -40,7 +40,6 @@ class SettingsMilestonesTest(StudioCourseTest): self.assertTrue(self.settings_detail.pre_requisite_course_options) - @skip # TODO: fix this. SOL-449 def test_prerequisite_course_save_successfully(self): """ Scenario: Selecting course from Pre-Requisite course drop down save the selected course as pre-requisite diff --git a/conf/locale/ar/LC_MESSAGES/django.mo b/conf/locale/ar/LC_MESSAGES/django.mo index 61b60b64d6..8e54e578d6 100644 Binary files a/conf/locale/ar/LC_MESSAGES/django.mo and b/conf/locale/ar/LC_MESSAGES/django.mo differ diff --git a/conf/locale/ar/LC_MESSAGES/django.po b/conf/locale/ar/LC_MESSAGES/django.po index 35503bbd13..b7e30196b1 100644 --- a/conf/locale/ar/LC_MESSAGES/django.po +++ b/conf/locale/ar/LC_MESSAGES/django.po @@ -131,7 +131,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:16+0000\n" +"POT-Creation-Date: 2015-09-18 13:23+0000\n" "PO-Revision-Date: 2015-08-12 08:13+0000\n" "Last-Translator: Ahmed Jazzar \n" "Language-Team: Arabic (http://www.transifex.com/open-edx/edx-platform/language/ar/)\n" @@ -3751,25 +3751,16 @@ msgid "Request user's username" msgstr "يُرجى طلب اسم المستخدم الخاص بالمستخدم" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's username. You must also set Open in New " -"Page to True to get the user's information." +msgid "Select True to request the user's username." msgstr "" -"يُرجى اختيار \"صحيح\" لطلب اسم المستخدم الخاص بالمستخدم، ويجب أيضًا ضبط " -"خاصية \"فتح في صفحة جديدة\" على خيار \"صحيح\" للحصول على معلومات المستخدم." #: common/lib/xmodule/xmodule/lti_module.py msgid "Request user's email" msgstr "يُرجى طلب البريد الإلكتروني للمستخدم" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's email address. You must also set Open in " -"New Page to True to get the user's information." +msgid "Select True to request the user's email address." msgstr "" -"يُرجى اختيار \"صحيح\" لطلب عنوان البريد الإلكتروني الخاص بالمستخدم. ويجب " -"أيضًا ضبط خاصية \"فتح في صفحة جديدة\" على خيار \"صحيح\" أيضًا للحصول على " -"معلومات المستخدم." #: common/lib/xmodule/xmodule/lti_module.py msgid "LTI Application Information" @@ -8405,10 +8396,6 @@ msgstr "" msgid "The supplied topic id {topic_id} is not valid" msgstr "الرقم التعريفي الذي جرى توفيره حول الموضوع {topic_id} غير صالح." -#: lms/djangoapps/teams/views.py -msgid "Error connecting to elasticsearch" -msgstr "" - #. Translators: 'ordering' is a string describing a way #. of ordering a list. For example, {ordering} may be #. 'name', indicating that the user wants to sort the @@ -12513,8 +12500,6 @@ msgstr "إرسال تغريدة حول أنّك سجَّلت في هذا الم msgid "Email someone to say you've registered for this course" msgstr "مراسلة شخص ما عبر البريد الإلكتروني لإخباره أنّك سجّلت في هذا المساق " -#. Translators: This text will be automatically posted to the student's -#. Twitter account. {url} should appear at the end of the text. #: lms/templates/courseware/course_about.html msgid "I just registered for {number} {title} through {account}: {url}" msgstr "سجّلتُ لتوّي في {number} {title} من خلال {url} :{account}" @@ -13018,6 +13003,8 @@ msgstr "سوف يفتح ملف PDF في نافذة متصفح جديدة أو ت msgid "Download Your Certificate" msgstr "تنزيل شهادتك" +#. Translators: This message appears to users when the system is processessing +#. course certificates, which can take a few hours. #: lms/templates/courseware/progress.html msgid "We're working on it..." msgstr "جاري العمل على ذلك..." @@ -17689,9 +17676,8 @@ msgstr "" msgid "Required Information to Create a re-run of a course" msgstr "المعلومات المطلوبة لإنشاء تشغيل ثانٍ لمساق ما" -#. Translators: This is an example name for a new course, seen when filling -#. out -#. the form to create a new course. +#. Translators: This is an example name for a new course, seen when +#. filling out the form to create a new course. #: cms/templates/course-create-rerun.html cms/templates/index.html msgid "e.g. Introduction to Computer Science" msgstr "مثلًا، مقدّمة لعلوم الكمبيوتر" @@ -19064,8 +19050,8 @@ msgid "Library Name" msgstr "اسم المكتبة" #. Translators: This is an example name for a new content library, seen when -#. filling out the form to create a new library. (A library is a collection of -#. content or problems.) +#. filling out the form to create a new library. +#. (A library is a collection of content or problems.) #: cms/templates/index.html msgid "e.g. Computer Science Problems" msgstr "مثلًا، مسائل علوم الكمبيوتر" @@ -19088,8 +19074,9 @@ msgstr "رمز المكتبة" #. Translators: This is an example for the "code" used to identify a library, #. seen when filling out the form to create a new library. This example is -#. short for "Computer Science Problems". The example number may contain -#. letters but must not contain spaces. +#. short +#. for "Computer Science Problems". The example number may contain letters +#. but must not contain spaces. #: cms/templates/index.html msgid "e.g. CSPROB" msgstr "مثلًا، CSPROB" @@ -19118,10 +19105,11 @@ msgstr "تشغيل المساق:" msgid "This course run is currently being created." msgstr "يجري حاليًّا إنشاء تشغيل هذا المساق." -#. Translators: This is a status message, used to inform the user of what the -#. system is doing. This status means that the user has requested to re-run an -#. existing course, and the system is currently in the process of duplicating -#. and configuring the existing course so that it can be re-run. +#. Translators: This is a status message, used to inform the user of +#. what the system is doing. This status means that the user has +#. requested to re-run an existing course, and the system is currently +#. in the process of duplicating and configuring the existing course +#. so that it can be re-run. #: cms/templates/index.html msgid "Configuring as re-run" msgstr "الضبط كتشغيل ثانٍ" @@ -19136,6 +19124,18 @@ msgstr "" "هذه الصفحة أو {link_start}إعادة فتحها{link_end} لتحديث لائحة المساقات. " "وسيتطلّب المساق الجديد ضبط بعض الإعدادات يدويًّا." +#. Translators: This is a status message for the course re-runs feature. +#. When a course admin indicates that a course should be re-run, the system +#. needs to process the request and prepare the new course. The status of +#. the process will follow this text. +#: cms/templates/index.html +msgid "This re-run processing status:" +msgstr "" + +#: cms/templates/index.html +msgid "Configuration Error" +msgstr "" + #: cms/templates/index.html msgid "" "A system error occurred while your course was being processed. Please go to " @@ -20153,7 +20153,7 @@ msgstr "يجب أن تزيد عن درجة النجاح في المساق أو #: cms/templates/settings_graders.html msgid "Grading Rules & Policies" -msgstr "سياسات قواعد & التقييم" +msgstr "سياسات وقواعد التقييم" #: cms/templates/settings_graders.html msgid "Deadlines, requirements, and logistics around grading student work" diff --git a/conf/locale/ar/LC_MESSAGES/djangojs.mo b/conf/locale/ar/LC_MESSAGES/djangojs.mo index 872abf7237..aa3ca60432 100644 Binary files a/conf/locale/ar/LC_MESSAGES/djangojs.mo and b/conf/locale/ar/LC_MESSAGES/djangojs.mo differ diff --git a/conf/locale/ar/LC_MESSAGES/djangojs.po b/conf/locale/ar/LC_MESSAGES/djangojs.po index 30b5b226b6..7dcd9aff6d 100644 --- a/conf/locale/ar/LC_MESSAGES/djangojs.po +++ b/conf/locale/ar/LC_MESSAGES/djangojs.po @@ -81,7 +81,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:15+0000\n" +"POT-Creation-Date: 2015-09-18 13:22+0000\n" "PO-Revision-Date: 2015-09-11 12:17+0000\n" "Last-Translator: Sarina Canelake \n" "Language-Team: Arabic (http://www.transifex.com/open-edx/edx-platform/language/ar/)\n" diff --git a/conf/locale/eo/LC_MESSAGES/django.mo b/conf/locale/eo/LC_MESSAGES/django.mo index ef27e9e07e..6c8f5b9885 100644 Binary files a/conf/locale/eo/LC_MESSAGES/django.mo and b/conf/locale/eo/LC_MESSAGES/django.mo differ diff --git a/conf/locale/eo/LC_MESSAGES/django.po b/conf/locale/eo/LC_MESSAGES/django.po index 847b04c14a..34dbcd2304 100644 --- a/conf/locale/eo/LC_MESSAGES/django.po +++ b/conf/locale/eo/LC_MESSAGES/django.po @@ -37,8 +37,8 @@ msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:47+0000\n" -"PO-Revision-Date: 2015-09-11 12:47:23.251524\n" +"POT-Creation-Date: 2015-09-18 13:39+0000\n" +"PO-Revision-Date: 2015-09-18 13:39:23.492978\n" "Last-Translator: \n" "Language-Team: openedx-translation \n" "MIME-Version: 1.0\n" @@ -4154,24 +4154,20 @@ msgid "Request user's username" msgstr "Réqüést üsér's üsérnämé Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σ#" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's username. You must also set Open in New " -"Page to True to get the user's information." +msgid "Select True to request the user's username." msgstr "" -"Séléçt Trüé tö réqüést thé üsér's üsérnämé. Ýöü müst älsö sét Öpén ïn Néw " -"Pägé tö Trüé tö gét thé üsér's ïnförmätïön. Ⱡ'σяєм ιρѕυм ∂#" +"Séléçt Trüé tö réqüést thé üsér's üsérnämé. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, " +"¢σηѕє¢тєтυя #" #: common/lib/xmodule/xmodule/lti_module.py msgid "Request user's email" msgstr "Réqüést üsér's émäïl Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, #" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's email address. You must also set Open in " -"New Page to True to get the user's information." +msgid "Select True to request the user's email address." msgstr "" -"Séléçt Trüé tö réqüést thé üsér's émäïl äddréss. Ýöü müst älsö sét Öpén ïn " -"Néw Pägé tö Trüé tö gét thé üsér's ïnförmätïön. Ⱡ'σяєм ιρѕ#" +"Séléçt Trüé tö réqüést thé üsér's émäïl äddréss. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт " +"αмєт, ¢σηѕє¢тєтυя α#" #: common/lib/xmodule/xmodule/lti_module.py msgid "LTI Application Information" @@ -9566,11 +9562,6 @@ msgstr "" "Thé süpplïéd töpïç ïd {topic_id} ïs nöt välïd Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, " "¢σηѕє¢тєтυя#" -#: lms/djangoapps/teams/views.py -msgid "Error connecting to elasticsearch" -msgstr "" -"Érrör çönnéçtïng tö élästïçséärçh Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тє#" - #. Translators: 'ordering' is a string describing a way #. of ordering a list. For example, {ordering} may be #. 'name', indicating that the user wants to sort the @@ -14176,8 +14167,6 @@ msgstr "" "Émäïl söméöné tö säý ýöü'vé régïstéréd för thïs çöürsé Ⱡ'σяєм ιρѕυм ∂σłσя " "ѕιт αмєт, ¢σηѕє¢тєтυя α#" -#. Translators: This text will be automatically posted to the student's -#. Twitter account. {url} should appear at the end of the text. #: lms/templates/courseware/course_about.html msgid "I just registered for {number} {title} through {account}: {url}" msgstr "" @@ -14762,6 +14751,8 @@ msgstr "" msgid "Download Your Certificate" msgstr "Döwnlöäd Ýöür Çértïfïçäté Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕ#" +#. Translators: This message appears to users when the system is processessing +#. course certificates, which can take a few hours. #: lms/templates/courseware/progress.html msgid "We're working on it..." msgstr "Wé'ré wörkïng ön ït... Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢#" @@ -20219,9 +20210,8 @@ msgstr "" "Réqüïréd Ìnförmätïön tö Çréäté ä ré-rün öf ä çöürsé Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт " "αмєт, ¢σηѕє¢тєтυя α#" -#. Translators: This is an example name for a new course, seen when filling -#. out -#. the form to create a new course. +#. Translators: This is an example name for a new course, seen when +#. filling out the form to create a new course. #: cms/templates/course-create-rerun.html cms/templates/index.html msgid "e.g. Introduction to Computer Science" msgstr "" @@ -21883,8 +21873,8 @@ msgid "Library Name" msgstr "Lïßrärý Nämé Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#" #. Translators: This is an example name for a new content library, seen when -#. filling out the form to create a new library. (A library is a collection of -#. content or problems.) +#. filling out the form to create a new library. +#. (A library is a collection of content or problems.) #: cms/templates/index.html msgid "e.g. Computer Science Problems" msgstr "é.g. Çömpütér Sçïénçé Prößléms Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢т#" @@ -21911,8 +21901,9 @@ msgstr "Lïßrärý Çödé Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#" #. Translators: This is an example for the "code" used to identify a library, #. seen when filling out the form to create a new library. This example is -#. short for "Computer Science Problems". The example number may contain -#. letters but must not contain spaces. +#. short +#. for "Computer Science Problems". The example number may contain letters +#. but must not contain spaces. #: cms/templates/index.html msgid "e.g. CSPROB" msgstr "é.g. ÇSPRÖB Ⱡ'σяєм ιρѕυм ∂σłσя #" @@ -21945,10 +21936,11 @@ msgstr "" "Thïs çöürsé rün ïs çürréntlý ßéïng çréätéd. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, " "¢σηѕє¢тєтυя #" -#. Translators: This is a status message, used to inform the user of what the -#. system is doing. This status means that the user has requested to re-run an -#. existing course, and the system is currently in the process of duplicating -#. and configuring the existing course so that it can be re-run. +#. Translators: This is a status message, used to inform the user of +#. what the system is doing. This status means that the user has +#. requested to re-run an existing course, and the system is currently +#. in the process of duplicating and configuring the existing course +#. so that it can be re-run. #: cms/templates/index.html msgid "Configuring as re-run" msgstr "Çönfïgürïng äs ré-rün Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, #" @@ -21968,6 +21960,18 @@ msgstr "" "αυтє ιяυяє ∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє ¢ιłłυм ∂σłσяє єυ " "ƒυgιαт ηυłłα ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт ηση ρяσι∂єηт,#" +#. Translators: This is a status message for the course re-runs feature. +#. When a course admin indicates that a course should be re-run, the system +#. needs to process the request and prepare the new course. The status of +#. the process will follow this text. +#: cms/templates/index.html +msgid "This re-run processing status:" +msgstr "Thïs ré-rün pröçéssïng stätüs: Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢т#" + +#: cms/templates/index.html +msgid "Configuration Error" +msgstr "Çönfïgürätïön Érrör Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт,#" + #: cms/templates/index.html msgid "" "A system error occurred while your course was being processed. Please go to " diff --git a/conf/locale/eo/LC_MESSAGES/djangojs.mo b/conf/locale/eo/LC_MESSAGES/djangojs.mo index ce310ce200..68ba09c6cf 100644 Binary files a/conf/locale/eo/LC_MESSAGES/djangojs.mo and b/conf/locale/eo/LC_MESSAGES/djangojs.mo differ diff --git a/conf/locale/eo/LC_MESSAGES/djangojs.po b/conf/locale/eo/LC_MESSAGES/djangojs.po index f6e1a17759..2995eb25f5 100644 --- a/conf/locale/eo/LC_MESSAGES/djangojs.po +++ b/conf/locale/eo/LC_MESSAGES/djangojs.po @@ -26,8 +26,8 @@ msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:46+0000\n" -"PO-Revision-Date: 2015-09-11 12:47:23.562016\n" +"POT-Creation-Date: 2015-09-18 13:38+0000\n" +"PO-Revision-Date: 2015-09-18 13:39:23.855088\n" "Last-Translator: \n" "Language-Team: openedx-translation \n" "MIME-Version: 1.0\n" diff --git a/conf/locale/es_419/LC_MESSAGES/django.mo b/conf/locale/es_419/LC_MESSAGES/django.mo index edba37db3d..2e95c65ebd 100644 Binary files a/conf/locale/es_419/LC_MESSAGES/django.mo and b/conf/locale/es_419/LC_MESSAGES/django.mo differ diff --git a/conf/locale/es_419/LC_MESSAGES/django.po b/conf/locale/es_419/LC_MESSAGES/django.po index eb12a0f477..8b3397c2bf 100644 --- a/conf/locale/es_419/LC_MESSAGES/django.po +++ b/conf/locale/es_419/LC_MESSAGES/django.po @@ -172,7 +172,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:16+0000\n" +"POT-Creation-Date: 2015-09-18 13:23+0000\n" "PO-Revision-Date: 2015-06-29 17:10+0000\n" "Last-Translator: Cristian Salamea \n" "Language-Team: Spanish (Latin America) (http://www.transifex.com/open-edx/edx-platform/language/es_419/)\n" @@ -3829,26 +3829,16 @@ msgid "Request user's username" msgstr "Solicite el nombre del usuario" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's username. You must also set Open in New " -"Page to True to get the user's information." +msgid "Select True to request the user's username." msgstr "" -"Seleccione True para solicitar el nombre de usuario al usuario. También " -"puede configurar La apertura en una nueva página en True para obtener la " -"información del usuario." #: common/lib/xmodule/xmodule/lti_module.py msgid "Request user's email" msgstr "Solicite la dirección de correo del usuario" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's email address. You must also set Open in " -"New Page to True to get the user's information." +msgid "Select True to request the user's email address." msgstr "" -"Seleccione True para solicitar el correo electrónico del usuario. También " -"puede configurar la apertura en una nueva página en True para obtener la " -"información del usuario." #: common/lib/xmodule/xmodule/lti_module.py msgid "LTI Application Information" @@ -8628,10 +8618,6 @@ msgstr "" msgid "The supplied topic id {topic_id} is not valid" msgstr "El ID de tema proporcionado {topic_id} no es válido" -#: lms/djangoapps/teams/views.py -msgid "Error connecting to elasticsearch" -msgstr "" - #. Translators: 'ordering' is a string describing a way #. of ordering a list. For example, {ordering} may be #. 'name', indicating that the user wants to sort the @@ -12818,8 +12804,6 @@ msgstr "Publica que te has registrado en este curso" msgid "Email someone to say you've registered for this course" msgstr "Envía un correo a tus amigos que te has registrado en este curso" -#. Translators: This text will be automatically posted to the student's -#. Twitter account. {url} should appear at the end of the text. #: lms/templates/courseware/course_about.html msgid "I just registered for {number} {title} through {account}: {url}" msgstr "" @@ -13337,6 +13321,8 @@ msgstr "El PDF se abrirá en una nueva ventana o pestaña del navegador." msgid "Download Your Certificate" msgstr "Descargar Tu certificaddo" +#. Translators: This message appears to users when the system is processessing +#. course certificates, which can take a few hours. #: lms/templates/courseware/progress.html msgid "We're working on it..." msgstr "Estamos trabajando en eso..." @@ -18149,9 +18135,8 @@ msgstr "" msgid "Required Information to Create a re-run of a course" msgstr "Información requerida para crear una reapertura del curso" -#. Translators: This is an example name for a new course, seen when filling -#. out -#. the form to create a new course. +#. Translators: This is an example name for a new course, seen when +#. filling out the form to create a new course. #: cms/templates/course-create-rerun.html cms/templates/index.html msgid "e.g. Introduction to Computer Science" msgstr "ej.: Introducción a las Ciencias de la Computación" @@ -19568,8 +19553,8 @@ msgid "Library Name" msgstr "Nombre" #. Translators: This is an example name for a new content library, seen when -#. filling out the form to create a new library. (A library is a collection of -#. content or problems.) +#. filling out the form to create a new library. +#. (A library is a collection of content or problems.) #: cms/templates/index.html msgid "e.g. Computer Science Problems" msgstr "ej.: Problemas de Ciencias de la Computación" @@ -19592,8 +19577,9 @@ msgstr "Código" #. Translators: This is an example for the "code" used to identify a library, #. seen when filling out the form to create a new library. This example is -#. short for "Computer Science Problems". The example number may contain -#. letters but must not contain spaces. +#. short +#. for "Computer Science Problems". The example number may contain letters +#. but must not contain spaces. #: cms/templates/index.html msgid "e.g. CSPROB" msgstr "ej: PROBCC" @@ -19622,10 +19608,11 @@ msgstr "Impartición del Curso:" msgid "This course run is currently being created." msgstr "Esta instancia del curso está actualmente siendo creada." -#. Translators: This is a status message, used to inform the user of what the -#. system is doing. This status means that the user has requested to re-run an -#. existing course, and the system is currently in the process of duplicating -#. and configuring the existing course so that it can be re-run. +#. Translators: This is a status message, used to inform the user of +#. what the system is doing. This status means that the user has +#. requested to re-run an existing course, and the system is currently +#. in the process of duplicating and configuring the existing course +#. so that it can be re-run. #: cms/templates/index.html msgid "Configuring as re-run" msgstr "Está siendo configurado para su reutilización." @@ -19640,6 +19627,18 @@ msgstr "" " esta página o {link_start}recarguela{link_end} para actualizar la lista de " "cursos. El nuevo curso necesitará alguna configuración manual." +#. Translators: This is a status message for the course re-runs feature. +#. When a course admin indicates that a course should be re-run, the system +#. needs to process the request and prepare the new course. The status of +#. the process will follow this text. +#: cms/templates/index.html +msgid "This re-run processing status:" +msgstr "" + +#: cms/templates/index.html +msgid "Configuration Error" +msgstr "" + #: cms/templates/index.html msgid "" "A system error occurred while your course was being processed. Please go to " diff --git a/conf/locale/es_419/LC_MESSAGES/djangojs.mo b/conf/locale/es_419/LC_MESSAGES/djangojs.mo index 3234268282..bea5eb8531 100644 Binary files a/conf/locale/es_419/LC_MESSAGES/djangojs.mo and b/conf/locale/es_419/LC_MESSAGES/djangojs.mo differ diff --git a/conf/locale/es_419/LC_MESSAGES/djangojs.po b/conf/locale/es_419/LC_MESSAGES/djangojs.po index b512fd14c7..ea4967845a 100644 --- a/conf/locale/es_419/LC_MESSAGES/djangojs.po +++ b/conf/locale/es_419/LC_MESSAGES/djangojs.po @@ -99,7 +99,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:15+0000\n" +"POT-Creation-Date: 2015-09-18 13:22+0000\n" "PO-Revision-Date: 2015-09-11 12:17+0000\n" "Last-Translator: Sarina Canelake \n" "Language-Team: Spanish (Latin America) (http://www.transifex.com/open-edx/edx-platform/language/es_419/)\n" diff --git a/conf/locale/fr/LC_MESSAGES/django.mo b/conf/locale/fr/LC_MESSAGES/django.mo index 4bb5fd4f7b..aa6677b8a7 100644 Binary files a/conf/locale/fr/LC_MESSAGES/django.mo and b/conf/locale/fr/LC_MESSAGES/django.mo differ diff --git a/conf/locale/fr/LC_MESSAGES/django.po b/conf/locale/fr/LC_MESSAGES/django.po index ec15d74683..33d13a197f 100644 --- a/conf/locale/fr/LC_MESSAGES/django.po +++ b/conf/locale/fr/LC_MESSAGES/django.po @@ -51,7 +51,7 @@ # Steven BERNARD , 2013 # Thomas Sihapanya , 2015 # Toreador , 2014 -# Xavier Antoviaque , 2014 +# Xavier Antoviaque , 2014-2015 # PETIT Yannick , 2013 # yepelboin , 2014-2015 # #-#-#-#-# django-studio.po (edx-platform) #-#-#-#-# @@ -178,7 +178,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:16+0000\n" +"POT-Creation-Date: 2015-09-18 13:23+0000\n" "PO-Revision-Date: 2015-06-19 17:16+0000\n" "Last-Translator: Xavier Antoviaque \n" "Language-Team: French (http://www.transifex.com/open-edx/edx-platform/language/fr/)\n" @@ -3704,9 +3704,7 @@ msgid "Request user's username" msgstr "Demander le nom de l'utilisateur" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's username. You must also set Open in New " -"Page to True to get the user's information." +msgid "Select True to request the user's username." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -3714,9 +3712,7 @@ msgid "Request user's email" msgstr "" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's email address. You must also set Open in " -"New Page to True to get the user's information." +msgid "Select True to request the user's email address." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -8142,10 +8138,6 @@ msgstr "" msgid "The supplied topic id {topic_id} is not valid" msgstr "" -#: lms/djangoapps/teams/views.py -msgid "Error connecting to elasticsearch" -msgstr "" - #. Translators: 'ordering' is a string describing a way #. of ordering a list. For example, {ordering} may be #. 'name', indicating that the user wants to sort the @@ -12220,8 +12212,6 @@ msgstr "Tweetez que vous êtes inscrits pour ce cours" msgid "Email someone to say you've registered for this course" msgstr "Envoyez par e-mail que vous êtes inscrits à ce cours" -#. Translators: This text will be automatically posted to the student's -#. Twitter account. {url} should appear at the end of the text. #: lms/templates/courseware/course_about.html msgid "I just registered for {number} {title} through {account}: {url}" msgstr "Je viens de m'inscrire pour {number} {title} via {account}: {url}" @@ -12726,6 +12716,8 @@ msgstr "" msgid "Download Your Certificate" msgstr "Télécharger votre certificat" +#. Translators: This message appears to users when the system is processessing +#. course certificates, which can take a few hours. #: lms/templates/courseware/progress.html msgid "We're working on it..." msgstr "Nous y travaillons" @@ -17322,9 +17314,8 @@ msgstr "" msgid "Required Information to Create a re-run of a course" msgstr "Informations requises pour créer une nouvelle session d'un cours" -#. Translators: This is an example name for a new course, seen when filling -#. out -#. the form to create a new course. +#. Translators: This is an example name for a new course, seen when +#. filling out the form to create a new course. #: cms/templates/course-create-rerun.html cms/templates/index.html msgid "e.g. Introduction to Computer Science" msgstr "par exemple, Introduction à l'Informatique" @@ -18651,8 +18642,8 @@ msgid "Library Name" msgstr "Nom de la Bibliothèque" #. Translators: This is an example name for a new content library, seen when -#. filling out the form to create a new library. (A library is a collection of -#. content or problems.) +#. filling out the form to create a new library. +#. (A library is a collection of content or problems.) #: cms/templates/index.html msgid "e.g. Computer Science Problems" msgstr "exemple: Exercices d'introduction à l'Informatique" @@ -18675,8 +18666,9 @@ msgstr "Code de la bibliothèque" #. Translators: This is an example for the "code" used to identify a library, #. seen when filling out the form to create a new library. This example is -#. short for "Computer Science Problems". The example number may contain -#. letters but must not contain spaces. +#. short +#. for "Computer Science Problems". The example number may contain letters +#. but must not contain spaces. #: cms/templates/index.html msgid "e.g. CSPROB" msgstr "MATHPROB par exemple" @@ -18705,10 +18697,11 @@ msgstr "Cours dispensé:" msgid "This course run is currently being created." msgstr "Création de cette session de cours..." -#. Translators: This is a status message, used to inform the user of what the -#. system is doing. This status means that the user has requested to re-run an -#. existing course, and the system is currently in the process of duplicating -#. and configuring the existing course so that it can be re-run. +#. Translators: This is a status message, used to inform the user of +#. what the system is doing. This status means that the user has +#. requested to re-run an existing course, and the system is currently +#. in the process of duplicating and configuring the existing course +#. so that it can be re-run. #: cms/templates/index.html msgid "Configuring as re-run" msgstr "Configuré comme relancé" @@ -18724,6 +18717,18 @@ msgstr "" "jour la liste de cours. Le nouveau cours aura besoin d'être configuré " "manuellement." +#. Translators: This is a status message for the course re-runs feature. +#. When a course admin indicates that a course should be re-run, the system +#. needs to process the request and prepare the new course. The status of +#. the process will follow this text. +#: cms/templates/index.html +msgid "This re-run processing status:" +msgstr "" + +#: cms/templates/index.html +msgid "Configuration Error" +msgstr "" + #: cms/templates/index.html msgid "" "A system error occurred while your course was being processed. Please go to " diff --git a/conf/locale/fr/LC_MESSAGES/djangojs.mo b/conf/locale/fr/LC_MESSAGES/djangojs.mo index 994533cba6..5d095f0194 100644 Binary files a/conf/locale/fr/LC_MESSAGES/djangojs.mo and b/conf/locale/fr/LC_MESSAGES/djangojs.mo differ diff --git a/conf/locale/fr/LC_MESSAGES/djangojs.po b/conf/locale/fr/LC_MESSAGES/djangojs.po index d3cf5f1ff2..cc98c56387 100644 --- a/conf/locale/fr/LC_MESSAGES/djangojs.po +++ b/conf/locale/fr/LC_MESSAGES/djangojs.po @@ -110,9 +110,9 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:15+0000\n" -"PO-Revision-Date: 2015-09-11 12:17+0000\n" -"Last-Translator: Sarina Canelake \n" +"POT-Creation-Date: 2015-09-18 13:22+0000\n" +"PO-Revision-Date: 2015-09-12 04:07+0000\n" +"Last-Translator: rafcha \n" "Language-Team: French (http://www.transifex.com/open-edx/edx-platform/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/conf/locale/he/LC_MESSAGES/django.mo b/conf/locale/he/LC_MESSAGES/django.mo index e0654e9f57..1c4d6498c0 100644 Binary files a/conf/locale/he/LC_MESSAGES/django.mo and b/conf/locale/he/LC_MESSAGES/django.mo differ diff --git a/conf/locale/he/LC_MESSAGES/django.po b/conf/locale/he/LC_MESSAGES/django.po index 670deccbe2..2ad495b497 100644 --- a/conf/locale/he/LC_MESSAGES/django.po +++ b/conf/locale/he/LC_MESSAGES/django.po @@ -61,7 +61,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:16+0000\n" +"POT-Creation-Date: 2015-09-18 13:23+0000\n" "PO-Revision-Date: 2015-05-28 20:00+0000\n" "Last-Translator: Nadav Stark \n" "Language-Team: Hebrew (http://www.transifex.com/open-edx/edx-platform/language/he/)\n" @@ -3294,9 +3294,7 @@ msgid "Request user's username" msgstr "" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's username. You must also set Open in New " -"Page to True to get the user's information." +msgid "Select True to request the user's username." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -3304,9 +3302,7 @@ msgid "Request user's email" msgstr "" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's email address. You must also set Open in " -"New Page to True to get the user's information." +msgid "Select True to request the user's email address." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -7428,10 +7424,6 @@ msgstr "" msgid "The supplied topic id {topic_id} is not valid" msgstr "" -#: lms/djangoapps/teams/views.py -msgid "Error connecting to elasticsearch" -msgstr "" - #. Translators: 'ordering' is a string describing a way #. of ordering a list. For example, {ordering} may be #. 'name', indicating that the user wants to sort the @@ -11284,8 +11276,6 @@ msgstr "" msgid "Email someone to say you've registered for this course" msgstr "" -#. Translators: This text will be automatically posted to the student's -#. Twitter account. {url} should appear at the end of the text. #: lms/templates/courseware/course_about.html msgid "I just registered for {number} {title} through {account}: {url}" msgstr "" @@ -11752,6 +11742,8 @@ msgstr "" msgid "Download Your Certificate" msgstr "" +#. Translators: This message appears to users when the system is processessing +#. course certificates, which can take a few hours. #: lms/templates/courseware/progress.html msgid "We're working on it..." msgstr "" @@ -15955,9 +15947,8 @@ msgstr "" msgid "Required Information to Create a re-run of a course" msgstr "" -#. Translators: This is an example name for a new course, seen when filling -#. out -#. the form to create a new course. +#. Translators: This is an example name for a new course, seen when +#. filling out the form to create a new course. #: cms/templates/course-create-rerun.html cms/templates/index.html msgid "e.g. Introduction to Computer Science" msgstr "" @@ -17127,8 +17118,8 @@ msgid "Library Name" msgstr "" #. Translators: This is an example name for a new content library, seen when -#. filling out the form to create a new library. (A library is a collection of -#. content or problems.) +#. filling out the form to create a new library. +#. (A library is a collection of content or problems.) #: cms/templates/index.html msgid "e.g. Computer Science Problems" msgstr "" @@ -17151,8 +17142,9 @@ msgstr "" #. Translators: This is an example for the "code" used to identify a library, #. seen when filling out the form to create a new library. This example is -#. short for "Computer Science Problems". The example number may contain -#. letters but must not contain spaces. +#. short +#. for "Computer Science Problems". The example number may contain letters +#. but must not contain spaces. #: cms/templates/index.html msgid "e.g. CSPROB" msgstr "" @@ -17179,10 +17171,11 @@ msgstr "" msgid "This course run is currently being created." msgstr "" -#. Translators: This is a status message, used to inform the user of what the -#. system is doing. This status means that the user has requested to re-run an -#. existing course, and the system is currently in the process of duplicating -#. and configuring the existing course so that it can be re-run. +#. Translators: This is a status message, used to inform the user of +#. what the system is doing. This status means that the user has +#. requested to re-run an existing course, and the system is currently +#. in the process of duplicating and configuring the existing course +#. so that it can be re-run. #: cms/templates/index.html msgid "Configuring as re-run" msgstr "" @@ -17194,6 +17187,18 @@ msgid "" " new course will need some manual configuration." msgstr "" +#. Translators: This is a status message for the course re-runs feature. +#. When a course admin indicates that a course should be re-run, the system +#. needs to process the request and prepare the new course. The status of +#. the process will follow this text. +#: cms/templates/index.html +msgid "This re-run processing status:" +msgstr "" + +#: cms/templates/index.html +msgid "Configuration Error" +msgstr "" + #: cms/templates/index.html msgid "" "A system error occurred while your course was being processed. Please go to " diff --git a/conf/locale/he/LC_MESSAGES/djangojs.mo b/conf/locale/he/LC_MESSAGES/djangojs.mo index ba46ce2143..f4ca29b7bf 100644 Binary files a/conf/locale/he/LC_MESSAGES/djangojs.mo and b/conf/locale/he/LC_MESSAGES/djangojs.mo differ diff --git a/conf/locale/he/LC_MESSAGES/djangojs.po b/conf/locale/he/LC_MESSAGES/djangojs.po index ec66282969..bddc5c4ceb 100644 --- a/conf/locale/he/LC_MESSAGES/djangojs.po +++ b/conf/locale/he/LC_MESSAGES/djangojs.po @@ -44,7 +44,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:15+0000\n" +"POT-Creation-Date: 2015-09-18 13:22+0000\n" "PO-Revision-Date: 2015-09-11 12:17+0000\n" "Last-Translator: Sarina Canelake \n" "Language-Team: Hebrew (http://www.transifex.com/open-edx/edx-platform/language/he/)\n" diff --git a/conf/locale/hi/LC_MESSAGES/django.mo b/conf/locale/hi/LC_MESSAGES/django.mo index c78a1fac83..c5a29112c6 100644 Binary files a/conf/locale/hi/LC_MESSAGES/django.mo and b/conf/locale/hi/LC_MESSAGES/django.mo differ diff --git a/conf/locale/hi/LC_MESSAGES/django.po b/conf/locale/hi/LC_MESSAGES/django.po index c4b90229bf..63791debf3 100644 --- a/conf/locale/hi/LC_MESSAGES/django.po +++ b/conf/locale/hi/LC_MESSAGES/django.po @@ -74,7 +74,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:16+0000\n" +"POT-Creation-Date: 2015-09-18 13:23+0000\n" "PO-Revision-Date: 2015-06-28 20:21+0000\n" "Last-Translator: ria1234 \n" "Language-Team: Hindi (http://www.transifex.com/open-edx/edx-platform/language/hi/)\n" @@ -3317,9 +3317,7 @@ msgid "Request user's username" msgstr "" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's username. You must also set Open in New " -"Page to True to get the user's information." +msgid "Select True to request the user's username." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -3327,9 +3325,7 @@ msgid "Request user's email" msgstr "" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's email address. You must also set Open in " -"New Page to True to get the user's information." +msgid "Select True to request the user's email address." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -7551,10 +7547,6 @@ msgstr "" msgid "The supplied topic id {topic_id} is not valid" msgstr "" -#: lms/djangoapps/teams/views.py -msgid "Error connecting to elasticsearch" -msgstr "" - #. Translators: 'ordering' is a string describing a way #. of ordering a list. For example, {ordering} may be #. 'name', indicating that the user wants to sort the @@ -11517,8 +11509,6 @@ msgstr "" msgid "Email someone to say you've registered for this course" msgstr "" -#. Translators: This text will be automatically posted to the student's -#. Twitter account. {url} should appear at the end of the text. #: lms/templates/courseware/course_about.html msgid "I just registered for {number} {title} through {account}: {url}" msgstr "" @@ -11981,6 +11971,8 @@ msgstr "" msgid "Download Your Certificate" msgstr "" +#. Translators: This message appears to users when the system is processessing +#. course certificates, which can take a few hours. #: lms/templates/courseware/progress.html msgid "We're working on it..." msgstr "" @@ -16292,9 +16284,8 @@ msgstr "" msgid "Required Information to Create a re-run of a course" msgstr "" -#. Translators: This is an example name for a new course, seen when filling -#. out -#. the form to create a new course. +#. Translators: This is an example name for a new course, seen when +#. filling out the form to create a new course. #: cms/templates/course-create-rerun.html cms/templates/index.html msgid "e.g. Introduction to Computer Science" msgstr "" @@ -17464,8 +17455,8 @@ msgid "Library Name" msgstr "" #. Translators: This is an example name for a new content library, seen when -#. filling out the form to create a new library. (A library is a collection of -#. content or problems.) +#. filling out the form to create a new library. +#. (A library is a collection of content or problems.) #: cms/templates/index.html msgid "e.g. Computer Science Problems" msgstr "" @@ -17488,8 +17479,9 @@ msgstr "" #. Translators: This is an example for the "code" used to identify a library, #. seen when filling out the form to create a new library. This example is -#. short for "Computer Science Problems". The example number may contain -#. letters but must not contain spaces. +#. short +#. for "Computer Science Problems". The example number may contain letters +#. but must not contain spaces. #: cms/templates/index.html msgid "e.g. CSPROB" msgstr "" @@ -17516,10 +17508,11 @@ msgstr "" msgid "This course run is currently being created." msgstr "" -#. Translators: This is a status message, used to inform the user of what the -#. system is doing. This status means that the user has requested to re-run an -#. existing course, and the system is currently in the process of duplicating -#. and configuring the existing course so that it can be re-run. +#. Translators: This is a status message, used to inform the user of +#. what the system is doing. This status means that the user has +#. requested to re-run an existing course, and the system is currently +#. in the process of duplicating and configuring the existing course +#. so that it can be re-run. #: cms/templates/index.html msgid "Configuring as re-run" msgstr "" @@ -17531,6 +17524,18 @@ msgid "" " new course will need some manual configuration." msgstr "" +#. Translators: This is a status message for the course re-runs feature. +#. When a course admin indicates that a course should be re-run, the system +#. needs to process the request and prepare the new course. The status of +#. the process will follow this text. +#: cms/templates/index.html +msgid "This re-run processing status:" +msgstr "" + +#: cms/templates/index.html +msgid "Configuration Error" +msgstr "" + #: cms/templates/index.html msgid "" "A system error occurred while your course was being processed. Please go to " diff --git a/conf/locale/hi/LC_MESSAGES/djangojs.mo b/conf/locale/hi/LC_MESSAGES/djangojs.mo index 43bf170536..c6b202d427 100644 Binary files a/conf/locale/hi/LC_MESSAGES/djangojs.mo and b/conf/locale/hi/LC_MESSAGES/djangojs.mo differ diff --git a/conf/locale/hi/LC_MESSAGES/djangojs.po b/conf/locale/hi/LC_MESSAGES/djangojs.po index 0b74296987..5b1f551cc8 100644 --- a/conf/locale/hi/LC_MESSAGES/djangojs.po +++ b/conf/locale/hi/LC_MESSAGES/djangojs.po @@ -47,7 +47,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:15+0000\n" +"POT-Creation-Date: 2015-09-18 13:22+0000\n" "PO-Revision-Date: 2015-09-11 12:17+0000\n" "Last-Translator: Sarina Canelake \n" "Language-Team: Hindi (http://www.transifex.com/open-edx/edx-platform/language/hi/)\n" diff --git a/conf/locale/ko_KR/LC_MESSAGES/django.mo b/conf/locale/ko_KR/LC_MESSAGES/django.mo index 1e798eb01a..ce1af6a7b9 100644 Binary files a/conf/locale/ko_KR/LC_MESSAGES/django.mo and b/conf/locale/ko_KR/LC_MESSAGES/django.mo differ diff --git a/conf/locale/ko_KR/LC_MESSAGES/django.po b/conf/locale/ko_KR/LC_MESSAGES/django.po index 31b139956b..0ed2380641 100644 --- a/conf/locale/ko_KR/LC_MESSAGES/django.po +++ b/conf/locale/ko_KR/LC_MESSAGES/django.po @@ -87,8 +87,8 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:16+0000\n" -"PO-Revision-Date: 2015-09-11 07:58+0000\n" +"POT-Creation-Date: 2015-09-18 13:23+0000\n" +"PO-Revision-Date: 2015-09-18 03:12+0000\n" "Last-Translator: Hongseob Lee \n" "Language-Team: Korean (Korea) (http://www.transifex.com/open-edx/edx-platform/language/ko_KR/)\n" "MIME-Version: 1.0\n" @@ -3412,20 +3412,16 @@ msgid "Request user's username" msgstr "아이디를 요청합니다." #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's username. You must also set Open in New " -"Page to True to get the user's information." -msgstr "아이디를 요청하려면 True를 입력하십시오. 아이디를 얻기 위해서는 '신규 페이지에서 열기'가 먼저 설정되어 있어야 합니다." +msgid "Select True to request the user's username." +msgstr "" #: common/lib/xmodule/xmodule/lti_module.py msgid "Request user's email" msgstr "이용자 이메일을 요청합니다." #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's email address. You must also set Open in " -"New Page to True to get the user's information." -msgstr "이용자 이메일 주소를 요청하려면 True를 입력합니다. 이를 위해 신규 페이지에서 열기가 먼저 설정되어 있어야 합니다." +msgid "Select True to request the user's email address." +msgstr "" #: common/lib/xmodule/xmodule/lti_module.py msgid "LTI Application Information" @@ -7808,10 +7804,6 @@ msgstr "" msgid "The supplied topic id {topic_id} is not valid" msgstr "제공된 주제 ID {topic_id}가 유효하지 않습니다." -#: lms/djangoapps/teams/views.py -msgid "Error connecting to elasticsearch" -msgstr "" - #. Translators: 'ordering' is a string describing a way #. of ordering a list. For example, {ordering} may be #. 'name', indicating that the user wants to sort the @@ -8292,7 +8284,7 @@ msgstr "마지막 수정:" #: lms/templates/wiki/article.html msgid "See all children" -msgstr "모든 하위내용 보기" +msgstr "모든 하위 문서 보기" #: lms/templates/wiki/article.html msgid "This article was last modified:" @@ -8869,7 +8861,7 @@ msgstr "" #. below a field meant to hold the user's full name. #: openedx/core/djangoapps/user_api/views.py lms/templates/register.html msgid "Needed for any certificates you may earn" -msgstr "앞으로 받게 될 이수증에 필요합니다." +msgstr "앞으로 받게 될 이수증에 필요합니다. 영문이름 병기를 원하시면 영문이름을 추가하세요." #. Translators: This label appears above a field on the registration form #. meant to hold the user's public username. @@ -8881,13 +8873,13 @@ msgstr "아이디" msgid "" "The name that will identify you in your courses - {bold_start}(cannot be " "changed later){bold_end}" -msgstr "강좌에서 표시되길 원하는 이름 - {bold_start}(변경 불가){bold_end}" +msgstr "영문/숫자로 이루어진 아이디 - {bold_start}(변경 불가){bold_end}" #. Translators: This example username is used as a placeholder in #. a field on the registration form meant to hold the user's username. #: openedx/core/djangoapps/user_api/views.py msgid "JaneDoe" -msgstr "홍길동" +msgstr "HongGilDong" #. Translators: This label appears above a dropdown menu on the registration #. form used to select the user's highest completed level of education. @@ -10742,7 +10734,7 @@ msgstr "학습자 상태 삭제" #: lms/templates/staff_problem_info.html #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "Rescore Student Submission" -msgstr "제출 재채점" +msgstr "답안 재채점" #: lms/templates/staff_problem_info.html msgid "Module Fields" @@ -11738,8 +11730,6 @@ msgstr "본 강좌에 등록한 것을 트위터에 트윗하세요." msgid "Email someone to say you've registered for this course" msgstr "이 강좌에 등록했음을 누군가에게 이메일하세요" -#. Translators: This text will be automatically posted to the student's -#. Twitter account. {url} should appear at the end of the text. #: lms/templates/courseware/course_about.html msgid "I just registered for {number} {title} through {account}: {url}" msgstr "{number} {title} through {account}: {url} 로 방금 등록 완료 " @@ -12048,7 +12038,7 @@ msgstr "학습자별 세부 성적 검토 및 조정" #: lms/templates/courseware/legacy_instructor_dashboard.html msgid "Select a problem and an action:" -msgstr "문제와 동작을 선택하세요:" +msgstr "문제와 액션을 선택하세요." #: lms/templates/courseware/legacy_instructor_dashboard.html msgid "" @@ -12071,8 +12061,7 @@ msgid "" "To download a CSV file containing profile information for students who are " "enrolled in this course, visit the Data Download section of the Instructor " "Dashboard." -msgstr "" -"강좌에 등록되어 있는 학습자 프로필 정보가 담긴 CSV 파일을 다운로드하기 위해, 교수자 대시보드의 자료 다운로드를 방문하세요. " +msgstr "강좌에 등록되어 있는 학습자 정보가 담긴 CSV 파일을 다운로드 하기 위해, 교수자 대시보드에서 데이터를 클릭하세요. " #: lms/templates/courseware/legacy_instructor_dashboard.html msgid "" @@ -12214,6 +12203,8 @@ msgstr "PDF는 새로운 브라우져 창 혹은 탭에 열릴 것입니다." msgid "Download Your Certificate" msgstr "강좌 이수증 다운로드하기" +#. Translators: This message appears to users when the system is processessing +#. course certificates, which can take a few hours. #: lms/templates/courseware/progress.html msgid "We're working on it..." msgstr "작업중입니다." @@ -13419,7 +13410,7 @@ msgstr "[[성함]]에게:" msgid "" "We have provided a course enrollment code for you in {course_name}. To " "enroll in the course, click the following link:" -msgstr "{course_name}에서 수강신청 코드를 제공했습니다. 강좌에 등록하기 위해 다음 링크를 클릭하세요:" +msgstr "{course_name}에서 수강신청 코드를 제공했습니다. 강좌에 등록하려면 다음 링크를 클릭하세요." #: lms/templates/emails/registration_codes_sale_email.txt msgid "HTML link from the attached CSV file" @@ -13881,7 +13872,7 @@ msgstr "" #: lms/templates/instructor/instructor_dashboard_2/data_download.html msgid "Grading Configuration" -msgstr "성적 설정" +msgstr "성적 가중치" #: lms/templates/instructor/instructor_dashboard_2/data_download.html msgid "Click to download a CSV of anonymized student IDs:" @@ -13917,7 +13908,7 @@ msgstr "버튼을 여러번 클릭하지 마세요. 그럴 경우, 생성 과정 msgid "" "Click to generate a CSV file of all students enrolled in this course, along " "with profile information such as email address and username:" -msgstr "이 강좌에 등록된 모든 학습자 CSV 파일과 이메일, 아이디 등의 프로필 정보를 생성하려면 클릭하세요." +msgstr "이 강좌에 등록된 모든 학습자 CSV 파일과 이메일, 아이디 등의 학습자 정보 보고서를 생성하려면 클릭하세요." #: lms/templates/instructor/instructor_dashboard_2/data_download.html msgid "Download profile information as a CSV" @@ -13931,7 +13922,7 @@ msgstr "강좌에 등록할 수 있으나 아직 처리가 완료되지 않은 #: lms/templates/instructor/instructor_dashboard_2/data_download.html msgid "Download a CSV of learners who can enroll" -msgstr "등록할 수 있는 학습자의 CSV를 다운로드 합니다." +msgstr "등록할 수 있는 학습자의 CSV 다운로드" #: lms/templates/instructor/instructor_dashboard_2/data_download.html msgid "" @@ -15015,7 +15006,7 @@ msgstr "학습자별 성적 사정" #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "Click this link to view the student's progress page:" -msgstr "학습자의 진도 페이지를 보기 위해서 이 링크를 클릭하세요:" +msgstr "학습자의 진도 페이지를 보려면 이 링크를 클릭하세요." #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "Student Progress Page" @@ -15027,7 +15018,7 @@ msgstr "학습자별 성적 조정" #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "Specify a problem in the course here with its complete location:" -msgstr "강좌에서 발생한 문제를 정확한 위치와 함께 기술하세요:" +msgstr "강좌에서 발생한 문제를 정확한 위치와 함께 기술하세요." #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "Problem location" @@ -15038,11 +15029,11 @@ msgstr "문제 위치 " msgid "" "You must provide the complete location of the problem. In the Staff Debug " "viewer, the location looks like this:" -msgstr "문제가 발생하는 자세한 위치를 알려주어야 합니다. Staff Debug Viewer에서 위치는 다음과 같습니다: " +msgstr "문제가 발생하는 자세한 위치를 알려주어야 합니다. Staff Debug Viewer에서 위치는 다음과 같습니다." #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "Next, select an action to perform for the given user and problem:" -msgstr "다음 작업은 지정된 사용자와 문제에 대한 수행을 선택합니다 :" +msgstr "다음 작업은 지정된 사용자와 문제에 대한 수행을 선택합니다." #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "" @@ -15064,7 +15055,7 @@ msgstr "" #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "Show Background Task History for Student" -msgstr "학습자에 대한 배경 작업 이력 보기" +msgstr "학습자 배경 작업 이력 보기" #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "Entrance Exam Adjustment" @@ -15112,11 +15103,11 @@ msgstr "액션을 선택하세요" #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "Reset ALL students' attempts" -msgstr "모든 학습자의 시도 초기화" +msgstr "전체 학습자의 문제 풀이 횟수 초기화" #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "Rescore ALL students' problem submissions" -msgstr "모든 학습자의 문제 제출 재채점" +msgstr "전체 학습자의 답안 재채점" #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "" @@ -15125,7 +15116,7 @@ msgid "" "submitted for this problem, click on this button" msgstr "" "이 작업은 백그라운드에서 이루어지며, 작업 상태가 공지 사항 탭의 표에 나타날 것입니다. 전체 작업의 상태를 보려면, 이 버튼을 " -"누르세요." +"클릭하세요." #: lms/templates/instructor/instructor_dashboard_2/student_admin.html msgid "Show Background Task History for Problem" @@ -16576,9 +16567,8 @@ msgstr "기관과 강좌 번호, 기관별 강좌 번호는 고유해야 합니 msgid "Required Information to Create a re-run of a course" msgstr "강좌 재운영을 만드는데 필요한 정보" -#. Translators: This is an example name for a new course, seen when filling -#. out -#. the form to create a new course. +#. Translators: This is an example name for a new course, seen when +#. filling out the form to create a new course. #: cms/templates/course-create-rerun.html cms/templates/index.html msgid "e.g. Introduction to Computer Science" msgstr "예: 컴퓨터 공학 개론" @@ -17828,8 +17818,8 @@ msgid "Library Name" msgstr "콘텐츠 보관함명" #. Translators: This is an example name for a new content library, seen when -#. filling out the form to create a new library. (A library is a collection of -#. content or problems.) +#. filling out the form to create a new library. +#. (A library is a collection of content or problems.) #: cms/templates/index.html msgid "e.g. Computer Science Problems" msgstr "ex 컴퓨터 개론 문제" @@ -17852,8 +17842,9 @@ msgstr "콘텐츠 보관함 코드" #. Translators: This is an example for the "code" used to identify a library, #. seen when filling out the form to create a new library. This example is -#. short for "Computer Science Problems". The example number may contain -#. letters but must not contain spaces. +#. short +#. for "Computer Science Problems". The example number may contain letters +#. but must not contain spaces. #: cms/templates/index.html msgid "e.g. CSPROB" msgstr "예: CSPROB" @@ -17880,10 +17871,11 @@ msgstr "기관별 강좌 번호" msgid "This course run is currently being created." msgstr "기관별 강좌 번호가 만들어지고 있습니다." -#. Translators: This is a status message, used to inform the user of what the -#. system is doing. This status means that the user has requested to re-run an -#. existing course, and the system is currently in the process of duplicating -#. and configuring the existing course so that it can be re-run. +#. Translators: This is a status message, used to inform the user of +#. what the system is doing. This status means that the user has +#. requested to re-run an existing course, and the system is currently +#. in the process of duplicating and configuring the existing course +#. so that it can be re-run. #: cms/templates/index.html msgid "Configuring as re-run" msgstr "다시 시작하기 위한 설정" @@ -17897,6 +17889,18 @@ msgstr "" "5-10분 후, 새 강좌가 귀하의 강좌 목록에 추가될 것입니다. 강좌 목록을 업데이트하기 위해, 페이지로 돌아가거나 " "{link_start} 새로고침{link_end} 하세요. 새 강좌엔 약간의 수동 설정이 필요할 수 있습니다." +#. Translators: This is a status message for the course re-runs feature. +#. When a course admin indicates that a course should be re-run, the system +#. needs to process the request and prepare the new course. The status of +#. the process will follow this text. +#: cms/templates/index.html +msgid "This re-run processing status:" +msgstr "" + +#: cms/templates/index.html +msgid "Configuration Error" +msgstr "" + #: cms/templates/index.html msgid "" "A system error occurred while your course was being processed. Please go to " @@ -19366,7 +19370,7 @@ msgstr "글 주소 \"%s\" 가 이미 존재합니다." #: wiki/forms.py msgid "Yes, I am sure" -msgstr "예. 확인했습니다." +msgstr "네, 삭제합니다." #: wiki/forms.py msgid "Purge" diff --git a/conf/locale/ko_KR/LC_MESSAGES/djangojs.mo b/conf/locale/ko_KR/LC_MESSAGES/djangojs.mo index 3cc978384a..a9f66897ba 100644 Binary files a/conf/locale/ko_KR/LC_MESSAGES/djangojs.mo and b/conf/locale/ko_KR/LC_MESSAGES/djangojs.mo differ diff --git a/conf/locale/ko_KR/LC_MESSAGES/djangojs.po b/conf/locale/ko_KR/LC_MESSAGES/djangojs.po index 4afc66f36e..0edf6a32cd 100644 --- a/conf/locale/ko_KR/LC_MESSAGES/djangojs.po +++ b/conf/locale/ko_KR/LC_MESSAGES/djangojs.po @@ -53,7 +53,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:15+0000\n" +"POT-Creation-Date: 2015-09-18 13:22+0000\n" "PO-Revision-Date: 2015-09-11 12:17+0000\n" "Last-Translator: Sarina Canelake \n" "Language-Team: Korean (Korea) (http://www.transifex.com/open-edx/edx-platform/language/ko_KR/)\n" @@ -2602,7 +2602,7 @@ msgstr "" #: lms/static/coffee/src/instructor_dashboard/data_download.js msgid "Error generating student profile information. Please try again." -msgstr "학습자 프로필 정보를 만드는 중 오류가 발생했습니다. 다시 시도하세요." +msgstr "학습자 정보를 만드는 중 오류가 발생했습니다. 다시 시도하세요." #: lms/static/coffee/src/instructor_dashboard/data_download.js #: lms/templates/search/search_loading.underscore diff --git a/conf/locale/pt_BR/LC_MESSAGES/django.mo b/conf/locale/pt_BR/LC_MESSAGES/django.mo index 314a89585f..17e94f637d 100644 Binary files a/conf/locale/pt_BR/LC_MESSAGES/django.mo and b/conf/locale/pt_BR/LC_MESSAGES/django.mo differ diff --git a/conf/locale/pt_BR/LC_MESSAGES/django.po b/conf/locale/pt_BR/LC_MESSAGES/django.po index 060b5b768b..5aba385803 100644 --- a/conf/locale/pt_BR/LC_MESSAGES/django.po +++ b/conf/locale/pt_BR/LC_MESSAGES/django.po @@ -225,7 +225,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:16+0000\n" +"POT-Creation-Date: 2015-09-18 13:23+0000\n" "PO-Revision-Date: 2015-07-20 00:15+0000\n" "Last-Translator: javiercencig \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/open-edx/edx-platform/language/pt_BR/)\n" @@ -3613,9 +3613,7 @@ msgid "Request user's username" msgstr "" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's username. You must also set Open in New " -"Page to True to get the user's information." +msgid "Select True to request the user's username." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -3623,9 +3621,7 @@ msgid "Request user's email" msgstr "" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's email address. You must also set Open in " -"New Page to True to get the user's information." +msgid "Select True to request the user's email address." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -7946,10 +7942,6 @@ msgstr "" msgid "The supplied topic id {topic_id} is not valid" msgstr "" -#: lms/djangoapps/teams/views.py -msgid "Error connecting to elasticsearch" -msgstr "" - #. Translators: 'ordering' is a string describing a way #. of ordering a list. For example, {ordering} may be #. 'name', indicating that the user wants to sort the @@ -11960,8 +11952,6 @@ msgid "Email someone to say you've registered for this course" msgstr "" "Envie um e-mail para alguém dizendo que você se inscreveu neste curso." -#. Translators: This text will be automatically posted to the student's -#. Twitter account. {url} should appear at the end of the text. #: lms/templates/courseware/course_about.html msgid "I just registered for {number} {title} through {account}: {url}" msgstr "" @@ -12446,6 +12436,8 @@ msgstr "" msgid "Download Your Certificate" msgstr "" +#. Translators: This message appears to users when the system is processessing +#. course certificates, which can take a few hours. #: lms/templates/courseware/progress.html msgid "We're working on it..." msgstr "" @@ -16887,9 +16879,8 @@ msgstr "" msgid "Required Information to Create a re-run of a course" msgstr "" -#. Translators: This is an example name for a new course, seen when filling -#. out -#. the form to create a new course. +#. Translators: This is an example name for a new course, seen when +#. filling out the form to create a new course. #: cms/templates/course-create-rerun.html cms/templates/index.html msgid "e.g. Introduction to Computer Science" msgstr "" @@ -18074,8 +18065,8 @@ msgid "Library Name" msgstr "" #. Translators: This is an example name for a new content library, seen when -#. filling out the form to create a new library. (A library is a collection of -#. content or problems.) +#. filling out the form to create a new library. +#. (A library is a collection of content or problems.) #: cms/templates/index.html msgid "e.g. Computer Science Problems" msgstr "" @@ -18098,8 +18089,9 @@ msgstr "" #. Translators: This is an example for the "code" used to identify a library, #. seen when filling out the form to create a new library. This example is -#. short for "Computer Science Problems". The example number may contain -#. letters but must not contain spaces. +#. short +#. for "Computer Science Problems". The example number may contain letters +#. but must not contain spaces. #: cms/templates/index.html msgid "e.g. CSPROB" msgstr "" @@ -18126,10 +18118,11 @@ msgstr "" msgid "This course run is currently being created." msgstr "" -#. Translators: This is a status message, used to inform the user of what the -#. system is doing. This status means that the user has requested to re-run an -#. existing course, and the system is currently in the process of duplicating -#. and configuring the existing course so that it can be re-run. +#. Translators: This is a status message, used to inform the user of +#. what the system is doing. This status means that the user has +#. requested to re-run an existing course, and the system is currently +#. in the process of duplicating and configuring the existing course +#. so that it can be re-run. #: cms/templates/index.html msgid "Configuring as re-run" msgstr "" @@ -18141,6 +18134,18 @@ msgid "" " new course will need some manual configuration." msgstr "" +#. Translators: This is a status message for the course re-runs feature. +#. When a course admin indicates that a course should be re-run, the system +#. needs to process the request and prepare the new course. The status of +#. the process will follow this text. +#: cms/templates/index.html +msgid "This re-run processing status:" +msgstr "" + +#: cms/templates/index.html +msgid "Configuration Error" +msgstr "" + #: cms/templates/index.html msgid "" "A system error occurred while your course was being processed. Please go to " diff --git a/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo b/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo index 34fca378ad..f85990f913 100644 Binary files a/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo and b/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo differ diff --git a/conf/locale/pt_BR/LC_MESSAGES/djangojs.po b/conf/locale/pt_BR/LC_MESSAGES/djangojs.po index 5ddb5ab327..d765649dca 100644 --- a/conf/locale/pt_BR/LC_MESSAGES/djangojs.po +++ b/conf/locale/pt_BR/LC_MESSAGES/djangojs.po @@ -102,6 +102,7 @@ # # Translators: # Alan Mól , 2015 +# Ana Paula D'Almeida Oliveira , 2015 # Andrea Z. Bitencourt , 2015 # Bruno Sette , 2015 # Cleomir Waiczyk , 2015 @@ -141,6 +142,7 @@ # Luiz Cardineli , 2015 # Magaly Munik da Rocha , 2014 # Marco Túlio Pires , 2014 +# Mariana Jó de Souza , 2015 # mmauryx , 2014 # Maurício Gonçalves Melara Camargo , 2015 # Mike Job Silva , 2015 @@ -152,9 +154,9 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:15+0000\n" -"PO-Revision-Date: 2015-09-11 12:17+0000\n" -"Last-Translator: Sarina Canelake \n" +"POT-Creation-Date: 2015-09-18 13:22+0000\n" +"PO-Revision-Date: 2015-09-13 01:15+0000\n" +"Last-Translator: Mariana Jó de Souza \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/open-edx/edx-platform/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/conf/locale/rtl/LC_MESSAGES/django.mo b/conf/locale/rtl/LC_MESSAGES/django.mo index d30df0c9ba..ff2efa53bc 100644 Binary files a/conf/locale/rtl/LC_MESSAGES/django.mo and b/conf/locale/rtl/LC_MESSAGES/django.mo differ diff --git a/conf/locale/rtl/LC_MESSAGES/django.po b/conf/locale/rtl/LC_MESSAGES/django.po index f8a24600be..b67885cd41 100644 --- a/conf/locale/rtl/LC_MESSAGES/django.po +++ b/conf/locale/rtl/LC_MESSAGES/django.po @@ -37,8 +37,8 @@ msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:47+0000\n" -"PO-Revision-Date: 2015-09-11 12:47:23.251524\n" +"POT-Creation-Date: 2015-09-18 13:39+0000\n" +"PO-Revision-Date: 2015-09-18 13:39:23.492978\n" "Last-Translator: \n" "Language-Team: openedx-translation \n" "MIME-Version: 1.0\n" @@ -3587,24 +3587,16 @@ msgid "Request user's username" msgstr "قثضعثسف عسثق'س عسثقرشوث" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's username. You must also set Open in New " -"Page to True to get the user's information." -msgstr "" -"سثمثذف فقعث فخ قثضعثسف فاث عسثق'س عسثقرشوث. غخع وعسف شمسخ سثف خحثر هر رثص " -"حشلث فخ فقعث فخ لثف فاث عسثق'س هربخقوشفهخر." +msgid "Select True to request the user's username." +msgstr "سثمثذف فقعث فخ قثضعثسف فاث عسثق'س عسثقرشوث." #: common/lib/xmodule/xmodule/lti_module.py msgid "Request user's email" msgstr "قثضعثسف عسثق'س ثوشهم" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's email address. You must also set Open in " -"New Page to True to get the user's information." -msgstr "" -"سثمثذف فقعث فخ قثضعثسف فاث عسثق'س ثوشهم شييقثسس. غخع وعسف شمسخ سثف خحثر هر " -"رثص حشلث فخ فقعث فخ لثف فاث عسثق'س هربخقوشفهخر." +msgid "Select True to request the user's email address." +msgstr "سثمثذف فقعث فخ قثضعثسف فاث عسثق'س ثوشهم شييقثسس." #: common/lib/xmodule/xmodule/lti_module.py msgid "LTI Application Information" @@ -8274,10 +8266,6 @@ msgstr "فثطف_سثشقذا شري خقيثق_زغ ذشررخف زث حقخد msgid "The supplied topic id {topic_id} is not valid" msgstr "فاث سعححمهثي فخحهذ هي {topic_id} هس رخف دشمهي" -#: lms/djangoapps/teams/views.py -msgid "Error connecting to elasticsearch" -msgstr "ثققخق ذخررثذفهرل فخ ثمشسفهذسثشقذا" - #. Translators: 'ordering' is a string describing a way #. of ordering a list. For example, {ordering} may be #. 'name', indicating that the user wants to sort the @@ -12392,8 +12380,6 @@ msgstr "فصثثف فاشف غخع'دث قثلهسفثقثي بخق فاهس ذ msgid "Email someone to say you've registered for this course" msgstr "ثوشهم سخوثخرث فخ سشغ غخع'دث قثلهسفثقثي بخق فاهس ذخعقسث" -#. Translators: This text will be automatically posted to the student's -#. Twitter account. {url} should appear at the end of the text. #: lms/templates/courseware/course_about.html msgid "I just registered for {number} {title} through {account}: {url}" msgstr "ه تعسف قثلهسفثقثي بخق {number} {title} فاقخعلا {account}: {url}" @@ -12905,6 +12891,8 @@ msgstr "حيب صهمم خحثر هر ش رثص زقخصسثق صهريخص خق msgid "Download Your Certificate" msgstr "يخصرمخشي غخعق ذثقفهبهذشفث" +#. Translators: This message appears to users when the system is processessing +#. course certificates, which can take a few hours. #: lms/templates/courseware/progress.html msgid "We're working on it..." msgstr "صث'قث صخقنهرل خر هف..." @@ -17621,9 +17609,8 @@ msgstr "" msgid "Required Information to Create a re-run of a course" msgstr "قثضعهقثي هربخقوشفهخر فخ ذقثشفث ش قث-قعر خب ش ذخعقسث" -#. Translators: This is an example name for a new course, seen when filling -#. out -#. the form to create a new course. +#. Translators: This is an example name for a new course, seen when +#. filling out the form to create a new course. #: cms/templates/course-create-rerun.html cms/templates/index.html msgid "e.g. Introduction to Computer Science" msgstr "ث.ل. هرفقخيعذفهخر فخ ذخوحعفثق سذهثرذث" @@ -19011,8 +18998,8 @@ msgid "Library Name" msgstr "مهزقشقغ رشوث" #. Translators: This is an example name for a new content library, seen when -#. filling out the form to create a new library. (A library is a collection of -#. content or problems.) +#. filling out the form to create a new library. +#. (A library is a collection of content or problems.) #: cms/templates/index.html msgid "e.g. Computer Science Problems" msgstr "ث.ل. ذخوحعفثق سذهثرذث حقخزمثوس" @@ -19035,8 +19022,9 @@ msgstr "مهزقشقغ ذخيث" #. Translators: This is an example for the "code" used to identify a library, #. seen when filling out the form to create a new library. This example is -#. short for "Computer Science Problems". The example number may contain -#. letters but must not contain spaces. +#. short +#. for "Computer Science Problems". The example number may contain letters +#. but must not contain spaces. #: cms/templates/index.html msgid "e.g. CSPROB" msgstr "ث.ل. ذسحقخز" @@ -19065,10 +19053,11 @@ msgstr "ذخعقسث قعر:" msgid "This course run is currently being created." msgstr "فاهس ذخعقسث قعر هس ذعققثرفمغ زثهرل ذقثشفثي." -#. Translators: This is a status message, used to inform the user of what the -#. system is doing. This status means that the user has requested to re-run an -#. existing course, and the system is currently in the process of duplicating -#. and configuring the existing course so that it can be re-run. +#. Translators: This is a status message, used to inform the user of +#. what the system is doing. This status means that the user has +#. requested to re-run an existing course, and the system is currently +#. in the process of duplicating and configuring the existing course +#. so that it can be re-run. #: cms/templates/index.html msgid "Configuring as re-run" msgstr "ذخربهلعقهرل شس قث-قعر" @@ -19083,6 +19072,18 @@ msgstr "" "فاهس حشلث خق {link_start}قثبقثسا هف{link_end} فخ عحيشفث فاث ذخعقسث مهسف. فاث" " رثص ذخعقسث صهمم رثثي سخوث وشرعشم ذخربهلعقشفهخر." +#. Translators: This is a status message for the course re-runs feature. +#. When a course admin indicates that a course should be re-run, the system +#. needs to process the request and prepare the new course. The status of +#. the process will follow this text. +#: cms/templates/index.html +msgid "This re-run processing status:" +msgstr "فاهس قث-قعر حقخذثسسهرل سفشفعس:" + +#: cms/templates/index.html +msgid "Configuration Error" +msgstr "ذخربهلعقشفهخر ثققخق" + #: cms/templates/index.html msgid "" "A system error occurred while your course was being processed. Please go to " diff --git a/conf/locale/rtl/LC_MESSAGES/djangojs.mo b/conf/locale/rtl/LC_MESSAGES/djangojs.mo index bff6e2b764..7681a8f6bc 100644 Binary files a/conf/locale/rtl/LC_MESSAGES/djangojs.mo and b/conf/locale/rtl/LC_MESSAGES/djangojs.mo differ diff --git a/conf/locale/rtl/LC_MESSAGES/djangojs.po b/conf/locale/rtl/LC_MESSAGES/djangojs.po index e725b27a06..83a9439414 100644 --- a/conf/locale/rtl/LC_MESSAGES/djangojs.po +++ b/conf/locale/rtl/LC_MESSAGES/djangojs.po @@ -26,8 +26,8 @@ msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:46+0000\n" -"PO-Revision-Date: 2015-09-11 12:47:23.562016\n" +"POT-Creation-Date: 2015-09-18 13:38+0000\n" +"PO-Revision-Date: 2015-09-18 13:39:23.855088\n" "Last-Translator: \n" "Language-Team: openedx-translation \n" "MIME-Version: 1.0\n" diff --git a/conf/locale/ru/LC_MESSAGES/django.mo b/conf/locale/ru/LC_MESSAGES/django.mo index 8a96148144..a85a1082b6 100644 Binary files a/conf/locale/ru/LC_MESSAGES/django.mo and b/conf/locale/ru/LC_MESSAGES/django.mo differ diff --git a/conf/locale/ru/LC_MESSAGES/django.po b/conf/locale/ru/LC_MESSAGES/django.po index a01ea90c31..0ade0393fb 100644 --- a/conf/locale/ru/LC_MESSAGES/django.po +++ b/conf/locale/ru/LC_MESSAGES/django.po @@ -180,7 +180,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:16+0000\n" +"POT-Creation-Date: 2015-09-18 13:23+0000\n" "PO-Revision-Date: 2015-08-05 08:47+0000\n" "Last-Translator: Weyedide \n" "Language-Team: Russian (http://www.transifex.com/open-edx/edx-platform/language/ru/)\n" @@ -3433,9 +3433,7 @@ msgid "Request user's username" msgstr "" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's username. You must also set Open in New " -"Page to True to get the user's information." +msgid "Select True to request the user's username." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -3443,9 +3441,7 @@ msgid "Request user's email" msgstr "" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's email address. You must also set Open in " -"New Page to True to get the user's information." +msgid "Select True to request the user's email address." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -7562,10 +7558,6 @@ msgstr "" msgid "The supplied topic id {topic_id} is not valid" msgstr "" -#: lms/djangoapps/teams/views.py -msgid "Error connecting to elasticsearch" -msgstr "" - #. Translators: 'ordering' is a string describing a way #. of ordering a list. For example, {ordering} may be #. 'name', indicating that the user wants to sort the @@ -11305,7 +11297,7 @@ msgstr "" #: lms/templates/combinedopenended/openended/open_ended_rubric.html msgid "Rubric" -msgstr "" +msgstr "Оценивание" #: lms/templates/combinedopenended/openended/open_ended_rubric.html msgid "" @@ -11424,8 +11416,6 @@ msgstr "" msgid "Email someone to say you've registered for this course" msgstr "" -#. Translators: This text will be automatically posted to the student's -#. Twitter account. {url} should appear at the end of the text. #: lms/templates/courseware/course_about.html msgid "I just registered for {number} {title} through {account}: {url}" msgstr "" @@ -11886,6 +11876,8 @@ msgstr "" msgid "Download Your Certificate" msgstr "" +#. Translators: This message appears to users when the system is processessing +#. course certificates, which can take a few hours. #: lms/templates/courseware/progress.html msgid "We're working on it..." msgstr "" @@ -16111,9 +16103,8 @@ msgstr "" msgid "Required Information to Create a re-run of a course" msgstr "" -#. Translators: This is an example name for a new course, seen when filling -#. out -#. the form to create a new course. +#. Translators: This is an example name for a new course, seen when +#. filling out the form to create a new course. #: cms/templates/course-create-rerun.html cms/templates/index.html msgid "e.g. Introduction to Computer Science" msgstr "" @@ -17283,8 +17274,8 @@ msgid "Library Name" msgstr "" #. Translators: This is an example name for a new content library, seen when -#. filling out the form to create a new library. (A library is a collection of -#. content or problems.) +#. filling out the form to create a new library. +#. (A library is a collection of content or problems.) #: cms/templates/index.html msgid "e.g. Computer Science Problems" msgstr "" @@ -17307,8 +17298,9 @@ msgstr "" #. Translators: This is an example for the "code" used to identify a library, #. seen when filling out the form to create a new library. This example is -#. short for "Computer Science Problems". The example number may contain -#. letters but must not contain spaces. +#. short +#. for "Computer Science Problems". The example number may contain letters +#. but must not contain spaces. #: cms/templates/index.html msgid "e.g. CSPROB" msgstr "" @@ -17335,10 +17327,11 @@ msgstr "" msgid "This course run is currently being created." msgstr "" -#. Translators: This is a status message, used to inform the user of what the -#. system is doing. This status means that the user has requested to re-run an -#. existing course, and the system is currently in the process of duplicating -#. and configuring the existing course so that it can be re-run. +#. Translators: This is a status message, used to inform the user of +#. what the system is doing. This status means that the user has +#. requested to re-run an existing course, and the system is currently +#. in the process of duplicating and configuring the existing course +#. so that it can be re-run. #: cms/templates/index.html msgid "Configuring as re-run" msgstr "" @@ -17350,6 +17343,18 @@ msgid "" " new course will need some manual configuration." msgstr "" +#. Translators: This is a status message for the course re-runs feature. +#. When a course admin indicates that a course should be re-run, the system +#. needs to process the request and prepare the new course. The status of +#. the process will follow this text. +#: cms/templates/index.html +msgid "This re-run processing status:" +msgstr "" + +#: cms/templates/index.html +msgid "Configuration Error" +msgstr "" + #: cms/templates/index.html msgid "" "A system error occurred while your course was being processed. Please go to " diff --git a/conf/locale/ru/LC_MESSAGES/djangojs.mo b/conf/locale/ru/LC_MESSAGES/djangojs.mo index cf9fa3b90a..13ebda6e63 100644 Binary files a/conf/locale/ru/LC_MESSAGES/djangojs.mo and b/conf/locale/ru/LC_MESSAGES/djangojs.mo differ diff --git a/conf/locale/ru/LC_MESSAGES/djangojs.po b/conf/locale/ru/LC_MESSAGES/djangojs.po index afbc2f6cef..8df7d3ab5a 100644 --- a/conf/locale/ru/LC_MESSAGES/djangojs.po +++ b/conf/locale/ru/LC_MESSAGES/djangojs.po @@ -101,9 +101,9 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:15+0000\n" -"PO-Revision-Date: 2015-09-11 12:17+0000\n" -"Last-Translator: Sarina Canelake \n" +"POT-Creation-Date: 2015-09-18 13:22+0000\n" +"PO-Revision-Date: 2015-09-14 14:57+0000\n" +"Last-Translator: Liubov Fomicheva \n" "Language-Team: Russian (http://www.transifex.com/open-edx/edx-platform/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/conf/locale/zh_CN/LC_MESSAGES/django.mo b/conf/locale/zh_CN/LC_MESSAGES/django.mo index 147bbeb29e..d9f2cb42ff 100644 Binary files a/conf/locale/zh_CN/LC_MESSAGES/django.mo and b/conf/locale/zh_CN/LC_MESSAGES/django.mo differ diff --git a/conf/locale/zh_CN/LC_MESSAGES/django.po b/conf/locale/zh_CN/LC_MESSAGES/django.po index 10880639f6..21ee046616 100644 --- a/conf/locale/zh_CN/LC_MESSAGES/django.po +++ b/conf/locale/zh_CN/LC_MESSAGES/django.po @@ -265,7 +265,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:16+0000\n" +"POT-Creation-Date: 2015-09-18 13:23+0000\n" "PO-Revision-Date: 2015-06-18 03:04+0000\n" "Last-Translator: louyihua \n" "Language-Team: Chinese (China) (http://www.transifex.com/open-edx/edx-platform/language/zh_CN/)\n" @@ -3472,9 +3472,7 @@ msgid "Request user's username" msgstr "" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's username. You must also set Open in New " -"Page to True to get the user's information." +msgid "Select True to request the user's username." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -3482,9 +3480,7 @@ msgid "Request user's email" msgstr "" #: common/lib/xmodule/xmodule/lti_module.py -msgid "" -"Select True to request the user's email address. You must also set Open in " -"New Page to True to get the user's information." +msgid "Select True to request the user's email address." msgstr "" #: common/lib/xmodule/xmodule/lti_module.py @@ -7649,10 +7645,6 @@ msgstr "" msgid "The supplied topic id {topic_id} is not valid" msgstr "" -#: lms/djangoapps/teams/views.py -msgid "Error connecting to elasticsearch" -msgstr "" - #. Translators: 'ordering' is a string describing a way #. of ordering a list. For example, {ordering} may be #. 'name', indicating that the user wants to sort the @@ -11534,8 +11526,6 @@ msgstr "在推特上发布你已注册此课程" msgid "Email someone to say you've registered for this course" msgstr "" -#. Translators: This text will be automatically posted to the student's -#. Twitter account. {url} should appear at the end of the text. #: lms/templates/courseware/course_about.html msgid "I just registered for {number} {title} through {account}: {url}" msgstr "我刚刚通过 {account} 注册了编号为 {number} 的课程 {title},链接是:{url}" @@ -11998,6 +11988,8 @@ msgstr "" msgid "Download Your Certificate" msgstr "" +#. Translators: This message appears to users when the system is processessing +#. course certificates, which can take a few hours. #: lms/templates/courseware/progress.html msgid "We're working on it..." msgstr "" @@ -16225,9 +16217,8 @@ msgstr "注意:机构名称、课程编号以及开课时间三者合在一起 msgid "Required Information to Create a re-run of a course" msgstr "创建一次课程重启所必需的信息" -#. Translators: This is an example name for a new course, seen when filling -#. out -#. the form to create a new course. +#. Translators: This is an example name for a new course, seen when +#. filling out the form to create a new course. #: cms/templates/course-create-rerun.html cms/templates/index.html msgid "e.g. Introduction to Computer Science" msgstr "例如,计算机科学导论" @@ -17430,8 +17421,8 @@ msgid "Library Name" msgstr "知识库名称" #. Translators: This is an example name for a new content library, seen when -#. filling out the form to create a new library. (A library is a collection of -#. content or problems.) +#. filling out the form to create a new library. +#. (A library is a collection of content or problems.) #: cms/templates/index.html msgid "e.g. Computer Science Problems" msgstr "例如,计算机科学问题" @@ -17454,8 +17445,9 @@ msgstr "知识库代码" #. Translators: This is an example for the "code" used to identify a library, #. seen when filling out the form to create a new library. This example is -#. short for "Computer Science Problems". The example number may contain -#. letters but must not contain spaces. +#. short +#. for "Computer Science Problems". The example number may contain letters +#. but must not contain spaces. #: cms/templates/index.html msgid "e.g. CSPROB" msgstr "例如,CSPROB" @@ -17482,10 +17474,11 @@ msgstr "开课时间" msgid "This course run is currently being created." msgstr "正在创建该课程的本次运行。" -#. Translators: This is a status message, used to inform the user of what the -#. system is doing. This status means that the user has requested to re-run an -#. existing course, and the system is currently in the process of duplicating -#. and configuring the existing course so that it can be re-run. +#. Translators: This is a status message, used to inform the user of +#. what the system is doing. This status means that the user has +#. requested to re-run an existing course, and the system is currently +#. in the process of duplicating and configuring the existing course +#. so that it can be re-run. #: cms/templates/index.html msgid "Configuring as re-run" msgstr "配置为重启" @@ -17498,6 +17491,18 @@ msgid "" msgstr "" "新的课程将在5-10分钟内添加到您的课程列表里中。返回到该页或者{link_start}刷新该页{link_end}以更新课程列表。新的课程将需要一些手动配置。" +#. Translators: This is a status message for the course re-runs feature. +#. When a course admin indicates that a course should be re-run, the system +#. needs to process the request and prepare the new course. The status of +#. the process will follow this text. +#: cms/templates/index.html +msgid "This re-run processing status:" +msgstr "" + +#: cms/templates/index.html +msgid "Configuration Error" +msgstr "" + #: cms/templates/index.html msgid "" "A system error occurred while your course was being processed. Please go to " diff --git a/conf/locale/zh_CN/LC_MESSAGES/djangojs.mo b/conf/locale/zh_CN/LC_MESSAGES/djangojs.mo index e1cecaecc2..68ef96f7a8 100644 Binary files a/conf/locale/zh_CN/LC_MESSAGES/djangojs.mo and b/conf/locale/zh_CN/LC_MESSAGES/djangojs.mo differ diff --git a/conf/locale/zh_CN/LC_MESSAGES/djangojs.po b/conf/locale/zh_CN/LC_MESSAGES/djangojs.po index 25a48e86a9..71c88777af 100644 --- a/conf/locale/zh_CN/LC_MESSAGES/djangojs.po +++ b/conf/locale/zh_CN/LC_MESSAGES/djangojs.po @@ -128,7 +128,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-platform\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2015-09-11 12:15+0000\n" +"POT-Creation-Date: 2015-09-18 13:22+0000\n" "PO-Revision-Date: 2015-09-11 12:17+0000\n" "Last-Translator: Sarina Canelake \n" "Language-Team: Chinese (China) (http://www.transifex.com/open-edx/edx-platform/language/zh_CN/)\n" diff --git a/lms/djangoapps/ccx/tests/factories.py b/lms/djangoapps/ccx/tests/factories.py index b2a99215c1..0185ac3fbe 100644 --- a/lms/djangoapps/ccx/tests/factories.py +++ b/lms/djangoapps/ccx/tests/factories.py @@ -8,7 +8,8 @@ from ccx.models import CustomCourseForEdX # pylint: disable=import-error class CcxFactory(DjangoModelFactory): # pylint: disable=missing-docstring - FACTORY_FOR = CustomCourseForEdX + class Meta(object): # pylint: disable=missing-docstring + model = CustomCourseForEdX display_name = "Test CCX" id = None # pylint: disable=redefined-builtin, invalid-name coach = SubFactory(UserFactory) diff --git a/lms/djangoapps/ccx/tests/test_tasks.py b/lms/djangoapps/ccx/tests/test_tasks.py index 8af45ad5b5..c1f568b4e2 100644 --- a/lms/djangoapps/ccx/tests/test_tasks.py +++ b/lms/djangoapps/ccx/tests/test_tasks.py @@ -94,17 +94,14 @@ class TestSendCCXCoursePublished(ModuleStoreTestCase): structure = CourseStructure.objects.get(course_id=course_key) self.assertEqual(structure.structure, ccx_structure) - def test_course_overview_deleted(self): - """Check that course overview is deleted after course published signal is sent + def test_course_overview_cached(self): + """Check that course overview is cached after course published signal is sent """ course_key = CCXLocator.from_course_locator(self.course.id, self.ccx.id) - overview = CourseOverview(id=course_key) - overview.version = 1 - overview.save() overview = CourseOverview.objects.filter(id=course_key) - self.assertEqual(len(overview), 1) + self.assertEqual(len(overview), 0) with mock_signal_receiver(SignalHandler.course_published) as receiver: self.call_fut(self.course.id) self.assertEqual(receiver.call_count, 3) overview = CourseOverview.objects.filter(id=course_key) - self.assertEqual(len(overview), 0) + self.assertEqual(len(overview), 1) diff --git a/lms/djangoapps/ccx/tests/test_views.py b/lms/djangoapps/ccx/tests/test_views.py index eb9ebc4e5e..7ec56e5df6 100644 --- a/lms/djangoapps/ccx/tests/test_views.py +++ b/lms/djangoapps/ccx/tests/test_views.py @@ -79,6 +79,7 @@ def ccx_dummy_request(): @attr('shard_1') +@ddt.ddt class TestCoachDashboard(SharedModuleStoreTestCase, LoginEnrollmentTestCase): """ Tests for Custom Courses views. @@ -387,6 +388,35 @@ class TestCoachDashboard(SharedModuleStoreTestCase, LoginEnrollmentTestCase): ).exists() ) + @ddt.data("dummy_student_id", "xyz@gmail.com") + def test_manage_add_single_invalid_student(self, student_id): + """enroll a single non valid student + """ + self.make_coach() + ccx = self.make_ccx() + course_key = CCXLocator.from_course_locator(self.course.id, ccx.id) + url = reverse( + 'ccx_manage_student', + kwargs={'course_id': course_key} + ) + redirect_url = reverse( + 'ccx_coach_dashboard', + kwargs={'course_id': course_key} + ) + data = { + 'student-action': 'add', + 'student-id': u','.join([student_id, ]), # pylint: disable=no-member + } + response = self.client.post(url, data=data, follow=True) + + error_message = 'Could not find a user with name or email "{student_id}" '.format( + student_id=student_id + ) + self.assertContains(response, error_message, status_code=200) + + # we were redirected to our current location + self.assertRedirects(response, redirect_url, status_code=302) + def test_manage_add_single_student(self): """enroll a single student who is a member of the class already """ diff --git a/lms/djangoapps/ccx/views.py b/lms/djangoapps/ccx/views.py index 859ff2ef02..520e5452d3 100644 --- a/lms/djangoapps/ccx/views.py +++ b/lms/djangoapps/ccx/views.py @@ -131,7 +131,7 @@ def dashboard(request, course, ccx=None): context['schedule'] = json.dumps(schedule, indent=4) context['save_url'] = reverse( 'save_ccx', kwargs={'course_id': ccx_locator}) - context['ccx_members'] = CourseEnrollment.objects.filter(course_id=ccx_locator) + context['ccx_members'] = CourseEnrollment.objects.filter(course_id=ccx_locator, is_active=True) context['gradebook_url'] = reverse( 'ccx_gradebook', kwargs={'course_id': ccx_locator}) context['grades_csv_url'] = reverse( @@ -440,6 +440,30 @@ def ccx_invite(request, course, ccx=None): return redirect(url) +def validate_student_email(email): + """ + validate student's email id + """ + error_message = None + try: + validate_email(email) + except ValidationError: + log.info( + 'Invalid user name or email when trying to enroll student: %s', + email + ) + if email: + error_message = _( + 'Could not find a user with name or email "{email}" ' + ).format(email=email) + else: + error_message = _( + 'Please enter a valid username or email.' + ) + + return error_message + + @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @coach_dashboard @@ -452,29 +476,32 @@ def ccx_student_management(request, course, ccx=None): action = request.POST.get('student-action', None) student_id = request.POST.get('student-id', '') user = email = None + error_message = "" + course_key = CCXLocator.from_course_locator(course.id, ccx.id) try: user = get_student_from_identifier(student_id) except User.DoesNotExist: email = student_id + error_message = validate_student_email(email) + if email and not error_message: + error_message = _( + 'Could not find a user with name or email "{email}" ' + ).format(email=email) else: email = user.email + error_message = validate_student_email(email) - course_key = CCXLocator.from_course_locator(course.id, ccx.id) - try: - validate_email(email) + if error_message is None: if action == 'add': # by decree, no emails sent to students added this way # by decree, any students added this way are auto_enrolled enroll_email(course_key, email, auto_enroll=True, email_students=False) elif action == 'revoke': unenroll_email(course_key, email, email_students=False) - except ValidationError: - log.info('Invalid user name or email when trying to enroll student: %s', email) + else: + messages.error(request, error_message) - url = reverse( - 'ccx_coach_dashboard', - kwargs={'course_id': course_key} - ) + url = reverse('ccx_coach_dashboard', kwargs={'course_id': course_key}) return redirect(url) diff --git a/lms/djangoapps/certificates/tests/factories.py b/lms/djangoapps/certificates/tests/factories.py index 8f5138a22d..7a6a3ac6ba 100644 --- a/lms/djangoapps/certificates/tests/factories.py +++ b/lms/djangoapps/certificates/tests/factories.py @@ -1,5 +1,7 @@ # Factories are self documenting # pylint: disable=missing-docstring +import factory +from django.core.files.base import ContentFile from factory.django import DjangoModelFactory, ImageField from student.models import LinkedInAddToProfileConfiguration @@ -12,7 +14,8 @@ from certificates.models import ( class GeneratedCertificateFactory(DjangoModelFactory): - FACTORY_FOR = GeneratedCertificate + class Meta(object): + model = GeneratedCertificate course_id = None status = CertificateStatuses.unavailable @@ -22,29 +25,39 @@ class GeneratedCertificateFactory(DjangoModelFactory): class CertificateWhitelistFactory(DjangoModelFactory): - FACTORY_FOR = CertificateWhitelist + class Meta(object): + model = CertificateWhitelist course_id = None whitelist = True class BadgeAssertionFactory(DjangoModelFactory): - FACTORY_FOR = BadgeAssertion + class Meta(object): + model = BadgeAssertion mode = 'honor' class BadgeImageConfigurationFactory(DjangoModelFactory): - FACTORY_FOR = BadgeImageConfiguration + class Meta(object): + model = BadgeImageConfiguration mode = 'honor' - icon = ImageField(color='blue', height=50, width=50, filename='test.png', format='PNG') + icon = factory.LazyAttribute( + lambda _: ContentFile( + ImageField()._make_data( # pylint: disable=protected-access + {'color': 'blue', 'width': 50, 'height': 50, 'format': 'PNG'} + ), 'test.png' + ) + ) class CertificateHtmlViewConfigurationFactory(DjangoModelFactory): - FACTORY_FOR = CertificateHtmlViewConfiguration + class Meta(object): + model = CertificateHtmlViewConfiguration enabled = True configuration = """{ @@ -76,7 +89,8 @@ class CertificateHtmlViewConfigurationFactory(DjangoModelFactory): class LinkedInAddToProfileConfigurationFactory(DjangoModelFactory): - FACTORY_FOR = LinkedInAddToProfileConfiguration + class Meta(object): + model = LinkedInAddToProfileConfiguration enabled = True company_identifier = "0_0dPSPyS070e0HsE9HNz_13_d11_" diff --git a/lms/djangoapps/certificates/tests/test_webview_views.py b/lms/djangoapps/certificates/tests/test_webview_views.py index bceab16b0e..3134df7971 100644 --- a/lms/djangoapps/certificates/tests/test_webview_views.py +++ b/lms/djangoapps/certificates/tests/test_webview_views.py @@ -478,12 +478,12 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): response = self.client.get(test_url) self.assertEqual(response.status_code, 200) self.assertContains(response, 'lang: fr') - self.assertContains(response, 'course name: {}'.format(self.course.display_name)) + self.assertContains(response, 'course name: course_title_0') # test with second organization template response = self.client.get(test_url) self.assertEqual(response.status_code, 200) self.assertContains(response, 'lang: fr') - self.assertContains(response, 'course name: {}'.format(self.course.display_name)) + self.assertContains(response, 'course name: course_title_0') @override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED) def test_certificate_custom_template_with_org(self): @@ -510,7 +510,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): ] response = self.client.get(test_url) self.assertEqual(response.status_code, 200) - self.assertContains(response, 'course name: {}'.format(self.course.display_name)) + self.assertContains(response, 'course name: course_title_0') @override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED) def test_certificate_custom_template_with_organization(self): diff --git a/lms/djangoapps/certificates/views/webview.py b/lms/djangoapps/certificates/views/webview.py index f4e010df73..15f339c155 100644 --- a/lms/djangoapps/certificates/views/webview.py +++ b/lms/djangoapps/certificates/views/webview.py @@ -4,12 +4,14 @@ Certificate HTML webview. from datetime import datetime from uuid import uuid4 import logging +import urllib from django.conf import settings from django.contrib.auth.models import User from django.http import HttpResponse from django.template import RequestContext from django.utils.translation import ugettext as _ +from django.core.urlresolvers import reverse from courseware.courses import course_image_url from edxmako.shortcuts import render_to_response @@ -105,9 +107,27 @@ def _update_certificate_context(context, course, user, user_certificate): context['accomplishment_copy_name'] = user_fullname context['accomplishment_copy_username'] = user.username context['accomplishment_copy_course_org'] = partner_short_name - context['accomplishment_copy_course_name'] = course.display_name - context['course_image_url'] = course_image_url(course) - context['share_settings'] = settings.FEATURES.get('SOCIAL_SHARING_SETTINGS', {}) + course_title_from_cert = context['certificate_data'].get('course_title', '') + accomplishment_copy_course_name = course_title_from_cert if course_title_from_cert else course.display_name + context['accomplishment_copy_course_name'] = accomplishment_copy_course_name + share_settings = settings.FEATURES.get('SOCIAL_SHARING_SETTINGS', {}) + context['facebook_share_enabled'] = share_settings.get('CERTIFICATE_FACEBOOK', False) + context['facebook_app_id'] = getattr(settings, "FACEBOOK_APP_ID", None) + context['facebook_share_text'] = share_settings.get( + 'CERTIFICATE_FACEBOOK_TEXT', + _("I completed the {course_title} course on {platform_name}.").format( + course_title=accomplishment_copy_course_name, + platform_name=platform_name + ) + ) + context['twitter_share_enabled'] = share_settings.get('CERTIFICATE_TWITTER', False) + context['twitter_share_text'] = share_settings.get( + 'CERTIFICATE_TWITTER_TEXT', + _("I completed a course on {platform_name}. Take a look at my certificate.").format( + platform_name=platform_name + ) + ) + context['course_number'] = course.number try: badge = BadgeAssertion.objects.get(user=user, course_id=course.location.course_key) @@ -374,6 +394,19 @@ def render_html_view(request, user_id, course_id): # Append/Override the existing view context values with request-time values _update_certificate_context(context, course, user, user_certificate) + share_url = request.build_absolute_uri( + reverse( + 'certificates:html_view', + kwargs=dict(user_id=str(user_id), course_id=unicode(course_id)) + ) + ) + context['share_url'] = share_url + twitter_url = 'https://twitter.com/intent/tweet?text={twitter_share_text}&url={share_url}'.format( + twitter_share_text=context['twitter_share_text'], + share_url=urllib.quote_plus(share_url) + ) + context['twitter_url'] = twitter_url + context['full_course_image_url'] = request.build_absolute_uri(course_image_url(course)) # If enabled, show the LinkedIn "add to profile" button # Clicking this button sends the user to LinkedIn where they @@ -389,6 +422,8 @@ def render_html_view(request, user_id, course_id): course_id=unicode(course.id) )) ) + else: + context['linked_in_url'] = None # Microsites will need to be able to override any hard coded # content that was put into the context in the diff --git a/lms/djangoapps/courseware/grades.py b/lms/djangoapps/courseware/grades.py index 3f9141d9aa..5b1a9741d5 100644 --- a/lms/djangoapps/courseware/grades.py +++ b/lms/djangoapps/courseware/grades.py @@ -15,6 +15,7 @@ from django.core.cache import cache import dogstats_wrapper as dog_stats_api from courseware import courses +from courseware.access import has_access from courseware.model_data import FieldDataCache, ScoresClient from student.models import anonymous_id_for_user from util.module_utils import yield_dynamic_descriptor_descendants @@ -306,7 +307,7 @@ def grade(student, request, course, keep_raw_scores=False, field_data_cache=None grade_summary = _grade(student, request, course, keep_raw_scores, field_data_cache, scores_client) responses = GRADES_UPDATED.send_robust( sender=None, - username=request.user.username, + username=student.username, grade_summary=grade_summary, course_key=course.id, deadline=course.end @@ -405,6 +406,10 @@ def _grade(student, request, course, keep_raw_scores, field_data_cache, scores_c descendants = yield_dynamic_descriptor_descendants(section_descriptor, student.id, create_module) for module_descriptor in descendants: + user_access = has_access(student, 'load', module_descriptor, module_descriptor.location.course_key) + if not user_access: + continue + (correct, total) = get_score( student, module_descriptor, diff --git a/lms/djangoapps/courseware/tests/factories.py b/lms/djangoapps/courseware/tests/factories.py index c47fc54f41..d7c75d089f 100644 --- a/lms/djangoapps/courseware/tests/factories.py +++ b/lms/djangoapps/courseware/tests/factories.py @@ -123,7 +123,8 @@ class GlobalStaffFactory(UserFactory): class StudentModuleFactory(DjangoModelFactory): - FACTORY_FOR = StudentModule + class Meta(object): + model = StudentModule module_type = "problem" student = factory.SubFactory(UserFactory) @@ -135,7 +136,8 @@ class StudentModuleFactory(DjangoModelFactory): class UserStateSummaryFactory(DjangoModelFactory): - FACTORY_FOR = XModuleUserStateSummaryField + class Meta(object): + model = XModuleUserStateSummaryField field_name = 'existing_field' value = json.dumps('old_value') @@ -143,7 +145,8 @@ class UserStateSummaryFactory(DjangoModelFactory): class StudentPrefsFactory(DjangoModelFactory): - FACTORY_FOR = XModuleStudentPrefsField + class Meta(object): + model = XModuleStudentPrefsField field_name = 'existing_field' value = json.dumps('old_value') @@ -152,7 +155,8 @@ class StudentPrefsFactory(DjangoModelFactory): class StudentInfoFactory(DjangoModelFactory): - FACTORY_FOR = XModuleStudentInfoField + class Meta(object): + model = XModuleStudentInfoField field_name = 'existing_field' value = json.dumps('old_value') diff --git a/lms/djangoapps/courseware/tests/test_model_data.py b/lms/djangoapps/courseware/tests/test_model_data.py index a123309f85..38b51b9fdf 100644 --- a/lms/djangoapps/courseware/tests/test_model_data.py +++ b/lms/djangoapps/courseware/tests/test_model_data.py @@ -7,7 +7,7 @@ from nose.plugins.attrib import attr from functools import partial from courseware.model_data import DjangoKeyValueStore, FieldDataCache, InvalidScopeError -from courseware.models import StudentModule +from courseware.models import StudentModule, XModuleUserStateSummaryField from courseware.models import XModuleStudentInfoField, XModuleStudentPrefsField from student.tests.factories import UserFactory @@ -394,7 +394,7 @@ class TestUserStateSummaryStorage(StorageTestBase, TestCase): factory = UserStateSummaryFactory scope = Scope.user_state_summary key_factory = user_state_summary_key - storage_class = factory.FACTORY_FOR + storage_class = XModuleUserStateSummaryField class TestStudentPrefsStorage(OtherUserFailureTestMixin, StorageTestBase, TestCase): diff --git a/lms/djangoapps/discussion_api/api.py b/lms/djangoapps/discussion_api/api.py index bc918c8be0..b16d85216c 100644 --- a/lms/djangoapps/discussion_api/api.py +++ b/lms/djangoapps/discussion_api/api.py @@ -515,8 +515,10 @@ def _do_extra_actions(api_content, cc_content, request_fields, actions_form, con signal.send(sender=None, user=context["request"].user, post=cc_content) if form_value: context["cc_requester"].vote(cc_content, "up") + api_content["vote_count"] += 1 else: context["cc_requester"].unvote(cc_content) + api_content["vote_count"] -= 1 def create_thread(request, thread_data): @@ -647,6 +649,7 @@ def update_thread(request, thread_id, update_data): # Only save thread object if some of the edited fields are in the thread data, not extra actions if set(update_data) - set(actions_form.fields): serializer.save() + # signal to update Teams when a user edits a thread thread_edited.send(sender=None, user=request.user, post=cc_thread) api_thread = serializer.data _do_extra_actions(api_thread, cc_thread, update_data.keys(), actions_form, context) diff --git a/lms/djangoapps/discussion_api/tests/test_api.py b/lms/djangoapps/discussion_api/tests/test_api.py index 525e19cdba..0549af17ad 100644 --- a/lms/djangoapps/discussion_api/tests/test_api.py +++ b/lms/djangoapps/discussion_api/tests/test_api.py @@ -1877,6 +1877,7 @@ class UpdateThreadTest( httpretty.reset() httpretty.enable() self.addCleanup(httpretty.disable) + self.user = UserFactory.create() self.register_get_user_response(self.user) self.request = RequestFactory().get("/test_path") @@ -2088,48 +2089,115 @@ class UpdateThreadTest( @ddt.data(*itertools.product([True, False], [True, False])) @ddt.unpack - def test_voted(self, old_voted, new_voted): + def test_voted(self, current_vote_status, new_vote_status): """ Test attempts to edit the "voted" field. - old_voted indicates whether the thread should be upvoted at the start of - the test. new_voted indicates the value for the "voted" field in the - update. If old_voted and new_voted are the same, no update should be - made. Otherwise, a vote should be PUT or DELETEd according to the - new_voted value. + current_vote_status indicates whether the thread should be upvoted at + the start of the test. new_vote_status indicates the value for the + "voted" field in the update. If current_vote_status and new_vote_status + are the same, no update should be made. Otherwise, a vote should be PUT + or DELETEd according to the new_vote_status value. """ - if old_voted: + if current_vote_status: self.register_get_user_response(self.user, upvoted_ids=["test_thread"]) self.register_thread_votes_response("test_thread") self.register_thread() - data = {"voted": new_voted} - if old_voted == new_voted: - result = update_thread(self.request, "test_thread", data) - else: - # Vote signals should only be sent if the number of votes has changed - with self.assert_signal_sent(api, 'thread_voted', sender=None, user=self.user, exclude_args=('post',)): - result = update_thread(self.request, "test_thread", data) - self.assertEqual(result["voted"], new_voted) + data = {"voted": new_vote_status} + result = update_thread(self.request, "test_thread", data) + self.assertEqual(result["voted"], new_vote_status) last_request_path = urlparse(httpretty.last_request().path).path votes_url = "/api/v1/threads/test_thread/votes" - if old_voted == new_voted: + if current_vote_status == new_vote_status: self.assertNotEqual(last_request_path, votes_url) else: self.assertEqual(last_request_path, votes_url) self.assertEqual( httpretty.last_request().method, - "PUT" if new_voted else "DELETE" + "PUT" if new_vote_status else "DELETE" ) actual_request_data = ( - httpretty.last_request().parsed_body if new_voted else + httpretty.last_request().parsed_body if new_vote_status else parse_qs(urlparse(httpretty.last_request().path).query) ) actual_request_data.pop("request_id", None) expected_request_data = {"user_id": [str(self.user.id)]} - if new_voted: + if new_vote_status: expected_request_data["value"] = ["up"] self.assertEqual(actual_request_data, expected_request_data) + @ddt.data(*itertools.product([True, False], [True, False], [True, False])) + @ddt.unpack + def test_vote_count(self, current_vote_status, first_vote, second_vote): + """ + Tests vote_count increases and decreases correctly from the same user + """ + #setup + starting_vote_count = 0 + if current_vote_status: + self.register_get_user_response(self.user, upvoted_ids=["test_thread"]) + starting_vote_count = 1 + self.register_thread_votes_response("test_thread") + self.register_thread(overrides={"votes": {"up_count": starting_vote_count}}) + + #first vote + data = {"voted": first_vote} + result = update_thread(self.request, "test_thread", data) + self.register_thread(overrides={"voted": first_vote}) + self.assertEqual(result["vote_count"], 1 if first_vote else 0) + + #second vote + data = {"voted": second_vote} + result = update_thread(self.request, "test_thread", data) + self.assertEqual(result["vote_count"], 1 if second_vote else 0) + + @ddt.data(*itertools.product([True, False], [True, False], [True, False], [True, False])) + @ddt.unpack + def test_vote_count_two_users( + self, + current_user1_vote, + current_user2_vote, + user1_vote, + user2_vote + ): + """ + Tests vote_count increases and decreases correctly from different users + """ + #setup + user2 = UserFactory.create() + self.register_get_user_response(user2) + request2 = RequestFactory().get("/test_path") + request2.user = user2 + CourseEnrollmentFactory.create(user=user2, course_id=self.course.id) + + vote_count = 0 + if current_user1_vote: + self.register_get_user_response(self.user, upvoted_ids=["test_thread"]) + vote_count += 1 + if current_user2_vote: + self.register_get_user_response(user2, upvoted_ids=["test_thread"]) + vote_count += 1 + + for (current_vote, user_vote, request) in \ + [(current_user1_vote, user1_vote, self.request), + (current_user2_vote, user2_vote, request2)]: + + self.register_thread_votes_response("test_thread") + self.register_thread(overrides={"votes": {"up_count": vote_count}}) + + data = {"voted": user_vote} + result = update_thread(request, "test_thread", data) + if current_vote == user_vote: + self.assertEqual(result["vote_count"], vote_count) + elif user_vote: + vote_count += 1 + self.assertEqual(result["vote_count"], vote_count) + self.register_get_user_response(self.user, upvoted_ids=["test_thread"]) + else: + vote_count -= 1 + self.assertEqual(result["vote_count"], vote_count) + self.register_get_user_response(self.user, upvoted_ids=[]) + @ddt.data(*itertools.product([True, False], [True, False])) @ddt.unpack def test_abuse_flagged(self, old_flagged, new_flagged): @@ -2196,15 +2264,15 @@ class UpdateCommentTest( def setUp(self): super(UpdateCommentTest, self).setUp() - self.user = UserFactory.create() - CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id) - httpretty.reset() httpretty.enable() self.addCleanup(httpretty.disable) + + self.user = UserFactory.create() self.register_get_user_response(self.user) self.request = RequestFactory().get("/test_path") self.request.user = self.user + CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id) def register_comment(self, overrides=None, thread_overrides=None, course=None): """ @@ -2414,48 +2482,117 @@ class UpdateCommentTest( @ddt.data(*itertools.product([True, False], [True, False])) @ddt.unpack - def test_voted(self, old_voted, new_voted): + def test_voted(self, current_vote_status, new_vote_status): """ Test attempts to edit the "voted" field. - old_voted indicates whether the comment should be upvoted at the start of - the test. new_voted indicates the value for the "voted" field in the - update. If old_voted and new_voted are the same, no update should be - made. Otherwise, a vote should be PUT or DELETEd according to the - new_voted value. + current_vote_status indicates whether the comment should be upvoted at + the start of the test. new_vote_status indicates the value for the + "voted" field in the update. If current_vote_status and new_vote_status + are the same, no update should be made. Otherwise, a vote should be PUT + or DELETEd according to the new_vote_status value. """ - if old_voted: + vote_count = 0 + if current_vote_status: self.register_get_user_response(self.user, upvoted_ids=["test_comment"]) + vote_count = 1 self.register_comment_votes_response("test_comment") - self.register_comment() - data = {"voted": new_voted} - if old_voted == new_voted: - result = update_comment(self.request, "test_comment", data) - else: - # Vote signals should only be sent if the number of votes has changed - with self.assert_signal_sent(api, 'comment_voted', sender=None, user=self.user, exclude_args=('post',)): - result = update_comment(self.request, "test_comment", data) - self.assertEqual(result["voted"], new_voted) + self.register_comment(overrides={"votes": {"up_count": vote_count}}) + data = {"voted": new_vote_status} + result = update_comment(self.request, "test_comment", data) + self.assertEqual(result["vote_count"], 1 if new_vote_status else 0) + self.assertEqual(result["voted"], new_vote_status) last_request_path = urlparse(httpretty.last_request().path).path votes_url = "/api/v1/comments/test_comment/votes" - if old_voted == new_voted: + if current_vote_status == new_vote_status: self.assertNotEqual(last_request_path, votes_url) else: self.assertEqual(last_request_path, votes_url) self.assertEqual( httpretty.last_request().method, - "PUT" if new_voted else "DELETE" + "PUT" if new_vote_status else "DELETE" ) actual_request_data = ( - httpretty.last_request().parsed_body if new_voted else + httpretty.last_request().parsed_body if new_vote_status else parse_qs(urlparse(httpretty.last_request().path).query) ) actual_request_data.pop("request_id", None) expected_request_data = {"user_id": [str(self.user.id)]} - if new_voted: + if new_vote_status: expected_request_data["value"] = ["up"] self.assertEqual(actual_request_data, expected_request_data) + @ddt.data(*itertools.product([True, False], [True, False], [True, False])) + @ddt.unpack + def test_vote_count(self, current_vote_status, first_vote, second_vote): + """ + Tests vote_count increases and decreases correctly from the same user + """ + #setup + starting_vote_count = 0 + if current_vote_status: + self.register_get_user_response(self.user, upvoted_ids=["test_comment"]) + starting_vote_count = 1 + self.register_comment_votes_response("test_comment") + self.register_comment(overrides={"votes": {"up_count": starting_vote_count}}) + + #first vote + data = {"voted": first_vote} + result = update_comment(self.request, "test_comment", data) + self.register_comment(overrides={"voted": first_vote}) + self.assertEqual(result["vote_count"], 1 if first_vote else 0) + + #second vote + data = {"voted": second_vote} + result = update_comment(self.request, "test_comment", data) + self.assertEqual(result["vote_count"], 1 if second_vote else 0) + + @ddt.data(*itertools.product([True, False], [True, False], [True, False], [True, False])) + @ddt.unpack + def test_vote_count_two_users( + self, + current_user1_vote, + current_user2_vote, + user1_vote, + user2_vote + ): + """ + Tests vote_count increases and decreases correctly from different users + """ + user2 = UserFactory.create() + self.register_get_user_response(user2) + request2 = RequestFactory().get("/test_path") + request2.user = user2 + CourseEnrollmentFactory.create(user=user2, course_id=self.course.id) + + vote_count = 0 + if current_user1_vote: + self.register_get_user_response(self.user, upvoted_ids=["test_comment"]) + vote_count += 1 + if current_user2_vote: + self.register_get_user_response(user2, upvoted_ids=["test_comment"]) + vote_count += 1 + + for (current_vote, user_vote, request) in \ + [(current_user1_vote, user1_vote, self.request), + (current_user2_vote, user2_vote, request2)]: + + self.register_comment_votes_response("test_comment") + self.register_comment(overrides={"votes": {"up_count": vote_count}}) + + data = {"voted": user_vote} + result = update_comment(request, "test_comment", data) + if current_vote == user_vote: + self.assertEqual(result["vote_count"], vote_count) + elif user_vote: + vote_count += 1 + self.assertEqual(result["vote_count"], vote_count) + self.register_get_user_response(self.user, upvoted_ids=["test_comment"]) + else: + vote_count -= 1 + self.assertEqual(result["vote_count"], vote_count) + self.register_get_user_response(self.user, upvoted_ids=[]) + @ddt.data(*itertools.product([True, False], [True, False])) @ddt.unpack def test_abuse_flagged(self, old_flagged, new_flagged): @@ -2912,6 +3049,12 @@ class RetrieveThreadTest( self.assertEqual(get_thread(self.request, self.thread_id), expected_response_data) self.assertEqual(httpretty.last_request().method, "GET") + def test_not_enrolled_in_course(self): + self.register_thread() + self.request.user = UserFactory.create() + with self.assertRaises(Http404): + get_thread(self.request, self.thread_id) + @ddt.data( *itertools.product( [ diff --git a/lms/djangoapps/discussion_api/tests/test_views.py b/lms/djangoapps/discussion_api/tests/test_views.py index 3046233734..960ce4f638 100644 --- a/lms/djangoapps/discussion_api/tests/test_views.py +++ b/lms/djangoapps/discussion_api/tests/test_views.py @@ -588,7 +588,6 @@ class ThreadViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): self.thread_id = "test_thread" def test_basic(self): - #from nose.tools import set_trace;set_trace() self.register_get_user_response(self.user) cs_thread = make_minimal_cs_thread({ "id": self.thread_id, @@ -608,7 +607,6 @@ class ThreadViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): self.assertEqual(httpretty.last_request().method, "DELETE") def test_delete_nonexistent_thread(self): - #from nose.tools import set_trace;set_trace() self.register_get_thread_error_response(self.thread_id, 404) response = self.client.delete(self.url) self.assertEqual(response.status_code, 404) diff --git a/lms/djangoapps/discussion_api/views.py b/lms/djangoapps/discussion_api/views.py index 5881c63444..7e7bc2710a 100644 --- a/lms/djangoapps/discussion_api/views.py +++ b/lms/djangoapps/discussion_api/views.py @@ -19,10 +19,11 @@ from discussion_api.api import ( get_comment_list, get_course, get_course_topics, + get_thread, get_thread_list, update_comment, update_thread, - get_thread) +) from discussion_api.forms import CommentListGetForm, ThreadListGetForm from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin diff --git a/lms/djangoapps/django_comment_client/tests/factories.py b/lms/djangoapps/django_comment_client/tests/factories.py index a3393b6010..d5ad2dbe74 100644 --- a/lms/djangoapps/django_comment_client/tests/factories.py +++ b/lms/djangoapps/django_comment_client/tests/factories.py @@ -3,11 +3,13 @@ from django_comment_common.models import Role, Permission class RoleFactory(DjangoModelFactory): - FACTORY_FOR = Role + class Meta(object): # pylint: disable=missing-docstring + model = Role name = 'Student' course_id = 'edX/toy/2012_Fall' class PermissionFactory(DjangoModelFactory): - FACTORY_FOR = Permission + class Meta(object): # pylint: disable=missing-docstring + model = Permission name = 'create_comment' diff --git a/lms/djangoapps/instructor/tests/test_legacy_raw_download_csv.py b/lms/djangoapps/instructor/tests/test_legacy_raw_download_csv.py index 4f7cfdf9ed..5eeb76cee6 100644 --- a/lms/djangoapps/instructor/tests/test_legacy_raw_download_csv.py +++ b/lms/djangoapps/instructor/tests/test_legacy_raw_download_csv.py @@ -5,6 +5,8 @@ Create course and answer a problem to test raw grade CSV from django.contrib.auth.models import User from django.core.urlresolvers import reverse +from instructor.utils import DummyRequest +from instructor.views.legacy import get_student_grade_summary_data from nose.plugins.attrib import attr from courseware.tests.test_submitting_problems import TestSubmittingProblems @@ -24,7 +26,7 @@ class TestRawGradeCSV(TestSubmittingProblems): super(TestRawGradeCSV, self).setUp() self.instructor = 'view2@test.com' - self.create_account('u2', self.instructor, self.password) + self.student_user2 = self.create_account('u2', self.instructor, self.password) self.activate_user(self.instructor) CourseStaffRole(self.course.id).add_users(User.objects.get(email=self.instructor)) self.logout() @@ -38,14 +40,20 @@ class TestRawGradeCSV(TestSubmittingProblems): self.add_dropdown_to_section(self.homework.location, 'p3', 1) self.refresh_course() + def answer_question(self): + """ + Answer a question correctly in the course + """ + self.login(self.instructor, self.password) + resp = self.submit_question_answer('p2', {'2_1': 'Correct'}) + self.assertEqual(resp.status_code, 200) + def test_download_raw_grades_dump(self): """ Grab raw grade report and make sure all grades are reported. """ # Answer second problem correctly with 2nd user to expose bug - self.login(self.instructor, self.password) - resp = self.submit_question_answer('p2', {'2_1': 'Correct'}) - self.assertEqual(resp.status_code, 200) + self.answer_question() url = reverse('instructor_dashboard_legacy', kwargs={'course_id': self.course.id.to_deprecated_string()}) msg = "url = {0}\n".format(url) @@ -58,3 +66,53 @@ class TestRawGradeCSV(TestSubmittingProblems): "2","u2","username","view2@test.com","","0.0","1.0","0.0" ''' self.assertEqual(body, expected_csv, msg) + + def test_grade_summary_data(self): + """ + Test grade summary data report generation + """ + self.answer_question() + + request = DummyRequest() + data = get_student_grade_summary_data(request, self.course, get_raw_scores=False) + expected_data = { + 'students': [self.student_user, self.student_user2], + 'header': [ + u'ID', u'Username', u'Full Name', u'edX email', u'External email', + u'HW 01', u'HW 02', u'HW 03', u'HW 04', u'HW 05', u'HW 06', u'HW 07', + u'HW 08', u'HW 09', u'HW 10', u'HW 11', u'HW 12', u'HW Avg', u'Lab 01', + u'Lab 02', u'Lab 03', u'Lab 04', u'Lab 05', u'Lab 06', u'Lab 07', + u'Lab 08', u'Lab 09', u'Lab 10', u'Lab 11', u'Lab 12', u'Lab Avg', u'Midterm', + u'Final' + ], + 'data': [ + [ + 1, u'u1', u'username', u'view@test.com', '', 0.0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ], + [ + 2, u'u2', u'username', u'view2@test.com', '', 0.3333333333333333, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0.03333333333333333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + ] + ], + 'assignments': [ + u'HW 01', u'HW 02', u'HW 03', u'HW 04', u'HW 05', u'HW 06', u'HW 07', u'HW 08', + u'HW 09', u'HW 10', u'HW 11', u'HW 12', u'HW Avg', u'Lab 01', u'Lab 02', + u'Lab 03', u'Lab 04', u'Lab 05', u'Lab 06', u'Lab 07', u'Lab 08', u'Lab 09', + u'Lab 10', u'Lab 11', u'Lab 12', u'Lab Avg', u'Midterm', u'Final' + ] + } + + for key in ['assignments', 'header']: + self.assertListEqual(expected_data[key], data[key]) + + for index, student in enumerate(expected_data['students']): + self.assertEqual( + student.username, + data['students'][index].username + ) + self.assertListEqual( + expected_data['data'][index], + data['data'][index] + ) diff --git a/lms/djangoapps/instructor_task/tests/factories.py b/lms/djangoapps/instructor_task/tests/factories.py index 4f67b52ef6..bd62c8f378 100644 --- a/lms/djangoapps/instructor_task/tests/factories.py +++ b/lms/djangoapps/instructor_task/tests/factories.py @@ -9,7 +9,8 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey class InstructorTaskFactory(DjangoModelFactory): - FACTORY_FOR = InstructorTask + class Meta(object): # pylint: disable=missing-docstring + model = InstructorTask task_type = 'rescore_problem' course_id = SlashSeparatedCourseKey("MITx", "999", "Robot_Super_Course") diff --git a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py index c6701ebc55..fe74ba6f37 100644 --- a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py +++ b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py @@ -9,6 +9,7 @@ Tests that CSV grade report generation works with unicode emails. import ddt from mock import Mock, patch import tempfile +from openedx.core.djangoapps.course_groups import cohorts import unicodecsv from django.core.urlresolvers import reverse from django.test.utils import override_settings @@ -662,7 +663,7 @@ class TestProblemReportCohortedContent(TestReportMixin, ContentGroupTestCase, In """ def setUp(self): super(TestProblemReportCohortedContent, self).setUp() - # contstruct cohorted problems to work on. + # construct cohorted problems to work on. self.add_course_content() vertical = ItemFactory.create( parent_location=self.problem_section.location, @@ -681,6 +682,23 @@ class TestProblemReportCohortedContent(TestReportMixin, ContentGroupTestCase, In group_access={self.course.user_partitions[0].id: [self.course.user_partitions[0].groups[1].id]} ) + def _format_user_grade(self, header_row, user, grade): + """ + Helper method that format the user grade + Args: + header_row(list): header row of csv containing Student ID, Email, Username etc + user(object): Django user object + grade(list): Users' grade list + """ + return dict(zip( + header_row, + [ + unicode(user.id), + user.email, + user.username, + ] + grade + )) + def test_cohort_content(self): self.submit_student_answer(self.alpha_user.username, u'Pröblem0', ['Option 1', 'Option 1']) resp = self.submit_student_answer(self.alpha_user.username, u'Pröblem1', ['Option 1', 'Option 1']) @@ -695,49 +713,75 @@ class TestProblemReportCohortedContent(TestReportMixin, ContentGroupTestCase, In self.assertDictContainsSubset( {'action_name': 'graded', 'attempted': 4, 'succeeded': 4, 'failed': 0}, result ) - problem_names = [u'Homework 1: Problem - Pröblem0', u'Homework 1: Problem - Pröblem1'] header_row = [u'Student ID', u'Email', u'Username', u'Final Grade'] for problem in problem_names: header_row += [problem + ' (Earned)', problem + ' (Possible)'] - self.verify_rows_in_csv([ - dict(zip( - header_row, - [ - unicode(self.staff_user.id), - self.staff_user.email, - self.staff_user.username, u'0.0', u'N/A', u'N/A', u'N/A', u'N/A' - ] - )), - dict(zip( - header_row, - [ - unicode(self.alpha_user.id), - self.alpha_user.email, - self.alpha_user.username, - u'1.0', u'2.0', u'2.0', u'N/A', u'N/A' - ] - )), - dict(zip( - header_row, - [ - unicode(self.beta_user.id), - self.beta_user.email, - self.beta_user.username, - u'0.5', u'N/A', u'N/A', u'1.0', u'2.0' - ] - )), - dict(zip( - header_row, - [ - unicode(self.non_cohorted_user.id), - self.non_cohorted_user.email, - self.non_cohorted_user.username, - u'0.0', u'N/A', u'N/A', u'N/A', u'N/A' - ] - )), - ]) + user_grades = [ + {'user': self.staff_user, 'grade': [u'0.0', u'N/A', u'N/A', u'N/A', u'N/A']}, + {'user': self.alpha_user, 'grade': [u'1.0', u'2.0', u'2.0', u'N/A', u'N/A']}, + {'user': self.beta_user, 'grade': [u'0.5', u'N/A', u'N/A', u'1.0', u'2.0']}, + {'user': self.non_cohorted_user, 'grade': [u'0.0', u'N/A', u'N/A', u'N/A', u'N/A']}, + ] + + # Verify generated grades and expected grades match + expected_grades = [self._format_user_grade(header_row, **user_grade) for user_grade in user_grades] + self.verify_rows_in_csv(expected_grades) + + @patch('courseware.grades.MaxScoresCache.get', Mock(return_value=1)) + def test_cohort_content_with_maxcache(self): + """ + Tests the cohoted course grading to test the scenario in which `max_scores_cache` is set for the course + problems. + """ + # Course is cohorted + self.assertTrue(cohorts.is_course_cohorted(self.course.id)) + + # Verify user groups + self.assertEquals( + cohorts.get_cohort(self.alpha_user, self.course.id).id, + self.course.user_partitions[0].groups[0].id, + "alpha_user should be assigned to the correct cohort" + ) + self.assertEquals( + cohorts.get_cohort(self.beta_user, self.course.id).id, + self.course.user_partitions[0].groups[1].id, + "beta_user should be assigned to the correct cohort" + ) + + # Verify user enrollment + for user in [self.alpha_user, self.beta_user, self.non_cohorted_user]: + self.assertTrue(CourseEnrollment.is_enrolled(user, self.course.id)) + + self.submit_student_answer(self.alpha_user.username, u'Pröblem0', ['Option 1', 'Option 1']) + resp = self.submit_student_answer(self.alpha_user.username, u'Pröblem1', ['Option 1', 'Option 1']) + self.assertEqual(resp.status_code, 404) + + resp = self.submit_student_answer(self.beta_user.username, u'Pröblem0', ['Option 1', 'Option 2']) + self.assertEqual(resp.status_code, 404) + self.submit_student_answer(self.beta_user.username, u'Pröblem1', ['Option 1', 'Option 2']) + + with patch('instructor_task.tasks_helper._get_current_task'): + result = upload_problem_grade_report(None, None, self.course.id, None, 'graded') + self.assertDictContainsSubset( + {'action_name': 'graded', 'attempted': 4, 'succeeded': 4, 'failed': 0}, result + ) + problem_names = [u'Homework 1: Problem - Pröblem0', u'Homework 1: Problem - Pröblem1'] + header_row = [u'Student ID', u'Email', u'Username', u'Final Grade'] + for problem in problem_names: + header_row += [problem + ' (Earned)', problem + ' (Possible)'] + + user_grades = [ + {'user': self.staff_user, 'grade': [u'0.0', u'N/A', u'N/A', u'N/A', u'N/A']}, + {'user': self.alpha_user, 'grade': [u'1.0', u'2.0', u'2.0', u'N/A', u'N/A']}, + {'user': self.beta_user, 'grade': [u'0.5', u'N/A', u'N/A', u'1.0', u'2.0']}, + {'user': self.non_cohorted_user, 'grade': [u'0.0', u'N/A', u'N/A', u'N/A', u'N/A']}, + ] + + # Verify generated grades and expected grades match + expected_grades = [self._format_user_grade(header_row, **grade) for grade in user_grades] + self.verify_rows_in_csv(expected_grades) @ddt.ddt diff --git a/lms/djangoapps/licenses/tests.py b/lms/djangoapps/licenses/tests.py index ceaf693b28..1c68c4c43a 100644 --- a/lms/djangoapps/licenses/tests.py +++ b/lms/djangoapps/licenses/tests.py @@ -32,7 +32,8 @@ log = logging.getLogger(__name__) class CourseSoftwareFactory(DjangoModelFactory): '''Factory for generating CourseSoftware objects in database''' - FACTORY_FOR = CourseSoftware + class Meta(object): # pylint: disable=missing-docstring + model = CourseSoftware name = SOFTWARE_1 full_name = SOFTWARE_1 @@ -47,7 +48,8 @@ class UserLicenseFactory(DjangoModelFactory): By default, the user assigned is null, indicating that the serial number has not yet been assigned. ''' - FACTORY_FOR = UserLicense + class Meta(object): # pylint: disable=missing-docstring + model = UserLicense user = None software = factory.SubFactory(CourseSoftwareFactory) diff --git a/lms/djangoapps/oauth2_handler/handlers.py b/lms/djangoapps/oauth2_handler/handlers.py index 5989dfb2fe..cab2ec5337 100644 --- a/lms/djangoapps/oauth2_handler/handlers.py +++ b/lms/djangoapps/oauth2_handler/handlers.py @@ -2,9 +2,9 @@ from django.conf import settings from django.core.cache import cache -from xmodule.modulestore.django import modulestore from courseware.access import has_access +from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.user_api.models import UserPreference from student.models import anonymous_id_for_user from student.models import UserProfile @@ -200,13 +200,13 @@ class CourseAccessHandler(object): course_ids = cache.get(key) if not course_ids: - courses = _get_all_courses() + course_keys = CourseOverview.get_all_course_keys() # Global staff have access to all courses. Filter courses for non-global staff. if not GlobalStaff().has_user(user): - courses = [course for course in courses if has_access(user, access_type, course)] + course_keys = [course_key for course_key in course_keys if has_access(user, access_type, course_key)] - course_ids = [unicode(course.id) for course in courses] + course_ids = [unicode(course_key) for course_key in course_keys] cache.set(key, course_ids, self.COURSE_CACHE_TIMEOUT) @@ -234,12 +234,3 @@ class IDTokenHandler(OpenIDHandler, ProfileHandler, CourseAccessHandler, Permiss class UserInfoHandler(OpenIDHandler, ProfileHandler, CourseAccessHandler, PermissionsHandler): """ Configure the UserInfo handler for the LMS. """ pass - - -def _get_all_courses(): - """ Utility function to list all available courses. """ - - ms_courses = modulestore().get_courses() - courses = [course for course in ms_courses if course.scope_ids.block_type == 'course'] - - return courses diff --git a/lms/djangoapps/oauth2_handler/tests.py b/lms/djangoapps/oauth2_handler/tests.py index d34d24072c..be6ff9bd74 100644 --- a/lms/djangoapps/oauth2_handler/tests.py +++ b/lms/djangoapps/oauth2_handler/tests.py @@ -2,16 +2,17 @@ from django.core.cache import cache from django.test.utils import override_settings from lang_pref import LANGUAGE_KEY -from opaque_keys.edx.locations import SlashSeparatedCourseKey -from xmodule.modulestore.tests.django_utils import TEST_DATA_MIXED_TOY_MODULESTORE +from xmodule.modulestore.tests.factories import (check_mongo_calls, CourseFactory) from student.models import anonymous_id_for_user from student.models import UserProfile -from student.roles import CourseStaffRole, CourseInstructorRole +from student.roles import (CourseInstructorRole, CourseStaffRole, GlobalStaff, + OrgInstructorRole, OrgStaffRole) from student.tests.factories import UserFactory, UserProfileFactory from openedx.core.djangoapps.user_api.preferences.api import set_user_preference from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase + # Will also run default tests for IDTokens and UserInfo from oauth2_provider.tests import IDTokenTestCase, UserInfoTestCase @@ -19,14 +20,10 @@ from oauth2_provider.tests import IDTokenTestCase, UserInfoTestCase class BaseTestMixin(ModuleStoreTestCase): profile = None - MODULESTORE = TEST_DATA_MIXED_TOY_MODULESTORE - def setUp(self): super(BaseTestMixin, self).setUp() - - self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') + self.course_key = CourseFactory.create(emit_signals=True).id self.course_id = unicode(self.course_key) - self.user_factory = UserFactory self.set_user(self.make_user()) @@ -77,7 +74,8 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase): self.assertEqual(language, locale) def test_no_special_course_access(self): - scopes, claims = self.get_id_token_values('openid course_instructor course_staff') + with check_mongo_calls(0): + scopes, claims = self.get_id_token_values('openid course_instructor course_staff') self.assertNotIn('course_staff', scopes) self.assertNotIn('staff_courses', claims) @@ -86,14 +84,15 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase): def test_course_staff_courses(self): CourseStaffRole(self.course_key).add_users(self.user) - - scopes, claims = self.get_id_token_values('openid course_staff') + with check_mongo_calls(0): + scopes, claims = self.get_id_token_values('openid course_staff') self.assertIn('course_staff', scopes) self.assertNotIn('staff_courses', claims) # should not return courses in id_token def test_course_instructor_courses(self): - CourseInstructorRole(self.course_key).add_users(self.user) + with check_mongo_calls(0): + CourseInstructorRole(self.course_key).add_users(self.user) scopes, claims = self.get_id_token_values('openid course_instructor') @@ -104,6 +103,7 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase): CourseStaffRole(self.course_key).add_users(self.user) course_id = unicode(self.course_key) + nonexistent_course_id = 'some/other/course' claims = { @@ -113,7 +113,8 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase): } } - scopes, claims = self.get_id_token_values(scope='openid course_staff', claims=claims) + with check_mongo_calls(0): + scopes, claims = self.get_id_token_values(scope='openid course_staff', claims=claims) self.assertIn('course_staff', scopes) self.assertIn('staff_courses', claims) @@ -133,6 +134,11 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase): class UserInfoTest(BaseTestMixin, UserInfoTestCase): + def setUp(self): + super(UserInfoTest, self).setUp() + # create another course in the DB that only global staff have access to + CourseFactory.create(emit_signals=True) + def token_for_scope(self, scope): full_scope = 'openid %s' % scope self.set_access_token_scope(full_scope) @@ -158,43 +164,64 @@ class UserInfoTest(BaseTestMixin, UserInfoTestCase): self.assertEqual(result.status_code, 200) return claims + def _assert_role_using_scope(self, scope, claim, assert_one_course=True): + with check_mongo_calls(0): + claims = self.get_with_scope(scope) + self.assertEqual(len(claims), 2) + courses = claims[claim] + self.assertIn(self.course_id, courses) + if assert_one_course: + self.assertEqual(len(courses), 1) + + def test_request_global_staff_courses_using_scope(self): + GlobalStaff().add_users(self.user) + self._assert_role_using_scope('course_staff', 'staff_courses', assert_one_course=False) + + def test_request_org_staff_courses_using_scope(self): + OrgStaffRole(self.course_key.org).add_users(self.user) + self._assert_role_using_scope('course_staff', 'staff_courses') + + def test_request_org_instructor_courses_using_scope(self): + OrgInstructorRole(self.course_key.org).add_users(self.user) + self._assert_role_using_scope('course_instructor', 'instructor_courses') + def test_request_staff_courses_using_scope(self): CourseStaffRole(self.course_key).add_users(self.user) - claims = self.get_with_scope('course_staff') - - courses = claims['staff_courses'] - self.assertIn(self.course_id, courses) - self.assertEqual(len(courses), 1) + self._assert_role_using_scope('course_staff', 'staff_courses') def test_request_instructor_courses_using_scope(self): CourseInstructorRole(self.course_key).add_users(self.user) - claims = self.get_with_scope('course_instructor') + self._assert_role_using_scope('course_instructor', 'instructor_courses') - courses = claims['instructor_courses'] + def _assert_role_using_claim(self, scope, claim): + values = [self.course_id, 'some_invalid_course'] + with check_mongo_calls(0): + claims = self.get_with_claim_value(scope, claim, values) + self.assertEqual(len(claims), 2) + + courses = claims[claim] self.assertIn(self.course_id, courses) self.assertEqual(len(courses), 1) + def test_request_global_staff_courses_with_claims(self): + GlobalStaff().add_users(self.user) + self._assert_role_using_claim('course_staff', 'staff_courses') + + def test_request_org_staff_courses_with_claims(self): + OrgStaffRole(self.course_key.org).add_users(self.user) + self._assert_role_using_claim('course_staff', 'staff_courses') + + def test_request_org_instructor_courses_with_claims(self): + OrgInstructorRole(self.course_key.org).add_users(self.user) + self._assert_role_using_claim('course_instructor', 'instructor_courses') + def test_request_staff_courses_with_claims(self): CourseStaffRole(self.course_key).add_users(self.user) - - values = [self.course_id, 'some_invalid_course'] - claims = self.get_with_claim_value('course_staff', 'staff_courses', values) - self.assertEqual(len(claims), 2) - - courses = claims['staff_courses'] - self.assertIn(self.course_id, courses) - self.assertEqual(len(courses), 1) + self._assert_role_using_claim('course_staff', 'staff_courses') def test_request_instructor_courses_with_claims(self): CourseInstructorRole(self.course_key).add_users(self.user) - - values = ['edX/toy/TT_2012_Fall', self.course_id, 'invalid_course_id'] - claims = self.get_with_claim_value('course_instructor', 'instructor_courses', values) - self.assertEqual(len(claims), 2) - - courses = claims['instructor_courses'] - self.assertIn(self.course_id, courses) - self.assertEqual(len(courses), 1) + self._assert_role_using_claim('course_instructor', 'instructor_courses') def test_permissions_scope(self): claims = self.get_with_scope('permissions') diff --git a/lms/djangoapps/teams/models.py b/lms/djangoapps/teams/models.py index accedf6b56..cbcb822554 100644 --- a/lms/djangoapps/teams/models.py +++ b/lms/djangoapps/teams/models.py @@ -28,6 +28,7 @@ from xmodule_django.models import CourseKeyField from util.model_utils import slugify from student.models import LanguageField, CourseEnrollment from .errors import AlreadyOnTeamInCourse, NotEnrolledInCourseForTeam, ImmutableMembershipFieldException +from teams.utils import emit_team_event from teams import TEAM_DISCUSSION_CONTEXT @@ -247,3 +248,6 @@ class CourseTeamMembership(models.Model): membership.team.last_activity_at = now membership.team.save() membership.save() + emit_team_event('edx.team.activity_updated', membership.team.course_id, { + 'team_id': membership.team_id, + }) diff --git a/lms/djangoapps/teams/serializers.py b/lms/djangoapps/teams/serializers.py index dae2578bd2..f51ad9e61c 100644 --- a/lms/djangoapps/teams/serializers.py +++ b/lms/djangoapps/teams/serializers.py @@ -90,6 +90,18 @@ class CourseTeamCreationSerializer(serializers.ModelSerializer): ) +class CourseTeamSerializerWithoutMembership(CourseTeamSerializer): + """The same as the `CourseTeamSerializer`, but elides the membership field. + + Intended to be used as a sub-serializer for serializing team + memberships, since the membership field is redundant in that case. + """ + + def __init__(self, *args, **kwargs): + super(CourseTeamSerializerWithoutMembership, self).__init__(*args, **kwargs) + del self.fields['membership'] + + class MembershipSerializer(serializers.ModelSerializer): """Serializes CourseTeamMemberships with information about both teams and users.""" profile_configuration = deepcopy(settings.ACCOUNT_VISIBILITY_CONFIGURATION) @@ -112,8 +124,7 @@ class MembershipSerializer(serializers.ModelSerializer): view_name='teams_detail', read_only=True, ), - expanded_serializer=CourseTeamSerializer(read_only=True), - exclude_expand_fields={'user'}, + expanded_serializer=CourseTeamSerializerWithoutMembership(read_only=True), ) class Meta(object): diff --git a/lms/djangoapps/teams/tests/factories.py b/lms/djangoapps/teams/tests/factories.py index 07077a5161..ee58e1ad69 100644 --- a/lms/djangoapps/teams/tests/factories.py +++ b/lms/djangoapps/teams/tests/factories.py @@ -18,8 +18,9 @@ class CourseTeamFactory(DjangoModelFactory): Note that team_id is not auto-generated from name when using the factory. """ - FACTORY_FOR = CourseTeam - FACTORY_DJANGO_GET_OR_CREATE = ('team_id',) + class Meta(object): # pylint: disable=missing-docstring + model = CourseTeam + django_get_or_create = ('team_id',) team_id = factory.Sequence('team-{0}'.format) discussion_topic_id = factory.LazyAttribute(lambda a: uuid4().hex) @@ -30,5 +31,6 @@ class CourseTeamFactory(DjangoModelFactory): class CourseTeamMembershipFactory(DjangoModelFactory): """Factory for CourseTeamMemberships.""" - FACTORY_FOR = CourseTeamMembership + class Meta(object): # pylint: disable=missing-docstring + model = CourseTeamMembership last_activity_at = LAST_ACTIVITY_AT diff --git a/lms/djangoapps/teams/tests/test_models.py b/lms/djangoapps/teams/tests/test_models.py index ba9fc6f53c..753665a473 100644 --- a/lms/djangoapps/teams/tests/test_models.py +++ b/lms/djangoapps/teams/tests/test_models.py @@ -24,8 +24,9 @@ from opaque_keys.edx.keys import CourseKey from student.tests.factories import CourseEnrollmentFactory, UserFactory from .factories import CourseTeamFactory, CourseTeamMembershipFactory -from ..models import CourseTeam, CourseTeamMembership +from teams.models import CourseTeam, CourseTeamMembership from teams import TEAM_DISCUSSION_CONTEXT +from util.testing import EventTestMixin COURSE_KEY1 = CourseKey.from_string('edx/history/1') COURSE_KEY2 = CourseKey.from_string('edx/history/2') @@ -114,7 +115,7 @@ class TeamMembershipTest(SharedModuleStoreTestCase): @ddt.ddt -class TeamSignalsTest(SharedModuleStoreTestCase): +class TeamSignalsTest(EventTestMixin, SharedModuleStoreTestCase): """Tests for handling of team-related signals.""" SIGNALS_LIST = ( @@ -133,7 +134,7 @@ class TeamSignalsTest(SharedModuleStoreTestCase): def setUp(self): """Create a user with a team to test signals.""" - super(TeamSignalsTest, self).setUp() + super(TeamSignalsTest, self).setUp('teams.utils.tracker') self.user = UserFactory.create(username="user") self.moderator = UserFactory.create(username="moderator") self.team = CourseTeamFactory(discussion_topic_id=self.DISCUSSION_TOPIC_ID) @@ -168,9 +169,14 @@ class TeamSignalsTest(SharedModuleStoreTestCase): now = datetime.utcnow().replace(tzinfo=pytz.utc) self.assertGreater(now, team.last_activity_at) self.assertGreater(now, team_membership.last_activity_at) + self.assert_event_emitted( + 'edx.team.activity_updated', + team_id=team.id, + ) else: self.assertEqual(team.last_activity_at, team_last_activity) self.assertEqual(team_membership.last_activity_at, team_membership_last_activity) + self.assert_no_events_were_emitted() @ddt.data( *itertools.product( diff --git a/lms/djangoapps/teams/tests/test_serializers.py b/lms/djangoapps/teams/tests/test_serializers.py index df2dffdf22..123b2f793b 100644 --- a/lms/djangoapps/teams/tests/test_serializers.py +++ b/lms/djangoapps/teams/tests/test_serializers.py @@ -70,10 +70,7 @@ class MembershipSerializerTestCase(SerializerTestCase): 'has_image': False } }) - self.assertEqual(data['team']['membership'][0]['user'], { - 'url': 'http://testserver/api/user/v1/accounts/' + username, - 'username': username - }) + self.assertNotIn('membership', data['team']) class BaseTopicSerializerTestCase(SerializerTestCase): diff --git a/lms/djangoapps/teams/tests/test_views.py b/lms/djangoapps/teams/tests/test_views.py index f1610a6d99..cc7fcc23ed 100644 --- a/lms/djangoapps/teams/tests/test_views.py +++ b/lms/djangoapps/teams/tests/test_views.py @@ -418,7 +418,7 @@ class TestListTeamsAPI(EventTestMixin, TeamAPITestCase): """Test cases for the team listing API endpoint.""" def setUp(self): # pylint: disable=arguments-differ - super(TestListTeamsAPI, self).setUp('teams.views.tracker') + super(TestListTeamsAPI, self).setUp('teams.utils.tracker') @ddt.data( (None, 401), @@ -592,7 +592,7 @@ class TestCreateTeamAPI(EventTestMixin, TeamAPITestCase): """Test cases for the team creation endpoint.""" def setUp(self): # pylint: disable=arguments-differ - super(TestCreateTeamAPI, self).setUp('teams.views.tracker') + super(TestCreateTeamAPI, self).setUp('teams.utils.tracker') @ddt.data( (None, 401), @@ -803,7 +803,7 @@ class TestDeleteTeamAPI(EventTestMixin, TeamAPITestCase): """Test cases for the team delete endpoint.""" def setUp(self): # pylint: disable=arguments-differ - super(TestDeleteTeamAPI, self).setUp('teams.views.tracker') + super(TestDeleteTeamAPI, self).setUp('teams.utils.tracker') @ddt.data( (None, 401), @@ -853,7 +853,7 @@ class TestUpdateTeamAPI(EventTestMixin, TeamAPITestCase): """Test cases for the team update endpoint.""" def setUp(self): # pylint: disable=arguments-differ - super(TestUpdateTeamAPI, self).setUp('teams.views.tracker') + super(TestUpdateTeamAPI, self).setUp('teams.utils.tracker') @ddt.data( (None, 401), @@ -1182,7 +1182,7 @@ class TestCreateMembershipAPI(EventTestMixin, TeamAPITestCase): """Test cases for the membership creation endpoint.""" def setUp(self): # pylint: disable=arguments-differ - super(TestCreateMembershipAPI, self).setUp('teams.views.tracker') + super(TestCreateMembershipAPI, self).setUp('teams.utils.tracker') @ddt.data( (None, 401), @@ -1346,7 +1346,7 @@ class TestDeleteMembershipAPI(EventTestMixin, TeamAPITestCase): """Test cases for the membership deletion endpoint.""" def setUp(self): # pylint: disable=arguments-differ - super(TestDeleteMembershipAPI, self).setUp('teams.views.tracker') + super(TestDeleteMembershipAPI, self).setUp('teams.utils.tracker') @ddt.data( (None, 401), diff --git a/lms/djangoapps/teams/utils.py b/lms/djangoapps/teams/utils.py new file mode 100644 index 0000000000..dc576397b5 --- /dev/null +++ b/lms/djangoapps/teams/utils.py @@ -0,0 +1,14 @@ +"""Utility methods related to teams.""" + +from eventtracking import tracker +from track import contexts + + +def emit_team_event(event_name, course_key, event_data): + """ + Emit team events with the correct course id context. + """ + context = contexts.course_context_from_course_id(course_key) + + with tracker.get_tracker().context(event_name, context): + tracker.emit(event_name, event_data) diff --git a/lms/djangoapps/teams/views.py b/lms/djangoapps/teams/views.py index 07f477fb6c..6a6af4e5d9 100644 --- a/lms/djangoapps/teams/views.py +++ b/lms/djangoapps/teams/views.py @@ -39,8 +39,6 @@ from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey from courseware.courses import get_course_with_access, has_access -from eventtracking import tracker -from track import contexts from student.models import CourseEnrollment, CourseAccessRole from student.roles import CourseStaffRole from django_comment_client.utils import has_discussion_privileges @@ -59,6 +57,7 @@ from .serializers import ( ) from .search_indexes import CourseTeamIndexer from .errors import AlreadyOnTeamInCourse, ElasticSearchConnectionError, NotEnrolledInCourseForTeam +from .utils import emit_team_event TEAM_MEMBERSHIPS_PER_PAGE = 2 TOPICS_PER_PAGE = 12 @@ -67,16 +66,6 @@ MAXIMUM_SEARCH_SIZE = 100000 log = logging.getLogger(__name__) -def emit_team_event(event_name, course_key, event_data): - """ - Emit team events with the correct course id context. - """ - context = contexts.course_context_from_course_id(course_key) - - with tracker.get_tracker().context(event_name, context): - tracker.emit(event_name, event_data) - - @receiver(post_save, sender=CourseTeam) def team_post_save_callback(sender, instance, **kwargs): # pylint: disable=unused-argument """ Emits signal after the team is saved. """ diff --git a/lms/djangoapps/verify_student/tests/factories.py b/lms/djangoapps/verify_student/tests/factories.py index 6514c594a9..d24e95804c 100644 --- a/lms/djangoapps/verify_student/tests/factories.py +++ b/lms/djangoapps/verify_student/tests/factories.py @@ -10,6 +10,7 @@ class SoftwareSecurePhotoVerificationFactory(DjangoModelFactory): """ Factory for SoftwareSecurePhotoVerification """ - FACTORY_FOR = SoftwareSecurePhotoVerification + class Meta(object): # pylint: disable=missing-docstring + model = SoftwareSecurePhotoVerification status = 'approved' diff --git a/lms/envs/test.py b/lms/envs/test.py index 1e52d4297c..d371d14ae5 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -269,6 +269,8 @@ OPENID_PROVIDER_TRUSTED_ROOTS = ['*'] ############################## OAUTH2 Provider ################################ FEATURES['ENABLE_OAUTH2_PROVIDER'] = True +# don't cache courses for testing +OIDC_COURSE_HANDLER_CACHE_TIMEOUT = 0 ########################### External REST APIs ################################# FEATURES['ENABLE_MOBILE_REST_API'] = True diff --git a/lms/static/js/spec/main.js b/lms/static/js/spec/main.js index 9fac876357..199d7f5acd 100644 --- a/lms/static/js/spec/main.js +++ b/lms/static/js/spec/main.js @@ -50,7 +50,6 @@ 'domReady': 'xmodule_js/common_static/js/vendor/domReady', 'mathjax': '//cdn.mathjax.org/mathjax/2.4-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured', 'youtube': '//www.youtube.com/player_api?noext', - 'tender': '//api.tenderapp.com/tender_widget', 'coffee/src/ajax_prefix': 'xmodule_js/common_static/coffee/src/ajax_prefix', 'coffee/src/instructor_dashboard/student_admin': 'coffee/src/instructor_dashboard/student_admin', 'xmodule_js/common_static/js/test/add_ajax_prefix': 'xmodule_js/common_static/js/test/add_ajax_prefix', diff --git a/lms/static/sass/_developer.scss b/lms/static/sass/_developer.scss index 5443e6ee27..4fdeaaf317 100644 --- a/lms/static/sass/_developer.scss +++ b/lms/static/sass/_developer.scss @@ -320,6 +320,33 @@ font: inherit; } } + //end instructor tools scratch space + + //efischer TNL-3226 + .search-field::-ms-clear { + width: 0px; + height: 0px; + } +} + +//efischer - TNL-3189 +//copied from cms/static/sass/elements/_system-feedback.scss#L106 +//along with some "hide the inherited value, we want none" action +.prompt.warning button { + @extend %btn-no-style; + box-shadow: none; + text-shadow: none; + + &:hover { + color: $orange-s2; + background: transparent; + box-shadow: none; + } + + &:focus { + box-shadow: none; + border: 0px; + } } //efischer - TNL-3189 diff --git a/lms/static/sass/_news.scss b/lms/static/sass/_news.scss index 9d73cfc578..d5f31a1b3b 100644 --- a/lms/static/sass/_news.scss +++ b/lms/static/sass/_news.scss @@ -8,7 +8,7 @@ padding-left: $baseline; padding-top: $baseline; padding-bottom: $baseline; - + .notification { @include news-font; margin-top: ($baseline*0.75); diff --git a/lms/static/sass/base/_utilities.scss b/lms/static/sass/base/_utilities.scss index ce32cc4d8d..84e35bc031 100644 --- a/lms/static/sass/base/_utilities.scss +++ b/lms/static/sass/base/_utilities.scss @@ -9,4 +9,4 @@ .sr-is-focusable:focus, .sr-is-focusable:active { @extend %no-outline; -} \ No newline at end of file +} diff --git a/lms/static/sass/course/_open_ended_grading.scss b/lms/static/sass/course/_open_ended_grading.scss index 0ade38b804..e9bf04d938 100644 --- a/lms/static/sass/course/_open_ended_grading.scss +++ b/lms/static/sass/course/_open_ended_grading.scss @@ -29,7 +29,7 @@ padding: ($baseline/2); border: 1px solid black; text-align: center; - + p { font-size: 0.9em; text-align: center; @@ -54,7 +54,7 @@ } .alert-message { - + img { vertical-align: baseline; } diff --git a/lms/static/sass/course/_rubric.scss b/lms/static/sass/course/_rubric.scss index b97eea5cd3..b0830e7881 100644 --- a/lms/static/sass/course/_rubric.scss +++ b/lms/static/sass/course/_rubric.scss @@ -14,7 +14,7 @@ .rubric { margin: 0; color: #3C3C3C; - + tr { margin: 0; height: 100%; @@ -58,7 +58,7 @@ .selected-grade, .selected-grade .rubric-label { background: #666; - color: white; + color: white; } input[type=radio]:checked + .rubric-label { diff --git a/lms/static/sass/course/_staff_grading.scss b/lms/static/sass/course/_staff_grading.scss index 418201d45c..f9a712e877 100644 --- a/lms/static/sass/course/_staff_grading.scss +++ b/lms/static/sass/course/_staff_grading.scss @@ -1,12 +1,12 @@ div.staff-grading, div.peer-grading { border: 1px solid lightgray; - + textarea.feedback-area { margin: 0; height: 75px; } - + div.feedback-area.track-changes { position: relative; margin: 0; @@ -76,7 +76,7 @@ div.peer-grading { min-width: 50px; text-size: 1.5em; } - + /* Toggled State */ input[type=radio]:checked + label { background: #666; @@ -92,15 +92,15 @@ div.peer-grading { width: 100%; table-layout: auto; text-align: center; - + th { padding: ($baseline/10); } - + td { padding: ($baseline/10); } - + td.problem-name { text-align: left; } @@ -124,11 +124,11 @@ div.peer-grading { padding: ($baseline/10); background-color: #ffcccc; } - + .submission-wrapper { padding: ($baseline/10); padding-bottom: ($baseline*0.75); - + h3 { margin-bottom: ($baseline/10); } @@ -140,7 +140,7 @@ div.peer-grading { .meta-info-wrapper { padding: ($baseline/10); background-color: #eee; - + div { display: inline; } @@ -151,7 +151,7 @@ div.peer-grading { padding: ($baseline/10); background-color: $yellow; } - + .breadcrumbs { margin: ($baseline/2) ($baseline/4); font-size: .8em; @@ -162,7 +162,7 @@ div.peer-grading { padding: ($baseline/2); background-color: #eee; font-size: .8em; - + > div { margin-bottom: ($baseline/4); padding: ($baseline/2); @@ -174,7 +174,7 @@ div.peer-grading { text-align: center; text-transform: uppercase; } - + p{ color: #777; } @@ -192,7 +192,7 @@ div.peer-grading { } .current-state { background: $white; - + } } @@ -205,7 +205,7 @@ div.peer-grading { font-size: 1.2em; } } - + .interstitial-page { text-align: center; diff --git a/lms/static/sass/course/_syllabus.scss b/lms/static/sass/course/_syllabus.scss index 8fb103cfd8..c0bd19a456 100644 --- a/lms/static/sass/course/_syllabus.scss +++ b/lms/static/sass/course/_syllabus.scss @@ -27,7 +27,7 @@ div.syllabus { td { padding-top: 15px !important; } - } + } td { border: none !important; @@ -50,7 +50,7 @@ div.syllabus { &.week_separator { padding: 0px !important; - + hr { margin: ($baseline/2); } diff --git a/lms/static/sass/course/_tabs.scss b/lms/static/sass/course/_tabs.scss index f16fc1df3d..62557e0609 100644 --- a/lms/static/sass/course/_tabs.scss +++ b/lms/static/sass/course/_tabs.scss @@ -14,4 +14,4 @@ div.static_tab_wrapper { border: 0; background: transparent !important; } -} \ No newline at end of file +} diff --git a/lms/static/sass/course/_textbook.scss b/lms/static/sass/course/_textbook.scss index ae4a577f63..b24a3a52a2 100755 --- a/lms/static/sass/course/_textbook.scss +++ b/lms/static/sass/course/_textbook.scss @@ -145,7 +145,7 @@ div.book-wrapper { display:none; } } - + &.last { left: 0; diff --git a/lms/static/sass/course/discussion/_form-wmd-toolbar.scss b/lms/static/sass/course/discussion/_form-wmd-toolbar.scss index b443f22566..2caf6cd928 100644 --- a/lms/static/sass/course/discussion/_form-wmd-toolbar.scss +++ b/lms/static/sass/course/discussion/_form-wmd-toolbar.scss @@ -27,45 +27,45 @@ } #wmd-button-row { - position: relative; + position: relative; margin-left: ($baseline/4); margin-right: ($baseline/4); margin-bottom: 0px; margin-top: ($baseline/2); - padding: 0px; + padding: 0px; height: 20px; } .wmd-spacer { - width: 1px; - height: 20px; + width: 1px; + height: 20px; margin-left: 14px; position: absolute; background-color: Silver; - display: inline-block; + display: inline-block; list-style: none; } .wmd-button { - width: 20px; - height: 20px; + width: 20px; + height: 20px; margin-left: ($baseline/4); margin-right: ($baseline/4); position: absolute; background-image: url(../images/wmd-buttons.png); background-repeat: no-repeat; background-position: 0px 0px; - display: inline-block; + display: inline-block; list-style: none; } .wmd-button > a { - width: 20px; - height: 20px; + width: 20px; + height: 20px; margin-left: ($baseline/4); margin-right: ($baseline/4); position: absolute; - display: inline-block; + display: inline-block; } diff --git a/lms/static/sass/course/instructor/_instructor_2.scss b/lms/static/sass/course/instructor/_instructor_2.scss index df043d97d9..bef79f2556 100644 --- a/lms/static/sass/course/instructor/_instructor_2.scss +++ b/lms/static/sass/course/instructor/_instructor_2.scss @@ -1956,16 +1956,20 @@ input[name="subject"] { width: 140px; } &.email { - width: 250px; + @include text-align(center); + width: ($baseline*8); word-wrap: break-word; } &.allowance-name { - width: 140px; + width: ($baseline*5); + text-align: center; } &.allowance-value { - width: 150px; + @include text-align(center); + width: ($baseline*5); } &.c_action { + @include text-align(center); width: 60px; } } @@ -1991,6 +1995,7 @@ input[name="subject"] { text-align: center; } td:nth-child(3){ + word-wrap: break-word; text-align: center; } td:nth-child(6){ @@ -1998,17 +2003,12 @@ input[name="subject"] { text-align: center; } - td{ - a.remove_allowance{ - @include margin-left(15px); - } - } td:last-child { padding-left: 17px; } } } - .exam-attempts-content { + .exam-attempts-content, .exam-allowances-content { padding-left: 0; padding-right: 0; } @@ -2033,7 +2033,7 @@ input[name="subject"] { span { background-color: #ccc; display: inline-block; - padding: 6px 12px; + padding: 7px 12px; cursor: pointer; } } diff --git a/lms/static/sass/course/layout/_courseware_preview.scss b/lms/static/sass/course/layout/_courseware_preview.scss index d56c6945fa..bf35255fc0 100644 --- a/lms/static/sass/course/layout/_courseware_preview.scss +++ b/lms/static/sass/course/layout/_courseware_preview.scss @@ -100,4 +100,4 @@ margin-right: $baseline; } } -} \ No newline at end of file +} diff --git a/lms/static/sass/course/layout/_footer.scss b/lms/static/sass/course/layout/_footer.scss index 8dc0ae540d..7efe6d8ae0 100644 --- a/lms/static/sass/course/layout/_footer.scss +++ b/lms/static/sass/course/layout/_footer.scss @@ -1,4 +1,4 @@ footer { box-shadow: $courseware-footer-shadow; margin-top: $courseware-footer-margin; -} \ No newline at end of file +} diff --git a/lms/static/sass/discussion/elements/_editor.scss b/lms/static/sass/discussion/elements/_editor.scss index 93b1af171b..c0456a3ba8 100644 --- a/lms/static/sass/discussion/elements/_editor.scss +++ b/lms/static/sass/discussion/elements/_editor.scss @@ -41,7 +41,7 @@ // CASE: inline styling // TO-DO: additional styling cleanup here necessary, for now this case was ported over from _discussion.scss .discussion-module { - + .wmd-panel { width: 100%; min-width: 500px; diff --git a/lms/static/sass/discussion/elements/_labels.scss b/lms/static/sass/discussion/elements/_labels.scss index 12f6a46ec5..04e39e2107 100644 --- a/lms/static/sass/discussion/elements/_labels.scss +++ b/lms/static/sass/discussion/elements/_labels.scss @@ -34,4 +34,4 @@ body.discussion, .discussion-module { @include forum-user-label($forum-color-community-ta); } -} \ No newline at end of file +} diff --git a/lms/static/sass/vendor/bi-app/_bi-app-ltr.scss b/lms/static/sass/vendor/bi-app/_bi-app-ltr.scss index 6278a31380..3b5dfab593 100755 --- a/lms/static/sass/vendor/bi-app/_bi-app-ltr.scss +++ b/lms/static/sass/vendor/bi-app/_bi-app-ltr.scss @@ -1,11 +1,11 @@ // ------------------------------------------ // left to right module -// authors: +// authors: // twitter.com/anasnakawa // twitter.com/victorzamfir -// licensed under the MIT license +// licensed under the MIT license // http://www.opensource.org/licenses/mit-license.php // ------------------------------------------ @import 'variables-ltr'; -@import 'mixins'; \ No newline at end of file +@import 'mixins'; diff --git a/lms/static/sass/vendor/bi-app/_bi-app-rtl.scss b/lms/static/sass/vendor/bi-app/_bi-app-rtl.scss index 17b7f2e90f..2e4b8271d8 100755 --- a/lms/static/sass/vendor/bi-app/_bi-app-rtl.scss +++ b/lms/static/sass/vendor/bi-app/_bi-app-rtl.scss @@ -1,11 +1,11 @@ // ------------------------------------------ // right to left module -// authors: +// authors: // twitter.com/anasnakawa // twitter.com/victorzamfir -// licensed under the MIT license +// licensed under the MIT license // http://www.opensource.org/licenses/mit-license.php // ------------------------------------------ @import 'variables-rtl'; -@import 'mixins'; \ No newline at end of file +@import 'mixins'; diff --git a/lms/static/sass/vendor/bi-app/_mixins.scss b/lms/static/sass/vendor/bi-app/_mixins.scss index 353999671d..e02d4d00ac 100755 --- a/lms/static/sass/vendor/bi-app/_mixins.scss +++ b/lms/static/sass/vendor/bi-app/_mixins.scss @@ -1,9 +1,9 @@ // ------------------------------------------ // bi app mixins -// authors: +// authors: // twitter.com/anasnakawa // twitter.com/victorzamfir -// licensed under the MIT license +// licensed under the MIT license // http://www.opensource.org/licenses/mit-license.php // ------------------------------------------ diff --git a/lms/static/sass/vendor/bi-app/_variables-ltr.scss b/lms/static/sass/vendor/bi-app/_variables-ltr.scss index 36d5a7b06e..12273051a3 100755 --- a/lms/static/sass/vendor/bi-app/_variables-ltr.scss +++ b/lms/static/sass/vendor/bi-app/_variables-ltr.scss @@ -1,15 +1,15 @@ // ------------------------------------------ // left to right variables to be used by bi-app mixins -// authors: +// authors: // twitter.com/anasnakawa // twitter.com/victorzamfir -// licensed under the MIT license +// licensed under the MIT license // http://www.opensource.org/licenses/mit-license.php // ------------------------------------------ // namespacing variables with bi-app to // avoid conflicting with other global variables -$bi-app-left : left; -$bi-app-right : right; -$bi-app-direction : ltr; -$bi-app-invert-direction: rtl; \ No newline at end of file +$bi-app-left : left; +$bi-app-right : right; +$bi-app-direction : ltr; +$bi-app-invert-direction: rtl; diff --git a/lms/static/sass/vendor/bi-app/_variables-rtl.scss b/lms/static/sass/vendor/bi-app/_variables-rtl.scss index 7300f17863..6b8da0bdbf 100755 --- a/lms/static/sass/vendor/bi-app/_variables-rtl.scss +++ b/lms/static/sass/vendor/bi-app/_variables-rtl.scss @@ -1,15 +1,15 @@ // ------------------------------------------ // right to left variables to be used by bi-app mixins -// authors: +// authors: // twitter.com/anasnakawa // twitter.com/victorzamfir -// licensed under the MIT license +// licensed under the MIT license // http://www.opensource.org/licenses/mit-license.php // ------------------------------------------ // namespacing variables with bi-app to // avoid conflicting with other global variables -$bi-app-left : right; +$bi-app-left : right; $bi-app-right : left; -$bi-app-direction : rtl; -$bi-app-invert-direction: ltr; \ No newline at end of file +$bi-app-direction : rtl; +$bi-app-invert-direction: ltr; diff --git a/lms/static/sass/views/_account-settings.scss b/lms/static/sass/views/_account-settings.scss index a5d2f7b2f0..18457f6757 100644 --- a/lms/static/sass/views/_account-settings.scss +++ b/lms/static/sass/views/_account-settings.scss @@ -1,7 +1,7 @@ // lms - application - account settings // ==================== -// Table of Contents +// Table of Contents // * +Container - Account Settings // * +Main - Header // * +Settings Section @@ -13,7 +13,7 @@ padding-top: ($baseline*2); .account-settings-container { - padding: 0; + padding: 0; } .ui-loading-indicator, diff --git a/lms/static/sass/views/_teams.scss b/lms/static/sass/views/_teams.scss index b6d82f444a..688535cf0f 100644 --- a/lms/static/sass/views/_teams.scss +++ b/lms/static/sass/views/_teams.scss @@ -579,7 +579,10 @@ .join-team-message { @extend %t-copy-sub1; + @include text-align(right); color: $gray-l1; + display: block; + margin-bottom: ($baseline/4); } .team-actions { diff --git a/lms/templates/ccx/coach_dashboard.html b/lms/templates/ccx/coach_dashboard.html index 529ccda196..3f781de688 100644 --- a/lms/templates/ccx/coach_dashboard.html +++ b/lms/templates/ccx/coach_dashboard.html @@ -22,27 +22,26 @@ from django.core.urlresolvers import reverse

    ${_("CCX Coach Dashboard")}

    - % if messages: -
      - % for message in messages: - % if message.tags: -
    • ${message}
    • - % else: -
    • ${message}
    • - % endif - % endfor -
    - % endif - %if not ccx: -
    - - - -
    - - -
    + % if messages: +
      + % for message in messages: + % if message.tags: +
    • ${message}
    • + % else: +
    • ${message}
    • + % endif + % endfor +
    + % endif +
    +
    + + +
    + +
    +
    %endif %if ccx: @@ -151,5 +150,9 @@ from django.core.urlresolvers import reverse $(setup_tabs); $(setup_management_form) - + $( document ).ready(function() { + if ($('#ccx_std_list_messages').length) { + $('#ccx_std_list_messages')[0].focus(); + } + }); diff --git a/lms/templates/ccx/enrollment.html b/lms/templates/ccx/enrollment.html index 2d054da3e4..c26dfac6f7 100644 --- a/lms/templates/ccx/enrollment.html +++ b/lms/templates/ccx/enrollment.html @@ -46,13 +46,21 @@
    -
    +

    ${_("Student List Management")}

    + %if messages: + +
    + %for message in messages: + ${message} + %endfor +
    + %endif @@ -66,7 +74,7 @@ - + %endfor diff --git a/lms/templates/certificates/_accomplishment-banner.html b/lms/templates/certificates/_accomplishment-banner.html index 466f2e3615..41e8d82445 100644 --- a/lms/templates/certificates/_accomplishment-banner.html +++ b/lms/templates/certificates/_accomplishment-banner.html @@ -1,19 +1,13 @@ <%! -import urllib from django.utils.translation import ugettext as _ -from django.core.urlresolvers import reverse +from django.template.defaultfilters import escapejs %> <%namespace name='static' file='../static_content.html'/> -<% - accomplishment_course_title = accomplishment_copy_course_name - if certificate_data and certificate_data.get('course_title', ''): - accomplishment_course_title = certificate_data.get('course_title', '') -%> <%block name="js_extra"> <%static:js group='certificates_wv'/>
    ${member.user} ${member.user.email}
    Revoke access
    ${_("Revoke access")}