Commit 8e923dec by Miles Steele

fix unicode comparisons, fix 404 access exception, fix initial link handling,…

fix unicode comparisons, fix 404 access exception, fix initial link handling, fix forum access modification permissions
parent a9749f38
...@@ -9,11 +9,14 @@ TODO sync instructor and staff flags ...@@ -9,11 +9,14 @@ TODO sync instructor and staff flags
{instructor: true, staff: true} {instructor: true, staff: true}
""" """
import logging
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from courseware.access import (get_access_group_name, from courseware.access import (get_access_group_name,
course_beta_test_group_name) course_beta_test_group_name)
from django_comment_common.models import Role from django_comment_common.models import Role
log = logging.getLogger(__name__)
def list_with_level(course, level): def list_with_level(course, level):
""" """
...@@ -23,7 +26,7 @@ def list_with_level(course, level): ...@@ -23,7 +26,7 @@ def list_with_level(course, level):
There could be other levels specific to the course. There could be other levels specific to the course.
If there is no Group for that course-level, returns an empty list If there is no Group for that course-level, returns an empty list
""" """
if level is 'beta': if level == 'beta':
grpname = course_beta_test_group_name(course.location) grpname = course_beta_test_group_name(course.location)
else: else:
grpname = get_access_group_name(course, level) grpname = get_access_group_name(course, level)
...@@ -31,6 +34,7 @@ def list_with_level(course, level): ...@@ -31,6 +34,7 @@ def list_with_level(course, level):
try: try:
return Group.objects.get(name=grpname).user_set.all() return Group.objects.get(name=grpname).user_set.all()
except Group.DoesNotExist: except Group.DoesNotExist:
log.info("list_with_level called with non-existant group named {}".format(grpname))
return [] return []
...@@ -59,10 +63,10 @@ def _change_access(course, user, level, action): ...@@ -59,10 +63,10 @@ def _change_access(course, user, level, action):
level is one of ['instructor', 'staff', 'beta'] level is one of ['instructor', 'staff', 'beta']
action is one of ['allow', 'revoke'] action is one of ['allow', 'revoke']
NOTE: will NOT create a group that does not yet exist. NOTE: will create a group if it does not yet exist.
""" """
if level is 'beta': if level == 'beta':
grpname = course_beta_test_group_name(course.location) grpname = course_beta_test_group_name(course.location)
elif level in ['instructor', 'staff']: elif level in ['instructor', 'staff']:
grpname = get_access_group_name(course, level) grpname = get_access_group_name(course, level)
......
...@@ -17,6 +17,7 @@ from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbid ...@@ -17,6 +17,7 @@ from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbid
from courseware.access import has_access from courseware.access import has_access
from courseware.courses import get_course_with_access, get_course_by_id from courseware.courses import get_course_with_access, get_course_by_id
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django_comment_client.utils import has_forum_access
from django_comment_common.models import (Role, from django_comment_common.models import (Role,
FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_ADMINISTRATOR,
FORUM_ROLE_MODERATOR, FORUM_ROLE_MODERATOR,
...@@ -33,6 +34,7 @@ import analytics.csvs ...@@ -33,6 +34,7 @@ import analytics.csvs
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def common_exceptions_400(func): def common_exceptions_400(func):
""" """
Catches common exceptions and renders matching 400 errors. Catches common exceptions and renders matching 400 errors.
...@@ -630,12 +632,33 @@ def list_forum_members(request, course_id): ...@@ -630,12 +632,33 @@ def list_forum_members(request, course_id):
Lists forum members of a certain rolename. Lists forum members of a certain rolename.
Limited to staff access. Limited to staff access.
Takes query parameter `rolename` The requesting user must be at least staff.
Staff forum admins can access all roles EXCEPT for FORUM_ROLE_ADMINISTRATOR
which is limited to instructors.
Takes query parameter `rolename`.
""" """
course = get_course_by_id(course_id)
has_instructor_access = has_access(request.user, course, 'instructor')
has_forum_admin = has_forum_access(
request.user, course_id, FORUM_ROLE_ADMINISTRATOR
)
rolename = request.GET.get('rolename') rolename = request.GET.get('rolename')
# default roles require either (staff & forum admin) or (instructor)
if not (has_forum_admin or has_instructor_access):
return HttpResponseBadRequest(
"Operation requires staff & forum admin or instructor access"
)
# EXCEPT FORUM_ROLE_ADMINISTRATOR requires (instructor)
if rolename == FORUM_ROLE_ADMINISTRATOR and not has_instructor_access:
return HttpResponseBadRequest("Operation requires instructor access.")
# filter out unsupported for roles
if not rolename in [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]: if not rolename in [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]:
return HttpResponseBadRequest() return HttpResponseBadRequest("Unrecognized rolename '{}'.".format(rolename))
try: try:
role = Role.objects.get(name=rolename, course_id=course_id) role = Role.objects.get(name=rolename, course_id=course_id)
...@@ -664,7 +687,7 @@ def list_forum_members(request, course_id): ...@@ -664,7 +687,7 @@ def list_forum_members(request, course_id):
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('instructor') @require_level('staff')
@require_query_params( @require_query_params(
email="the target users email", email="the target users email",
rolename="the forum role", rolename="the forum role",
...@@ -675,20 +698,46 @@ def update_forum_role_membership(request, course_id): ...@@ -675,20 +698,46 @@ def update_forum_role_membership(request, course_id):
""" """
Modify user's forum role. Modify user's forum role.
The requesting user must be at least staff.
Staff forum admins can access all roles EXCEPT for FORUM_ROLE_ADMINISTRATOR
which is limited to instructors.
No one can revoke an instructors FORUM_ROLE_ADMINISTRATOR status.
Query parameters: Query parameters:
email is the target users email - `email` is the target users email
rolename is one of [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA] - `rolename` is one of [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]
action is one of ['allow', 'revoke'] - `action` is one of ['allow', 'revoke']
""" """
course = get_course_by_id(course_id)
has_instructor_access = has_access(request.user, course, 'instructor')
has_forum_admin = has_forum_access(
request.user, course_id, FORUM_ROLE_ADMINISTRATOR
)
email = request.GET.get('email') email = request.GET.get('email')
rolename = request.GET.get('rolename') rolename = request.GET.get('rolename')
action = request.GET.get('action') action = request.GET.get('action')
# default roles require either (staff & forum admin) or (instructor)
if not (has_forum_admin or has_instructor_access):
return HttpResponseBadRequest(
"Operation requires staff & forum admin or instructor access"
)
# EXCEPT FORUM_ROLE_ADMINISTRATOR requires (instructor)
if rolename == FORUM_ROLE_ADMINISTRATOR and not has_instructor_access:
return HttpResponseBadRequest("Operation requires instructor access.")
if not rolename in [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]: if not rolename in [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]:
return HttpResponseBadRequest() return HttpResponseBadRequest("Unrecognized rolename '{}'.".format(rolename))
user = User.objects.get(email=email)
target_is_instructor = has_access(user, course, 'instructor')
# cannot revoke instructor
if target_is_instructor and action == 'revoke' and rolename == FORUM_ROLE_ADMINISTRATOR:
return HttpResponseBadRequest("Cannot revoke instructor forum admin privelages.")
try: try:
user = User.objects.get(email=email)
access.update_forum_role_membership(course_id, user, rolename, action) access.update_forum_role_membership(course_id, user, rolename, action)
except Role.DoesNotExist: except Role.DoesNotExist:
return HttpResponseBadRequest("Role does not exist.") return HttpResponseBadRequest("Role does not exist.")
......
...@@ -40,7 +40,7 @@ def instructor_dashboard_2(request, course_id): ...@@ -40,7 +40,7 @@ def instructor_dashboard_2(request, course_id):
} }
if not access['staff']: if not access['staff']:
raise Http404 raise Http404()
sections = [ sections = [
_section_course_info(course_id), _section_course_info(course_id),
......
...@@ -74,18 +74,24 @@ setup_instructor_dashboard = (idash_content) => ...@@ -74,18 +74,24 @@ setup_instructor_dashboard = (idash_content) =>
# plantTimeout 0, -> section.data('wrapper')?.onExit?() # plantTimeout 0, -> section.data('wrapper')?.onExit?()
# activate an initial section by programmatically clicking on it. # activate an initial section by 'clicking' on it.
# check for a deep-link, or click the first link. # check for a deep-link, or click the first link.
click_first_link = ->
link = links.eq(0)
link.click()
link.data('wrapper')?.onClickTitle?()
if (new RegExp "^#{HASH_LINK_PREFIX}").test location.hash if (new RegExp "^#{HASH_LINK_PREFIX}").test location.hash
rmatch = (new RegExp "^#{HASH_LINK_PREFIX}(.*)").exec location.hash rmatch = (new RegExp "^#{HASH_LINK_PREFIX}(.*)").exec location.hash
section_name = rmatch[1] section_name = rmatch[1]
link = links.filter "[data-section='#{section_name}']" link = links.filter "[data-section='#{section_name}']"
link.click() if link.length == 1
link.data('wrapper')?.onClickTitle?() link.click()
link.data('wrapper')?.onClickTitle?()
else
click_first_link()
else else
link = links.eq(0) click_first_link()
link.click()
link.data('wrapper')?.onClickTitle?()
......
...@@ -57,16 +57,18 @@ ...@@ -57,16 +57,18 @@
</div> </div>
%endif %endif
%if section_data['access']['forum_admin']: %if section_data['access']['instructor']:
<div class="auth-list-container" data-rolename="Administrator" data-display-name="Forum Admins"> <div class="auth-list-container" data-rolename="Administrator" data-display-name="Forum Admins">
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div> <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'] }"> <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="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Forum Admin"> <input type="button" name="allow" value="Grant Forum Admin">
</div>
<div class="request-response-error"></div>
</div> </div>
<div class="request-response-error"></div> %endif
</div>
%if section_data['access']['instructor'] or section_data['access']['forum_admin']:
<div class="auth-list-container" data-rolename="Moderator" data-display-name="Forum Moderators"> <div class="auth-list-container" data-rolename="Moderator" data-display-name="Forum Moderators">
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div> <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'] }"> <div class="auth-list-add" data-endpoint="${ section_data['update_forum_role_membership_url'] }">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment