From 2735b2b5bbe6e80f7fbcf1004d796be96af78cbe Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 17 Jul 2015 12:11:38 -0400 Subject: [PATCH] Revert "Decorated instructor dashboard with sudo_required." --- .../contentstore/features/help.feature | 1 - .../contentstore/tests/test_contentstore.py | 1 - .../contentstore/tests/test_permissions.py | 5 +- .../contentstore/views/component.py | 7 - cms/djangoapps/contentstore/views/library.py | 8 - .../views/tests/test_container_page.py | 2 - .../views/tests/test_course_index.py | 1 - .../contentstore/views/tests/test_library.py | 5 +- .../contentstore/views/tests/test_user.py | 1 - cms/djangoapps/contentstore/views/user.py | 2 - cms/djangoapps/course_creators/admin.py | 2 +- .../course_creators/tests/test_admin.py | 12 -- cms/envs/common.py | 6 - cms/static/sass/elements/_modal.scss | 126 ------------- cms/templates/sudo/sudo.html | 42 ----- cms/urls.py | 5 +- common/djangoapps/course_modes/admin.py | 2 +- .../course_modes/tests/test_views.py | 1 - .../django_sudo_helpers/__init__.py | 0 .../django_sudo_helpers/decorators.py | 61 ------ .../django_sudo_helpers/tests/__init__.py | 0 .../django_sudo_helpers/tests/utils.py | 15 -- common/djangoapps/edx_admin/__init__.py | 0 common/djangoapps/edx_admin/admin.py | 31 ---- common/djangoapps/edx_admin/models.py | 3 - common/djangoapps/external_auth/admin.py | 2 +- common/djangoapps/student/admin.py | 2 +- .../student/tests/test_admin_views.py | 15 +- common/djangoapps/terrain/steps.py | 20 -- common/djangoapps/track/admin.py | 2 +- common/djangoapps/util/admin.py | 2 +- .../xmodule/modulestore/tests/django_utils.py | 11 -- .../test/acceptance/pages/common/sudo_page.py | 57 ------ .../pages/lms/instructor_dashboard.py | 173 ++++++------------ .../test/acceptance/pages/lms/staff_view.py | 21 ++- .../discussion/test_cohort_management.py | 14 +- common/test/acceptance/tests/helpers.py | 10 - .../test_lms_cohorted_courseware_search.py | 7 +- .../lms/test_lms_instructor_dashboard.py | 59 +++--- .../tests/lms/test_lms_user_preview.py | 105 ++++------- .../tests/studio/test_studio_course_team.py | 9 - .../tests/studio/test_studio_library.py | 2 - .../tests/test_cohorted_courseware.py | 8 +- .../bulk_email/tests/test_course_optout.py | 3 +- lms/djangoapps/bulk_email/tests/test_email.py | 3 +- .../bulk_email/tests/test_err_handling.py | 3 +- lms/djangoapps/courseware/admin.py | 2 +- .../courseware/features/lti.feature | 3 - .../features/staff_debug_info.feature | 13 ++ .../courseware/features/staff_debug_info.py | 51 ++++++ .../courseware/tests/test_entrance_exam.py | 1 - lms/djangoapps/courseware/tests/test_tabs.py | 1 - .../tests/test_view_authentication.py | 36 ---- lms/djangoapps/courseware/tests/test_views.py | 2 - lms/djangoapps/courseware/views.py | 5 - .../instructor/features/bulk_email.feature | 2 - .../instructor/features/bulk_email.py | 10 +- lms/djangoapps/instructor/features/common.py | 14 +- lms/djangoapps/instructor/tests/test_api.py | 67 ------- .../tests/test_api_email_localization.py | 1 - .../instructor/tests/test_certificates.py | 6 - .../instructor/tests/test_ecommerce.py | 1 - lms/djangoapps/instructor/tests/test_email.py | 1 - .../tests/test_legacy_enrollment.py | 3 +- .../tests/test_legacy_raw_download_csv.py | 1 - .../instructor/tests/test_legacy_xss.py | 3 +- .../instructor/tests/test_spoc_gradebook.py | 1 - .../tests/views/test_instructor_dashboard.py | 12 -- lms/djangoapps/instructor/views/api.py | 42 ----- .../instructor/views/instructor_dashboard.py | 49 +---- lms/djangoapps/instructor/views/legacy.py | 2 - lms/djangoapps/shoppingcart/admin.py | 2 +- lms/djangoapps/verify_student/admin.py | 6 +- lms/envs/common.py | 7 - .../instructor_dashboard/data_download.coffee | 71 ++++--- .../instructor_dashboard/membership.coffee | 120 ++++++------ .../instructor_dashboard/send_email.coffee | 21 +-- .../instructor_dashboard/student_admin.coffee | 120 +++++------- .../src/instructor_dashboard/util.coffee | 9 +- .../js/spec/staff_debug_actions_spec.js | 60 ++++-- lms/static/js/staff_debug_actions.js | 116 ++++++++++-- .../instructor_dashboard_2/student_admin.html | 9 +- lms/templates/staff_problem_info.html | 13 +- lms/templates/sudo/sudo.html | 21 --- lms/urls.py | 4 +- .../content/course_structures/admin.py | 6 +- openedx/core/djangoapps/credit/admin.py | 2 - requirements/edx/github.txt | 1 - 88 files changed, 555 insertions(+), 1229 deletions(-) delete mode 100644 cms/templates/sudo/sudo.html delete mode 100644 common/djangoapps/django_sudo_helpers/__init__.py delete mode 100644 common/djangoapps/django_sudo_helpers/decorators.py delete mode 100644 common/djangoapps/django_sudo_helpers/tests/__init__.py delete mode 100644 common/djangoapps/django_sudo_helpers/tests/utils.py delete mode 100644 common/djangoapps/edx_admin/__init__.py delete mode 100644 common/djangoapps/edx_admin/admin.py delete mode 100644 common/djangoapps/edx_admin/models.py delete mode 100644 common/test/acceptance/pages/common/sudo_page.py create mode 100644 lms/djangoapps/courseware/features/staff_debug_info.feature create mode 100644 lms/djangoapps/courseware/features/staff_debug_info.py delete mode 100644 lms/templates/sudo/sudo.html diff --git a/cms/djangoapps/contentstore/features/help.feature b/cms/djangoapps/contentstore/features/help.feature index 9e7da805f2..567a2f2526 100644 --- a/cms/djangoapps/contentstore/features/help.feature +++ b/cms/djangoapps/contentstore/features/help.feature @@ -33,7 +33,6 @@ Feature: CMS.Help Then I should see online help for "grading" And I am viewing the course team settings - And I get sudo access with password "test" Then I should see online help for "course-team" And I select the Advanced Settings diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index bac6b41199..79feeae460 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -1343,7 +1343,6 @@ class ContentStoreTest(ContentStoreTestCase): resp = self._show_course_overview(course_key) self.assertEqual(resp.status_code, 200) self.assertContains(resp, 'Chapter 2') - self.grant_sudo_access(unicode(course_key), self.user_password) # go to various pages test_get_html('import_handler') diff --git a/cms/djangoapps/contentstore/tests/test_permissions.py b/cms/djangoapps/contentstore/tests/test_permissions.py index f2253d0c4d..f509e807f2 100644 --- a/cms/djangoapps/contentstore/tests/test_permissions.py +++ b/cms/djangoapps/contentstore/tests/test_permissions.py @@ -22,10 +22,10 @@ class TestCourseAccess(ModuleStoreTestCase): Create a pool of users w/o granting them any permissions """ - self.user_password = super(TestCourseAccess, self).setUp() + user_password = super(TestCourseAccess, self).setUp() self.client = AjaxEnabledTestClient() - self.client.login(username=self.user.username, password=self.user_password) + self.client.login(username=self.user.username, password=user_password) # create a course via the view handler which has a different strategy for permissions than the factory self.course_key = self.store.make_course_key('myu', 'mydept.mycourse', 'myrun') @@ -93,7 +93,6 @@ class TestCourseAccess(ModuleStoreTestCase): user_by_role[role].append(user) self.assertTrue(auth.has_course_author_access(user, self.course_key), "{} does not have access".format(user)) - self.grant_sudo_access(unicode(self.course_key), self.user_password) course_team_url = reverse_course_url('course_team_handler', self.course_key) response = self.client.get_html(course_team_url) for role in [CourseInstructorRole, CourseStaffRole]: # Global and org-based roles don't appear on this page diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index 6435123beb..994af7c1db 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -29,7 +29,6 @@ from opaque_keys.edx.keys import UsageKey from student.auth import has_course_author_access from django.utils.translation import ugettext as _ -from sudo.utils import revoke_sudo_privileges from models.settings.course_grading import CourseGradingModel __all__ = ['OPEN_ENDED_COMPONENT_TYPES', @@ -164,12 +163,6 @@ def container_handler(request, usage_key_string): with modulestore().bulk_operations(usage_key.course_key): try: course, xblock, lms_link, preview_lms_link = _get_item_in_course(request, usage_key) - - # Revoke sudo privileges from a request explicitly - region = unicode(course.id) - if request.is_sudo(region=region): - revoke_sudo_privileges(request, region=region) - except ItemNotFoundError: return HttpResponseBadRequest() diff --git a/cms/djangoapps/contentstore/views/library.py b/cms/djangoapps/contentstore/views/library.py index 6a88c9c80d..1323aa4f09 100644 --- a/cms/djangoapps/contentstore/views/library.py +++ b/cms/djangoapps/contentstore/views/library.py @@ -17,8 +17,6 @@ from django.conf import settings from django.utils.translation import ugettext as _ from django.views.decorators.http import require_http_methods from django.views.decorators.csrf import ensure_csrf_cookie -from django_sudo_helpers.decorators import sudo_required -from sudo.utils import revoke_sudo_privileges from edxmako.shortcuts import render_to_response from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey @@ -70,11 +68,6 @@ def _display_library(library_key_string, request): """ Displays single library """ - - # Revoke sudo privileges from a request explicitly - if request.is_sudo(region=library_key_string): - revoke_sudo_privileges(request, region=library_key_string) - library_key = CourseKey.from_string(library_key_string) if not isinstance(library_key, LibraryLocator): log.exception("Non-library key passed to content libraries API.") # Should never happen due to url regex @@ -204,7 +197,6 @@ def library_blocks_view(library, user, response_format): }) -@sudo_required def manage_library_users(request, library_key_string): """ Studio UI for editing the users within a library. diff --git a/cms/djangoapps/contentstore/views/tests/test_container_page.py b/cms/djangoapps/contentstore/views/tests/test_container_page.py index 032f6b268d..b22e06327f 100644 --- a/cms/djangoapps/contentstore/views/tests/test_container_page.py +++ b/cms/djangoapps/contentstore/views/tests/test_container_page.py @@ -12,7 +12,6 @@ from django.utils import http import contentstore.views.component as views from contentstore.views.tests.utils import StudioPageTestCase -from django_sudo_helpers.tests.utils import sudo_middleware_process_request from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.factories import ItemFactory @@ -172,7 +171,6 @@ class ContainerPageTestCase(StudioPageTestCase): """ request = RequestFactory().get('foo') request.user = self.user - sudo_middleware_process_request(request) # Check for invalid 'usage_key_strings' self.assertRaises( diff --git a/cms/djangoapps/contentstore/views/tests/test_course_index.py b/cms/djangoapps/contentstore/views/tests/test_course_index.py index 397ee36caa..fa3a97cffb 100644 --- a/cms/djangoapps/contentstore/views/tests/test_course_index.py +++ b/cms/djangoapps/contentstore/views/tests/test_course_index.py @@ -114,7 +114,6 @@ class TestCourseIndex(CourseTestCase): """ course_staff_client, course_staff = self.create_non_staff_authed_user_client() for course in [self.course, self.odd_course]: - self.grant_sudo_access(unicode(course.id), 'foo') permission_url = reverse_course_url('course_team_handler', course.id, kwargs={'email': course_staff.email}) self.client.post( diff --git a/cms/djangoapps/contentstore/views/tests/test_library.py b/cms/djangoapps/contentstore/views/tests/test_library.py index 51d87dc4b4..9e1abbbdf0 100644 --- a/cms/djangoapps/contentstore/views/tests/test_library.py +++ b/cms/djangoapps/contentstore/views/tests/test_library.py @@ -30,10 +30,10 @@ class UnitTestLibraries(ModuleStoreTestCase): """ def setUp(self): - self.user_password = super(UnitTestLibraries, self).setUp() + user_password = super(UnitTestLibraries, self).setUp() self.client = AjaxEnabledTestClient() - self.client.login(username=self.user.username, password=self.user_password) + self.client.login(username=self.user.username, password=user_password) ###################################################### # Tests for /library/ - list and create libraries: @@ -207,7 +207,6 @@ class UnitTestLibraries(ModuleStoreTestCase): """ library = LibraryFactory.create() extra_user, _ = self.create_non_staff_user() - self.grant_sudo_access(unicode(library.location.library_key), self.user_password) manage_users_url = reverse_library_url('manage_library_users', unicode(library.location.library_key)) response = self.client.get(manage_users_url) diff --git a/cms/djangoapps/contentstore/views/tests/test_user.py b/cms/djangoapps/contentstore/views/tests/test_user.py index c2e058904b..ce99a266ef 100644 --- a/cms/djangoapps/contentstore/views/tests/test_user.py +++ b/cms/djangoapps/contentstore/views/tests/test_user.py @@ -14,7 +14,6 @@ from student import auth class UsersTestCase(CourseTestCase): def setUp(self): super(UsersTestCase, self).setUp() - self.grant_sudo_access(unicode(self.course.id), self.user_password) self.ext_user = User.objects.create_user( "joe", "joe@comedycentral.com", "haha") self.ext_user.is_active = True diff --git a/cms/djangoapps/contentstore/views/user.py b/cms/djangoapps/contentstore/views/user.py index 27d84051f4..7ee92c0ba4 100644 --- a/cms/djangoapps/contentstore/views/user.py +++ b/cms/djangoapps/contentstore/views/user.py @@ -11,7 +11,6 @@ from xmodule.modulestore.django import modulestore from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locator import LibraryLocator from util.json_request import JsonResponse, expect_json -from django_sudo_helpers.decorators import sudo_required from student.roles import CourseInstructorRole, CourseStaffRole, LibraryUserRole from course_creators.views import user_requested_access @@ -39,7 +38,6 @@ def request_course_creator(request): @login_required @ensure_csrf_cookie @require_http_methods(("GET", "POST", "PUT", "DELETE")) -@sudo_required def course_team_handler(request, course_key_string=None, email=None): """ The restful handler for course team users. diff --git a/cms/djangoapps/course_creators/admin.py b/cms/djangoapps/course_creators/admin.py index 2fd7260c48..5eaa8c4ac3 100644 --- a/cms/djangoapps/course_creators/admin.py +++ b/cms/djangoapps/course_creators/admin.py @@ -5,7 +5,7 @@ django admin page for the course creators table from course_creators.models import CourseCreator, update_creator_state, send_user_notification, send_admin_notification from course_creators.views import update_course_creator_group -from django.contrib import admin +from ratelimitbackend import admin from django.conf import settings from django.dispatch import receiver from edxmako.shortcuts import render_to_string diff --git a/cms/djangoapps/course_creators/tests/test_admin.py b/cms/djangoapps/course_creators/tests/test_admin.py index 057550c280..47131cb198 100644 --- a/cms/djangoapps/course_creators/tests/test_admin.py +++ b/cms/djangoapps/course_creators/tests/test_admin.py @@ -11,7 +11,6 @@ import mock from course_creators.admin import CourseCreatorAdmin from course_creators.models import CourseCreator from django.core import mail -from sudo.utils import region_name from student.roles import CourseCreatorRole from student import auth @@ -47,16 +46,6 @@ class CourseCreatorAdminTest(TestCase): "STUDIO_REQUEST_EMAIL": self.studio_request_email } - def grant_sudo_access(self, region, password): - """ - Grant sudo access to staff or instructor user. - """ - self.client.post( - '/sudo/?region={}'.format(region_name(region)), - {'password': password}, - follow=True - ) - @mock.patch('course_creators.admin.render_to_string', mock.Mock(side_effect=mock_render_to_string, autospec=True)) @mock.patch('django.contrib.auth.models.User.email_user') def test_change_status(self, email_user): @@ -172,7 +161,6 @@ class CourseCreatorAdminTest(TestCase): self.assertFalse(self.creator_admin.has_change_permission(self.request)) def test_rate_limit_login(self): - self.grant_sudo_access('django_admin', 'foo') with mock.patch.dict('django.conf.settings.FEATURES', {'ENABLE_CREATOR_GROUP': True}): post_params = {'username': self.user.username, 'password': 'wrong_password'} # try logging in 30 times, the default limit in the number of failed diff --git a/cms/envs/common.py b/cms/envs/common.py index d9efbcc665..a683dce912 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -319,9 +319,6 @@ MIDDLEWARE_CLASSES = ( # catches any uncaught RateLimitExceptions and returns a 403 instead of a 500 'ratelimitbackend.middleware.RateLimitMiddleware', - # force re-authentication before activating administrative functions - 'sudo.middleware.SudoMiddleware', - # for expiring inactive sessions 'session_inactivity_timeout.middleware.SessionInactivityTimeout', @@ -764,9 +761,6 @@ INSTALLED_APPS = ( 'openedx.core.djangoapps.credit', 'xblock_django', - - # Allows sudo-mode - 'sudo', ) diff --git a/cms/static/sass/elements/_modal.scss b/cms/static/sass/elements/_modal.scss index c1dc3a8ca2..3d9c4663cc 100644 --- a/cms/static/sass/elements/_modal.scss +++ b/cms/static/sass/elements/_modal.scss @@ -70,129 +70,3 @@ width: 100%; background: $black; } - -.sudo-modal { - @extend .modal; - background: $shadow-d2; - border: 1px solid rgba(0, 0, 0, 0.9); - border-radius: 0; - box-shadow: 0 15px 80px 15px rgba(0,0,0, 0.5); - color: $white; - display: none; - left: 50%; - padding: 8px; - position: absolute; - width: 480px; - height: auto; - - .inner-wrapper { - @extend %ui-depth1; - background: rgb(245,245,245); - border-radius: 0; - border: 1px solid rgba(0, 0, 0, 0.9); - box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.7); - overflow: hidden; - padding-left: ($baseline/2); - padding-right: ($baseline/2); - padding-bottom: ($baseline/2); - position: relative; - - header { - @extend %ui-depth1; - overflow: hidden; - padding: 28px $baseline 0; - position: relative; - - &::before { - @include background-image(radial-gradient(50% 50%, circle closest-side, rgba(255,255,255, 0.8) 0%, rgba(255,255,255, 0) 100%)); - content: ""; - display: block; - height: 400px; - left: 0; - margin: 0 auto; - position: absolute; - top: -140px; - width: 100%; - z-index: 1; - } - - hr { - border: none; - margin: 0; - position: relative; - z-index: 2; - - &::after { - bottom: 0; - content: ""; - display: block; - position: absolute; - top: -1px; - } - } - - h2 { - position: relative; - text-align: center; - text-shadow: 0 1px rgba(255,255,255, 0.4); - z-index: 2; - } - } - - form { - margin-bottom: 12px; - padding: 0 ($baseline*2) $baseline; - position: relative; - z-index: 2; - - label { - color: rgb(51, 51, 51); - - &.field-error { - display: block; - color: #8F0E0E; - - + input, + textarea { - border: 1px solid #CA1111; - color: #8F0E0E; - } - } - } - - input[type="password"] { - background: rgb(255,255,255); - display: block; - height: 45px; - margin-bottom: $baseline; - width: 100%; - } - - input[type="submit"] { - border: 1px solid #CFC6C6; - border-radius: 3px; - box-shadow: 0px 1px 0px 0px #FFF inset; - color: #333; - display: inline-block; - font-weight: bold; - background-color: #EEE; - background-image: linear-gradient(#EEE, #D6CECE); - padding: 12px 18px; - text-decoration: none; - text-shadow: 0px 1px 0px #F9F8F8; - background-clip: padding-box; - font-size: 0.8125em; - } - } - } -} - -#sudo_overlay { - position: fixed; - top: 0px; - left: 0px; - display: block; - height: 100%; - width: 100%; - background: #000; - opacity: 0.5; -} diff --git a/cms/templates/sudo/sudo.html b/cms/templates/sudo/sudo.html deleted file mode 100644 index bd2017c759..0000000000 --- a/cms/templates/sudo/sudo.html +++ /dev/null @@ -1,42 +0,0 @@ -{% block body %} - {% load i18n %} - {% load compressed %} - {% compressed_css 'style-main' %} - - - - - - - -
-{% endblock %} \ No newline at end of file diff --git a/cms/urls.py b/cms/urls.py index df148530a3..2c69ccfea1 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -2,8 +2,7 @@ from django.conf import settings from django.conf.urls import patterns, include, url # There is a course creators admin table. -from edx_admin import admin - +from ratelimitbackend import admin admin.autodiscover() # pylint: disable=bad-continuation @@ -51,8 +50,6 @@ urlpatterns = patterns( url(r'^heartbeat$', include('heartbeat.urls')), url(r'^user_api/', include('openedx.core.djangoapps.user_api.legacy_urls')), - - url(r'^sudo/$', 'sudo.views.sudo'), ) # User creation and updating views diff --git a/common/djangoapps/course_modes/admin.py b/common/djangoapps/course_modes/admin.py index 4ace2e48f1..ac3c9efaa4 100644 --- a/common/djangoapps/course_modes/admin.py +++ b/common/djangoapps/course_modes/admin.py @@ -3,7 +3,7 @@ Django admin page for course modes """ from django.conf import settings from pytz import timezone, UTC -from django.contrib import admin +from ratelimitbackend import admin from course_modes.models import CourseMode from django import forms diff --git a/common/djangoapps/course_modes/tests/test_views.py b/common/djangoapps/course_modes/tests/test_views.py index f24fe0ac09..3883a5acd8 100644 --- a/common/djangoapps/course_modes/tests/test_views.py +++ b/common/djangoapps/course_modes/tests/test_views.py @@ -380,7 +380,6 @@ class AdminCourseModePageTest(ModuleStoreTestCase): } self.client.login(username=user.username, password='test') - self.grant_sudo_access('django_admin', 'test') # creating new course mode from django admin page response = self.client.post(reverse('admin:course_modes_coursemode_add'), data=data) diff --git a/common/djangoapps/django_sudo_helpers/__init__.py b/common/djangoapps/django_sudo_helpers/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/common/djangoapps/django_sudo_helpers/decorators.py b/common/djangoapps/django_sudo_helpers/decorators.py deleted file mode 100644 index 8d1fb11da9..0000000000 --- a/common/djangoapps/django_sudo_helpers/decorators.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Custom decorator for django-sudo. -""" -from functools import wraps - -from sudo.settings import RESET_TOKEN -from sudo.utils import new_sudo_token_on_activity -from sudo.views import redirect_to_sudo -from util.json_request import JsonResponse - - -def sudo_required(func_or_region): - """ - Enforces a view to have elevated privileges. - Should likely be paired with ``@login_required``. - - >>> @sudo_required - >>> def secure_page(request): - >>> ... - - Can also specify a particular sudo region (to only - allow access to that region). - - Also get course_id, course_key_string and library_key_string - from kwargs and set as region if region itself is None. - - >>> @sudo_required('admin_page') - >>> def secure_admin_page(request): - >>> ... - """ - def wrapper(func): # pylint: disable=missing-docstring - @wraps(func) - def inner(request, *args, **kwargs): # pylint: disable=missing-docstring - course_specific_region = kwargs.get('course_id') - if 'course_key_string' in kwargs: - course_specific_region = kwargs.get('course_key_string') - if 'library_key_string' in kwargs: - course_specific_region = kwargs.get('library_key_string') - - # N.B. region is captured from the enclosing sudo_required function - if not request.is_sudo(region=region or course_specific_region): - response_format = request.REQUEST.get('format', 'html') - if (response_format == 'json' or - 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json')): - return JsonResponse({'error': 'Unauthorized'}, status=401) - - return redirect_to_sudo(request.get_full_path(), region=region or course_specific_region) - - if RESET_TOKEN is True: - # Provide new sudo token content and reset timeout on activity - new_sudo_token_on_activity(request, region=region or course_specific_region) - - return func(request, *args, **kwargs) - return inner - - if callable(func_or_region): - region = None - return wrapper(func_or_region) - else: - region = func_or_region - return wrapper diff --git a/common/djangoapps/django_sudo_helpers/tests/__init__.py b/common/djangoapps/django_sudo_helpers/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/common/djangoapps/django_sudo_helpers/tests/utils.py b/common/djangoapps/django_sudo_helpers/tests/utils.py deleted file mode 100644 index 02dc68a30e..0000000000 --- a/common/djangoapps/django_sudo_helpers/tests/utils.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -django_sudo_heplers.utils -""" -import django.contrib.sessions.middleware -import sudo.middleware - - -def sudo_middleware_process_request(request): - """ - Initialize the session and is_sudo on request object. - """ - session_middleware = django.contrib.sessions.middleware.SessionMiddleware() - session_middleware.process_request(request) - sudo_middleware = sudo.middleware.SudoMiddleware() - sudo_middleware.process_request(request) diff --git a/common/djangoapps/edx_admin/__init__.py b/common/djangoapps/edx_admin/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/common/djangoapps/edx_admin/admin.py b/common/djangoapps/edx_admin/admin.py deleted file mode 100644 index 6b7669dc99..0000000000 --- a/common/djangoapps/edx_admin/admin.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -RatelimitSudoAdminSite -""" - -from django.contrib.admin import * # pylint: disable=wildcard-import, unused-wildcard-import -from django.contrib.admin import (site as django_site, - autodiscover as django_autodiscover) -from ratelimitbackend.admin import RateLimitAdminSite -from sudo.admin import SudoAdminSite - - -class RatelimitSudoAdminSite(RateLimitAdminSite, SudoAdminSite): - """ - A class that includes the features of both RateLimitAdminSite and SudoAdminSite - """ - pass - - -site = RatelimitSudoAdminSite() # pylint: disable=invalid-name - - -def autodiscover(): # pylint: disable=function-redefined - """ - Auto-Discover admin models. - """ - django_autodiscover() - - # pylint: disable=protected-access - for model, modeladmin in django_site._registry.items(): - if model not in site._registry: - site.register(model, modeladmin.__class__) diff --git a/common/djangoapps/edx_admin/models.py b/common/djangoapps/edx_admin/models.py deleted file mode 100644 index 0f4957a236..0000000000 --- a/common/djangoapps/edx_admin/models.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -This space intentionally left blank -""" diff --git a/common/djangoapps/external_auth/admin.py b/common/djangoapps/external_auth/admin.py index 1ee18dadc1..fad0917604 100644 --- a/common/djangoapps/external_auth/admin.py +++ b/common/djangoapps/external_auth/admin.py @@ -3,7 +3,7 @@ django admin pages for courseware model ''' from external_auth.models import * -from django.contrib import admin +from ratelimitbackend import admin class ExternalAuthMapAdmin(admin.ModelAdmin): diff --git a/common/djangoapps/student/admin.py b/common/djangoapps/student/admin.py index 1ec853ae59..7f355da572 100644 --- a/common/djangoapps/student/admin.py +++ b/common/djangoapps/student/admin.py @@ -9,7 +9,7 @@ from student.models import UserProfile, UserTestGroup, CourseEnrollmentAllowed, from student.models import ( CourseEnrollment, Registration, PendingNameChange, CourseAccessRole, LinkedInAddToProfileConfiguration ) -from django.contrib import admin +from ratelimitbackend import admin from student.roles import REGISTERED_ACCESS_ROLES from xmodule.modulestore.django import modulestore diff --git a/common/djangoapps/student/tests/test_admin_views.py b/common/djangoapps/student/tests/test_admin_views.py index 6e28f97381..910bfe474e 100644 --- a/common/djangoapps/student/tests/test_admin_views.py +++ b/common/djangoapps/student/tests/test_admin_views.py @@ -17,9 +17,6 @@ class AdminCourseRolesPageTest(ModuleStoreTestCase): self.user.save() self.course = CourseFactory.create(org='edx') - self.client.login(username=self.user.username, password='test') - self.grant_sudo_access('django_admin', 'test') - def test_save_valid_data(self): data = { @@ -29,6 +26,8 @@ class AdminCourseRolesPageTest(ModuleStoreTestCase): 'email': self.user.email } + self.client.login(username=self.user.username, password='test') + # # adding new role from django admin page response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data) self.assertRedirects(response, reverse('admin:student_courseaccessrole_changelist')) @@ -52,6 +51,8 @@ class AdminCourseRolesPageTest(ModuleStoreTestCase): 'course_id': unicode(self.course.id) } + self.client.login(username=self.user.username, password='test') + # # adding new role from django admin page response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data) self.assertRedirects(response, reverse('admin:student_courseaccessrole_changelist')) @@ -68,6 +69,8 @@ class AdminCourseRolesPageTest(ModuleStoreTestCase): } + self.client.login(username=self.user.username, password='test') + # # adding new role from django admin page response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data) self.assertRedirects(response, reverse('admin:student_courseaccessrole_changelist')) @@ -85,6 +88,8 @@ class AdminCourseRolesPageTest(ModuleStoreTestCase): } + self.client.login(username=self.user.username, password='test') + # # adding new role from django admin page response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data) self.assertRedirects(response, reverse('admin:student_courseaccessrole_changelist')) @@ -104,6 +109,8 @@ class AdminCourseRolesPageTest(ModuleStoreTestCase): 'email': email } + self.client.login(username=self.user.username, password='test') + # Adding new role with invalid data response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data) self.assertContains( @@ -129,6 +136,8 @@ class AdminCourseRolesPageTest(ModuleStoreTestCase): 'email': self.user.email } + self.client.login(username=self.user.username, password='test') + # # adding new role from django admin page response = self.client.post(reverse('admin:student_courseaccessrole_add'), data=data) self.assertContains( diff --git a/common/djangoapps/terrain/steps.py b/common/djangoapps/terrain/steps.py index f87d81e076..7006c42389 100644 --- a/common/djangoapps/terrain/steps.py +++ b/common/djangoapps/terrain/steps.py @@ -241,23 +241,3 @@ def view_course_team_settings(_step, whom): world.click_course_settings() link_css = 'li.nav-course-settings-team a' world.css_click(link_css) - - -@step('I get sudo access with password "([^"]*)"$') -def i_get_sudo_access(_step, password): - """ - Get sudo access for instructor or staff user. - Set the password value of the element to the specified password. - Note that wait_for empty is due to password field - It will return password like this **** not text. - """ - - sudo_form = world.css_find('form.sudo-form') - # check if sudo form is available then submit password to get sudo access - # otherwise return True because sudo access already given. - if len(sudo_form) > 0: - css_selector = 'input[id=id_password]' - world.retry_on_exception(lambda: world.css_find(css_selector)[0].fill(password)) - world.wait_for(lambda _: not world.css_has_value(css_selector, '', index=0)) - world.css_click('input[type=submit]') - return True diff --git a/common/djangoapps/track/admin.py b/common/djangoapps/track/admin.py index d75f206846..e0835f5a8a 100644 --- a/common/djangoapps/track/admin.py +++ b/common/djangoapps/track/admin.py @@ -3,6 +3,6 @@ django admin pages for courseware model ''' from track.models import TrackingLog -from django.contrib import admin +from ratelimitbackend import admin admin.site.register(TrackingLog) diff --git a/common/djangoapps/util/admin.py b/common/djangoapps/util/admin.py index 8b20fc9fa1..de82409cc3 100644 --- a/common/djangoapps/util/admin.py +++ b/common/djangoapps/util/admin.py @@ -1,6 +1,6 @@ """Admin interface for the util app. """ -from django.contrib import admin +from ratelimitbackend import admin from util.models import RateLimitConfiguration diff --git a/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py b/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py index f84f840172..2a05094abc 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py @@ -17,7 +17,6 @@ from request_cache.middleware import RequestCache from courseware.field_overrides import OverrideFieldData # pylint: disable=import-error from openedx.core.lib.tempdir import mkdtemp_clean -from sudo.utils import region_name from xmodule.contentstore.django import _CONTENTSTORE from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore, clear_existing_modulestores @@ -423,13 +422,3 @@ class ModuleStoreTestCase(TestCase): fields={"display_name": "Syllabus"}, ) return self.toy_loc - - def grant_sudo_access(self, region, password): - """ - Grant sudo access to staff or instructor user. - """ - self.client.post( - '/sudo/?region={}'.format(region_name(region)), - {'password': password}, - follow=True - ) diff --git a/common/test/acceptance/pages/common/sudo_page.py b/common/test/acceptance/pages/common/sudo_page.py deleted file mode 100644 index cfb9af4d12..0000000000 --- a/common/test/acceptance/pages/common/sudo_page.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Django sudo page to get sudo access. -""" - -from bok_choy.javascript import wait_for_js -from bok_choy.page_object import PageObject - - -class SudoPage(PageObject): - """ - Sudo page to get sudo access - """ - SUDO_FORM = 'form.sudo-form' - - def __init__(self, browser, redirect_page): - super(SudoPage, self).__init__(browser) - self.redirect_page = redirect_page - - def is_browser_on_page(self): - return self.q(css=self.SUDO_FORM).present - - @property - def url(self): - """ - Construct a URL to the page which needs sudo access. - """ - return self.redirect_page.url - - @property - def sudo_password_input(self): - """ - Returns sudo password input box. - """ - return self.q(css='{} input[id=id_password]'.format(self.SUDO_FORM)) - - @property - def submit_button(self): - """ - Returns submit button. - """ - return self.q(css='{} input[type=submit]'.format(self.SUDO_FORM)) - - @wait_for_js - def submit_sudo_password_and_get_access(self, password): - """ - Fill password in input field and click submit. - """ - input_box = self.sudo_password_input.first.results[0] - input_box.send_keys(password) - self.click_submit() - self.redirect_page.wait_for_page() - - def click_submit(self): - """ - Click on submit button. - """ - return self.submit_button.click() diff --git a/common/test/acceptance/pages/lms/instructor_dashboard.py b/common/test/acceptance/pages/lms/instructor_dashboard.py index f3e836c28a..b6c494e9a3 100644 --- a/common/test/acceptance/pages/lms/instructor_dashboard.py +++ b/common/test/acceptance/pages/lms/instructor_dashboard.py @@ -759,14 +759,12 @@ class DataDownloadPage(PageObject): return self.report_download_links.map(lambda el: el.text) -# pylint: disable=invalid-name class StudentAdminPage(PageObject): """ Student admin section of the Instructor dashboard. """ url = None - ENTRANCE_EXAM_CONTAINER = ".entrance-exam-grade-container" - SG_CONTAINER = ".student-grade-container" + EE_CONTAINER = ".entrance-exam-grade-container" def is_browser_on_page(self): """ @@ -775,161 +773,89 @@ class StudentAdminPage(PageObject): return self.q(css='a[data-section=student_admin].active-section').present @property - def entrance_exam_student_email_input(self): + def student_email_input(self): """ - Returns email address/username input box for entrance exam. + Returns email address/username input box. """ - return self.q(css='{} input[name=entrance-exam-student-select-grade]'.format(self.ENTRANCE_EXAM_CONTAINER)) - - @property - def entrance_exam_reset_attempts_button(self): - """ - Returns reset student attempts button for entrance exam. - """ - return self.q(css='{} input[name=reset-entrance-exam-attempts]'.format(self.ENTRANCE_EXAM_CONTAINER)) - - @property - def entrance_exam_rescore_submission_button(self): - """ - Returns rescore student submission button for entrance exam. - """ - return self.q(css='{} input[name=rescore-entrance-exam]'.format(self.ENTRANCE_EXAM_CONTAINER)) - - @property - def skip_entrance_exam_button(self): - """ - Return Let Student Skip Entrance Exam button. - """ - return self.q(css='{} input[name=skip-entrance-exam]'.format(self.ENTRANCE_EXAM_CONTAINER)) - - @property - def entrance_exam_delete_student_state_button(self): - """ - Returns delete student state button for entrance exam. - """ - return self.q(css='{} input[name=delete-entrance-exam-state]'.format(self.ENTRANCE_EXAM_CONTAINER)) - - @property - def background_task_history_button(self): - """ - Returns show background task history for student button for entrance exam. - """ - return self.q(css='{} input[name=entrance-exam-task-history]'.format(self.ENTRANCE_EXAM_CONTAINER)) - - @property - def entrance_exam_top_notification(self): - """ - Returns show background task history for student button for entrance exam. - """ - return self.q(css='{} .request-response-error'.format(self.ENTRANCE_EXAM_CONTAINER)).first + return self.q(css='{} input[name=entrance-exam-student-select-grade]'.format(self.EE_CONTAINER)) @property def reset_attempts_button(self): """ Returns reset student attempts button. """ - return self.q(css='{} input[name=reset-attempts-single]'.format(self.SG_CONTAINER)) + return self.q(css='{} input[name=reset-entrance-exam-attempts]'.format(self.EE_CONTAINER)) @property def rescore_submission_button(self): """ Returns rescore student submission button. """ - return self.q(css='{} input[name=rescore-problem-single]'.format(self.SG_CONTAINER)) + return self.q(css='{} input[name=rescore-entrance-exam]'.format(self.EE_CONTAINER)) + + @property + def skip_entrance_exam_button(self): + """ + Return Let Student Skip Entrance Exam button. + """ + return self.q(css='{} input[name=skip-entrance-exam]'.format(self.EE_CONTAINER)) @property def delete_student_state_button(self): """ Returns delete student state button. """ - return self.q(css='{} input[name=delete-state-single]'.format(self.SG_CONTAINER)) + return self.q(css='{} input[name=delete-entrance-exam-state]'.format(self.EE_CONTAINER)) + + @property + def background_task_history_button(self): + """ + Returns show background task history for student button. + """ + return self.q(css='{} input[name=entrance-exam-task-history]'.format(self.EE_CONTAINER)) @property def top_notification(self): """ Returns show background task history for student button. """ - return self.q(css='{} .request-response-error'.format(self.SG_CONTAINER)).first + return self.q(css='{} .request-response-error'.format(self.EE_CONTAINER)).first - def is_entrance_exam_student_email_input_visible(self): + def is_student_email_input_visible(self): """ - Returns True if student email address/username input box is present - for entrance exam. + Returns True if student email address/username input box is present. """ - return self.entrance_exam_student_email_input.is_present() + return self.student_email_input.is_present() - def is_entrance_exam_reset_attempts_button_visible(self): + def is_reset_attempts_button_visible(self): """ - Returns True if reset student attempts button is present - for entrance exam. + Returns True if reset student attempts button is present. """ - return self.entrance_exam_reset_attempts_button.is_present() + return self.reset_attempts_button.is_present() - def is_entrance_exam_rescore_submission_button_visible(self): + def is_rescore_submission_button_visible(self): """ - Returns True if rescore student submission button is present - for entrance exam. + Returns True if rescore student submission button is present. """ - return self.entrance_exam_rescore_submission_button.is_present() + return self.rescore_submission_button.is_present() - def is_entrance_exam_delete_student_state_button_visible(self): + def is_delete_student_state_button_visible(self): """ - Returns True if delete student state for entrance exam button is present - for entrance exam. + Returns True if delete student state for entrance exam button is present. """ - return self.entrance_exam_delete_student_state_button.is_present() + return self.delete_student_state_button.is_present() def is_background_task_history_button_visible(self): """ - Returns True if show background task history for student button is present - for entrance exam. + Returns True if show background task history for student button is present. """ return self.background_task_history_button.is_present() def is_background_task_history_table_visible(self): """ - Returns True if background task history table is present - for entrance exam. + Returns True if background task history table is present. """ - return self.q(css='{} .entrance-exam-task-history-table'.format(self.ENTRANCE_EXAM_CONTAINER)).is_present() - - def entrance_exam_click_reset_attempts_button(self): - """ - clicks reset student attempts button for entrance exam. - """ - return self.entrance_exam_reset_attempts_button.click() - - def entrance_exam_click_rescore_submissions_button(self): - """ - clicks rescore submissions button for entrance exam. - """ - return self.entrance_exam_rescore_submission_button.click() - - def click_skip_entrance_exam_button(self): - """ - clicks let student skip entrance exam button for entrance exam. - """ - return self.skip_entrance_exam_button.click() - - def entrance_exam_click_delete_student_state_button(self): - """ - clicks delete student state button for entrance exam. - """ - return self.entrance_exam_delete_student_state_button.click() - - def entrance_exam_click_task_history_button(self): - """ - clicks background task history button for entrance exam. - """ - return self.background_task_history_button.click() - - def set_student_email_for_ee(self, email_addres): - """ - Sets given email address as value of student email address/username input box - for entrance exam. - """ - input_box = self.entrance_exam_student_email_input.first.results[0] - input_box.send_keys(email_addres) + return self.q(css='{} .entrance-exam-task-history-table'.format(self.EE_CONTAINER)).is_present() def click_reset_attempts_button(self): """ @@ -943,13 +869,30 @@ class StudentAdminPage(PageObject): """ return self.rescore_submission_button.click() + def click_skip_entrance_exam_button(self): + """ + clicks let student skip entrance exam button. + """ + return self.skip_entrance_exam_button.click() + def click_delete_student_state_button(self): """ - clicks delete student state button and confirm the action. + clicks delete student state button. """ - with self.handle_alert(confirm=True): - self.delete_student_state_button.click() - self.wait_for_ajax() + return self.delete_student_state_button.click() + + def click_task_history_button(self): + """ + clicks background task history button. + """ + return self.background_task_history_button.click() + + def set_student_email(self, email_addres): + """ + Sets given email address as value of student email address/username input box. + """ + input_box = self.student_email_input.first.results[0] + input_box.send_keys(email_addres) class CertificatesPage(PageObject): diff --git a/common/test/acceptance/pages/lms/staff_view.py b/common/test/acceptance/pages/lms/staff_view.py index 64a3451e82..b4eeb5d505 100644 --- a/common/test/acceptance/pages/lms/staff_view.py +++ b/common/test/acceptance/pages/lms/staff_view.py @@ -68,14 +68,31 @@ class StaffDebugPage(PageObject): def is_browser_on_page(self): return self.q(css='section.staff-modal').present - def click_student_grade_adjustments(self, user=None): + def reset_attempts(self, user=None): """ This clicks on the reset attempts link with an optionally specified user. """ if user: self.q(css='input[id^=sd_fu_]').first.fill(user) - self.q(css='section.staff-modal a.staff-debug-grade-adjustments').click() + self.q(css='section.staff-modal a.staff-debug-reset').click() + + def delete_state(self, user=None): + """ + This delete's a student's state for the problem + """ + if user: + self.q(css='input[id^=sd_fu_]').fill(user) + self.q(css='section.staff-modal a.staff-debug-sdelete').click() + + def rescore(self, user=None): + """ + This clicks on the reset attempts link with an optionally + specified user. + """ + if user: + self.q(css='input[id^=sd_fu_]').first.fill(user) + self.q(css='section.staff-modal a.staff-debug-rescore').click() @property def idash_msg(self): diff --git a/common/test/acceptance/tests/discussion/test_cohort_management.py b/common/test/acceptance/tests/discussion/test_cohort_management.py index 0d4fcd8cfb..921f471dbf 100644 --- a/common/test/acceptance/tests/discussion/test_cohort_management.py +++ b/common/test/acceptance/tests/discussion/test_cohort_management.py @@ -9,7 +9,7 @@ from pytz import UTC, utc from bok_choy.promise import EmptyPromise from nose.plugins.attrib import attr from .helpers import CohortTestMixin -from ..helpers import UniqueCourseTest, EventsTestMixin, create_user_partition_json, get_sudo_access +from ..helpers import UniqueCourseTest, EventsTestMixin, create_user_partition_json from xmodule.partitions.partitions import Group from ...fixtures.course import CourseFixture, XBlockFixtureDesc from ...pages.lms.auto_auth import AutoAuthPage @@ -53,16 +53,14 @@ class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin ).visit().get_user_id() # login as an instructor - instructor_password = 'test' self.instructor_name = "instructor_user" self.instructor_id = AutoAuthPage( self.browser, username=self.instructor_name, email="instructor_user@example.com", - course_id=self.course_id, staff=True, password=instructor_password + course_id=self.course_id, staff=True ).visit().get_user_id() # go to the membership page on the instructor dashboard self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id) - get_sudo_access(self.browser, self.instructor_dashboard_page, instructor_password) self.instructor_dashboard_page.visit() self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management() @@ -650,16 +648,14 @@ class CohortDiscussionTopicsTest(UniqueCourseTest, CohortTestMixin): self.cohort_id = self.add_manual_cohort(self.course_fixture, self.cohort_name) # login as an instructor - self.instructor_password = 'test' self.instructor_name = "instructor_user" self.instructor_id = AutoAuthPage( self.browser, username=self.instructor_name, email="instructor_user@example.com", - course_id=self.course_id, staff=True, password=self.instructor_password + course_id=self.course_id, staff=True ).visit().get_user_id() # go to the membership page on the instructor dashboard self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id) - get_sudo_access(self.browser, self.instructor_dashboard_page, self.instructor_password) self.instructor_dashboard_page.visit() self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management() self.cohort_management_page.wait_for_page() @@ -944,16 +940,14 @@ class CohortContentGroupAssociationTest(UniqueCourseTest, CohortTestMixin): }) # login as an instructor - instructor_password = 'test' self.instructor_name = "instructor_user" self.instructor_id = AutoAuthPage( self.browser, username=self.instructor_name, email="instructor_user@example.com", - course_id=self.course_id, staff=True, password=instructor_password + course_id=self.course_id, staff=True ).visit().get_user_id() # go to the membership page on the instructor dashboard self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id) - get_sudo_access(self.browser, self.instructor_dashboard_page, instructor_password) self.instructor_dashboard_page.visit() self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management() diff --git a/common/test/acceptance/tests/helpers.py b/common/test/acceptance/tests/helpers.py index 3c2e45e15d..1b8b597520 100644 --- a/common/test/acceptance/tests/helpers.py +++ b/common/test/acceptance/tests/helpers.py @@ -25,7 +25,6 @@ from selenium.webdriver.support.select import Select from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from unittest import TestCase -from ..pages.common.sudo_page import SudoPage from ..pages.common import BASE_URL @@ -685,12 +684,3 @@ class TestWithSearchIndexMixin(object): def _cleanup_index_file(self): """ Removes search index backing file """ remove_file(self.TEST_INDEX_FILENAME) - - -def get_sudo_access(browser, redirect_page, password): - """ - Get sudo access for instructor or staff user. - """ - sudo_password_page = SudoPage(browser, redirect_page) - sudo_password_page.visit() - sudo_password_page.submit_sudo_password_and_get_access(password) diff --git a/common/test/acceptance/tests/lms/test_lms_cohorted_courseware_search.py b/common/test/acceptance/tests/lms/test_lms_cohorted_courseware_search.py index da6905f116..323d1bebf0 100644 --- a/common/test/acceptance/tests/lms/test_lms_cohorted_courseware_search.py +++ b/common/test/acceptance/tests/lms/test_lms_cohorted_courseware_search.py @@ -9,7 +9,6 @@ from ...pages.studio.overview import CourseOutlinePage from ...pages.lms.courseware_search import CoursewareSearchPage from ...pages.lms.staff_view import StaffPage from ...fixtures.course import XBlockFixtureDesc -from ..helpers import get_sudo_access from nose.plugins.attrib import attr @@ -84,13 +83,13 @@ class CoursewareSearchCohortTest(ContainerBase): super(CoursewareSearchCohortTest, self).tearDown() os.remove(self.TEST_INDEX_FILENAME) - def _auto_auth(self, username, email, staff, password='test'): + def _auto_auth(self, username, email, staff): """ Logout and login with given credentials. """ LogoutPage(self.browser).visit() StudioAutoAuthPage(self.browser, username=username, email=email, - course_id=self.course_id, staff=staff, password=password).visit() + course_id=self.course_id, staff=staff).visit() def _studio_reindex(self): """ @@ -194,7 +193,7 @@ class CoursewareSearchCohortTest(ContainerBase): Each cohort is assigned one student. """ instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id) - get_sudo_access(self.browser, instructor_dashboard_page, 'test') + instructor_dashboard_page.visit() cohort_management_page = instructor_dashboard_page.select_cohort_management() def add_cohort_with_student(cohort_name, content_group, student): 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 de598cde13..0b1fc27b18 100644 --- a/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py +++ b/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py @@ -6,7 +6,7 @@ End-to-end tests for the LMS Instructor Dashboard. from nose.plugins.attrib import attr from bok_choy.promise import EmptyPromise -from ..helpers import UniqueCourseTest, get_modal_alert, EventsTestMixin, get_sudo_access +from ..helpers import UniqueCourseTest, get_modal_alert, EventsTestMixin from ...pages.common.logout import LogoutPage from ...pages.lms.auto_auth import AutoAuthPage from ...pages.lms.instructor_dashboard import InstructorDashboardPage @@ -22,9 +22,7 @@ class BaseInstructorDashboardTest(EventsTestMixin, UniqueCourseTest): Logs in as an instructor and returns the id. """ username = "test_instructor_{uuid}".format(uuid=self.unique_id[0:6]) - auto_auth_page = AutoAuthPage( - self.browser, username=username, course_id=self.course_id, staff=True, password="test" - ) + auto_auth_page = AutoAuthPage(self.browser, username=username, course_id=self.course_id, staff=True) return username, auto_auth_page.visit().get_user_id() def visit_instructor_dashboard(self): @@ -32,7 +30,6 @@ class BaseInstructorDashboardTest(EventsTestMixin, UniqueCourseTest): Visits the instructor dashboard. """ instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id) - get_sudo_access(self.browser, instructor_dashboard_page, "test") instructor_dashboard_page.visit() return instructor_dashboard_page @@ -145,10 +142,10 @@ class EntranceExamGradeTest(BaseInstructorDashboardTest): Then I see Student Email input box, Reset Student Attempt, Rescore Student Submission, Delete Student State for entrance exam and Show Background Task History for Student buttons """ - self.assertTrue(self.student_admin_section.is_entrance_exam_student_email_input_visible()) - self.assertTrue(self.student_admin_section.is_entrance_exam_reset_attempts_button_visible()) - self.assertTrue(self.student_admin_section.is_entrance_exam_rescore_submission_button_visible()) - self.assertTrue(self.student_admin_section.is_entrance_exam_delete_student_state_button_visible()) + self.assertTrue(self.student_admin_section.is_student_email_input_visible()) + self.assertTrue(self.student_admin_section.is_reset_attempts_button_visible()) + self.assertTrue(self.student_admin_section.is_rescore_submission_button_visible()) + self.assertTrue(self.student_admin_section.is_delete_student_state_button_visible()) self.assertTrue(self.student_admin_section.is_background_task_history_button_visible()) def test_clicking_reset_student_attempts_button_without_email_shows_error(self): @@ -161,10 +158,10 @@ class EntranceExamGradeTest(BaseInstructorDashboardTest): Then I should be shown an Error Notification And The Notification message should read 'Please enter a student email address or username.' """ - self.student_admin_section.entrance_exam_click_reset_attempts_button() + self.student_admin_section.click_reset_attempts_button() self.assertEqual( 'Please enter a student email address or username.', - self.student_admin_section.entrance_exam_top_notification.text[0] + self.student_admin_section.top_notification.text[0] ) def test_clicking_reset_student_attempts_button_with_success(self): @@ -177,8 +174,8 @@ class EntranceExamGradeTest(BaseInstructorDashboardTest): email address or username Then I should be shown an alert with success message """ - self.student_admin_section.set_student_email_for_ee(self.student_identifier) - self.student_admin_section.entrance_exam_click_reset_attempts_button() + self.student_admin_section.set_student_email(self.student_identifier) + self.student_admin_section.click_reset_attempts_button() alert = get_modal_alert(self.student_admin_section.browser) alert.dismiss() @@ -191,10 +188,10 @@ class EntranceExamGradeTest(BaseInstructorDashboardTest): Adjustment after non existing student email address or username Then I should be shown an error message """ - self.student_admin_section.set_student_email_for_ee('non_existing@example.com') - self.student_admin_section.entrance_exam_click_reset_attempts_button() + self.student_admin_section.set_student_email('non_existing@example.com') + self.student_admin_section.click_reset_attempts_button() self.student_admin_section.wait_for_ajax() - self.assertGreater(len(self.student_admin_section.entrance_exam_top_notification.text[0]), 0) + self.assertGreater(len(self.student_admin_section.top_notification.text[0]), 0) def test_clicking_rescore_submission_button_with_success(self): """ @@ -205,8 +202,8 @@ class EntranceExamGradeTest(BaseInstructorDashboardTest): Adjustment after entering a valid student email address or username Then I should be shown an alert with success message """ - self.student_admin_section.set_student_email_for_ee(self.student_identifier) - self.student_admin_section.entrance_exam_click_rescore_submissions_button() + self.student_admin_section.set_student_email(self.student_identifier) + self.student_admin_section.click_rescore_submissions_button() alert = get_modal_alert(self.student_admin_section.browser) alert.dismiss() @@ -219,10 +216,10 @@ class EntranceExamGradeTest(BaseInstructorDashboardTest): Adjustment after non existing student email address or username Then I should be shown an error message """ - self.student_admin_section.set_student_email_for_ee('non_existing@example.com') - self.student_admin_section.entrance_exam_click_rescore_submissions_button() + self.student_admin_section.set_student_email('non_existing@example.com') + self.student_admin_section.click_rescore_submissions_button() self.student_admin_section.wait_for_ajax() - self.assertGreater(len(self.student_admin_section.entrance_exam_top_notification.text[0]), 0) + self.assertGreater(len(self.student_admin_section.top_notification.text[0]), 0) def test_clicking_skip_entrance_exam_button_with_success(self): """ @@ -234,7 +231,7 @@ class EntranceExamGradeTest(BaseInstructorDashboardTest): email address or username Then I should be shown an alert with success message """ - self.student_admin_section.set_student_email_for_ee(self.student_identifier) + self.student_admin_section.set_student_email(self.student_identifier) self.student_admin_section.click_skip_entrance_exam_button() #first we have window.confirm alert = get_modal_alert(self.student_admin_section.browser) @@ -254,14 +251,14 @@ class EntranceExamGradeTest(BaseInstructorDashboardTest): student email address or username Then I should be shown an error message """ - self.student_admin_section.set_student_email_for_ee('non_existing@example.com') + self.student_admin_section.set_student_email('non_existing@example.com') self.student_admin_section.click_skip_entrance_exam_button() #first we have window.confirm alert = get_modal_alert(self.student_admin_section.browser) alert.accept() self.student_admin_section.wait_for_ajax() - self.assertGreater(len(self.student_admin_section.entrance_exam_top_notification.text[0]), 0) + self.assertGreater(len(self.student_admin_section.top_notification.text[0]), 0) def test_clicking_delete_student_attempts_button_with_success(self): """ @@ -273,8 +270,8 @@ class EntranceExamGradeTest(BaseInstructorDashboardTest): email address or username Then I should be shown an alert with success message """ - self.student_admin_section.set_student_email_for_ee(self.student_identifier) - self.student_admin_section.entrance_exam_click_delete_student_state_button() + self.student_admin_section.set_student_email(self.student_identifier) + self.student_admin_section.click_delete_student_state_button() alert = get_modal_alert(self.student_admin_section.browser) alert.dismiss() @@ -289,10 +286,10 @@ class EntranceExamGradeTest(BaseInstructorDashboardTest): email address or username Then I should be shown an error message """ - self.student_admin_section.set_student_email_for_ee('non_existing@example.com') - self.student_admin_section.entrance_exam_click_delete_student_state_button() + self.student_admin_section.set_student_email('non_existing@example.com') + self.student_admin_section.click_delete_student_state_button() self.student_admin_section.wait_for_ajax() - self.assertGreater(len(self.student_admin_section.entrance_exam_top_notification.text[0]), 0) + self.assertGreater(len(self.student_admin_section.top_notification.text[0]), 0) def test_clicking_task_history_button_with_success(self): """ @@ -304,8 +301,8 @@ class EntranceExamGradeTest(BaseInstructorDashboardTest): email address or username Then I should be shown an table listing all background tasks """ - self.student_admin_section.set_student_email_for_ee(self.student_identifier) - self.student_admin_section.entrance_exam_click_task_history_button() + self.student_admin_section.set_student_email(self.student_identifier) + self.student_admin_section.click_task_history_button() self.assertTrue(self.student_admin_section.is_background_task_history_table_visible()) diff --git a/common/test/acceptance/tests/lms/test_lms_user_preview.py b/common/test/acceptance/tests/lms/test_lms_user_preview.py index 0ff88471d2..292bbf8dcf 100644 --- a/common/test/acceptance/tests/lms/test_lms_user_preview.py +++ b/common/test/acceptance/tests/lms/test_lms_user_preview.py @@ -3,12 +3,10 @@ Tests the "preview" selector in the LMS that allows changing between Staff, Student, and Content Groups. """ -from ..helpers import UniqueCourseTest, create_user_partition_json, get_modal_alert +from ..helpers import UniqueCourseTest, create_user_partition_json from ...pages.studio.auto_auth import AutoAuthPage from ...pages.lms.courseware import CoursewarePage -from ...pages.lms.instructor_dashboard import InstructorDashboardPage, StudentAdminPage from ...pages.lms.staff_view import StaffPage -from ...pages.common.sudo_page import SudoPage from ...fixtures.course import CourseFixture, XBlockFixtureDesc from xmodule.partitions.partitions import Group from textwrap import dedent @@ -38,9 +36,8 @@ class StaffViewTest(UniqueCourseTest): # Auto-auth register for the course. # Do this as global staff so that you will see the Staff View - self.staff_password = 'test' AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, - course_id=self.course_id, staff=True, password=self.staff_password).visit() + course_id=self.course_id, staff=True).visit() def _goto_staff_page(self): """ @@ -102,41 +99,26 @@ class StaffDebugTest(CourseWithoutContentGroupsTest): """ Tests that verify the staff debug info. """ - - def _goto_student_admin_section(self): - """ - Get sudo access and return student admin section. - """ - instructor_page = InstructorDashboardPage(self.browser, self.course_id) - sudo_page = SudoPage(self.browser, instructor_page) - sudo_page.wait_for_page() - sudo_page.submit_sudo_password_and_get_access(self.staff_password) - - student_admin_section = StudentAdminPage(self.browser) - student_admin_section.wait_for_page() - return student_admin_section - def test_reset_attempts_empty(self): """ Test that we reset even when there is no student state """ staff_debug_page = self._goto_staff_page().open_staff_debug_info() - staff_debug_page.click_student_grade_adjustments() - student_admin_section = self._goto_student_admin_section() - student_admin_section.click_reset_attempts_button() - alert = get_modal_alert(student_admin_section.browser) - alert.dismiss() + staff_debug_page.reset_attempts() + msg = staff_debug_page.idash_msg[0] + self.assertEqual(u'Successfully reset the attempts ' + 'for user {}'.format(self.USERNAME), msg) def test_delete_state_empty(self): """ Test that we delete properly even when there isn't state to delete. """ staff_debug_page = self._goto_staff_page().open_staff_debug_info() - staff_debug_page.click_student_grade_adjustments() - student_admin_section = self._goto_student_admin_section() - student_admin_section.click_delete_student_state_button() - self.assertEqual(len(student_admin_section.top_notification.text[0]), 0) + staff_debug_page.delete_state() + msg = staff_debug_page.idash_msg[0] + self.assertEqual(u'Successfully deleted student state ' + 'for user {}'.format(self.USERNAME), msg) def test_reset_attempts_state(self): """ @@ -146,11 +128,10 @@ class StaffDebugTest(CourseWithoutContentGroupsTest): staff_page.answer_problem() staff_debug_page = staff_page.open_staff_debug_info() - staff_debug_page.click_student_grade_adjustments() - student_admin_section = self._goto_student_admin_section() - student_admin_section.click_reset_attempts_button() - alert = get_modal_alert(student_admin_section.browser) - alert.dismiss() + staff_debug_page.reset_attempts() + msg = staff_debug_page.idash_msg[0] + self.assertEqual(u'Successfully reset the attempts ' + 'for user {}'.format(self.USERNAME), msg) def test_rescore_state(self): """ @@ -160,11 +141,9 @@ class StaffDebugTest(CourseWithoutContentGroupsTest): staff_page.answer_problem() staff_debug_page = staff_page.open_staff_debug_info() - staff_debug_page.click_student_grade_adjustments() - student_admin_section = self._goto_student_admin_section() - student_admin_section.click_rescore_submissions_button() - alert = get_modal_alert(student_admin_section.browser) - alert.dismiss() + staff_debug_page.rescore() + msg = staff_debug_page.idash_msg[0] + self.assertEqual(u'Successfully rescored problem for user STAFF_TESTER', msg) def test_student_state_delete(self): """ @@ -174,10 +153,10 @@ class StaffDebugTest(CourseWithoutContentGroupsTest): staff_page.answer_problem() staff_debug_page = staff_page.open_staff_debug_info() - staff_debug_page.click_student_grade_adjustments() - student_admin_section = self._goto_student_admin_section() - student_admin_section.click_delete_student_state_button() - self.assertEqual(len(student_admin_section.top_notification.text[0]), 0) + staff_debug_page.delete_state() + msg = staff_debug_page.idash_msg[0] + self.assertEqual(u'Successfully deleted student state ' + 'for user {}'.format(self.USERNAME), msg) def test_student_by_email(self): """ @@ -187,11 +166,10 @@ class StaffDebugTest(CourseWithoutContentGroupsTest): staff_page.answer_problem() staff_debug_page = staff_page.open_staff_debug_info() - staff_debug_page.click_student_grade_adjustments(self.EMAIL) - student_admin_section = self._goto_student_admin_section() - student_admin_section.click_reset_attempts_button() - alert = get_modal_alert(student_admin_section.browser) - alert.dismiss() + staff_debug_page.reset_attempts(self.EMAIL) + msg = staff_debug_page.idash_msg[0] + self.assertEqual(u'Successfully reset the attempts ' + 'for user {}'.format(self.EMAIL), msg) def test_bad_student(self): """ @@ -201,10 +179,10 @@ class StaffDebugTest(CourseWithoutContentGroupsTest): staff_page.answer_problem() staff_debug_page = staff_page.open_staff_debug_info() - staff_debug_page.click_student_grade_adjustments('INVALIDUSER') - student_admin_section = self._goto_student_admin_section() - student_admin_section.click_delete_student_state_button() - self.assertGreater(len(student_admin_section.top_notification.text[0]), 0) + staff_debug_page.delete_state('INVALIDUSER') + msg = staff_debug_page.idash_msg[0] + self.assertEqual(u'Failed to delete student state. ' + 'User does not exist.', msg) def test_reset_attempts_for_problem_loaded_via_ajax(self): """ @@ -215,11 +193,10 @@ class StaffDebugTest(CourseWithoutContentGroupsTest): staff_page.answer_problem() staff_debug_page = staff_page.open_staff_debug_info() - staff_debug_page.click_student_grade_adjustments() - student_admin_section = self._goto_student_admin_section() - student_admin_section.click_reset_attempts_button() - alert = get_modal_alert(student_admin_section.browser) - alert.dismiss() + staff_debug_page.reset_attempts() + msg = staff_debug_page.idash_msg[0] + self.assertEqual(u'Successfully reset the attempts ' + 'for user {}'.format(self.USERNAME), msg) def test_rescore_state_for_problem_loaded_via_ajax(self): """ @@ -230,11 +207,9 @@ class StaffDebugTest(CourseWithoutContentGroupsTest): staff_page.answer_problem() staff_debug_page = staff_page.open_staff_debug_info() - staff_debug_page.click_student_grade_adjustments() - student_admin_section = self._goto_student_admin_section() - student_admin_section.click_rescore_submissions_button() - alert = get_modal_alert(student_admin_section.browser) - alert.dismiss() + staff_debug_page.rescore() + msg = staff_debug_page.idash_msg[0] + self.assertEqual(u'Successfully rescored problem for user STAFF_TESTER', msg) def test_student_state_delete_for_problem_loaded_via_ajax(self): """ @@ -245,10 +220,10 @@ class StaffDebugTest(CourseWithoutContentGroupsTest): staff_page.answer_problem() staff_debug_page = staff_page.open_staff_debug_info() - staff_debug_page.click_student_grade_adjustments() - student_admin_section = self._goto_student_admin_section() - student_admin_section.click_delete_student_state_button() - self.assertEqual(len(student_admin_section.top_notification.text[0]), 0) + staff_debug_page.delete_state() + msg = staff_debug_page.idash_msg[0] + self.assertEqual(u'Successfully deleted student state ' + 'for user {}'.format(self.USERNAME), msg) class CourseWithContentGroupsTest(StaffViewTest): diff --git a/common/test/acceptance/tests/studio/test_studio_course_team.py b/common/test/acceptance/tests/studio/test_studio_course_team.py index b289b5bc05..80dda78a09 100644 --- a/common/test/acceptance/tests/studio/test_studio_course_team.py +++ b/common/test/acceptance/tests/studio/test_studio_course_team.py @@ -5,7 +5,6 @@ from flaky import flaky from nose.plugins.attrib import attr from .base_studio_test import StudioCourseTest -from ..helpers import get_sudo_access from ...pages.studio.auto_auth import AutoAuthPage from ...pages.studio.users import CourseTeamPage @@ -39,7 +38,6 @@ class CourseTeamPageTest(StudioCourseTest): self.page = CourseTeamPage( # pylint:disable=attribute-defined-outside-init self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run'] ) - get_sudo_access(self.browser, self.page, self.user.get('password')) self._go_to_course_team_page() def _go_to_course_team_page(self): @@ -127,7 +125,6 @@ class CourseTeamPageTest(StudioCourseTest): self.page.add_user_to_course(self.other_user.get('email')) self._assert_user_present(self.other_user, present=True) self.log_in(self.other_user) - get_sudo_access(self.browser, self.page, self.other_user.get('password')) self._assert_current_course(visible=True) @flaky # TODO fix this, see TNL-2667 @@ -146,7 +143,6 @@ class CourseTeamPageTest(StudioCourseTest): self._assert_user_present(self.other_user, present=True) self.log_in(self.other_user) - get_sudo_access(self.browser, self.page, self.other_user.get('password')) self._assert_current_course(visible=True) self._go_to_course_team_page() @@ -208,7 +204,6 @@ class CourseTeamPageTest(StudioCourseTest): self._assert_is_admin(other) self.log_in(self.other_user) - get_sudo_access(self.browser, self.page, self.other_user.get('password')) self._go_to_course_team_page() other = self.page.get_user(self.other_user.get('email')) self.assertTrue(other.is_current_user) @@ -240,14 +235,12 @@ class CourseTeamPageTest(StudioCourseTest): # precondition check - frank is an admin and can add/delete/promote/demote users self.log_in(self.other_user) - get_sudo_access(self.browser, self.page, self.other_user.get('password')) self._go_to_course_team_page() other = self.page.get_user(self.other_user.get('email')) self.assertTrue(other.is_current_user) self._assert_can_manage_users() self.log_in(self.user) - get_sudo_access(self.browser, self.page, self.user.get('password')) self._go_to_course_team_page() other = self.page.get_user(self.other_user.get('email')) other.click_demote() @@ -256,7 +249,6 @@ class CourseTeamPageTest(StudioCourseTest): self._assert_is_staff(other) self.log_in(self.other_user) - get_sudo_access(self.browser, self.page, self.other_user.get('password')) self._go_to_course_team_page() other = self.page.get_user(self.other_user.get('email')) self.assertTrue(other.is_current_user) @@ -342,7 +334,6 @@ class CourseTeamPageTest(StudioCourseTest): self.assertFalse(current.can_promote) self.log_in(self.other_user) - get_sudo_access(self.browser, self.page, self.other_user.get('password')) self._go_to_course_team_page() current = self.page.get_user(self.user.get('email')) diff --git a/common/test/acceptance/tests/studio/test_studio_library.py b/common/test/acceptance/tests/studio/test_studio_library.py index d25e196c29..0086024f69 100644 --- a/common/test/acceptance/tests/studio/test_studio_library.py +++ b/common/test/acceptance/tests/studio/test_studio_library.py @@ -7,7 +7,6 @@ from flaky import flaky from .base_studio_test import StudioLibraryTest from ...fixtures.course import XBlockFixtureDesc -from ..helpers import get_sudo_access from ...pages.studio.auto_auth import AutoAuthPage from ...pages.studio.utils import add_component from ...pages.studio.library import LibraryEditPage @@ -515,7 +514,6 @@ class LibraryUsersPageTest(StudioLibraryTest): AutoAuthPage(self.browser, username="second", email="second@example.com", no_login=True).visit() self.page = LibraryUsersPage(self.browser, self.library_key) - get_sudo_access(self.browser, self.page, self.user.get("password")) self.page.visit() def _refresh_page(self): diff --git a/common/test/acceptance/tests/test_cohorted_courseware.py b/common/test/acceptance/tests/test_cohorted_courseware.py index 7dec1d1412..a893252f89 100644 --- a/common/test/acceptance/tests/test_cohorted_courseware.py +++ b/common/test/acceptance/tests/test_cohorted_courseware.py @@ -11,7 +11,6 @@ from ..pages.studio.settings_group_configurations import GroupConfigurationsPage from ..pages.studio.auto_auth import AutoAuthPage as StudioAutoAuthPage from ..fixtures.course import XBlockFixtureDesc from ..fixtures import LMS_BASE_URL -from .helpers import get_sudo_access from ..pages.studio.component_editor import ComponentVisibilityEditorView from ..pages.lms.instructor_dashboard import InstructorDashboardPage from ..pages.lms.courseware import CoursewarePage @@ -55,12 +54,8 @@ class EndToEndCohortedCoursewareTest(ContainerBase): ).visit() # Start logged in as the staff user. - self.instructor_password = 'test' StudioAutoAuthPage( - self.browser, - username=self.staff_user["username"], - email=self.staff_user["email"], - password=self.instructor_password + self.browser, username=self.staff_user["username"], email=self.staff_user["email"] ).visit() def populate_course_fixture(self, course_fixture): @@ -143,7 +138,6 @@ class EndToEndCohortedCoursewareTest(ContainerBase): Each cohort is assigned one student. """ instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id) - get_sudo_access(self.browser, instructor_dashboard_page, self.instructor_password) instructor_dashboard_page.visit() cohort_management_page = instructor_dashboard_page.select_cohort_management() diff --git a/lms/djangoapps/bulk_email/tests/test_course_optout.py b/lms/djangoapps/bulk_email/tests/test_course_optout.py index 09b87ed5ff..e72edd222d 100644 --- a/lms/djangoapps/bulk_email/tests/test_course_optout.py +++ b/lms/djangoapps/bulk_email/tests/test_course_optout.py @@ -27,7 +27,7 @@ class TestOptoutCourseEmails(ModuleStoreTestCase): def setUp(self): super(TestOptoutCourseEmails, self).setUp() course_title = u"ẗëṡẗ title イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ" - self.course = CourseFactory.create(display_name=course_title, run='T12015') + self.course = CourseFactory.create(display_name=course_title) self.instructor = AdminFactory.create() self.student = UserFactory.create() CourseEnrollmentFactory.create(user=self.student, course_id=self.course.id) @@ -47,7 +47,6 @@ class TestOptoutCourseEmails(ModuleStoreTestCase): """Navigate to the instructor dash's email view""" # Pull up email view on instructor dashboard url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()}) - self.grant_sudo_access(unicode(self.course.id), 'test') response = self.client.get(url) email_section = '
' # If this fails, it is likely because ENABLE_INSTRUCTOR_EMAIL is set to False diff --git a/lms/djangoapps/bulk_email/tests/test_email.py b/lms/djangoapps/bulk_email/tests/test_email.py index 7bb2b827d9..9a85bd2ba5 100644 --- a/lms/djangoapps/bulk_email/tests/test_email.py +++ b/lms/djangoapps/bulk_email/tests/test_email.py @@ -53,7 +53,7 @@ class EmailSendFromDashboardTestCase(ModuleStoreTestCase): def setUp(self): super(EmailSendFromDashboardTestCase, self).setUp() course_title = u"ẗëṡẗ title イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ" - self.course = CourseFactory.create(display_name=course_title, run="1T2015") + self.course = CourseFactory.create(display_name=course_title) self.instructor = InstructorFactory(course_key=self.course.id) @@ -75,7 +75,6 @@ class EmailSendFromDashboardTestCase(ModuleStoreTestCase): self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()}) # Response loads the whole instructor dashboard, so no need to explicitly # navigate to a particular email section - self.grant_sudo_access(unicode(self.course.id), 'test') response = self.client.get(self.url) email_section = '
' # If this fails, it is likely because ENABLE_INSTRUCTOR_EMAIL is set to False diff --git a/lms/djangoapps/bulk_email/tests/test_err_handling.py b/lms/djangoapps/bulk_email/tests/test_err_handling.py index a2457051df..9b8db791d9 100644 --- a/lms/djangoapps/bulk_email/tests/test_err_handling.py +++ b/lms/djangoapps/bulk_email/tests/test_err_handling.py @@ -47,10 +47,9 @@ class TestEmailErrors(ModuleStoreTestCase): def setUp(self): super(TestEmailErrors, self).setUp() course_title = u"ẗëṡẗ title イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ" - self.course = CourseFactory.create(display_name=course_title, run="1T2015") + self.course = CourseFactory.create(display_name=course_title) self.instructor = AdminFactory.create() self.client.login(username=self.instructor.username, password="test") - self.grant_sudo_access(unicode(self.course.id), 'test') # load initial content (since we don't run migrations as part of tests): call_command("loaddata", "course_email_template.json") diff --git a/lms/djangoapps/courseware/admin.py b/lms/djangoapps/courseware/admin.py index 16843ba22d..78c9fc826f 100644 --- a/lms/djangoapps/courseware/admin.py +++ b/lms/djangoapps/courseware/admin.py @@ -3,7 +3,7 @@ django admin pages for courseware model ''' from courseware.models import StudentModule, OfflineComputedGrade, OfflineComputedGradeLog -from django.contrib import admin +from ratelimitbackend import admin admin.site.register(StudentModule) diff --git a/lms/djangoapps/courseware/features/lti.feature b/lms/djangoapps/courseware/features/lti.feature index c3d6c2f643..3ac9564f7a 100644 --- a/lms/djangoapps/courseware/features/lti.feature +++ b/lms/djangoapps/courseware/features/lti.feature @@ -51,7 +51,6 @@ Feature: LMS.LTI component Then I see text "Problem Scores: 5/10" And I see graph with total progress "5%" Then I click on the "Instructor" tab - Then I get sudo access with password "test" And I click on the "Student Admin" tab And I click on the "View Gradebook" link And I see in the gradebook table that "HW" is "50" @@ -91,7 +90,6 @@ Feature: LMS.LTI component Then I see text "Problem Scores: 8/10" And I see graph with total progress "8%" Then I click on the "Instructor" tab - Then I get sudo access with password "test" And I click on the "Student Admin" tab And I click on the "View Gradebook" link And I see in the gradebook table that "HW" is "80" @@ -118,7 +116,6 @@ Feature: LMS.LTI component Then I see text "Problem Scores: 0/10" And I see graph with total progress "0%" Then I click on the "Instructor" tab - Then I get sudo access with password "test" And I click on the "Student Admin" tab And I click on the "View Gradebook" link And I see in the gradebook table that "HW" is "0" diff --git a/lms/djangoapps/courseware/features/staff_debug_info.feature b/lms/djangoapps/courseware/features/staff_debug_info.feature new file mode 100644 index 0000000000..519e4b3c13 --- /dev/null +++ b/lms/djangoapps/courseware/features/staff_debug_info.feature @@ -0,0 +1,13 @@ +@shard_1 +Feature: LMS.Debug staff info links + As a course staff in an edX course + In order to test my understanding of the material + I want to click on staff debug info links + + Scenario: I can reset student attempts + When i am staff member for the course "model_course" + And I am viewing a "multiple choice" problem + And I can view staff debug info + Then I can reset student attempts + Then I cannot see delete student state link + Then I cannot see rescore student submission link diff --git a/lms/djangoapps/courseware/features/staff_debug_info.py b/lms/djangoapps/courseware/features/staff_debug_info.py new file mode 100644 index 0000000000..9d183a5be9 --- /dev/null +++ b/lms/djangoapps/courseware/features/staff_debug_info.py @@ -0,0 +1,51 @@ +""" +Steps for staff_debug_info.feature lettuce tests +""" + +from django.contrib.auth.models import User +from lettuce import world, step +from common import create_course, course_id +from courseware.courses import get_course_by_id +from instructor.access import allow_access + + +@step(u'i am staff member for the course "([^"]*)"$') +def i_am_staff_member_for_the_course(step, course_number): + # Create the course + create_course(step, course_number) + course = get_course_by_id(course_id(course_number)) + + # Create the user + world.create_user('robot', 'test') + user = User.objects.get(username='robot') + + # Add user as a course staff. + allow_access(course, user, "staff") + + world.log_in(username='robot', password='test') + + +@step(u'I can view staff debug info') +def view_staff_debug_info(step): + css_selector = "a.instructor-info-action" + world.css_click(css_selector) + world.wait_for_visible("section.staff-modal") + + +@step(u'I can reset student attempts') +def view_staff_debug_info(step): + css_selector = "a.staff-debug-reset" + world.css_click(css_selector) + world.wait_for_ajax_complete() + + +@step(u'I cannot see delete student state link') +def view_staff_debug_info(step): + css_selector = "a.staff-debug-sdelete" + world.is_css_not_present(css_selector) + + +@step(u'I cannot see rescore student submission link') +def view_staff_debug_info(step): + css_selector = "a.staff-debug-rescore" + world.is_css_not_present(css_selector) diff --git a/lms/djangoapps/courseware/tests/test_entrance_exam.py b/lms/djangoapps/courseware/tests/test_entrance_exam.py index 504e366ea9..46dbe5413d 100644 --- a/lms/djangoapps/courseware/tests/test_entrance_exam.py +++ b/lms/djangoapps/courseware/tests/test_entrance_exam.py @@ -386,7 +386,6 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase): # hit skip entrance exam api in instructor app instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') url = reverse('mark_student_can_skip_entrance_exam', kwargs={'course_id': unicode(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.request.user.email, diff --git a/lms/djangoapps/courseware/tests/test_tabs.py b/lms/djangoapps/courseware/tests/test_tabs.py index 828b03d8ed..b50d4d793a 100644 --- a/lms/djangoapps/courseware/tests/test_tabs.py +++ b/lms/djangoapps/courseware/tests/test_tabs.py @@ -383,7 +383,6 @@ class EntranceExamsTabsTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): instructor = InstructorFactory(course_key=self.course.id) self.client.logout() self.client.login(username=instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') url = reverse('mark_student_can_skip_entrance_exam', kwargs={'course_id': unicode(self.course.id)}) response = self.client.post(url, { diff --git a/lms/djangoapps/courseware/tests/test_view_authentication.py b/lms/djangoapps/courseware/tests/test_view_authentication.py index a1609b62b8..52cf037a9e 100644 --- a/lms/djangoapps/courseware/tests/test_view_authentication.py +++ b/lms/djangoapps/courseware/tests/test_view_authentication.py @@ -67,7 +67,6 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): 'book_index': index}) for index, __ in enumerate(course.textbooks) ]) - self.grant_sudo_access(unicode(course.id), 'test') for url in urls: self.assert_request_status_code(404, url) @@ -82,7 +81,6 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): 'book_index': index}) for index in xrange(len(course.textbooks)) ]) - self.grant_sudo_access(unicode(course.id), 'test') for url in urls: self.assert_request_status_code(200, url) @@ -209,8 +207,6 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): urls = [reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()}), reverse('instructor_dashboard', kwargs={'course_id': self.test_course.id.to_deprecated_string()})] - self.grant_sudo_access(unicode(self.course.id), 'test') - self.grant_sudo_access(unicode(self.test_course.id), 'test') # Shouldn't be able to get to the instructor pages for url in urls: self.assert_request_status_code(404, url) @@ -222,8 +218,6 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): """ self.login(self.staff_user) - self.grant_sudo_access(unicode(self.course.id), 'test') - self.grant_sudo_access(unicode(self.test_course.id), 'test') # Now should be able to get to self.course, but not self.test_course url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()}) self.assert_request_status_code(200, url) @@ -238,8 +232,6 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): """ self.login(self.instructor_user) - self.grant_sudo_access(unicode(self.course.id), 'test') - self.grant_sudo_access(unicode(self.test_course.id), 'test') # Now should be able to get to self.course, but not self.test_course url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()}) self.assert_request_status_code(200, url) @@ -253,9 +245,6 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): and student profile pages for course in their org. """ self.login(self.org_staff_user) - self.grant_sudo_access(unicode(self.course.id), 'test') - self.grant_sudo_access(unicode(self.test_course.id), 'test') - self.grant_sudo_access(unicode(self.other_org_course.id), 'test') url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()}) self.assert_request_status_code(200, url) @@ -271,9 +260,6 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): and student profile pages for course in their org. """ self.login(self.org_instructor_user) - self.grant_sudo_access(unicode(self.course.id), 'test') - self.grant_sudo_access(unicode(self.test_course.id), 'test') - self.grant_sudo_access(unicode(self.other_org_course.id), 'test') url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()}) self.assert_request_status_code(200, url) @@ -289,8 +275,6 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): """ self.login(self.global_staff_user) - self.grant_sudo_access(unicode(self.course.id), 'test') - self.grant_sudo_access(unicode(self.test_course.id), 'test') # and now should be able to load both urls = [reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()}), reverse('instructor_dashboard', kwargs={'course_id': self.test_course.id.to_deprecated_string()})] @@ -403,26 +387,6 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): self.login(self.global_staff_user) self.assertTrue(self.enroll(self.course)) - def test_org_instructor_cannot_access_without_sudo(self): - """ - Test that org instructor cannot load the instructor dashboard without sudo access - and it redirect org instructor to sudo password page. - """ - self.login(self.org_instructor_user) - url = reverse('instructor_dashboard', kwargs={'course_id': unicode(self.course.id)}) - response = self.assert_request_status_code(401, url) - self.assertIn('Unauthorized', response.content) - - def test_org_staff_cannot_access_without_sudo(self): - """ - Test that org staff cannot load the instructor dashboard without sudo access - and it redirect org staff to sudo password page. - """ - self.login(self.org_staff_user) - url = reverse('instructor_dashboard', kwargs={'course_id': unicode(self.course.id)}) - response = self.assert_request_status_code(401, url) - self.assertIn('Unauthorized', response.content) - @attr('shard_1') class TestBetatesterAccess(ModuleStoreTestCase, CourseAccessTestMixin): diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 9a75eb633b..8dbfcb42d0 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -32,7 +32,6 @@ from course_modes.models import CourseMode from courseware.testutils import RenderXBlockTestMixin from courseware.tests.factories import StudentModuleFactory from edxmako.tests import mako_middleware_process_request -from django_sudo_helpers.tests.utils import sudo_middleware_process_request from student.models import CourseEnrollment from student.tests.factories import AdminFactory, UserFactory, CourseEnrollmentFactory from util.tests.test_date_utils import fake_ugettext, fake_pgettext @@ -1153,7 +1152,6 @@ class TestIndexView(ModuleStoreTestCase): ) request.user = user mako_middleware_process_request(request) - sudo_middleware_process_request(request) # Trigger the assertions embedded in the ViewCheckerBlocks response = views.index(request, unicode(course.id), chapter=chapter.url_name, section=section.url_name) diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index 9a0b7b6a7f..403d47a2d9 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -25,7 +25,6 @@ from django.shortcuts import redirect from certificates import api as certs_api from edxmako.shortcuts import render_to_response, render_to_string, marketing_link from django.views.decorators.csrf import ensure_csrf_cookie -from sudo.utils import revoke_sudo_privileges from django.views.decorators.cache import cache_control from django.db import transaction from markupsafe import escape @@ -341,10 +340,6 @@ def index(request, course_id, chapter=None, section=None, - HTTPresponse """ - # Revoke sudo privileges from a request explicitly - if request.is_sudo(region=course_id): - revoke_sudo_privileges(request, region=course_id) - course_key = CourseKey.from_string(course_id) user = User.objects.prefetch_related("groups").get(id=request.user.id) diff --git a/lms/djangoapps/instructor/features/bulk_email.feature b/lms/djangoapps/instructor/features/bulk_email.feature index b1766bf9ca..39d2c00c83 100644 --- a/lms/djangoapps/instructor/features/bulk_email.feature +++ b/lms/djangoapps/instructor/features/bulk_email.feature @@ -7,8 +7,6 @@ Feature: LMS.Instructor Dash Bulk Email Scenario: Send bulk email Given there is a course with a staff, instructor and student And I am logged in to the course as "" - Then I go to instructor tab - Then I get sudo access with password "test" When I send email to "" Then Email is sent to "" diff --git a/lms/djangoapps/instructor/features/bulk_email.py b/lms/djangoapps/instructor/features/bulk_email.py index 81c11ea35e..90eed1ebd9 100644 --- a/lms/djangoapps/instructor/features/bulk_email.py +++ b/lms/djangoapps/instructor/features/bulk_email.py @@ -96,13 +96,6 @@ def log_into_the_course(step, role): # pylint: disable=unused-argument world.expected_addresses['myself'] = [my_email] -@step("I go to instructor tab") -def i_got_to_instructor_tab(step): # pylint: disable=unused-argument - url = '/courses/{}'.format(world.bulk_email_course_key) - world.visit(url) - world.css_click('a[href="{}/instructor"]'.format(url)) - - @step(u'I send email to "([^"]*)"') def when_i_send_an_email(step, recipient): # pylint: disable=unused-argument @@ -122,6 +115,9 @@ def when_i_send_an_email(step, recipient): # pylint: disable=unused-argument call_command('loaddata', 'course_email_template.json') # Go to the email section of the instructor dash + url = '/courses/{}'.format(world.bulk_email_course_key) + world.visit(url) + world.css_click('a[href="{}/instructor"]'.format(url)) world.css_click('a[data-section="send_email"]') # Select the recipient diff --git a/lms/djangoapps/instructor/features/common.py b/lms/djangoapps/instructor/features/common.py index d71a2edf9c..27e15d7aca 100644 --- a/lms/djangoapps/instructor/features/common.py +++ b/lms/djangoapps/instructor/features/common.py @@ -12,7 +12,6 @@ from mock import patch from nose.tools import assert_in # pylint: disable=no-name-in-module from courseware.tests.factories import StaffFactory, InstructorFactory -from terrain.steps import i_get_sudo_access @step(u'Given I am "([^"]*)" for a very large course') @@ -72,17 +71,11 @@ def i_am_staff_or_instructor(step, role): # pylint: disable=unused-argument ) -def go_to_instructor_tab(step): +def go_to_section(section_name): # section name should be one of # course_info, membership, student_admin, data_download, analytics, send_email world.visit(u'/courses/{}'.format(world.course_key)) world.css_click(u'a[href="/courses/{}/instructor"]'.format(world.course_key)) - i_get_sudo_access(step, 'test') - - -def go_to_section(section_name): - # section name should be one of - # course_info, membership, student_admin, data_download, analytics, send_email world.css_click('a[data-section="{0}"]'.format(section_name)) @@ -91,7 +84,6 @@ def click_a_button(step, button): # pylint: disable=unused-argument if button == "Generate Grade Report": # Go to the data download section of the instructor dash - go_to_instructor_tab(step) go_to_section("data_download") # Click generate grade report button @@ -109,21 +101,18 @@ def click_a_button(step, button): # pylint: disable=unused-argument elif button == "Grading Configuration": # Go to the data download section of the instructor dash - go_to_instructor_tab(step) go_to_section("data_download") world.css_click('input[name="dump-gradeconf"]') elif button == "List enrolled students' profile information": # Go to the data download section of the instructor dash - go_to_instructor_tab(step) go_to_section("data_download") world.css_click('input[name="list-profiles"]') elif button == "Download profile information as a CSV": # Go to the data download section of the instructor dash - go_to_instructor_tab(step) go_to_section("data_download") world.css_click('input[name="list-profiles-csv"]') @@ -143,5 +132,4 @@ def click_a_button(step, tab_name): # pylint: disable=unused-argument 'Analytics': 'analytics', 'Email': 'send_email', } - go_to_instructor_tab(step) go_to_section(tab_name_dict[tab_name]) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 523f965037..e5d7ec3aec 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -283,7 +283,6 @@ class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase): CourseEnrollment.enroll(staff_member, self.course.id) CourseFinanceAdminRole(self.course.id).add_users(staff_member) self.client.login(username=staff_member.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') # Try to promote to forums admin - not working # update_forum_role(self.course.id, staff_member, FORUM_ROLE_ADMINISTRATOR, 'allow') @@ -306,23 +305,6 @@ class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase): "Staff member should not be allowed to access endpoint " + endpoint ) - def test_staff_level_without_sudo_access(self): - """ - Ensure that a staff member redirected to sudo password page without sudo access. - """ - staff_member = StaffFactory(course_key=self.course.id) - CourseEnrollment.enroll(staff_member, self.course.id) - CourseFinanceAdminRole(self.course.id).add_users(staff_member) - self.client.login(username=staff_member.username, password='test') - - for endpoint, args in self.staff_level_endpoints: - self._access_endpoint( - endpoint, - args, - 401, - "" - ) - def test_instructor_level(self): """ Ensure that an instructor member can access all endpoints. @@ -332,7 +314,6 @@ class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase): CourseFinanceAdminRole(self.course.id).add_users(inst) self.client.login(username=inst.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') for endpoint, args in self.staff_level_endpoints: # TODO: make these work @@ -356,31 +337,6 @@ class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase): "Instructor should be allowed to access endpoint " + endpoint ) - def test_instructor_level_without_sudo_access(self): - """ - Ensure that an instructor member redirected to sudo password page without sudo access. - """ - inst = InstructorFactory(course_key=self.course.id) - CourseEnrollment.enroll(inst, self.course.id) - CourseFinanceAdminRole(self.course.id).add_users(inst) - self.client.login(username=inst.username, password='test') - - for endpoint, args in self.staff_level_endpoints: - self._access_endpoint( - endpoint, - args, - 401, - "" - ) - - for endpoint, args in self.instructor_level_endpoints: - self._access_endpoint( - endpoint, - args, - 401, - "" - ) - @attr('shard_1') @patch.dict(settings.FEATURES, {'ALLOW_AUTOMATED_SIGNUPS': True}) @@ -395,7 +351,6 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(ModuleStoreTestCase, Log self.course = CourseFactory.create() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') self.url = reverse('register_and_enroll_students', kwargs={'course_id': self.course.id.to_deprecated_string()}) self.not_enrolled_student = UserFactory( @@ -700,7 +655,6 @@ class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase): self.course = CourseFactory.create() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') self.enrolled_student = UserFactory(username='EnrolledStudent', first_name='Enrolled', last_name='Student') CourseEnrollment.enroll( @@ -1277,7 +1231,6 @@ class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase): manually enrolling the students for the paid courses. """ paid_course = self.create_paid_course() - self.grant_sudo_access(unicode(paid_course.id), 'test') url = reverse('students_update_enrollment', kwargs={'course_id': paid_course.id.to_deprecated_string()}) params = {'identifiers': self.notregistered_email, 'action': 'enroll', 'email_students': False, 'auto_enroll': False} @@ -1303,7 +1256,6 @@ class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase): test to unenroll allow to enroll user. """ paid_course = self.create_paid_course() - self.grant_sudo_access(unicode(paid_course.id), 'test') url = reverse('students_update_enrollment', kwargs={'course_id': paid_course.id.to_deprecated_string()}) params = {'identifiers': self.notregistered_email, 'action': 'enroll', 'email_students': False, 'auto_enroll': False, 'reason': 'testing..'} @@ -1354,7 +1306,6 @@ class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase): test unenrolled user already not enrolled in a course. """ paid_course = self.create_paid_course() - self.grant_sudo_access(unicode(paid_course.id), 'test') course_enrollment = CourseEnrollment.objects.filter( user__email=self.notregistered_email, course_id=paid_course.id ) @@ -1452,7 +1403,6 @@ class TestInstructorAPIBulkBetaEnrollment(ModuleStoreTestCase, LoginEnrollmentTe self.course = CourseFactory.create() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') self.beta_tester = BetaTesterFactory(course_key=self.course.id) CourseEnrollment.enroll( @@ -1781,7 +1731,6 @@ class TestInstructorAPILevelsAccess(ModuleStoreTestCase, LoginEnrollmentTestCase self.course = CourseFactory.create() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') self.other_instructor = InstructorFactory(course_key=self.course.id) self.other_staff = StaffFactory(course_key=self.course.id) @@ -2020,7 +1969,6 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa self.course_mode.save() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') self.cart = Order.get_cart_for_user(self.instructor) self.coupon_code = 'abcde' self.coupon = Coupon(code=self.coupon_code, description='testing code', course_id=self.course.id, @@ -2448,7 +2396,6 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa UserProfileFactory.create(user=self.students[0], meta='{"company": "asdasda"}') self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') url = reverse('get_enrollment_report', kwargs={'course_id': self.course.id.to_deprecated_string()}) response = self.client.get(url, {}) self.assertIn('Your detailed enrollment report is being generated!', response.content) @@ -2498,7 +2445,6 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa CourseFinanceAdminRole(self.course.id).add_users(self.instructor) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') url = reverse('get_enrollment_report', kwargs={'course_id': self.course.id.to_deprecated_string()}) response = self.client.get(url, {}) @@ -2521,7 +2467,6 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa CourseFinanceAdminRole(self.course.id).add_users(self.instructor) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') url = reverse('get_enrollment_report', kwargs={'course_id': self.course.id.to_deprecated_string()}) response = self.client.get(url, {}) @@ -2547,7 +2492,6 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa CourseFinanceAdminRole(self.course.id).add_users(self.instructor) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') url = reverse('get_enrollment_report', kwargs={'course_id': self.course.id.to_deprecated_string()}) response = self.client.get(url, {}) @@ -2705,7 +2649,6 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase) self.course = CourseFactory.create() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') self.student = UserFactory() CourseEnrollment.enroll(self.student, self.course.id) @@ -2875,8 +2818,6 @@ class TestEntranceExamInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollm # Add instructor to invalid ee course CourseInstructorRole(self.course_with_invalid_ee.id).add_users(self.instructor) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course_with_invalid_ee.id), 'test') - self.grant_sudo_access(unicode(self.course.id), 'test') self.student = UserFactory() CourseEnrollment.enroll(self.student, self.course.id) @@ -2986,7 +2927,6 @@ class TestEntranceExamInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollm self.client.logout() staff_user = StaffFactory(course_key=self.course.id) self.client.login(username=staff_user.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') url = reverse('reset_student_attempts_for_entrance_exam', kwargs={'course_id': unicode(self.course.id)}) response = self.client.get(url, { @@ -3117,7 +3057,6 @@ class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase): self.course = CourseFactory.create() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') test_subject = u'\u1234 test subject' test_message = u'\u6824 test message' self.full_test_message = { @@ -3243,7 +3182,6 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase): ) self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') self.student = UserFactory() CourseEnrollment.enroll(self.student, self.course.id) @@ -3360,7 +3298,6 @@ class TestInstructorEmailContentList(ModuleStoreTestCase, LoginEnrollmentTestCas self.course = CourseFactory.create() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') self.tasks = {} self.emails = {} self.emails_info = {} @@ -3615,7 +3552,6 @@ class TestDueDateExtensions(ModuleStoreTestCase, LoginEnrollmentTestCase): self.instructor = InstructorFactory(course_key=course.id) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') def test_change_due_date(self): url = reverse('change_due_date', kwargs={'course_id': self.course.id.to_deprecated_string()}) @@ -3740,7 +3676,6 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase): CourseModeFactory.create(course_id=self.course.id, min_price=50) self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') CourseSalesAdminRole(self.course.id).add_users(self.instructor) url = reverse('generate_registration_codes', @@ -4216,7 +4151,6 @@ class TestBulkCohorting(ModuleStoreTestCase): Verify that we get the error we expect for a given file input. """ self.client.login(username=self.staff_user.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') response = self.call_add_users_to_cohorts(file_content, suffix=file_suffix) self.assertEqual(response.status_code, 400) result = json.loads(response.content) @@ -4230,7 +4164,6 @@ class TestBulkCohorting(ModuleStoreTestCase): """ mock_store_upload.return_value = (None, 'fake_file_name.csv') self.client.login(username=self.staff_user.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') response = self.call_add_users_to_cohorts(file_content) self.assertEqual(response.status_code, 204) self.assertTrue(mock_store_upload.called) diff --git a/lms/djangoapps/instructor/tests/test_api_email_localization.py b/lms/djangoapps/instructor/tests/test_api_email_localization.py index 5f70efd271..d9fd87df93 100644 --- a/lms/djangoapps/instructor/tests/test_api_email_localization.py +++ b/lms/djangoapps/instructor/tests/test_api_email_localization.py @@ -33,7 +33,6 @@ class TestInstructorAPIEnrollmentEmailLocalization(ModuleStoreTestCase): self.instructor = InstructorFactory(course_key=self.course.id) set_user_preference(self.instructor, LANGUAGE_KEY, 'zh-cn') self.client.login(username=self.instructor.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') self.student = UserFactory.create() set_user_preference(self.student, LANGUAGE_KEY, 'fr') diff --git a/lms/djangoapps/instructor/tests/test_certificates.py b/lms/djangoapps/instructor/tests/test_certificates.py index 6362d92165..84cae88433 100644 --- a/lms/djangoapps/instructor/tests/test_certificates.py +++ b/lms/djangoapps/instructor/tests/test_certificates.py @@ -96,7 +96,6 @@ class CertificatesInstructorDashTest(ModuleStoreTestCase): def _assert_certificates_visible(self, is_visible): """Check that the certificates section is visible on the instructor dash. """ - self.grant_sudo_access(unicode(self.course.id), 'test') response = self.client.get(self.url) if is_visible: self.assertContains(response, "Certificates") @@ -123,7 +122,6 @@ class CertificatesInstructorDashTest(ModuleStoreTestCase): def _assert_certificate_status(self, cert_name, expected_status): """Check the certificate status display on the instructor dash. """ - self.grant_sudo_access(unicode(self.course.id), 'test') response = self.client.get(self.url) if expected_status == 'started': @@ -140,7 +138,6 @@ class CertificatesInstructorDashTest(ModuleStoreTestCase): def _assert_enable_certs_button_is_disabled(self): """Check that the "enable student-generated certificates" button is disabled. """ - self.grant_sudo_access(unicode(self.course.id), 'test') response = self.client.get(self.url) expected_html = '' self.assertContains(response, expected_html) @@ -182,13 +179,11 @@ class CertificatesInstructorApiTest(ModuleStoreTestCase): # Global staff have access self.client.login(username=self.global_staff.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') response = self.client.post(url) self.assertEqual(response.status_code, 302) def test_generate_example_certificates(self): self.client.login(username=self.global_staff.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') url = reverse( 'generate_example_certificates', kwargs={'course_id': unicode(self.course.id)} @@ -207,7 +202,6 @@ class CertificatesInstructorApiTest(ModuleStoreTestCase): @ddt.data(True, False) def test_enable_certificate_generation(self, is_enabled): self.client.login(username=self.global_staff.username, password='test') - self.grant_sudo_access(unicode(self.course.id), 'test') url = reverse( 'enable_certificate_generation', kwargs={'course_id': unicode(self.course.id)} diff --git a/lms/djangoapps/instructor/tests/test_ecommerce.py b/lms/djangoapps/instructor/tests/test_ecommerce.py index 24bf4b6861..7fb4f895ac 100644 --- a/lms/djangoapps/instructor/tests/test_ecommerce.py +++ b/lms/djangoapps/instructor/tests/test_ecommerce.py @@ -29,7 +29,6 @@ class TestECommerceDashboardViews(ModuleStoreTestCase): # Create instructor account self.instructor = AdminFactory.create() self.client.login(username=self.instructor.username, password="test") - self.grant_sudo_access(unicode(self.course.id), "test") mode = CourseMode( course_id=self.course.id.to_deprecated_string(), mode_slug='honor', mode_display_name='honor', min_price=10, currency='usd' diff --git a/lms/djangoapps/instructor/tests/test_email.py b/lms/djangoapps/instructor/tests/test_email.py index 9aa7b9b0c1..d18e9f403a 100644 --- a/lms/djangoapps/instructor/tests/test_email.py +++ b/lms/djangoapps/instructor/tests/test_email.py @@ -29,7 +29,6 @@ class TestNewInstructorDashboardEmailViewMongoBacked(ModuleStoreTestCase): # Create instructor account instructor = AdminFactory.create() self.client.login(username=instructor.username, password="test") - self.grant_sudo_access(unicode(self.course.id), "test") # URL for instructor dash self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()}) diff --git a/lms/djangoapps/instructor/tests/test_legacy_enrollment.py b/lms/djangoapps/instructor/tests/test_legacy_enrollment.py index 467a44de97..314185bcdd 100644 --- a/lms/djangoapps/instructor/tests/test_legacy_enrollment.py +++ b/lms/djangoapps/instructor/tests/test_legacy_enrollment.py @@ -35,8 +35,6 @@ class TestInstructorEnrollsStudent(ModuleStoreTestCase, LoginEnrollmentTestCase) self.course = CourseFactory.create() - self.grant_sudo_access(unicode(self.course.id), "test") - self.users = [ UserFactory.create(username="student%d" % i, email="student%d@test.com" % i) for i in xrange(USER_COUNT) @@ -54,6 +52,7 @@ class TestInstructorEnrollsStudent(ModuleStoreTestCase, LoginEnrollmentTestCase) """ course = self.course + # Run the Un-enroll students command url = reverse('instructor_dashboard_legacy', kwargs={'course_id': course.id.to_deprecated_string()}) response = self.client.post( 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 a3a9490cbd..4f7cfdf9ed 100644 --- a/lms/djangoapps/instructor/tests/test_legacy_raw_download_csv.py +++ b/lms/djangoapps/instructor/tests/test_legacy_raw_download_csv.py @@ -44,7 +44,6 @@ class TestRawGradeCSV(TestSubmittingProblems): """ # Answer second problem correctly with 2nd user to expose bug self.login(self.instructor, self.password) - self.grant_sudo_access(unicode(self.course.id), self.password) resp = self.submit_question_answer('p2', {'2_1': 'Correct'}) self.assertEqual(resp.status_code, 200) diff --git a/lms/djangoapps/instructor/tests/test_legacy_xss.py b/lms/djangoapps/instructor/tests/test_legacy_xss.py index 4d8ec5d741..ff541ff69c 100644 --- a/lms/djangoapps/instructor/tests/test_legacy_xss.py +++ b/lms/djangoapps/instructor/tests/test_legacy_xss.py @@ -52,10 +52,9 @@ class TestXss(ModuleStoreTestCase): ) req.user = self._instructor req.session = {} - req.is_sudo = lambda region=None: True mako_middleware_process_request(req) - resp = legacy.instructor_dashboard(request=req, course_id=self._course.id.to_deprecated_string()) + resp = legacy.instructor_dashboard(req, self._course.id.to_deprecated_string()) respUnicode = resp.content.decode(settings.DEFAULT_CHARSET) self.assertNotIn(self._evil_student.profile.name, respUnicode) self.assertIn(escape(self._evil_student.profile.name), respUnicode) diff --git a/lms/djangoapps/instructor/tests/test_spoc_gradebook.py b/lms/djangoapps/instructor/tests/test_spoc_gradebook.py index 0ccf42a00b..2e0ca99a73 100644 --- a/lms/djangoapps/instructor/tests/test_spoc_gradebook.py +++ b/lms/djangoapps/instructor/tests/test_spoc_gradebook.py @@ -39,7 +39,6 @@ class TestGradebook(ModuleStoreTestCase): kwargs['grading_policy'] = self.grading_policy self.course = CourseFactory.create(**kwargs) - self.grant_sudo_access(unicode(self.course.id), 'test') chapter = ItemFactory.create( parent_location=self.course.location, category="sequential", diff --git a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py index 27e19953cf..3694308b45 100644 --- a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py +++ b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py @@ -43,7 +43,6 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase): # Create instructor account self.instructor = AdminFactory.create() self.client.login(username=self.instructor.username, password="test") - self.grant_sudo_access(unicode(self.course.id), 'test') # URL for instructor dash self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()}) @@ -203,7 +202,6 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase): student_cart.purchase() self.client.login(username=self.instructor.username, password="test") - self.grant_sudo_access(unicode(self.course.id), 'test') CourseFinanceAdminRole(self.course.id).add_users(self.instructor) single_purchase_total = PaidCourseRegistration.get_total_amount_of_purchased_item(self.course.id) bulk_purchase_total = CourseRegCodeItem.get_total_amount_of_purchased_item(self.course.id) @@ -236,13 +234,3 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase): expected_result, 'CCX Coaches are able to create their own Custom Courses based on this course' in response.content ) - - def test_sudo_required_on_dashboard(self): - """ - Test that sudo_required redirect user to password page. - """ - # Logout to remove sudo access. - self.client.logout() - self.client.login(username=self.instructor.username, password="test") - response = self.client.get(self.url, content_type='html', HTTP_ACCEPT='html') - self.assertEqual(response.status_code, 302) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index a57572ce50..6027525c94 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -52,7 +52,6 @@ from django_comment_common.models import ( ) from edxmako.shortcuts import render_to_response, render_to_string from courseware.models import StudentModule -from django_sudo_helpers.decorators import sudo_required from shoppingcart.models import ( Coupon, CourseRegistrationCode, @@ -318,7 +317,6 @@ COUNTRY_INDEX = 3 @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def register_and_enroll_students(request, course_id): # pylint: disable=too-many-statements """ Create new account and Enroll students in this course. @@ -519,7 +517,6 @@ def create_and_enroll_user(email, username, name, country, password, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @require_post_params(action="enroll or unenroll", identifiers="stringified list of emails and/or usernames") -@sudo_required def students_update_enrollment(request, course_id): """ Enroll or unenroll students by email. @@ -687,7 +684,6 @@ def students_update_enrollment(request, course_id): identifiers="stringified list of emails and/or usernames", action="add or remove", ) -@sudo_required def bulk_beta_modify_access(request, course_id): """ Enroll or unenroll users in beta testing program. @@ -769,7 +765,6 @@ def bulk_beta_modify_access(request, course_id): rolename="'instructor', 'staff', 'beta', or 'ccx_coach'", action="'allow' or 'revoke'" ) -@sudo_required def modify_access(request, course_id): """ Modify staff/instructor access of other user. @@ -845,7 +840,6 @@ def modify_access(request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('instructor') @require_query_params(rolename="'instructor', 'staff', or 'beta'") -@sudo_required def list_course_role_members(request, course_id): """ List instructors and staff. @@ -896,7 +890,6 @@ def list_course_role_members(request, course_id): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def get_grading_config(request, course_id): """ Respond with json which contains a html formatted grade summary. @@ -917,7 +910,6 @@ def get_grading_config(request, course_id): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def get_sale_records(request, course_id, csv=False): # pylint: disable=unused-argument, redefined-outer-name """ return the summary of all sales records for a particular course @@ -949,7 +941,6 @@ def get_sale_records(request, course_id, csv=False): # pylint: disable=unused-a @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def get_sale_order_records(request, course_id): # pylint: disable=unused-argument, redefined-outer-name """ return the summary of all sales records for a particular course @@ -991,7 +982,6 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume @require_level('staff') @require_POST -@sudo_required def sale_validation(request, course_id): """ This method either invalidate or re validate the sale against the invoice number depending upon the event type @@ -1057,7 +1047,6 @@ def re_validate_invoice(obj_invoice): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def get_students_features(request, course_id, csv=False): # pylint: disable=redefined-outer-name """ Respond with json which contains a summary of all enrolled students profile information. @@ -1133,7 +1122,6 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def get_students_who_may_enroll(request, course_id): """ Initiate generation of a CSV file containing information about @@ -1165,7 +1153,6 @@ def get_students_who_may_enroll(request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_POST @require_level('staff') -@sudo_required def add_users_to_cohorts(request, course_id): """ View method that accepts an uploaded file (using key "uploaded-file") @@ -1210,7 +1197,6 @@ def add_users_to_cohorts(request, course_id): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def get_coupon_codes(request, course_id): # pylint: disable=unused-argument """ Respond with csv which contains a summary of all Active Coupons. @@ -1240,7 +1226,6 @@ def get_coupon_codes(request, course_id): # pylint: disable=unused-argument @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required @require_finance_admin def get_enrollment_report(request, course_id): """ @@ -1266,7 +1251,6 @@ def get_enrollment_report(request, course_id): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required @require_finance_admin def get_exec_summary_report(request, course_id): """ @@ -1366,7 +1350,6 @@ def random_code_generator(): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @require_POST -@sudo_required def get_registration_codes(request, course_id): # pylint: disable=unused-argument """ Respond with csv which contains a summary of all Registration Codes. @@ -1570,7 +1553,6 @@ def generate_registration_codes(request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @require_POST -@sudo_required def active_registration_codes(request, course_id): # pylint: disable=unused-argument """ Respond with csv which contains a summary of all Active Registration Codes. @@ -1602,7 +1584,6 @@ def active_registration_codes(request, course_id): # pylint: disable=unused-arg @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @require_POST -@sudo_required def spent_registration_codes(request, course_id): # pylint: disable=unused-argument """ Respond with csv which contains a summary of all Spent(used) Registration Codes. @@ -1633,7 +1614,6 @@ def spent_registration_codes(request, course_id): # pylint: disable=unused-argu @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def get_anon_ids(request, course_id): # pylint: disable=unused-argument """ Respond with 2-column CSV output of user-id, anonymized-user-id @@ -1672,7 +1652,6 @@ def get_anon_ids(request, course_id): # pylint: disable=unused-argument @require_query_params( unique_student_identifier="email or username of student for whom to get progress url" ) -@sudo_required def get_student_progress_url(request, course_id): """ Get the progress url of a student. @@ -1702,7 +1681,6 @@ def get_student_progress_url(request, course_id): problem_to_reset="problem urlname to reset" ) @common_exceptions_400 -@sudo_required def reset_student_attempts(request, course_id): """ @@ -1781,7 +1759,6 @@ def reset_student_attempts(request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @common_exceptions_400 -@sudo_required def reset_student_attempts_for_entrance_exam(request, course_id): # pylint: disable=invalid-name """ @@ -1848,7 +1825,6 @@ def reset_student_attempts_for_entrance_exam(request, course_id): # pylint: dis @require_level('instructor') @require_query_params(problem_to_reset="problem urlname to reset") @common_exceptions_400 -@sudo_required def rescore_problem(request, course_id): """ Starts a background process a students attempts counter. Optionally deletes student state for a problem. @@ -1903,7 +1879,6 @@ def rescore_problem(request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('instructor') @common_exceptions_400 -@sudo_required def rescore_entrance_exam(request, course_id): """ Starts a background process a students attempts counter for entrance exam. @@ -1955,7 +1930,6 @@ def rescore_entrance_exam(request, course_id): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def list_background_email_tasks(request, course_id): # pylint: disable=unused-argument """ List background email tasks. @@ -1974,7 +1948,6 @@ def list_background_email_tasks(request, course_id): # pylint: disable=unused-a @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def list_email_content(request, course_id): # pylint: disable=unused-argument """ List the content of bulk emails sent @@ -1993,7 +1966,6 @@ def list_email_content(request, course_id): # pylint: disable=unused-argument @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def list_instructor_tasks(request, course_id): """ List instructor tasks. @@ -2039,7 +2011,6 @@ def list_instructor_tasks(request, course_id): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def list_entrance_exam_instructor_tasks(request, course_id): # pylint: disable=invalid-name """ List entrance exam related instructor tasks. @@ -2074,7 +2045,6 @@ def list_entrance_exam_instructor_tasks(request, course_id): # pylint: disable= @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def list_report_downloads(_request, course_id): """ List grade CSV files that are available for download for this course. @@ -2094,7 +2064,6 @@ def list_report_downloads(_request, course_id): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required @require_finance_admin def list_financial_report_downloads(_request, course_id): """ @@ -2115,7 +2084,6 @@ def list_financial_report_downloads(_request, course_id): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def calculate_grades_csv(request, course_id): """ AlreadyRunningError is raised if the course's grades are already being updated. @@ -2140,7 +2108,6 @@ def calculate_grades_csv(request, course_id): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def problem_grade_report(request, course_id): """ Request a CSV showing students' grades for all problems in the @@ -2170,7 +2137,6 @@ def problem_grade_report(request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @require_query_params('rolename') -@sudo_required def list_forum_members(request, course_id): """ Lists forum members of a certain rolename. @@ -2233,7 +2199,6 @@ def list_forum_members(request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @require_post_params(send_to="sending to whom", subject="subject line", message="message text") -@sudo_required def send_email(request, course_id): """ Send an email to self, staff, or everyone involved in a course. @@ -2292,7 +2257,6 @@ def send_email(request, course_id): action="'allow' or 'revoke'", ) @common_exceptions_400 -@sudo_required def update_forum_role_membership(request, course_id): """ Modify user's forum role. @@ -2378,7 +2342,6 @@ def _display_unit(unit): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @require_query_params('student', 'url', 'due_datetime') -@sudo_required def change_due_date(request, course_id): """ Grants a due date extension to a student for a particular unit. @@ -2400,7 +2363,6 @@ def change_due_date(request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @require_query_params('student', 'url') -@sudo_required def reset_due_date(request, course_id): """ Rescinds a due date extension for a student on a particular unit. @@ -2427,7 +2389,6 @@ def reset_due_date(request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @require_query_params('url') -@sudo_required def show_unit_extensions(request, course_id): """ Shows all of the students which have due date extensions for the given unit. @@ -2442,7 +2403,6 @@ def show_unit_extensions(request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @require_query_params('student') -@sudo_required def show_student_extensions(request, course_id): """ Shows all of the due date extensions granted to a particular student in a @@ -2529,7 +2489,6 @@ def enable_certificate_generation(request, course_id=None): #---- Gradebook (shown to small courses only) ---- @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') -@sudo_required def spoc_gradebook(request, course_id): """ Show the gradebook for this course: @@ -2571,7 +2530,6 @@ def spoc_gradebook(request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') @require_POST -@sudo_required def mark_student_can_skip_entrance_exam(request, course_id): # pylint: disable=invalid-name """ Mark a student to skip entrance exam. diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py index c5f78ea90d..4339a7a7a8 100644 --- a/lms/djangoapps/instructor/views/instructor_dashboard.py +++ b/lms/djangoapps/instructor/views/instructor_dashboard.py @@ -34,7 +34,6 @@ from courseware.courses import get_course_by_id, get_studio_url from django_comment_client.utils import has_forum_access from django_comment_common.models import FORUM_ROLE_ADMINISTRATOR from student.models import CourseEnrollment -from django_sudo_helpers.decorators import sudo_required from shoppingcart.models import Coupon, PaidCourseRegistration, CourseRegCodeItem from course_modes.models import CourseMode, CourseModesArchive from student.roles import CourseFinanceAdminRole, CourseSalesAdminRole @@ -66,33 +65,8 @@ class InstructorDashboardTab(CourseTab): return user and has_access(user, 'staff', course, course.id) -def check_staff_or_404(): - """ - Decorator with argument that requires an access level of the requesting - user. If the requirement is not satisfied, returns an - Http404 (404). - - Assumes that request is in args[0]. - Assumes that course_id is in kwargs['course_id']. - """ - - def decorator(func): # pylint: disable=missing-docstring - def wrapped(*args, **kwargs): # pylint: disable=missing-docstring - request = args[0] - course = get_course_by_id(CourseKey.from_string(kwargs['course_id'])) - user_is_staff = has_access(request.user, "staff", course) - if user_is_staff: - return func(*args, **kwargs) - else: - raise Http404 - return wrapped - return decorator - - @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) -@check_staff_or_404() -@sudo_required def instructor_dashboard_2(request, course_id): """ Display the instructor dashboard for a course. """ try: @@ -112,16 +86,16 @@ def instructor_dashboard_2(request, course_id): 'forum_admin': has_forum_access(request.user, course_key, FORUM_ROLE_ADMINISTRATOR), } - is_white_label = CourseMode.is_white_label(course_key) + if not access['staff']: + raise Http404() - unique_student_identifier = request.GET.get("unique_student_identifier", "") - problem_to_reset = request.GET.get("problem_to_reset", "") + is_white_label = CourseMode.is_white_label(course_key) sections = [ _section_course_info(course, access), _section_membership(course, access, is_white_label), _section_cohort_management(course, access), - _section_student_admin(course, access, unique_student_identifier, problem_to_reset), + _section_student_admin(course, access), _section_data_download(course, access), ] @@ -433,21 +407,11 @@ def _is_small_course(course_key): return is_small_course -def _section_student_admin(course, access, unique_student_identifier, problem_to_reset): +def _section_student_admin(course, access): """ Provide data for the corresponding dashboard section """ course_key = course.id is_small_course = _is_small_course(course_key) - problem_url = None - if problem_to_reset: - problem_url = reverse( - 'jump_to', - kwargs={ - 'course_id': unicode(course_key), - 'location': problem_to_reset - } - ) - section_data = { 'section_key': 'student_admin', 'section_display_name': _('Student Admin'), @@ -470,9 +434,6 @@ def _section_student_admin(course, access, unique_student_identifier, problem_to 'list_entrace_exam_instructor_tasks_url': reverse('list_entrance_exam_instructor_tasks', kwargs={'course_id': unicode(course_key)}), 'spoc_gradebook_url': reverse('spoc_gradebook', kwargs={'course_id': unicode(course_key)}), - 'unique_student_identifier': unique_student_identifier, - 'problem_to_reset': problem_to_reset, - 'problem_url': problem_url, } return section_data diff --git a/lms/djangoapps/instructor/views/legacy.py b/lms/djangoapps/instructor/views/legacy.py index 6e8ca50ac9..173a36ae41 100644 --- a/lms/djangoapps/instructor/views/legacy.py +++ b/lms/djangoapps/instructor/views/legacy.py @@ -52,7 +52,6 @@ from student.models import ( CourseEnrollment, CourseEnrollmentAllowed, ) -from django_sudo_helpers.decorators import sudo_required import track.views from django.utils.translation import ugettext as _ @@ -80,7 +79,6 @@ def split_by_comma_and_whitespace(a_str): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) -@sudo_required def instructor_dashboard(request, course_id): """Display the instructor dashboard for a course.""" course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) diff --git a/lms/djangoapps/shoppingcart/admin.py b/lms/djangoapps/shoppingcart/admin.py index 7fa009ecd0..fae033a849 100644 --- a/lms/djangoapps/shoppingcart/admin.py +++ b/lms/djangoapps/shoppingcart/admin.py @@ -1,5 +1,5 @@ """Django admin interface for the shopping cart models. """ -from django.contrib import admin +from ratelimitbackend import admin from shoppingcart.models import ( PaidCourseRegistrationAnnotation, Coupon, diff --git a/lms/djangoapps/verify_student/admin.py b/lms/djangoapps/verify_student/admin.py index cda4087799..6a7c69b9e2 100644 --- a/lms/djangoapps/verify_student/admin.py +++ b/lms/djangoapps/verify_student/admin.py @@ -1,8 +1,4 @@ -""" -django admin pages for verify_student models -""" - -from django.contrib import admin +from ratelimitbackend import admin from config_models.admin import ConfigurationModelAdmin from verify_student.models import ( SoftwareSecurePhotoVerification, diff --git a/lms/envs/common.py b/lms/envs/common.py index 4b11d62e9a..833c8c6d61 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -1176,10 +1176,6 @@ MIDDLEWARE_CLASSES = ( # catches any uncaught RateLimitExceptions and returns a 403 instead of a 500 'ratelimitbackend.middleware.RateLimitMiddleware', - - # force re-authentication before activating administrative functions - 'sudo.middleware.SudoMiddleware', - # needs to run after locale middleware (or anything that modifies the request context) 'edxmako.middleware.MakoMiddleware', @@ -1901,9 +1897,6 @@ INSTALLED_APPS = ( # Surveys 'survey', - # Allows sudo-mode - 'sudo', - 'lms.djangoapps.lms_xblock', 'openedx.core.djangoapps.content.course_overviews', diff --git a/lms/static/coffee/src/instructor_dashboard/data_download.coffee b/lms/static/coffee/src/instructor_dashboard/data_download.coffee index 38f97c34c9..4fa8de081e 100644 --- a/lms/static/coffee/src/instructor_dashboard/data_download.coffee +++ b/lms/static/coffee/src/instructor_dashboard/data_download.coffee @@ -27,13 +27,13 @@ class DataDownload @$problem_grade_report_csv_btn = @$section.find("input[name='problem-grade-report']'") # response areas - @$download = @$section.find '.data-download-container' - @$download_display_text = @$download.find '.data-display-text' + @$download = @$section.find '.data-download-container' + @$download_display_text = @$download.find '.data-display-text' @$download_request_response_error = @$download.find '.request-response-error' - @$reports = @$section.find '.reports-download-container' - @$download_display_table = @$reports.find '.data-display-table' - @$reports_request_response = @$reports.find '.request-response' - @$reports_request_response_error = @$reports.find '.request-response-error' + @$reports = @$section.find '.reports-download-container' + @$download_display_table = @$reports.find '.data-display-table' + @$reports_request_response = @$reports.find '.request-response' + @$reports_request_response_error = @$reports.find '.request-response-error' @report_downloads = new (ReportDownloads()) @$section @instructor_tasks = new (PendingInstructorTasks()) @$section @@ -58,14 +58,12 @@ class DataDownload $.ajax dataType: 'json' url: url - error: std_ajax_err((=> + error: (std_ajax_err) => @$reports_request_response_error.text gettext("Error generating student profile information. Please try again.") - $(".msg-error").css({"display": "block"}) - ), true) - + $(".msg-error").css({"display":"block"}) success: (data) => @$reports_request_response.text data['status'] - $(".msg-confirm").css({"display": "block"}) + $(".msg-confirm").css({"display":"block"}) @$list_studs_btn.click (e) => url = @$list_studs_btn.data 'endpoint' @@ -78,11 +76,9 @@ class DataDownload $.ajax dataType: 'json' url: url - error: std_ajax_err((=> + error: (std_ajax_err) => @clear_display() @$download_request_response_error.text gettext("Error getting student list.") - ), true) - success: (data) => @clear_display() @@ -99,7 +95,7 @@ class DataDownload $table_placeholder = $ '
', class: 'slickgrid' @$download_display_table.append $table_placeholder grid = new Slick.Grid($table_placeholder, grid_data, columns, options) - # grid.autosizeColumns() + # grid.autosizeColumns() @$list_may_enroll_csv_btn.click (e) => @clear_display() @@ -108,14 +104,12 @@ class DataDownload $.ajax dataType: 'json' url: url - error: std_ajax_err((=> + error: (std_ajax_err) => @$reports_request_response_error.text gettext("Error generating list of students who may enroll. Please try again.") - $(".msg-error").css({"display": "block"}) - ), true) - + $(".msg-error").css({"display":"block"}) success: (data) => @$reports_request_response.text data['status'] - $(".msg-confirm").css({"display": "block"}) + $(".msg-confirm").css({"display":"block"}) @$grade_config_btn.click (e) => url = @$grade_config_btn.data 'endpoint' @@ -123,10 +117,9 @@ class DataDownload $.ajax dataType: 'json' url: url - error: std_ajax_err((=> + error: (std_ajax_err) => @clear_display() @$download_request_response_error.text gettext("Error retrieving grading configuration.") - ), true) success: (data) => @clear_display() @$download_display_text.html data['grading_config_summary'] @@ -138,22 +131,20 @@ class DataDownload @onClickGradeDownload @$problem_grade_report_csv_btn, gettext("Error generating problem grade report. Please try again.") onClickGradeDownload: (button, errorMessage) -> - # Clear any CSS styling from the request-response areas - #$(".msg-confirm").css({"display":"none"}) - #$(".msg-error").css({"display":"none"}) - @clear_display() - url = button.data 'endpoint' - $.ajax - dataType: 'json' - url: url - error: std_ajax_err((=> - @.$reports_request_response_error.text gettext('Error generating student profile information. Please try again.') - $('.msg-error').css 'display': 'block' - ), true - ) - success: (data) => - @$reports_request_response.text data['status'] - $(".msg-confirm").css({"display": "block"}) + # Clear any CSS styling from the request-response areas + #$(".msg-confirm").css({"display":"none"}) + #$(".msg-error").css({"display":"none"}) + @clear_display() + url = button.data 'endpoint' + $.ajax + dataType: 'json' + url: url + error: (std_ajax_err) => + @$reports_request_response_error.text errorMessage + $(".msg-error").css({"display":"block"}) + success: (data) => + @$reports_request_response.text data['status'] + $(".msg-confirm").css({"display":"block"}) # handler for when the section title is clicked. onClickTitle: -> @@ -175,8 +166,8 @@ class DataDownload @$reports_request_response.empty() @$reports_request_response_error.empty() # Clear any CSS styling from the request-response areas - $(".msg-confirm").css({"display": "none"}) - $(".msg-error").css({"display": "none"}) + $(".msg-confirm").css({"display":"none"}) + $(".msg-error").css({"display":"none"}) # export for use # create parent namespaces if they do not already exist. diff --git a/lms/static/coffee/src/instructor_dashboard/membership.coffee b/lms/static/coffee/src/instructor_dashboard/membership.coffee index 057c89cec5..4dc86bc849 100644 --- a/lms/static/coffee/src/instructor_dashboard/membership.coffee +++ b/lms/static/coffee/src/instructor_dashboard/membership.coffee @@ -14,7 +14,7 @@ emailStudents = false class MemberListWidget # create a MemberListWidget `$container` is a jquery object to embody. # `params` holds template parameters. `params` should look like the defaults below. - constructor: (@$container, params = {}) -> + constructor: (@$container, params={}) -> params = _.defaults params, title: "Member List" info: """ @@ -117,15 +117,14 @@ class AuthListWidget extends MemberListWidget # create revoke button and insert it into the row label_trans = gettext("Revoke access") - $revoke_btn = $ _.template('
<%= label %>
', - {label: label_trans}), + $revoke_btn = $ _.template('
<%= label %>
', {label: label_trans}), class: 'revoke' $revoke_btn.click => - @modify_member_access member.email, 'revoke', (error) => - # abort on error - return @show_errors error unless error is null - @clear_errors() - @reload_list() + @modify_member_access member.email, 'revoke', (error) => + # abort on error + return @show_errors error unless error is null + @clear_errors() + @reload_list() @add_row [member.username, member.email, $revoke_btn] # clear error display @@ -140,13 +139,11 @@ class AuthListWidget extends MemberListWidget $.ajax dataType: 'json' url: @list_endpoint - data: - rolename: @rolename + data: rolename: @rolename success: (data) => cb? null, data[@rolename] - error: std_ajax_err((=> - # Translators: A rolename appears this sentence. A rolename is something like "staff" or "beta tester". + error: std_ajax_err => + `// Translators: A rolename appears this sentence. A rolename is something like "staff" or "beta tester".` cb? gettext("Error fetching list for role") + " '#{@rolename}'" - ), true) # send ajax request to modify access # (add or remove them from the list) @@ -161,9 +158,7 @@ class AuthListWidget extends MemberListWidget rolename: @rolename action: action success: (data) => @member_response data - error: std_ajax_err((=> - cb? gettext "Error changing user's permissions." - ), true) + error: std_ajax_err => cb? gettext "Error changing user's permissions." member_response: (data) -> @clear_errors() @@ -209,15 +204,15 @@ class @AutoEnrollmentViaCsv event.preventDefault() data = new FormData(event.currentTarget) $.ajax - dataType: 'json' - type: 'POST' - url: event.currentTarget.action - data: data - processData: false - contentType: false - success: (data) => - @processing = false - @display_response data + dataType: 'json' + type: 'POST' + url: event.currentTarget.action + data: data + processData: false + contentType: false + success: (data) => + @processing = false + @display_response data return false @@ -251,7 +246,7 @@ class @AutoEnrollmentViaCsv if student_result.is_general_error details.push student_result.response else - response_message = student_result.username + ' (' + student_result.email + '): ' + ' (' + student_result.response + ')' + response_message = student_result.username + ' ('+ student_result.email + '): ' + ' (' + student_result.response + ')' details.push response_message @$results.append @render_notification_view type, title, message, details @@ -266,23 +261,23 @@ class @AutoEnrollmentViaCsv render_notification_view: (type, title, message, details) -> notification_model = new NotificationModel() notification_model.set({ - 'type': type, - 'title': title, - 'message': message, - 'details': details, + 'type': type, + 'title': title, + 'message': message, + 'details': details, }); - view = new NotificationView(model: notification_model); + view = new NotificationView(model:notification_model); view.render() return view.$el.html() class BetaTesterBulkAddition constructor: (@$container) -> # gather elements - @$identifier_input = @$container.find("textarea[name='student-ids-for-beta']") - @$btn_beta_testers = @$container.find("input[name='beta-testers']") - @$checkbox_autoenroll = @$container.find("input[name='auto-enroll']") + @$identifier_input = @$container.find("textarea[name='student-ids-for-beta']") + @$btn_beta_testers = @$container.find("input[name='beta-testers']") + @$checkbox_autoenroll = @$container.find("input[name='auto-enroll']") @$checkbox_emailstudents = @$container.find("input[name='email-students-beta']") - @$task_response = @$container.find(".request-response") + @$task_response = @$container.find(".request-response") @$request_response_error = @$container.find(".request-response-error") # click handlers @@ -301,10 +296,7 @@ class BetaTesterBulkAddition url: @$btn_beta_testers.data 'endpoint' data: send_data success: (data) => @display_response data - error: std_ajax_err((=> - @fail_with_error gettext "Error adding/removing users as beta testers." - ), true) - + error: std_ajax_err => @fail_with_error gettext "Error adding/removing users as beta testers." # clear the input text field clear_input: -> @@ -373,13 +365,13 @@ class BetaTesterBulkAddition class BatchEnrollment constructor: (@$container) -> # gather elements - @$identifier_input = @$container.find("textarea[name='student-ids']") - @$enrollment_button = @$container.find(".enrollment-button") - @$is_course_white_label = @$container.find("#is_course_white_label").val() - @$reason_field = @$container.find("textarea[name='reason-field']") - @$checkbox_autoenroll = @$container.find("input[name='auto-enroll']") + @$identifier_input = @$container.find("textarea[name='student-ids']") + @$enrollment_button = @$container.find(".enrollment-button") + @$is_course_white_label = @$container.find("#is_course_white_label").val() + @$reason_field = @$container.find("textarea[name='reason-field']") + @$checkbox_autoenroll = @$container.find("input[name='auto-enroll']") @$checkbox_emailstudents = @$container.find("input[name='email-students']") - @$task_response = @$container.find(".request-response") + @$task_response = @$container.find(".request-response") @$request_response_error = @$container.find(".request-response-error") # attach click handler for enrollment buttons @@ -403,9 +395,8 @@ class BatchEnrollment url: $(event.target).data 'endpoint' data: send_data success: (data) => @display_response data - error: std_ajax_err((=> - @fail_with_error gettext "Error enrolling/unenrolling users." - ), true) + error: std_ajax_err => @fail_with_error gettext "Error enrolling/unenrolling users." + # clear the input text field clear_input: -> @@ -487,7 +478,7 @@ class BatchEnrollment else allowed.push student_results - # The instructor is trying to unenroll someone who is not enrolled or allowed to enroll; non-sensical action. + # The instructor is trying to unenroll someone who is not enrolled or allowed to enroll; non-sensical action. else if data_from_server.action is 'unenroll' and not (student_results.before.enrollment) and not (student_results.before.allowed) notunenrolled.push student_results @@ -581,11 +572,11 @@ class AuthList # rolename is the name of Role for forums for the forum endpoints constructor: (@$container, @rolename) -> # gather elements - @$display_table = @$container.find('.auth-list-table') + @$display_table = @$container.find('.auth-list-table') @$request_response_error = @$container.find('.request-response-error') - @$add_section = @$container.find('.auth-list-add') - @$allow_field = @$add_section.find("input[name='email']") - @$allow_button = @$add_section.find("input[name='allow']") + @$add_section = @$container.find('.auth-list-add') + @$allow_field = @$add_section.find("input[name='email']") + @$allow_button = @$add_section.find("input[name='allow']") # attach click handler @$allow_button.click => @@ -606,7 +597,7 @@ class AuthList options = enableCellNavigation: true enableColumnReorder: false - # autoHeight: true + # autoHeight: true forceFitColumns: true # this is a hack to put a button/link in a slick grid cell @@ -627,10 +618,10 @@ class AuthList field: 'first_name' name: 'First Name' , - # id: 'last_name' - # field: 'last_name' - # name: 'Last Name' - # , + # id: 'last_name' + # field: 'last_name' + # name: 'Last Name' + # , id: 'revoke' field: 'revoke' name: 'Revoke' @@ -655,13 +646,9 @@ class AuthList $.ajax dataType: 'json' url: @$display_table.data 'endpoint' - data: - rolename: @rolename + data: rolename: @rolename success: load_auth_list - error: std_ajax_err((=> - @$request_response_error.text "Error fetching list for '#{@rolename}'" - ), true) - + error: std_ajax_err => @$request_response_error.text "Error fetching list for '#{@rolename}'" # slickgrid's layout collapses when rendered @@ -683,9 +670,8 @@ class AuthList rolename: @rolename action: action success: (data) -> cb?(data) - error: std_ajax_err((=> - @$request_response_error.text gettext "Error changing user's permissions." - ), true) + error: std_ajax_err => @$request_response_error.text gettext "Error changing user's permissions." + # Membership Section class Membership diff --git a/lms/static/coffee/src/instructor_dashboard/send_email.coffee b/lms/static/coffee/src/instructor_dashboard/send_email.coffee index 6a950bfdc6..9851658b5a 100644 --- a/lms/static/coffee/src/instructor_dashboard/send_email.coffee +++ b/lms/static/coffee/src/instructor_dashboard/send_email.coffee @@ -79,9 +79,9 @@ class SendEmail data: send_data success: (data) => @display_response success_message - error: std_ajax_err((=> + + error: std_ajax_err => @fail_with_error gettext('Error sending email.') - ), true) else @$task_response.empty() @@ -99,41 +99,38 @@ class SendEmail else @$history_request_response_error.text gettext("There is no email history for this course.") # Enable the msg-warning css display - @$history_request_response_error.css({"display": "block"}) - error: std_ajax_err((=> + @$history_request_response_error.css({"display":"block"}) + error: std_ajax_err => @$history_request_response_error.text gettext("There was an error obtaining email task history for this course.") - ), true) # List content history for emails sent @$btn_task_history_email_content.click => url = @$btn_task_history_email_content.data 'endpoint' $.ajax dataType: 'json' - url: url + url : url success: (data) => if data.emails.length create_email_content_table @$table_email_content_history, @$email_content_table_inner, data.emails create_email_message_views @$email_messages_wrapper, data.emails else @$content_request_response_error.text gettext("There is no email history for this course.") - @$content_request_response_error.css({"display": "block"}) - error: std_ajax_err((=> + @$content_request_response_error.css({"display":"block"}) + error: std_ajax_err => @$content_request_response_error.text gettext("There was an error obtaining email content history for this course.") - ), true) - fail_with_error: (msg) -> console.warn msg @$task_response.empty() @$request_response_error.empty() @$request_response_error.text msg - $(".msg-confirm").css({"display": "none"}) + $(".msg-confirm").css({"display":"none"}) display_response: (data_from_server) -> @$task_response.empty() @$request_response_error.empty() @$task_response.text(data_from_server) - $(".msg-confirm").css({"display": "block"}) + $(".msg-confirm").css({"display":"block"}) # Email Section diff --git a/lms/static/coffee/src/instructor_dashboard/student_admin.coffee b/lms/static/coffee/src/instructor_dashboard/student_admin.coffee index 4114e19821..cabef93b0c 100644 --- a/lms/static/coffee/src/instructor_dashboard/student_admin.coffee +++ b/lms/static/coffee/src/instructor_dashboard/student_admin.coffee @@ -32,42 +32,37 @@ class @StudentAdmin # some buttons are optional because they can be flipped by the instructor task feature switch # student-specific @$field_student_select_progress = find_and_assert @$section, "input[name='student-select-progress']" - @$field_student_select_grade = find_and_assert @$section, "input[name='student-select-grade']" - @$progress_link = find_and_assert @$section, "a.progress-link" + @$field_student_select_grade = find_and_assert @$section, "input[name='student-select-grade']" + @$progress_link = find_and_assert @$section, "a.progress-link" @$field_problem_select_single = find_and_assert @$section, "input[name='problem-select-single']" - @$btn_reset_attempts_single = find_and_assert @$section, "input[name='reset-attempts-single']" - @$btn_delete_state_single = @$section.find "input[name='delete-state-single']" - @$btn_rescore_problem_single = @$section.find "input[name='rescore-problem-single']" - @$btn_task_history_single = @$section.find "input[name='task-history-single']" - @$table_task_history_single = @$section.find ".task-history-single-table" + @$btn_reset_attempts_single = find_and_assert @$section, "input[name='reset-attempts-single']" + @$btn_delete_state_single = @$section.find "input[name='delete-state-single']" + @$btn_rescore_problem_single = @$section.find "input[name='rescore-problem-single']" + @$btn_task_history_single = @$section.find "input[name='task-history-single']" + @$table_task_history_single = @$section.find ".task-history-single-table" # entrance-exam-specific - @$field_entrance_exam_student_select_grade = @$section.find "input[name='entrance-exam-student-select-grade']" - @$btn_reset_entrance_exam_attempts = @$section.find "input[name='reset-entrance-exam-attempts']" - @$btn_delete_entrance_exam_state = @$section.find "input[name='delete-entrance-exam-state']" - @$btn_rescore_entrance_exam = @$section.find "input[name='rescore-entrance-exam']" - @$btn_skip_entrance_exam = @$section.find "input[name='skip-entrance-exam']" - @$btn_entrance_exam_task_history = @$section.find "input[name='entrance-exam-task-history']" - @$table_entrance_exam_task_history = @$section.find ".entrance-exam-task-history-table" + @$field_entrance_exam_student_select_grade = @$section.find "input[name='entrance-exam-student-select-grade']" + @$btn_reset_entrance_exam_attempts = @$section.find "input[name='reset-entrance-exam-attempts']" + @$btn_delete_entrance_exam_state = @$section.find "input[name='delete-entrance-exam-state']" + @$btn_rescore_entrance_exam = @$section.find "input[name='rescore-entrance-exam']" + @$btn_skip_entrance_exam = @$section.find "input[name='skip-entrance-exam']" + @$btn_entrance_exam_task_history = @$section.find "input[name='entrance-exam-task-history']" + @$table_entrance_exam_task_history = @$section.find ".entrance-exam-task-history-table" # course-specific - @$field_problem_select_all = @$section.find "input[name='problem-select-all']" - @$btn_reset_attempts_all = @$section.find "input[name='reset-attempts-all']" - @$btn_rescore_problem_all = @$section.find "input[name='rescore-problem-all']" - @$btn_task_history_all = @$section.find "input[name='task-history-all']" - @$table_task_history_all = @$section.find ".task-history-all-table" - @instructor_tasks = new (PendingInstructorTasks()) @$section + @$field_problem_select_all = @$section.find "input[name='problem-select-all']" + @$btn_reset_attempts_all = @$section.find "input[name='reset-attempts-all']" + @$btn_rescore_problem_all = @$section.find "input[name='rescore-problem-all']" + @$btn_task_history_all = @$section.find "input[name='task-history-all']" + @$table_task_history_all = @$section.find ".task-history-all-table" + @instructor_tasks = new (PendingInstructorTasks()) @$section # response areas @$request_response_error_progress = find_and_assert @$section, ".student-specific-container .request-response-error" @$request_response_error_grade = find_and_assert @$section, ".student-grade-container .request-response-error" - @$request_response_error_ee = @$section.find ".entrance-exam-grade-container .request-response-error" - @$request_response_error_all = @$section.find ".course-specific-container .request-response-error" - - $student_grade_container = find_and_assert @$section, ".student-grade-container" - unique_student_identifier = @$field_student_select_grade.val() - if unique_student_identifier - @scroll_to_section($student_grade_container) + @$request_response_error_ee = @$section.find ".entrance-exam-grade-container .request-response-error" + @$request_response_error_all = @$section.find ".course-specific-container .request-response-error" # attach click handlers @@ -83,13 +78,10 @@ class @StudentAdmin $.ajax dataType: 'json' url: @$progress_link.data 'endpoint' - data: - unique_student_identifier: unique_student_identifier + data: unique_student_identifier: unique_student_identifier success: @clear_errors_then (data) -> window.location = data.progress_url - error: std_ajax_err((=> - @$request_response_error_progress.text full_error_message - ), true) + error: std_ajax_err => @$request_response_error_progress.text full_error_message # reset attempts for student on problem @$btn_reset_attempts_single.click => @@ -105,19 +97,15 @@ class @StudentAdmin delete_module: false success_message = gettext("Success! Problem attempts reset for problem '<%= problem_id %>' and student '<%= student_id %>'.") error_message = gettext("Error resetting problem attempts for problem '<%= problem_id %>' and student '<%= student_id %>'. Make sure that the problem and student identifiers are complete and correct.") - full_success_message = _.template(success_message, - {problem_id: problem_to_reset, student_id: unique_student_identifier}) - full_error_message = _.template(error_message, - {problem_id: problem_to_reset, student_id: unique_student_identifier}) + full_success_message = _.template(success_message, {problem_id: problem_to_reset, student_id: unique_student_identifier}) + full_error_message = _.template(error_message, {problem_id: problem_to_reset, student_id: unique_student_identifier}) $.ajax dataType: 'json' url: @$btn_reset_attempts_single.data 'endpoint' data: send_data success: @clear_errors_then -> alert full_success_message - error: std_ajax_err((=> - @$request_response_error_grade.text full_error_message - ), true) + error: std_ajax_err => @$request_response_error_grade.text full_error_message # delete state for student on problem @$btn_delete_state_single.click => @@ -128,8 +116,7 @@ class @StudentAdmin if not problem_to_reset return @$request_response_error_grade.text gettext("Please enter a problem location.") confirm_message = gettext("Delete student '<%= student_id %>'s state on problem '<%= problem_id %>'?") - full_confirm_message = _.template(confirm_message, - {student_id: unique_student_identifier, problem_id: problem_to_reset}) + full_confirm_message = _.template(confirm_message, {student_id: unique_student_identifier, problem_id: problem_to_reset}) if window.confirm full_confirm_message send_data = @@ -137,17 +124,14 @@ class @StudentAdmin problem_to_reset: problem_to_reset delete_module: true error_message = gettext("Error deleting student '<%= student_id %>'s state on problem '<%= problem_id %>'. Make sure that the problem and student identifiers are complete and correct.") - full_error_message = _.template(error_message, - {student_id: unique_student_identifier, problem_id: problem_to_reset}) + full_error_message = _.template(error_message, {student_id: unique_student_identifier, problem_id: problem_to_reset}) $.ajax dataType: 'json' url: @$btn_delete_state_single.data 'endpoint' data: send_data success: @clear_errors_then -> alert gettext('Module state successfully deleted.') - error: std_ajax_err((=> - @$request_response_error_grade.text full_error_message - ), true) + error: std_ajax_err => @$request_response_error_grade.text full_error_message else # Clear error messages if "Cancel" was chosen on confirmation alert @clear_errors() @@ -164,20 +148,16 @@ class @StudentAdmin unique_student_identifier: unique_student_identifier problem_to_reset: problem_to_reset success_message = gettext("Started rescore problem task for problem '<%= problem_id %>' and student '<%= student_id %>'. Click the 'Show Background Task History for Student' button to see the status of the task.") - full_success_message = _.template(success_message, - {student_id: unique_student_identifier, problem_id: problem_to_reset}) + full_success_message = _.template(success_message, {student_id: unique_student_identifier, problem_id: problem_to_reset}) error_message = gettext("Error starting a task to rescore problem '<%= problem_id %>' for student '<%= student_id %>'. Make sure that the the problem and student identifiers are complete and correct.") - full_error_message = _.template(error_message, - {student_id: unique_student_identifier, problem_id: problem_to_reset}) + full_error_message = _.template(error_message, {student_id: unique_student_identifier, problem_id: problem_to_reset}) $.ajax dataType: 'json' url: @$btn_rescore_problem_single.data 'endpoint' data: send_data success: @clear_errors_then -> alert full_success_message - error: std_ajax_err((=> - @$request_response_error_grade.text full_error_message - ), true) + error: std_ajax_err => @$request_response_error_grade.text full_error_message # list task history for student+problem @$btn_task_history_single.click => @@ -191,8 +171,7 @@ class @StudentAdmin unique_student_identifier: unique_student_identifier problem_location_str: problem_to_reset error_message = gettext("Error getting task history for problem '<%= problem_id %>' and student '<%= student_id %>'. Make sure that the problem and student identifiers are complete and correct.") - full_error_message = _.template(error_message, - {student_id: unique_student_identifier, problem_id: problem_to_reset}) + full_error_message = _.template(error_message, {student_id: unique_student_identifier, problem_id: problem_to_reset}) $.ajax dataType: 'json' @@ -200,11 +179,9 @@ class @StudentAdmin data: send_data success: @clear_errors_then (data) => create_task_list_table @$table_task_history_single, data.tasks - error: std_ajax_err((=> - @$request_response_error_grade.text full_error_message - ), true) + error: std_ajax_err => @$request_response_error_grade.text full_error_message - # reset entrance exam attempts for student + # reset entrance exam attempts for student @$btn_reset_entrance_exam_attempts.click => unique_student_identifier = @$field_entrance_exam_student_select_grade.val() if not unique_student_identifier @@ -221,13 +198,12 @@ class @StudentAdmin success_message = gettext("Entrance exam attempts is being reset for student '{student_id}'.") full_success_message = interpolate_text(success_message, {student_id: unique_student_identifier}) alert full_success_message - error: std_ajax_err((=> + error: std_ajax_err => error_message = gettext("Error resetting entrance exam attempts for student '{student_id}'. Make sure student identifier is correct.") full_error_message = interpolate_text(error_message, {student_id: unique_student_identifier}) @$request_response_error_ee.text full_error_message - ), true) - # start task to rescore entrance exam for student + # start task to rescore entrance exam for student @$btn_rescore_entrance_exam.click => unique_student_identifier = @$field_entrance_exam_student_select_grade.val() if not unique_student_identifier @@ -248,7 +224,7 @@ class @StudentAdmin full_error_message = interpolate_text(error_message, {student_id: unique_student_identifier}) @$request_response_error_ee.text full_error_message - # Mark a student to skip entrance exam + # Mark a student to skip entrance exam @$btn_skip_entrance_exam.click => unique_student_identifier = @$field_entrance_exam_student_select_grade.val() if not unique_student_identifier @@ -270,7 +246,7 @@ class @StudentAdmin error_message = gettext("An error occurred. Make sure that the student's username or email address is correct and try again.") @$request_response_error_ee.text error_message - # delete student state for entrance exam + # delete student state for entrance exam @$btn_delete_entrance_exam_state.click => unique_student_identifier = @$field_entrance_exam_student_select_grade.val() if not unique_student_identifier @@ -332,9 +308,7 @@ class @StudentAdmin url: @$btn_reset_attempts_all.data 'endpoint' data: send_data success: @clear_errors_then -> alert full_success_message - error: std_ajax_err((=> - @$request_response_error_all.text full_error_message - ), true) + error: std_ajax_err => @$request_response_error_all.text full_error_message else # Clear error messages if "Cancel" was chosen on confirmation alert @clear_errors() @@ -360,9 +334,7 @@ class @StudentAdmin url: @$btn_rescore_problem_all.data 'endpoint' data: send_data success: @clear_errors_then -> alert full_success_message - error: std_ajax_err((=> - @$request_response_error_all.text full_error_message - ), true) + error: std_ajax_err => @$request_response_error_all.text full_error_message else # Clear error messages if "Cancel" was chosen on confirmation alert @clear_errors() @@ -381,9 +353,7 @@ class @StudentAdmin data: send_data success: @clear_errors_then (data) => create_task_list_table @$table_task_history_all, data.tasks - error: std_ajax_err((=> - @$request_response_error_all.text gettext("Error listing task history for this student and problem.") - ), true) + error: std_ajax_err => @$request_response_error_all.text gettext("Error listing task history for this student and problem.") # wraps a function, but first clear the error displays clear_errors_then: (cb) -> @@ -401,10 +371,6 @@ class @StudentAdmin @$request_response_error_ee.empty() @$request_response_error_all.empty() - - scroll_to_section: (element) -> - $(window).scrollTop(element.offset().top).scrollLeft(element.offset().left) - # handler for when the section title is clicked. onClickTitle: -> @instructor_tasks.task_poller.start() diff --git a/lms/static/coffee/src/instructor_dashboard/util.coffee b/lms/static/coffee/src/instructor_dashboard/util.coffee index b1c89f760f..7a9f80b167 100644 --- a/lms/static/coffee/src/instructor_dashboard/util.coffee +++ b/lms/static/coffee/src/instructor_dashboard/util.coffee @@ -19,12 +19,10 @@ find_and_assert = ($root, selector) -> # # wraps a `handler` function so that first # it prints basic error information to the console. -@std_ajax_err = (handler, sudo_reload=false) -> (jqXHR, textStatus, errorThrown) -> +@std_ajax_err = (handler) -> (jqXHR, textStatus, errorThrown) -> console.warn """ajax error textStatus: #{textStatus} errorThrown: #{errorThrown}""" - if sudo_reload == true and jqXHR.status == 401 - window.location.reload() handler.apply this, arguments @@ -299,10 +297,7 @@ class @PendingInstructorTasks @$no_tasks_message.empty() @$no_tasks_message.append $('

').text gettext("No tasks currently running.") @$no_tasks_message.show() - error: std_ajax_err((=> - console.error "Error finding pending tasks to display" - ), true) - + error: std_ajax_err => console.error "Error finding pending tasks to display" ### /Pending Instructor Tasks Section #### class KeywordValidator diff --git a/lms/static/js/spec/staff_debug_actions_spec.js b/lms/static/js/spec/staff_debug_actions_spec.js index 92e8c4d995..11c6e1a64c 100644 --- a/lms/static/js/spec/staff_debug_actions_spec.js +++ b/lms/static/js/spec/staff_debug_actions_spec.js @@ -4,7 +4,6 @@ define(['backbone', 'jquery', 'js/staff_debug_actions'], describe('StaffDebugActions', function () { var location = 'i4x://edX/Open_DemoX/edx_demo_course/problem/test_loc'; var locationName = 'test_loc'; - var action = {location: location, locationName: locationName}; var fixture_id = 'sd_fu_' + locationName; var fixture = $('', { id: fixture_id, placeholder: "userman" }); var escapableLocationName = 'test\.\*\+\?\^\:\$\{\}\(\)\|\]\[loc'; @@ -14,11 +13,7 @@ define(['backbone', 'jquery', 'js/staff_debug_actions'], describe('get_url ', function () { it('defines url to courseware ajax entry point', function () { spyOn(StaffDebug, "get_current_url").andReturn("/courses/edX/Open_DemoX/edx_demo_course/courseware/stuff"); - $('body').append(fixture); - var expected_url = '/courses/edX/Open_DemoX/edx_demo_course/instructor?unique_student_identifier=userman&problem_to_reset=' + encodeURIComponent(action.location); - expect(StaffDebug.get_url(action)).toBe(expected_url); - - $('#' + fixture_id).remove(); + expect(StaffDebug.get_url('rescore_problem')).toBe('/courses/edX/Open_DemoX/edx_demo_course/instructor/api/rescore_problem'); }); }); @@ -49,20 +44,63 @@ define(['backbone', 'jquery', 'js/staff_debug_actions'], $('#' + escapableFixture_id).remove(); }); }); - describe('student_grade_adjustemnts', function () { + describe('reset', function () { it('makes an ajax call with the expected parameters', function () { $('body').append(fixture); - spyOn(StaffDebug, 'goto_student_admin'); + spyOn($, 'ajax'); + StaffDebug.reset(locationName, location); - StaffDebug.student_grade_adjustemnts(locationName, location); + expect($.ajax.mostRecentCall.args[0]['type']).toEqual('GET'); + expect($.ajax.mostRecentCall.args[0]['data']).toEqual({ + 'problem_to_reset': location, + 'unique_student_identifier': 'userman', + 'delete_module': false + }); + expect($.ajax.mostRecentCall.args[0]['url']).toEqual( + '/instructor/api/reset_student_attempts' + ); + $('#' + fixture_id).remove(); + }); + }); + describe('sdelete', function () { + it('makes an ajax call with the expected parameters', function () { + $('body').append(fixture); - var expected_url = get_url(action) + '#view-student_admin'; + spyOn($, 'ajax'); + StaffDebug.sdelete(locationName, location); - expect(StaffDebug.goto_student_admin).toHaveBeenCalledWith(expected_url); + expect($.ajax.mostRecentCall.args[0]['type']).toEqual('GET'); + expect($.ajax.mostRecentCall.args[0]['data']).toEqual({ + 'problem_to_reset': location, + 'unique_student_identifier': 'userman', + 'delete_module': true + }); + expect($.ajax.mostRecentCall.args[0]['url']).toEqual( + '/instructor/api/reset_student_attempts' + ); $('#' + fixture_id).remove(); }); }); + describe('rescore', function () { + it('makes an ajax call with the expected parameters', function () { + $('body').append(fixture); + + spyOn($, 'ajax'); + StaffDebug.rescore(locationName, location); + + expect($.ajax.mostRecentCall.args[0]['type']).toEqual('GET'); + expect($.ajax.mostRecentCall.args[0]['data']).toEqual({ + 'problem_to_reset': location, + 'unique_student_identifier': 'userman', + 'delete_module': false + }); + expect($.ajax.mostRecentCall.args[0]['url']).toEqual( + '/instructor/api/rescore_problem' + ); + $('#' + fixture_id).remove(); + }); + }); }); }); \ No newline at end of file diff --git a/lms/static/js/staff_debug_actions.js b/lms/static/js/staff_debug_actions.js index 0dab8cacec..35ce0a4048 100644 --- a/lms/static/js/staff_debug_actions.js +++ b/lms/static/js/staff_debug_actions.js @@ -3,15 +3,13 @@ var StaffDebug = (function(){ get_current_url = function() { return window.location.pathname; - }; + } get_url = function(action){ - var problem_to_reset = encodeURIComponent(action.location); - var unique_student_identifier = get_user(action.locationName); var pathname = this.get_current_url(); - var url = pathname.substr(0,pathname.indexOf('/courseware')) + '/instructor'+ '?unique_student_identifier=' + unique_student_identifier + '&problem_to_reset=' + problem_to_reset; + var url = pathname.substr(0,pathname.indexOf('/courseware')) + '/instructor/api/' + action; return url; - }; + } sanitized_string = function(string) { return string.replace(/[.*+?^:${}()|[\]\\]/g, "\\$&"); @@ -24,21 +22,95 @@ var StaffDebug = (function(){ uname = $('#sd_fu_' + locname).attr('placeholder'); } return uname; - }; + } - goto_student_admin = function(location) { - window.location = location; - }; + do_idash_action = function(action){ + var pdata = { + 'problem_to_reset': action.location, + 'unique_student_identifier': get_user(action.locationName), + 'delete_module': action.delete_module + } + $.ajax({ + type: "GET", + url: get_url(action.method), + data: pdata, + success: function(data){ + var text = _.template( + action.success_msg, + {user: data.student}, + {interpolate: /\{(.+?)\}/g} + ) + var html = _.template( + '

{text}

', + {text: text}, + {interpolate: /\{(.+?)\}/g} + ) + $("#result_"+action.locationName).html(html); + }, + error: function(request, status, error) { + var response_json; + try { + response_json = $.parseJSON(request.responseText); + } catch(e) { + response_json = { error: gettext('Unknown Error Occurred.') }; + } + var text = _.template( + '{error_msg} {error}', + { + error_msg: action.error_msg, + error: response_json.error + }, + {interpolate: /\{(.+?)\}/g} + ) + var html = _.template( + '

{text}

', + {text: text}, + {interpolate: /\{(.+?)\}/g} + ) + $("#result_"+action.locationName).html(html); + }, + dataType: 'json' + }); + } - student_grade_adjustemnts = function(locname, location){ - var action = {locationName: locname, location: location}; - var instructor_tab_url = get_url(action); - this.goto_student_admin(instructor_tab_url + '#view-student_admin'); - }; + reset = function(locname, location){ + this.do_idash_action({ + locationName: locname, + location: location, + method: 'reset_student_attempts', + success_msg: gettext('Successfully reset the attempts for user {user}'), + error_msg: gettext('Failed to reset attempts.'), + delete_module: false + }); + } + + sdelete = function(locname, location){ + this.do_idash_action({ + locationName: locname, + location: location, + method: 'reset_student_attempts', + success_msg: gettext('Successfully deleted student state for user {user}'), + error_msg: gettext('Failed to delete student state.'), + delete_module: true + }); + } + + rescore = function(locname, location){ + this.do_idash_action({ + locationName: locname, + location: location, + method: 'rescore_problem', + success_msg: gettext('Successfully rescored problem for user {user}'), + error_msg: gettext('Failed to rescore problem.'), + delete_module: false + }); + } return { - student_grade_adjustemnts: student_grade_adjustemnts, - goto_student_admin: goto_student_admin, + reset: reset, + sdelete: sdelete, + rescore: rescore, + do_idash_action: do_idash_action, get_current_url: get_current_url, get_url: get_url, get_user: get_user, @@ -49,8 +121,16 @@ var StaffDebug = (function(){ // Register click handlers $(document).ready(function() { var $courseContent = $('.course-content'); - $courseContent.on("click", '.staff-debug-grade-adjustments', function() { - StaffDebug.student_grade_adjustemnts($(this).parent().data('location-name'), $(this).parent().data('location')); + $courseContent.on("click", '.staff-debug-reset', function() { + StaffDebug.reset($(this).parent().data('location-name'), $(this).parent().data('location')); + return false; + }); + $courseContent.on("click", '.staff-debug-sdelete', function() { + StaffDebug.sdelete($(this).parent().data('location-name'), $(this).parent().data('location')); + return false; + }); + $courseContent.on("click", '.staff-debug-rescore', function() { + StaffDebug.rescore($(this).parent().data('location-name'), $(this).parent().data('location')); return false; }); }); diff --git a/lms/templates/instructor/instructor_dashboard_2/student_admin.html b/lms/templates/instructor/instructor_dashboard_2/student_admin.html index afd0037792..a7d3b92279 100644 --- a/lms/templates/instructor/instructor_dashboard_2/student_admin.html +++ b/lms/templates/instructor/instructor_dashboard_2/student_admin.html @@ -39,22 +39,17 @@

${_("Student-specific grade adjustment")}

- %if section_data['problem_url']: - - %endif


## Translators: A location (string of text) follows this sentence. diff --git a/lms/templates/staff_problem_info.html b/lms/templates/staff_problem_info.html index b32f16311c..f146db3eaa 100644 --- a/lms/templates/staff_problem_info.html +++ b/lms/templates/staff_problem_info.html @@ -1,6 +1,3 @@ -<%! from django.utils.translation import ugettext as _ %> -<%! from django.template.defaultfilters import escapejs %> - <%namespace name='static' file='/static_content.html'/> <%! from django.utils.translation import ugettext as _ @@ -70,7 +67,15 @@ ${block_content}
diff --git a/lms/templates/sudo/sudo.html b/lms/templates/sudo/sudo.html deleted file mode 100644 index 3dcfe1f0bf..0000000000 --- a/lms/templates/sudo/sudo.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "main_django.html" %} -{% load i18n %} - -{% block body %} -
-
-
-

{% trans "Confirm Your Password to Access the Instructor Dashboard" %}

-
-
-
-
{% csrf_token %} - {{ form.as_p }} -

- -

-
-
-
-
-{% endblock %} \ No newline at end of file diff --git a/lms/urls.py b/lms/urls.py index 575389fe4a..adc0a090c6 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -1,11 +1,11 @@ from django.conf import settings from django.conf.urls import patterns, include, url +from ratelimitbackend import admin from django.conf.urls.static import static import django.contrib.auth.views from microsite_configuration import microsite import auth_exchange.views -from edx_admin import admin # Uncomment the next two lines to enable the admin: if settings.DEBUG or settings.FEATURES.get('ENABLE_DJANGO_ADMIN_SITE'): @@ -80,8 +80,6 @@ urlpatterns = ( # Course content API url(r'^api/course_structure/', include('course_structure_api.urls', namespace='course_structure_api')), - url(r'^sudo/$', 'sudo.views.sudo'), - # User API endpoints url(r'^api/user/', include('openedx.core.djangoapps.user_api.urls')), diff --git a/openedx/core/djangoapps/content/course_structures/admin.py b/openedx/core/djangoapps/content/course_structures/admin.py index 759863c390..64bbdad86c 100644 --- a/openedx/core/djangoapps/content/course_structures/admin.py +++ b/openedx/core/djangoapps/content/course_structures/admin.py @@ -1,8 +1,4 @@ -""" -django admin pages for course_structures model -""" - -from django.contrib import admin +from ratelimitbackend import admin from .models import CourseStructure diff --git a/openedx/core/djangoapps/credit/admin.py b/openedx/core/djangoapps/credit/admin.py index b3c14f556b..5700f0ef73 100644 --- a/openedx/core/djangoapps/credit/admin.py +++ b/openedx/core/djangoapps/credit/admin.py @@ -1,12 +1,10 @@ """ Django admin page for credit eligibility """ - from ratelimitbackend import admin from openedx.core.djangoapps.credit.models import ( CreditCourse, CreditProvider, CreditEligibility, CreditRequest ) -from django.contrib import admin class CreditCourseAdmin(admin.ModelAdmin): diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt index 760f5ae3b5..b84022cd4f 100644 --- a/requirements/edx/github.txt +++ b/requirements/edx/github.txt @@ -12,7 +12,6 @@ -e git+https://github.com/edx/django-pipeline.git@88ec8a011e481918fdc9d2682d4017c835acd8be#egg=django-pipeline -e git+https://github.com/edx/django-wiki.git@cd0b2b31997afccde519fe5b3365e61a9edb143f#egg=django-wiki -e git+https://github.com/edx/django-oauth2-provider.git@0.2.7-fork-edx-5#egg=django-oauth2-provider --e git+https://github.com/edx/django-sudo.git@5ceb91236b477ce2726c538a2d8631884bda2684#egg=django-sudo -e git+https://github.com/edx/MongoDBProxy.git@25b99097615bda06bd7cdfe5669ed80dc2a7fed0#egg=mongodb_proxy git+https://github.com/edx/nltk.git@2.0.6#egg=nltk==2.0.6 -e git+https://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev