diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index ff711c237c..c5b4a19d3a 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -14,6 +14,7 @@ from datehelper import time_ago_in_words import django_comment_client.utils as utils from urllib import urlencode +from django_comment_client.permissions import check_permissions_by_view import json import comment_client as cc @@ -159,6 +160,26 @@ def forum_form_discussion(request, course_id): } return render_to_response('discussion/index.html', context) +def get_annotated_content_info(course_id, content, user, is_thread): + permissions = { + 'editable': check_permissions_by_view(user, course_id, content, "update_thread" if is_thread else "update_comment"), + 'can_reply': check_permissions_by_view(user, course_id, content, "create_comment" if is_thread else "create_sub_comment"), + 'can_endorse': check_permissions_by_view(user, course_id, content, "endorse_comment") if not is_thread else False, + 'can_delete': check_permissions_by_view(user, course_id, content, "delete_thread" if is_thread else "delete_comment"), + 'can_openclose': check_permissions_by_view(user, course_id, content, "openclose_thread") if is_thread else False, + 'can_vote': check_permissions_by_view(user, course_id, content, "vote_for_thread" if is_thread else "vote_for_comment"), + } + return permissions + +def get_annotated_content_infos(course_id, thread, user, is_thread=True): + infos = {} + def _annotate(content, is_thread=is_thread): + infos[str(content['id'])] = get_annotated_content_info(course_id, content, user, is_thread) + for child in content.get('children', []): + _annotate(child, is_thread=False) + _annotate(thread) + return infos + def render_single_thread(request, discussion_id, course_id, thread_id): thread = cc.Thread.find(thread_id).retrieve(recursive=True) diff --git a/lms/djangoapps/django_comment_client/permissions.py b/lms/djangoapps/django_comment_client/permissions.py index 9ca2b46af0..f9d9580412 100644 --- a/lms/djangoapps/django_comment_client/permissions.py +++ b/lms/djangoapps/django_comment_client/permissions.py @@ -5,6 +5,7 @@ from django.dispatch import receiver from student.models import CourseEnrollment import logging +from util.cache import cache @receiver(post_save, sender=CourseEnrollment) @@ -17,9 +18,20 @@ def assign_default_role(sender, instance, **kwargs): logging.info("assign_default_role: adding %s as %s" % (instance.user, role)) instance.user.roles.add(role) +def cached_has_permission(user, permission, course_id=None): + """ + Call has_permission if it's not cached. A change in a user's role or + a role's permissions will only become effective after CACHE_LIFESPAN seconds. + """ + CACHE_LIFESPAN = 60 + key = "permission_%d_%s_%s" % (user.id, str(course_id), permission) + val = cache.get(key, None) + if val not in [True, False]: + val = has_permission(user, permission, course_id=course_id) + cache.set(key, val, CACHE_LIFESPAN) + return val + def has_permission(user, permission, course_id=None): - # if user.permissions.filter(name=permission).exists(): - # return True for role in user.roles.filter(course_id=course_id): if role.has_permission(permission): return True @@ -60,7 +72,7 @@ def check_conditions_permissions(user, permissions, course_id, **kwargs): if isinstance(per, basestring): if per in CONDITIONS: return check_condition(user, per, course_id, kwargs) - return has_permission(user, per, course_id=course_id) + return cached_has_permission(user, per, course_id=course_id) elif isinstance(per, list) and operator in ["and", "or"]: results = [test(user, x, operator="and") for x in per] if operator == "or": diff --git a/lms/djangoapps/django_comment_client/utils.py b/lms/djangoapps/django_comment_client/utils.py index aac409435a..1aeef06ec7 100644 --- a/lms/djangoapps/django_comment_client/utils.py +++ b/lms/djangoapps/django_comment_client/utils.py @@ -5,7 +5,8 @@ from xmodule.modulestore import Location from xmodule.modulestore.django import modulestore from django.http import HttpResponse from django.utils import simplejson - +from django.db import connection +import logging from django.conf import settings import operator import itertools @@ -128,6 +129,31 @@ class ViewNameMiddleware(object): def process_view(self, request, view_func, view_args, view_kwargs): request.view_name = view_func.__name__ +class QueryCountDebugMiddleware(object): + """ + This middleware will log the number of queries run + and the total time taken for each request (with a + status code of 200). It does not currently support + multi-db setups. + """ + def process_response(self, request, response): + if response.status_code == 200: + total_time = 0 + + for query in connection.queries: + query_time = query.get('time') + if query_time is None: + # django-debug-toolbar monkeypatches the connection + # cursor wrapper and adds extra information in each + # item in connection.queries. The query time is stored + # under the key "duration" rather than "time" and is + # in milliseconds, not seconds. + query_time = query.get('duration', 0) / 1000 + total_time += float(query_time) + + logging.info('%s queries run, total %s seconds' % (len(connection.queries), total_time)) + return response + def get_annotated_content_info(course_id, content, user, type): return { 'editable': check_permissions_by_view(user, course_id, content, "update_thread" if type == 'thread' else "update_comment"), diff --git a/lms/envs/common.py b/lms/envs/common.py index 931a88e0c7..eaa05f4a70 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -329,6 +329,7 @@ MIDDLEWARE_CLASSES = ( # 'debug_toolbar.middleware.DebugToolbarMiddleware', 'django_comment_client.utils.ViewNameMiddleware', + 'django_comment_client.utils.QueryCountDebugMiddleware', ) ############################### Pipeline ####################################### diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee index c5931ddda5..2e26e191b0 100644 --- a/lms/static/coffee/src/discussion/content.coffee +++ b/lms/static/coffee/src/discussion/content.coffee @@ -402,3 +402,5 @@ initializeFollowThread = (thread) -> $local(".admin-delete").remove() if not Discussion.getContentInfo id, 'can_openclose' $local(".discussion-openclose").remove() + if not Discussion.getContentInfo id, 'can_vote' + $local(".discussion-vote").css "visibility", "hidden"