diff --git a/common/djangoapps/django_comment_common/models.py b/common/djangoapps/django_comment_common/models.py index c831bf88a9..b6f804543d 100644 --- a/common/djangoapps/django_comment_common/models.py +++ b/common/djangoapps/django_comment_common/models.py @@ -46,7 +46,14 @@ def assign_default_role(course_id, user): """ Assign forum default role 'Student' to user """ - role, __ = Role.objects.get_or_create(course_id=course_id, name=FORUM_ROLE_STUDENT) + assign_role(course_id, user, FORUM_ROLE_STUDENT) + + +def assign_role(course_id, user, rolename): + """ + Assign forum role `rolename` to user + """ + role, __ = Role.objects.get_or_create(course_id=course_id, name=rolename) user.roles.add(role) diff --git a/common/djangoapps/django_comment_common/utils.py b/common/djangoapps/django_comment_common/utils.py index bccc127733..a87aaed698 100644 --- a/common/djangoapps/django_comment_common/utils.py +++ b/common/djangoapps/django_comment_common/utils.py @@ -1,4 +1,9 @@ -from django_comment_common.models import Role +""" +Common comment client utility functions. +""" + +from django_comment_common.models import Role, FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, \ + FORUM_ROLE_COMMUNITY_TA, FORUM_ROLE_STUDENT class ThreadContext(object): @@ -7,14 +12,14 @@ class ThreadContext(object): COURSE = 'course' -_STUDENT_ROLE_PERMISSIONS = ["vote", "update_thread", "follow_thread", "unfollow_thread", - "update_comment", "create_sub_comment", "unvote", "create_thread", - "follow_commentable", "unfollow_commentable", "create_comment", ] +STUDENT_ROLE_PERMISSIONS = ["vote", "update_thread", "follow_thread", "unfollow_thread", + "update_comment", "create_sub_comment", "unvote", "create_thread", + "follow_commentable", "unfollow_commentable", "create_comment", ] -_MODERATOR_ROLE_PERMISSIONS = ["edit_content", "delete_thread", "openclose_thread", - "endorse_comment", "delete_comment", "see_all_cohorts"] +MODERATOR_ROLE_PERMISSIONS = ["edit_content", "delete_thread", "openclose_thread", + "endorse_comment", "delete_comment", "see_all_cohorts"] -_ADMINISTRATOR_ROLE_PERMISSIONS = ["manage_moderator"] +ADMINISTRATOR_ROLE_PERMISSIONS = ["manage_moderator"] def _save_forum_role(course_key, name): @@ -34,18 +39,18 @@ def seed_permissions_roles(course_key): """ Create and assign permissions for forum roles """ - administrator_role = _save_forum_role(course_key, "Administrator") - moderator_role = _save_forum_role(course_key, "Moderator") - community_ta_role = _save_forum_role(course_key, "Community TA") - student_role = _save_forum_role(course_key, "Student") + administrator_role = _save_forum_role(course_key, FORUM_ROLE_ADMINISTRATOR) + moderator_role = _save_forum_role(course_key, FORUM_ROLE_MODERATOR) + community_ta_role = _save_forum_role(course_key, FORUM_ROLE_COMMUNITY_TA) + student_role = _save_forum_role(course_key, FORUM_ROLE_STUDENT) - for per in _STUDENT_ROLE_PERMISSIONS: + for per in STUDENT_ROLE_PERMISSIONS: student_role.add_permission(per) - for per in _MODERATOR_ROLE_PERMISSIONS: + for per in MODERATOR_ROLE_PERMISSIONS: moderator_role.add_permission(per) - for per in _ADMINISTRATOR_ROLE_PERMISSIONS: + for per in ADMINISTRATOR_ROLE_PERMISSIONS: administrator_role.add_permission(per) moderator_role.inherit_permissions(student_role) @@ -62,21 +67,21 @@ def are_permissions_roles_seeded(course_id): the database """ try: - administrator_role = Role.objects.get(name="Administrator", course_id=course_id) - moderator_role = Role.objects.get(name="Moderator", course_id=course_id) - student_role = Role.objects.get(name="Student", course_id=course_id) + administrator_role = Role.objects.get(name=FORUM_ROLE_ADMINISTRATOR, course_id=course_id) + moderator_role = Role.objects.get(name=FORUM_ROLE_MODERATOR, course_id=course_id) + student_role = Role.objects.get(name=FORUM_ROLE_STUDENT, course_id=course_id) except: return False - for per in _STUDENT_ROLE_PERMISSIONS: + for per in STUDENT_ROLE_PERMISSIONS: if not student_role.has_permission(per): return False - for per in _MODERATOR_ROLE_PERMISSIONS + _STUDENT_ROLE_PERMISSIONS: + for per in MODERATOR_ROLE_PERMISSIONS + STUDENT_ROLE_PERMISSIONS: if not moderator_role.has_permission(per): return False - for per in _ADMINISTRATOR_ROLE_PERMISSIONS + _MODERATOR_ROLE_PERMISSIONS + _STUDENT_ROLE_PERMISSIONS: + for per in ADMINISTRATOR_ROLE_PERMISSIONS + MODERATOR_ROLE_PERMISSIONS + STUDENT_ROLE_PERMISSIONS: if not administrator_role.has_permission(per): return False diff --git a/lms/djangoapps/ccx/migrations/0004_seed_forum_roles_in_ccx_courses.py b/lms/djangoapps/ccx/migrations/0004_seed_forum_roles_in_ccx_courses.py new file mode 100644 index 0000000000..5e577bf406 --- /dev/null +++ b/lms/djangoapps/ccx/migrations/0004_seed_forum_roles_in_ccx_courses.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import logging + +from ccx_keys.locator import CCXLocator +from courseware.courses import get_course_by_id +from django.db import migrations +from django.http import Http404 + +from django_comment_common.models import FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, \ + FORUM_ROLE_COMMUNITY_TA, FORUM_ROLE_STUDENT +from django_comment_common.utils import STUDENT_ROLE_PERMISSIONS, MODERATOR_ROLE_PERMISSIONS, \ + ADMINISTRATOR_ROLE_PERMISSIONS + +log = logging.getLogger("edx.ccx") + + +def seed_forum_roles_for_existing_ccx(apps, schema_editor): + """ + Seed forum roles and make CCX coach forum admin for respective CCX(s). + + Arguments: + apps (Applications): Apps in edX platform. + schema_editor (SchemaEditor): For editing database schema i.e create, delete field (column) + + """ + CustomCourseForEdX = apps.get_model("ccx", "CustomCourseForEdX") + Role = apps.get_model("django_comment_common", "Role") + Permission = apps.get_model("django_comment_common", "Permission") + db_alias = schema_editor.connection.alias + # This will need to be changed if ccx gets moved out of the default db for some reason. + if db_alias != 'default': + log.info("Skipping CCX migration on non-default database.") + return + + for ccx in CustomCourseForEdX.objects.all(): + if not ccx.course_id or ccx.course_id.deprecated: + # prevent migration for deprecated course ids or invalid ids. + log.warning( + "Skipping CCX %s due to invalid or deprecated course_id: %s.", + ccx.id, + ccx.course_id + ) + continue + + ccx_locator = CCXLocator.from_course_locator(ccx.course_id, unicode(ccx.id)) + + # Create forum roles. + admin_role, _ = Role.objects.get_or_create(name=FORUM_ROLE_ADMINISTRATOR, course_id=ccx_locator) + moderator_role, _ = Role.objects.get_or_create(name=FORUM_ROLE_MODERATOR, course_id=ccx_locator) + community_ta_role, _ = Role.objects.get_or_create(name=FORUM_ROLE_COMMUNITY_TA, course_id=ccx_locator) + student_role, _ = Role.objects.get_or_create(name=FORUM_ROLE_STUDENT, course_id=ccx_locator) + + # Add permissions for each role. + for name in ADMINISTRATOR_ROLE_PERMISSIONS: + admin_role.permissions.add(Permission.objects.get_or_create(name=name)[0]) + for name in MODERATOR_ROLE_PERMISSIONS: + moderator_role.permissions.add(Permission.objects.get_or_create(name=name)[0]) + for name in STUDENT_ROLE_PERMISSIONS: + student_role.permissions.add(Permission.objects.get_or_create(name=name)[0]) + for permission in student_role.permissions.all(): + moderator_role.permissions.add(permission) + for permission in moderator_role.permissions.all(): + community_ta_role.permissions.add(permission) + for permission in moderator_role.permissions.all(): + admin_role.permissions.add(permission) + + # Make CCX coach forum admin. + ccx.coach.roles.add(admin_role) + + log.info("Seeded forum permissions for CCX %s", ccx_locator) + + +class Migration(migrations.Migration): + + dependencies = [ + ('ccx', '0003_add_master_course_staff_in_ccx'), + ('django_comment_common', '0002_forumsconfig'), + ] + + operations = [ + migrations.RunPython(code=seed_forum_roles_for_existing_ccx, reverse_code=migrations.RunPython.noop) + ] diff --git a/lms/djangoapps/ccx/tests/test_views.py b/lms/djangoapps/ccx/tests/test_views.py index bd371edd8f..16fe73aee7 100644 --- a/lms/djangoapps/ccx/tests/test_views.py +++ b/lms/djangoapps/ccx/tests/test_views.py @@ -17,6 +17,9 @@ from courseware.tests.factories import StudentModuleFactory from courseware.tests.helpers import LoginEnrollmentTestCase from courseware.tabs import get_course_tab_list from courseware.testutils import FieldOverrideTestMixin +from django_comment_client.utils import has_forum_access +from django_comment_common.models import FORUM_ROLE_ADMINISTRATOR +from django_comment_common.utils import are_permissions_roles_seeded from instructor.access import ( allow_access, list_with_level, @@ -423,6 +426,10 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase): list_staff_master_course = list_with_level(self.course, 'staff') list_instructor_master_course = list_with_level(self.course, 'instructor') + # assert that forum roles are seeded + self.assertTrue(are_permissions_roles_seeded(course_key)) + self.assertTrue(has_forum_access(self.coach.username, course_key, FORUM_ROLE_ADMINISTRATOR)) + with ccx_course(course_key) as course_ccx: list_staff_ccx_course = list_with_level(course_ccx, 'staff') self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course)) diff --git a/lms/djangoapps/ccx/views.py b/lms/djangoapps/ccx/views.py index c79dca9a34..76f30fdaf5 100644 --- a/lms/djangoapps/ccx/views.py +++ b/lms/djangoapps/ccx/views.py @@ -30,6 +30,8 @@ from courseware.access import has_access from courseware.courses import get_course_by_id from courseware.field_overrides import disable_overrides +from django_comment_common.models import FORUM_ROLE_ADMINISTRATOR, assign_role +from django_comment_common.utils import seed_permissions_roles from edxmako.shortcuts import render_to_response from lms.djangoapps.grades.course_grades import iterate_grades_for from opaque_keys.edx.keys import CourseKey @@ -220,6 +222,11 @@ def create_ccx(request, course, ccx=None): ccx_id = CCXLocator.from_course_locator(course.id, unicode(ccx.id)) + # Create forum roles + seed_permissions_roles(ccx_id) + # Assign administrator forum role to CCX coach + assign_role(ccx_id, request.user, FORUM_ROLE_ADMINISTRATOR) + url = reverse('ccx_coach_dashboard', kwargs={'course_id': ccx_id}) # Enroll the coach in the course diff --git a/lms/djangoapps/django_comment_client/utils.py b/lms/djangoapps/django_comment_client/utils.py index 43172f8e58..af45edb9aa 100644 --- a/lms/djangoapps/django_comment_client/utils.py +++ b/lms/djangoapps/django_comment_client/utils.py @@ -14,7 +14,6 @@ import pystache_custom as pystache from opaque_keys.edx.locations import i4xEncoder from opaque_keys.edx.keys import CourseKey from xmodule.modulestore.django import modulestore -from lms.djangoapps.ccx.overrides import get_current_ccx from django_comment_common.models import Role, FORUM_ROLE_STUDENT from django_comment_client.permissions import check_permissions_by_view, has_permission, get_team @@ -806,11 +805,8 @@ def is_commentable_cohorted(course_key, commentable_id): def is_discussion_enabled(course_id): """ - Return True if Discussion is enabled for a course; else False + Return True if discussions are enabled; else False """ - if settings.FEATURES.get('CUSTOM_COURSES_EDX', False): - if get_current_ccx(course_id): - return False return settings.FEATURES.get('ENABLE_DISCUSSION_SERVICE')