def create_thread(request, course_id, commentable_id): """ Given a course and commentble ID, create the thread """ log.debug("Creating new thread in %r, id %r", course_id, commentable_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'load', course_key) post = request.POST if course.allow_anonymous: anonymous = post.get('anonymous', 'false').lower() == 'true' else: anonymous = False if course.allow_anonymous_to_peers: anonymous_to_peers = post.get('anonymous_to_peers', 'false').lower() == 'true' else: anonymous_to_peers = False if 'title' not in post or not post['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in post or not post['body'].strip(): return JsonError(_("Body can't be empty")) thread = cc.Thread( anonymous=anonymous, anonymous_to_peers=anonymous_to_peers, commentable_id=commentable_id, course_id=course_key.to_deprecated_string(), user_id=request.user.id, thread_type=post["thread_type"], body=post["body"], title=post["title"] ) # Cohort the thread if required try: group_id = get_group_id_for_comments_service(request, course_key, commentable_id) except ValueError: return HttpResponseBadRequest("Invalid cohort id") if group_id is not None: thread.group_id = group_id thread.save() #patch for backward compatibility to comments service if not 'pinned' in thread.attributes: thread['pinned'] = False if post.get('auto_subscribe', 'false').lower() == 'true': user = cc.User.from_django_user(request.user) user.follow(thread) data = thread.to_dict() add_thread_group_name(data, course_key) add_courseware_context([data], course) if request.is_ajax(): return ajax_content_response(request, course_key, data) else: return JsonResponse(safe_content(data, course_key))
def create_thread(request, course_id, commentable_id): """ Given a course and commentble ID, create the thread """ log.debug("Creating new thread in %r, id %r", course_id, commentable_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'load', course_key) post = request.POST if course.allow_anonymous: anonymous = post.get('anonymous', 'false').lower() == 'true' else: anonymous = False if course.allow_anonymous_to_peers: anonymous_to_peers = post.get('anonymous_to_peers', 'false').lower() == 'true' else: anonymous_to_peers = False if 'title' not in post or not post['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in post or not post['body'].strip(): return JsonError(_("Body can't be empty")) thread = cc.Thread( anonymous=anonymous, anonymous_to_peers=anonymous_to_peers, commentable_id=commentable_id, course_id=course_key.to_deprecated_string(), user_id=request.user.id, thread_type=post["thread_type"], body=post["body"], title=post["title"] ) # Cohort the thread if required try: group_id = get_group_id_for_comments_service(request, course_key, commentable_id) except ValueError: return HttpResponseBadRequest("Invalid cohort id") if group_id is not None: thread.group_id = group_id thread.save() # patch for backward compatibility to comments service if 'pinned' not in thread.attributes: thread['pinned'] = False follow = post.get('auto_subscribe', 'false').lower() == 'true' user = cc.User.from_django_user(request.user) if follow: user.follow(thread) event_data = get_thread_created_event_data(thread, follow) data = thread.to_dict() # Calls to id map are expensive, but we need this more than once. # Prefetch it. id_map = get_discussion_id_map(course, request.user) add_courseware_context([data], course, request.user, id_map=id_map) track_forum_event(request, THREAD_CREATED_EVENT_NAME, course, thread, event_data, id_map=id_map) if thread.get('group_id'): # Send a notification message, if enabled, when anyone posts a new thread on # a cohorted/private discussion, except the poster him/herself _send_discussion_notification( 'open-edx.lms.discussions.cohorted-thread-added', unicode(course_key), thread, request.user, excerpt=_get_excerpt(thread.body), recipient_group_id=thread.get('group_id'), recipient_exclude_user_ids=[request.user.id], is_anonymous_user=anonymous or anonymous_to_peers ) # call into the social_engagement django app to # rescore this user _update_user_engagement_score(course_key, request.user.id) add_thread_group_name(data, course_key) if thread.get('group_id') and not thread.get('group_name'): thread['group_name'] = get_cohort_by_id(course_key, thread.get('group_id')).name data = thread.to_dict() if request.is_ajax(): return ajax_content_response(request, course_key, data) else: return JsonResponse(prepare_content(data, course_key))
def get_threads(request, course_key, discussion_id=None, per_page=THREADS_PER_PAGE): """ This may raise an appropriate subclass of cc.utils.CommentClientError if something goes wrong. """ default_query_params = { 'page': 1, 'per_page': per_page, 'sort_key': 'date', 'sort_order': 'desc', 'text': '', 'commentable_id': discussion_id, 'course_id': course_key.to_deprecated_string(), 'user_id': request.user.id, } if not request.GET.get('sort_key'): # If the user did not select a sort key, use their last used sort key cc_user = cc.User.from_django_user(request.user) cc_user.retrieve() # TODO: After the comment service is updated this can just be user.default_sort_key because the service returns the default value default_query_params['sort_key'] = cc_user.get('default_sort_key') or default_query_params['sort_key'] else: # If the user clicked a sort key, update their default sort key cc_user = cc.User.from_django_user(request.user) cc_user.default_sort_key = request.GET.get('sort_key') cc_user.save() #there are 2 dimensions to consider when executing a search with respect to group id #is user a moderator #did the user request a group #if the user requested a group explicitly, give them that group, otherwise, if mod, show all, else if student, use cohort group_id = request.GET.get('group_id') if group_id == "all": group_id = None if not group_id: if not cached_has_permission(request.user, "see_all_cohorts", course_key): group_id = get_cohort_id(request.user, course_key) if group_id: default_query_params["group_id"] = group_id #so by default, a moderator sees all items, and a student sees his cohort query_params = merge_dict( default_query_params, strip_none( extract( request.GET, [ 'page', 'sort_key', 'sort_order', 'text', 'commentable_ids', 'flagged', 'unread', 'unanswered', ] ) ) ) threads, page, num_pages, corrected_text = cc.Thread.search(query_params) #now add the group name if the thread has a group id for thread in threads: add_thread_group_name(thread, course_key) #patch for backward compatibility to comments service if not 'pinned' in thread: thread['pinned'] = False query_params['page'] = page query_params['num_pages'] = num_pages query_params['corrected_text'] = corrected_text return threads, query_params
def single_thread(request, course_id, discussion_id, thread_id): course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, 'load_forum', course_key) course_settings = make_course_settings(course, include_category_map=True) cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() # Currently, the front end always loads responses via AJAX, even for this # page; it would be a nice optimization to avoid that extra round trip to # the comments service. try: thread = cc.Thread.find(thread_id).retrieve( recursive=request.is_ajax(), user_id=request.user.id, response_skip=request.GET.get("resp_skip"), response_limit=request.GET.get("resp_limit") ) except cc.utils.CommentClientRequestError as e: if e.status_code == 404: raise Http404 raise is_staff = cached_has_permission(request.user, 'openclose_thread', course.id) if request.is_ajax(): with newrelic.agent.FunctionTrace(nr_transaction, "get_annotated_content_infos"): annotated_content_info = utils.get_annotated_content_infos(course_key, thread, request.user, user_info=user_info) content = utils.safe_content(thread.to_dict(), course_key, is_staff) add_thread_group_name(content, course_key) with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context([content], course) return utils.JsonResponse({ 'content': content, 'annotated_content_info': annotated_content_info, }) else: threads, query_params = get_threads(request, course_key) threads.append(thread.to_dict()) with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context(threads, course) for thread in threads: add_thread_group_name(thread, course_key) #patch for backward compatibility with comments service if not "pinned" in thread: thread["pinned"] = False threads = [utils.safe_content(thread, course_key, is_staff) for thread in threads] with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info) with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): user_cohort = get_cohort_id(request.user, course_key) context = { 'discussion_id': discussion_id, 'csrf': csrf(request)['csrf_token'], 'init': '', # TODO: What is this? 'user_info': _attr_safe_json(user_info), 'annotated_content_info': _attr_safe_json(annotated_content_info), 'course': course, #'recent_active_threads': recent_active_threads, 'course_id': course.id.to_deprecated_string(), # TODO: Why pass both course and course.id to template? 'thread_id': thread_id, 'threads': _attr_safe_json(threads), 'roles': _attr_safe_json(utils.get_role_ids(course_key)), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_key), 'thread_pages': query_params['num_pages'], 'is_course_cohorted': is_course_cohorted(course_key), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, 'staff', course), 'cohorts': course_settings["cohorts"], 'user_cohort': user_cohort, 'sort_preference': cc_user.default_sort_key, 'category_map': course_settings["category_map"], 'course_settings': _attr_safe_json(course_settings) } return render_to_response('discussion/index.html', context)
def single_thread(request, course_key, discussion_id, thread_id): """ Renders a response to display a single discussion thread. """ nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) course_settings = make_course_settings(course, request.user) cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() is_moderator = has_permission(request.user, "see_all_cohorts", course_key) # Verify that the student has access to this thread if belongs to a discussion module if discussion_id not in utils.get_discussion_categories_ids(course, request.user): raise Http404 # Currently, the front end always loads responses via AJAX, even for this # page; it would be a nice optimization to avoid that extra round trip to # the comments service. try: thread = cc.Thread.find(thread_id).retrieve( recursive=request.is_ajax(), user_id=request.user.id, response_skip=request.GET.get("resp_skip"), response_limit=request.GET.get("resp_limit") ) except cc.utils.CommentClientRequestError as e: if e.status_code == 404: raise Http404 raise # verify that the thread belongs to the requesting student's cohort if is_commentable_cohorted(course_key, discussion_id) and not is_moderator: user_group_id = get_cohort_id(request.user, course_key) if getattr(thread, "group_id", None) is not None and user_group_id != thread.group_id: raise Http404 is_staff = has_permission(request.user, 'openclose_thread', course.id) if request.is_ajax(): with newrelic.agent.FunctionTrace(nr_transaction, "get_annotated_content_infos"): annotated_content_info = utils.get_annotated_content_infos( course_key, thread, request.user, user_info=user_info ) content = utils.prepare_content(thread.to_dict(), course_key, is_staff) add_thread_group_name(content, course_key) return utils.JsonResponse({ 'content': content, 'annotated_content_info': annotated_content_info, }) else: try: threads, query_params = get_threads(request, course) except ValueError: return HttpResponseBadRequest("Invalid group_id") threads.append(thread.to_dict()) with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context(threads, course, request.user) for thread in threads: # patch for backward compatibility with comments service if "pinned" not in thread: thread["pinned"] = False threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads] with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info) with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): user_cohort = get_cohort_id(request.user, course_key) context = { 'discussion_id': discussion_id, 'csrf': csrf(request)['csrf_token'], 'init': '', # TODO: What is this? 'user_info': _attr_safe_json(user_info), 'annotated_content_info': _attr_safe_json(annotated_content_info), 'course': course, #'recent_active_threads': recent_active_threads, 'course_id': course.id.to_deprecated_string(), # TODO: Why pass both course and course.id to template? 'thread_id': thread_id, 'threads': _attr_safe_json(threads), 'roles': _attr_safe_json(utils.get_role_ids(course_key)), 'is_moderator': is_moderator, 'thread_pages': query_params['num_pages'], 'is_course_cohorted': is_course_cohorted(course_key), 'flag_moderator': ( has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, 'staff', course) ), 'cohorts': course_settings["cohorts"], 'user_cohort': user_cohort, 'sort_preference': cc_user.default_sort_key, 'category_map': course_settings["category_map"], 'course_settings': _attr_safe_json(course_settings), 'cohorted_commentables': (get_cohorted_commentables(course.id)), 'has_permission_to_create_thread': has_permission(request.user, "create_thread", course.id), 'has_permission_to_create_comment': has_permission(request.user, "create_comment", course.id), 'has_permission_to_create_subcomment': has_permission(request.user, "create_subcomment", course.id), 'has_permission_to_openclose_thread': has_permission(request.user, "openclose_thread", course.id) } return render_to_response('discussion/index.html', context)
def create_thread(request, course_id, commentable_id): """ Given a course and commentble ID, create the thread """ log.debug("Creating new thread in %r, id %r", course_id, commentable_id) course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key) post = request.POST user = request.user if course.allow_anonymous: anonymous = post.get('anonymous', 'false').lower() == 'true' else: anonymous = False if course.allow_anonymous_to_peers: anonymous_to_peers = post.get('anonymous_to_peers', 'false').lower() == 'true' else: anonymous_to_peers = False if 'title' not in post or not post['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in post or not post['body'].strip(): return JsonError(_("Body can't be empty")) params = { 'anonymous': anonymous, 'anonymous_to_peers': anonymous_to_peers, 'commentable_id': commentable_id, 'course_id': course_key.to_deprecated_string(), 'user_id': user.id, 'thread_type': post["thread_type"], 'body': post["body"], 'title': post["title"], } # Check for whether this commentable belongs to a team, and add the right context if get_team(commentable_id) is not None: params['context'] = ThreadContext.STANDALONE else: params['context'] = ThreadContext.COURSE thread = cc.Thread(**params) # Divide the thread if required try: group_id = get_group_id_for_comments_service(request, course_key, commentable_id) except ValueError: return HttpResponseServerError("Invalid group id for commentable") if group_id is not None: thread.group_id = group_id thread.save() thread_created.send(sender=None, user=user, post=thread) # patch for backward compatibility to comments service if 'pinned' not in thread.attributes: thread['pinned'] = False follow = post.get('auto_subscribe', 'false').lower() == 'true' if follow: cc_user = cc.User.from_django_user(user) cc_user.follow(thread) thread_followed.send(sender=None, user=user, post=thread) data = thread.to_dict() add_courseware_context([data], course, user) track_thread_created_event(request, course, thread, follow) if thread.get('group_id'): # Send a notification message, if enabled, when anyone posts a new thread on # a cohorted/private discussion, except the poster him/herself _send_discussion_notification( 'open-edx.lms.discussions.cohorted-thread-added', unicode(course_key), thread, request.user, excerpt=_get_excerpt(thread.body), recipient_group_id=thread.get('group_id'), recipient_exclude_user_ids=[request.user.id], is_anonymous_user=anonymous or anonymous_to_peers) add_thread_group_name(data, course_key) if thread.get('group_id') and not thread.get('group_name'): thread['group_name'] = get_cohort_by_id(course_key, thread.get('group_id')).name data = thread.to_dict() if request.is_ajax(): return ajax_content_response(request, course_key, data) else: return JsonResponse(prepare_content(data, course_key))
def create_thread(request, course_id, commentable_id): """ Given a course and commentble ID, create the thread """ log.debug("Creating new thread in %r, id %r", course_id, commentable_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, "load", course_key) post = request.POST if course.allow_anonymous: anonymous = post.get("anonymous", "false").lower() == "true" else: anonymous = False if course.allow_anonymous_to_peers: anonymous_to_peers = post.get("anonymous_to_peers", "false").lower() == "true" else: anonymous_to_peers = False if "title" not in post or not post["title"].strip(): return JsonError(_("Title can't be empty")) if "body" not in post or not post["body"].strip(): return JsonError(_("Body can't be empty")) thread = cc.Thread( anonymous=anonymous, anonymous_to_peers=anonymous_to_peers, commentable_id=commentable_id, course_id=course_key.to_deprecated_string(), user_id=request.user.id, thread_type=post["thread_type"], body=post["body"], title=post["title"], ) user = cc.User.from_django_user(request.user) # kevinchugh because the new requirement is that all groups will be determined # by the group id in the request this all goes away # not anymore, only for admins # Cohort the thread if the commentable is cohorted. if is_commentable_cohorted(course_key, commentable_id): user_group_id = get_cohort_id(user, course_key) # TODO (vshnayder): once we have more than just cohorts, we'll want to # change this to a single get_group_for_user_and_commentable function # that can do different things depending on the commentable_id if cached_has_permission(request.user, "see_all_cohorts", course_key): # admins can optionally choose what group to post as try: group_id = int(post.get("group_id", user_group_id)) get_cohort_by_id(course_key, group_id) except (ValueError, CourseUserGroup.DoesNotExist): return HttpResponseBadRequest("Invalid cohort id") else: # regular users always post with their own id. group_id = user_group_id if group_id: thread.group_id = group_id thread.save() # patch for backward compatibility to comments service if not "pinned" in thread.attributes: thread["pinned"] = False if post.get("auto_subscribe", "false").lower() == "true": user = cc.User.from_django_user(request.user) user.follow(thread) data = thread.to_dict() add_thread_group_name(data, course_key) add_courseware_context([data], course) if request.is_ajax(): return ajax_content_response(request, course_key, data) else: return JsonResponse(safe_content(data, course_key))
def create_thread(request, course_id, commentable_id): """ Given a course and commentble ID, create the thread """ log.debug("Creating new thread in %r, id %r", course_id, commentable_id) course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key) post = request.POST user = request.user if course.allow_anonymous: anonymous = post.get('anonymous', 'false').lower() == 'true' else: anonymous = False if course.allow_anonymous_to_peers: anonymous_to_peers = post.get('anonymous_to_peers', 'false').lower() == 'true' else: anonymous_to_peers = False if 'title' not in post or not post['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in post or not post['body'].strip(): return JsonError(_("Body can't be empty")) params = { 'anonymous': anonymous, 'anonymous_to_peers': anonymous_to_peers, 'commentable_id': commentable_id, 'course_id': course_key.to_deprecated_string(), 'user_id': user.id, 'thread_type': post["thread_type"], 'body': post["body"], 'title': post["title"], } # Check for whether this commentable belongs to a team, and add the right context if get_team(commentable_id) is not None: params['context'] = ThreadContext.STANDALONE else: params['context'] = ThreadContext.COURSE thread = cc.Thread(**params) # Divide the thread if required try: group_id = get_group_id_for_comments_service(request, course_key, commentable_id) except ValueError: return HttpResponseServerError("Invalid group id for commentable") if group_id is not None: thread.group_id = group_id thread.save() thread_created.send(sender=None, user=user, post=thread) # patch for backward compatibility to comments service if 'pinned' not in thread.attributes: thread['pinned'] = False follow = post.get('auto_subscribe', 'false').lower() == 'true' if follow: cc_user = cc.User.from_django_user(user) cc_user.follow(thread) thread_followed.send(sender=None, user=user, post=thread) data = thread.to_dict() add_courseware_context([data], course, user) track_thread_created_event(request, course, thread, follow) if thread.get('group_id'): # Send a notification message, if enabled, when anyone posts a new thread on # a cohorted/private discussion, except the poster him/herself _send_discussion_notification( 'open-edx.lms.discussions.cohorted-thread-added', unicode(course_key), thread, request.user, excerpt=_get_excerpt(thread.body), recipient_group_id=thread.get('group_id'), recipient_exclude_user_ids=[request.user.id], is_anonymous_user=anonymous or anonymous_to_peers ) add_thread_group_name(data, course_key) if thread.get('group_id') and not thread.get('group_name'): thread['group_name'] = get_cohort_by_id(course_key, thread.get('group_id')).name data = thread.to_dict() if request.is_ajax(): return ajax_content_response(request, course_key, data) else: return JsonResponse(prepare_content(data, course_key))
def get_threads(request, course_key, discussion_id=None, per_page=THREADS_PER_PAGE): """ This may raise an appropriate subclass of cc.utils.CommentClientError if something goes wrong, or ValueError if the group_id is invalid. """ default_query_params = { 'page': 1, 'per_page': per_page, 'sort_key': 'date', 'sort_order': 'desc', 'text': '', 'commentable_id': discussion_id, 'course_id': course_key.to_deprecated_string(), 'user_id': request.user.id, 'group_id': get_group_id_for_comments_service( request, course_key, discussion_id), # may raise ValueError } if not request.GET.get('sort_key'): # If the user did not select a sort key, use their last used sort key cc_user = cc.User.from_django_user(request.user) cc_user.retrieve() # TODO: After the comment service is updated this can just be user.default_sort_key because the service returns the default value default_query_params['sort_key'] = cc_user.get( 'default_sort_key') or default_query_params['sort_key'] else: # If the user clicked a sort key, update their default sort key cc_user = cc.User.from_django_user(request.user) cc_user.default_sort_key = request.GET.get('sort_key') cc_user.save() #there are 2 dimensions to consider when executing a search with respect to group id #is user a moderator #did the user request a group query_params = merge_dict( default_query_params, strip_none( extract(request.GET, [ 'page', 'sort_key', 'sort_order', 'text', 'commentable_ids', 'flagged', 'unread', 'unanswered', ]))) threads, page, num_pages, corrected_text = cc.Thread.search(query_params) #now add the group name if the thread has a group id for thread in threads: add_thread_group_name(thread, course_key) #patch for backward compatibility to comments service if not 'pinned' in thread: thread['pinned'] = False query_params['page'] = page query_params['num_pages'] = num_pages query_params['corrected_text'] = corrected_text return threads, query_params
def single_thread(request, course_id, discussion_id, thread_id): course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, 'load_forum', course_key) course_settings = make_course_settings(course, include_category_map=True) cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() is_moderator = cached_has_permission(request.user, "see_all_cohorts", course_key) # Currently, the front end always loads responses via AJAX, even for this # page; it would be a nice optimization to avoid that extra round trip to # the comments service. try: thread = cc.Thread.find(thread_id).retrieve( recursive=request.is_ajax(), user_id=request.user.id, response_skip=request.GET.get("resp_skip"), response_limit=request.GET.get("resp_limit")) except cc.utils.CommentClientRequestError as e: if e.status_code == 404: raise Http404 raise # verify that the thread belongs to the requesting student's cohort if is_commentable_cohorted(course_key, discussion_id) and not is_moderator: user_group_id = get_cohort_id(request.user, course_key) if hasattr(thread, "group_id") and user_group_id != thread.group_id: raise Http404 is_staff = cached_has_permission(request.user, 'openclose_thread', course.id) if request.is_ajax(): with newrelic.agent.FunctionTrace(nr_transaction, "get_annotated_content_infos"): annotated_content_info = utils.get_annotated_content_infos( course_key, thread, request.user, user_info=user_info) content = utils.safe_content(thread.to_dict(), course_key, is_staff) add_thread_group_name(content, course_key) with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context([content], course) return utils.JsonResponse({ 'content': content, 'annotated_content_info': annotated_content_info, }) else: try: threads, query_params = get_threads(request, course_key) except ValueError: return HttpResponseBadRequest("Invalid group_id") threads.append(thread.to_dict()) with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context(threads, course) for thread in threads: add_thread_group_name(thread, course_key) #patch for backward compatibility with comments service if not "pinned" in thread: thread["pinned"] = False threads = [ utils.safe_content(thread, course_key, is_staff) for thread in threads ] with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads( course_key, threads, request.user, user_info) with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): user_cohort = get_cohort_id(request.user, course_key) context = { 'discussion_id': discussion_id, 'csrf': csrf(request)['csrf_token'], 'init': '', # TODO: What is this? 'user_info': _attr_safe_json(user_info), 'annotated_content_info': _attr_safe_json(annotated_content_info), 'course': course, #'recent_active_threads': recent_active_threads, 'course_id': course.id.to_deprecated_string( ), # TODO: Why pass both course and course.id to template? 'thread_id': thread_id, 'threads': _attr_safe_json(threads), 'roles': _attr_safe_json(utils.get_role_ids(course_key)), 'is_moderator': is_moderator, 'thread_pages': query_params['num_pages'], 'is_course_cohorted': is_course_cohorted(course_key), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, 'staff', course), 'cohorts': course_settings["cohorts"], 'user_cohort': user_cohort, 'sort_preference': cc_user.default_sort_key, 'category_map': course_settings["category_map"], 'course_settings': _attr_safe_json(course_settings) } return render_to_response('discussion/index.html', context)