diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 57da0c54350..856f0afd6c8 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -47,7 +47,10 @@ CourseInstructorRole, CourseStaffRole, GlobalStaff, - UserBasedRole + UserBasedRole, + CourseRerunCreatorRole, + OrgRerunCreatorRole, + OrgCourseCreatorRole, ) from common.djangoapps.util.course import get_link_for_about_page from common.djangoapps.util.date_utils import get_default_time_display @@ -304,10 +307,11 @@ def course_rerun_handler(request, course_key_string): GET html: return html page with form to rerun a course for the given course id """ - # Only global staff (PMs) are able to rerun courses during the soft launch - if not GlobalStaff().has_user(request.user): - raise PermissionDenied() course_key = CourseKey.from_string(course_key_string) + + if not GlobalStaff().has_user(request.user) and not _rerun_permission(request.user, course_key): + raise PermissionDenied() + with modulestore().bulk_operations(course_key): course_module = get_course_and_check_access(course_key, request.user, depth=3) if request.method == 'GET': @@ -550,6 +554,8 @@ def format_in_process_course_view(uca): split_archived = settings.FEATURES.get('ENABLE_SEPARATE_ARCHIVED_COURSES', False) active_courses, archived_courses = _process_courses_list(courses_iter, in_process_course_actions, split_archived) in_process_course_actions = [format_in_process_course_view(uca) for uca in in_process_course_actions] + active_courses = _set_rerun_permission_for_courses(user, active_courses) + archived_courses = _set_rerun_permission_for_courses(user, archived_courses) return render_to_response('index.html', { 'courses': active_courses, @@ -849,11 +855,17 @@ def _create_or_rerun_course(request): Returns the destination course_key and overriding fields for the new course. Raises DuplicateCourseError and InvalidKeyError """ - if not auth.user_has_role(request.user, CourseCreatorRole()): - raise PermissionDenied() - try: org = request.json.get('org') + + rerun_permission = ( + OrgRerunCreatorRole(org).has_user(request.user) + or OrgCourseCreatorRole(org).has_user(request.user) + ) + + if not auth.user_has_role(request.user, CourseCreatorRole()) and not rerun_permission: + raise PermissionDenied() + course = request.json.get('number', request.json.get('course')) display_name = request.json.get('display_name') # force the start date for reruns and allow us to override start via the client @@ -1863,3 +1875,24 @@ def _get_course_creator_status(user): course_creator_status = 'granted' return course_creator_status + + +def _rerun_permission(user, course_key): + """ + Helper method to check if user can rerun-course + """ + return ( + CourseRerunCreatorRole(course_key).has_user(user) + or OrgRerunCreatorRole(course_key.org).has_user(user) + ) + + +def _set_rerun_permission_for_courses(user, courses): + """ + iterate over courses dict and set the key 'rerun_permission' + """ + for course in courses: + course_key = CourseKey.from_string(course['course_key']) + course['rerun_permission'] = _rerun_permission(user, course_key) + + return courses diff --git a/cms/djangoapps/contentstore/views/organization.py b/cms/djangoapps/contentstore/views/organization.py index 7ece98201ac..f29e3efa02b 100644 --- a/cms/djangoapps/contentstore/views/organization.py +++ b/cms/djangoapps/contentstore/views/organization.py @@ -20,6 +20,6 @@ class OrganizationListView(View): @method_decorator(login_required) def get(self, request, *args, **kwargs): # lint-amnesty, pylint: disable=unused-argument """Returns organization list as json.""" - organizations = get_organizations() - org_names_list = [(org["short_name"]) for org in organizations] + # EDUNEXT: Organizations list must not be visible for users, reason why an empty array is returned + org_names_list = [] return HttpResponse(dump_js_escaped_json(org_names_list), content_type='application/json; charset=utf-8') # lint-amnesty, pylint: disable=http-response-with-content-type-json diff --git a/cms/djangoapps/contentstore/views/tests/test_course_index.py b/cms/djangoapps/contentstore/views/tests/test_course_index.py index 92e428c5851..cf9ffde1d08 100644 --- a/cms/djangoapps/contentstore/views/tests/test_course_index.py +++ b/cms/djangoapps/contentstore/views/tests/test_course_index.py @@ -385,13 +385,13 @@ def check_index_page(self, separate_archived_courses, org): @ddt.data( # Staff user has course staff access - (True, 'staff', None, 3, 18), - (False, 'staff', None, 3, 18), + (True, 'staff', None, 3, 20), + (False, 'staff', None, 3, 20), # Base user has global staff access - (True, 'user', ORG, 3, 18), - (False, 'user', ORG, 3, 18), - (True, 'user', None, 3, 18), - (False, 'user', None, 3, 18), + (True, 'user', ORG, 3, 20), + (False, 'user', ORG, 3, 20), + (True, 'user', None, 3, 20), + (False, 'user', None, 3, 20), ) @ddt.unpack def test_separate_archived_courses(self, separate_archived_courses, username, org, mongo_queries, sql_queries): diff --git a/cms/djangoapps/contentstore/views/tests/test_organizations.py b/cms/djangoapps/contentstore/views/tests/test_organizations.py index 6f3b6767110..51dd6c39262 100644 --- a/cms/djangoapps/contentstore/views/tests/test_organizations.py +++ b/cms/djangoapps/contentstore/views/tests/test_organizations.py @@ -17,7 +17,8 @@ def setUp(self): self.staff = UserFactory(is_staff=True) self.client.login(username=self.staff.username, password='test') self.org_names_listing_url = reverse('organizations') - self.org_short_names = ["alphaX", "betaX", "orgX"] + # EDUNEXT: Organizations list must not be visible for users, reason why an empty array is set + self.org_short_names = [] for index, short_name in enumerate(self.org_short_names): add_organization(organization_data={ 'name': 'Test Organization %s' % index, diff --git a/cms/static/js/features_jsx/studio/CourseOrLibraryListing.jsx b/cms/static/js/features_jsx/studio/CourseOrLibraryListing.jsx index ef925419efb..7d8fde5d6b4 100644 --- a/cms/static/js/features_jsx/studio/CourseOrLibraryListing.jsx +++ b/cms/static/js/features_jsx/studio/CourseOrLibraryListing.jsx @@ -40,7 +40,7 @@ export function CourseOrLibraryListing(props) { { item.lms_link && item.rerun_link &&