add forum list management
This commit is contained in:
@@ -11,6 +11,10 @@ TODO sync instructor and staff flags
|
||||
|
||||
from django.contrib.auth.models import User, Group
|
||||
from courseware.access import get_access_group_name
|
||||
from django_comment_common.models import (Role,
|
||||
FORUM_ROLE_ADMINISTRATOR,
|
||||
FORUM_ROLE_MODERATOR,
|
||||
FORUM_ROLE_COMMUNITY_TA)
|
||||
|
||||
|
||||
def list_with_level(course, level):
|
||||
@@ -55,3 +59,23 @@ def _change_access(course, user, level, mode):
|
||||
user.groups.remove(group)
|
||||
else:
|
||||
raise ValueError("unrecognized mode '{}'".format(mode))
|
||||
|
||||
|
||||
def update_forum_role_membership(course_id, user, rolename, mode):
|
||||
"""
|
||||
Change forum access of user.
|
||||
|
||||
rolename is one of [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]
|
||||
|
||||
mode is one of ['alow', 'revoke']
|
||||
"""
|
||||
role = Role.objects.get(course_id=course_id, name=rolename)
|
||||
|
||||
if mode == 'allow':
|
||||
role.users.add(user)
|
||||
elif mode == 'revoke':
|
||||
role.users.remove(user)
|
||||
print "\n" * 5
|
||||
print role.users.all()
|
||||
else:
|
||||
raise ValueError("unrecognized mode '{}'".format(mode))
|
||||
|
||||
@@ -15,11 +15,15 @@ from django.http import HttpResponse, HttpResponseBadRequest
|
||||
|
||||
from courseware.courses import get_course_with_access
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django_comment_common.models import (Role,
|
||||
FORUM_ROLE_ADMINISTRATOR,
|
||||
FORUM_ROLE_MODERATOR,
|
||||
FORUM_ROLE_COMMUNITY_TA)
|
||||
|
||||
from courseware.models import StudentModule
|
||||
import instructor.enrollment as enrollment
|
||||
from instructor.enrollment import split_input_list, enroll_emails, unenroll_emails
|
||||
from instructor.access import allow_access, revoke_access, list_with_level
|
||||
import instructor.access as access
|
||||
import analytics.basic
|
||||
import analytics.distributions
|
||||
import analytics.csvs
|
||||
@@ -57,21 +61,21 @@ def access_allow_revoke(request, course_id):
|
||||
|
||||
Query parameters:
|
||||
email is the target users email
|
||||
level is one of ['instructor', 'staff']
|
||||
rolename is one of ['instructor', 'staff']
|
||||
mode is one of ['allow', 'revoke']
|
||||
"""
|
||||
course = get_course_with_access(request.user, course_id, 'instructor', depth=None)
|
||||
|
||||
email = request.GET.get('email')
|
||||
level = request.GET.get('level')
|
||||
rolename = request.GET.get('rolename')
|
||||
mode = request.GET.get('mode')
|
||||
|
||||
user = User.objects.get(email=email)
|
||||
|
||||
if mode == 'allow':
|
||||
allow_access(course, user, level)
|
||||
access.allow_access(course, user, rolename)
|
||||
elif mode == 'revoke':
|
||||
revoke_access(course, user, level)
|
||||
access.revoke_access(course, user, rolename)
|
||||
else:
|
||||
raise ValueError("unrecognized mode '{}'".format(mode))
|
||||
|
||||
@@ -88,10 +92,17 @@ def list_instructors_staff(request, course_id):
|
||||
"""
|
||||
List instructors and staff.
|
||||
Requires staff access.
|
||||
|
||||
rolename is one of ['instructor', 'staff']
|
||||
"""
|
||||
course = get_course_with_access(request.user, course_id, 'staff', depth=None)
|
||||
|
||||
def extract_user(user):
|
||||
rolename = request.GET.get('rolename', '')
|
||||
|
||||
if not rolename in ['instructor', 'staff']:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
def extract_user_info(user):
|
||||
return {
|
||||
'username': user.username,
|
||||
'email': user.email,
|
||||
@@ -101,8 +112,7 @@ def list_instructors_staff(request, course_id):
|
||||
|
||||
response_payload = {
|
||||
'course_id': course_id,
|
||||
'instructor': map(extract_user, list_with_level(course, 'instructor')),
|
||||
'staff': map(extract_user, list_with_level(course, 'staff')),
|
||||
rolename: map(extract_user_info, access.list_with_level(course, rolename)),
|
||||
}
|
||||
response = HttpResponse(json.dumps(response_payload), content_type="application/json")
|
||||
return response
|
||||
@@ -301,3 +311,76 @@ def reset_student_attempts(request, course_id):
|
||||
}
|
||||
response = HttpResponse(json.dumps(response_payload), content_type="application/json")
|
||||
return response
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
def list_forum_members(request, course_id):
|
||||
"""
|
||||
Resets a students attempts counter. Optionally deletes student state for a problem.
|
||||
Limited to staff access.
|
||||
|
||||
Takes query parameter rolename
|
||||
"""
|
||||
course = get_course_with_access(request.user, course_id, 'staff', depth=None)
|
||||
|
||||
rolename = request.GET.get('rolename', '')
|
||||
|
||||
if not rolename in [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
try:
|
||||
role = Role.objects.get(name=rolename, course_id=course_id)
|
||||
users = role.users.all().order_by('username')
|
||||
except Role.DoesNotExist:
|
||||
users = []
|
||||
|
||||
def extract_user_info(user):
|
||||
return {
|
||||
'username': user.username,
|
||||
'email': user.email,
|
||||
'first_name': user.first_name,
|
||||
'last_name': user.last_name,
|
||||
}
|
||||
|
||||
response_payload = {
|
||||
'course_id': course_id,
|
||||
rolename: map(extract_user_info, users),
|
||||
}
|
||||
response = HttpResponse(json.dumps(response_payload), content_type="application/json")
|
||||
return response
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
def update_forum_role_membership(request, course_id):
|
||||
"""
|
||||
Modify forum role access.
|
||||
|
||||
Query parameters:
|
||||
email is the target users email
|
||||
rolename is one of [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]
|
||||
mode is one of ['allow', 'revoke']
|
||||
"""
|
||||
course = get_course_with_access(request.user, course_id, 'instructor', depth=None)
|
||||
|
||||
email = request.GET.get('email', '')
|
||||
rolename = request.GET.get('rolename', '')
|
||||
mode = request.GET.get('mode', '')
|
||||
|
||||
if not rolename in [access.FORUM_ROLE_ADMINISTRATOR, access.FORUM_ROLE_MODERATOR, access.FORUM_ROLE_COMMUNITY_TA]:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
try:
|
||||
user = User.objects.get(email=email)
|
||||
access.update_forum_role_membership(course_id, user, rolename, mode)
|
||||
except User.DoesNotExist, Role.DoesNotExist:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
response_payload = {
|
||||
'course_id': course_id,
|
||||
'mode': mode,
|
||||
'DONE': 'YES',
|
||||
}
|
||||
response = HttpResponse(json.dumps(response_payload), content_type="application/json")
|
||||
return response
|
||||
|
||||
@@ -109,6 +109,8 @@ def _section_membership(course_id):
|
||||
'unenroll_button_url': reverse('enroll_unenroll', kwargs={'course_id': course_id}),
|
||||
'list_instructors_staff_url': reverse('list_instructors_staff', kwargs={'course_id': course_id}),
|
||||
'access_allow_revoke_url': reverse('access_allow_revoke', kwargs={'course_id': course_id}),
|
||||
'list_forum_members_url': reverse('list_forum_members', kwargs={'course_id': course_id}),
|
||||
'update_forum_role_membership_url': reverse('update_forum_role_membership', kwargs={'course_id': course_id}),
|
||||
}
|
||||
return section_data
|
||||
|
||||
|
||||
@@ -94,26 +94,26 @@ class BatchEnrollment
|
||||
|
||||
|
||||
# manages a list of instructors or staff and the control of their access.
|
||||
class AuthorityList
|
||||
# level is in ['instructor', 'staff']
|
||||
constructor: (@$container, @level) ->
|
||||
log 'setting up instructor dashboard subsection - authlist management for #{@level}'
|
||||
class AuthList
|
||||
# rolename is in ['instructor', 'staff'] for instructor_staff endpoints
|
||||
# rolename is the name of Role for forums for the forum endpoints
|
||||
constructor: (@$container, @rolename) ->
|
||||
log "setting up instructor dashboard subsection - authlist management for #{@rolename}"
|
||||
|
||||
@$display_table = @$container.find('.auth-list-table')
|
||||
$add_section = @$container.find('.auth-list-add')
|
||||
$allow_field = $add_section.find("input[name='email']")
|
||||
$allow_button = $add_section.find("input[name='allow']")
|
||||
@list_endpoint = @$display_table.data 'endpoint'
|
||||
@access_change_endpoint = $add_section.data 'endpoint'
|
||||
@$add_section = @$container.find('.auth-list-add')
|
||||
$allow_field = @$add_section.find("input[name='email']")
|
||||
$allow_button = @$add_section.find("input[name='allow']")
|
||||
|
||||
$allow_button.click =>
|
||||
@access_change($allow_field.val(), @level, 'allow', @reload_auth_list)
|
||||
@access_change($allow_field.val(), @rolename, 'allow', @reload_auth_list)
|
||||
$allow_field.val ''
|
||||
|
||||
@reload_auth_list()
|
||||
|
||||
reload_auth_list: =>
|
||||
$.getJSON @list_endpoint, (data) =>
|
||||
list_endpoint = @$display_table.data 'endpoint'
|
||||
$.getJSON list_endpoint, {rolename: @rolename}, (data) =>
|
||||
log data
|
||||
|
||||
@$display_table.empty()
|
||||
@@ -138,7 +138,7 @@ class AuthorityList
|
||||
"<span class='revoke-link'>Revoke Access</span>"
|
||||
]
|
||||
|
||||
table_data = data[@level]
|
||||
table_data = data[@rolename]
|
||||
log 'table_data', table_data
|
||||
|
||||
$table_placeholder = $ '<div/>', class: 'slickgrid'
|
||||
@@ -150,11 +150,11 @@ class AuthorityList
|
||||
grid.onClick.subscribe (e, args) =>
|
||||
item = args.grid.getDataItem(args.row)
|
||||
if args.cell is 2
|
||||
@access_change(item.email, @level, 'revoke', @reload_auth_list)
|
||||
@access_change(item.email, @rolename, 'revoke', @reload_auth_list)
|
||||
|
||||
access_change: (email, level, mode, cb) ->
|
||||
url = @access_change_endpoint
|
||||
$.getJSON @access_change_endpoint, {email: email, level: @level, mode: mode}, (data) ->
|
||||
access_change: (email, rolename, mode, cb) ->
|
||||
access_change_endpoint = @$add_section.data 'endpoint'
|
||||
$.getJSON access_change_endpoint, {email: email, rolename: @rolename, mode: mode}, (data) ->
|
||||
log data
|
||||
cb?()
|
||||
|
||||
@@ -166,15 +166,31 @@ class Membership
|
||||
|
||||
# isolate sections from each other's errors.
|
||||
plantTimeout 0, => @batchenrollment = new BatchEnrollment @$section.find '.batch-enrollment'
|
||||
plantTimeout 0, => @stafflist = new AuthorityList (@$section.find '.auth-list-container.auth-list-staff'), 'staff'
|
||||
plantTimeout 0, => @instructorlist = new AuthorityList (@$section.find '.auth-list-container.auth-list-instructor'), 'instructor'
|
||||
plantTimeout 0, => @stafflist = new AuthList (@$section.find '.auth-list-container.auth-list-staff'), 'staff'
|
||||
plantTimeout 0, => @instructorlist = new AuthList (@$section.find '.auth-list-container.auth-list-instructor'), 'instructor'
|
||||
|
||||
# TODO names like 'Administrator' should come from server through template.
|
||||
plantTimeout 0, => @forum_admin_list = new AuthList (@$section.find '.auth-list-container.auth-list-forum-admin'), 'Administrator'
|
||||
plantTimeout 0, => @forum_mod_list = new AuthList (@$section.find '.auth-list-container.auth-list-forum-moderator'), 'Moderator'
|
||||
plantTimeout 0, => @forum_comta_list = new AuthList (@$section.find '.auth-list-container.auth-list-forum-community-ta'), 'Community TA'
|
||||
|
||||
onClickTitle: ->
|
||||
@stafflist.$display_table.empty()
|
||||
@stafflist.reload_auth_list()
|
||||
|
||||
@instructorlist.$display_table.empty()
|
||||
@instructorlist.reload_auth_list()
|
||||
|
||||
@forum_admin_list.$display_table.empty()
|
||||
@forum_admin_list.reload_auth_list()
|
||||
|
||||
@forum_mod_list.$display_table.empty()
|
||||
@forum_mod_list.reload_auth_list()
|
||||
|
||||
@forum_comta_list.$display_table.empty()
|
||||
@forum_comta_list.reload_auth_list()
|
||||
|
||||
|
||||
|
||||
# exports
|
||||
_.defaults window, InstructorDashboard: {}
|
||||
|
||||
@@ -92,21 +92,20 @@
|
||||
.vert-right {
|
||||
float: right;
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.auth-list-container {
|
||||
margin-bottom: 1.5em;
|
||||
.auth-list-container {
|
||||
margin-bottom: 1.5em;
|
||||
|
||||
.auth-list-table {
|
||||
.slickgrid {
|
||||
height: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
.auth-list-add {
|
||||
margin-top: 0.5em;
|
||||
.auth-list-table {
|
||||
.slickgrid {
|
||||
height: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
.auth-list-add {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.batch-enrollment {
|
||||
|
||||
@@ -8,6 +8,33 @@
|
||||
<input type="button" name="enroll" value="Enroll" data-endpoint="${ section_data['enroll_button_url'] }" >
|
||||
<input type="button" name="unenroll" value="Unenroll" data-endpoint="${ section_data['unenroll_button_url'] }" >
|
||||
<div class="task-response"></div>
|
||||
|
||||
<div class="auth-list-container auth-list-forum-admin">
|
||||
<h2>Instructor Management</h2>
|
||||
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div>
|
||||
<div class="auth-list-add" data-endpoint="${ section_data['update_forum_role_membership_url'] }">
|
||||
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
|
||||
<input type="button" name="allow" value="Grant Forum Admin">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="auth-list-container auth-list-forum-moderator">
|
||||
<h2>Instructor Management</h2>
|
||||
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div>
|
||||
<div class="auth-list-add" data-endpoint="${ section_data['update_forum_role_membership_url'] }">
|
||||
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
|
||||
<input type="button" name="allow" value="Grant Forum Moderator">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="auth-list-container auth-list-forum-community-ta">
|
||||
<h2>Instructor Management</h2>
|
||||
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div>
|
||||
<div class="auth-list-add" data-endpoint="${ section_data['update_forum_role_membership_url'] }">
|
||||
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
|
||||
<input type="button" name="allow" value="Grant Community TA">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vert-right instructor-staff-management">
|
||||
|
||||
@@ -271,6 +271,10 @@ if settings.COURSEWARE_ENABLED:
|
||||
'instructor.views.api.get_student_progress_url', name="get_student_progress_url"),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/instructor_dashboard/api/reset_student_attempts$',
|
||||
'instructor.views.api.reset_student_attempts', name="reset_student_attempts"),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/instructor_dashboard/api/list_forum_members$',
|
||||
'instructor.views.api.list_forum_members', name="list_forum_members"),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/instructor_dashboard/api/update_forum_role_membership$',
|
||||
'instructor.views.api.update_forum_role_membership', name="update_forum_role_membership"),
|
||||
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/gradebook$',
|
||||
'instructor.views.legacy.gradebook', name='gradebook'),
|
||||
|
||||
Reference in New Issue
Block a user