def forum_form_discussion(request, course_id): """ Renders the main Discussion page, potentially filtered by a search query """ course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, 'load_forum', course_id) course_settings = make_course_settings(course, include_category_map=True) try: unsafethreads, query_params = get_threads(request, course_id) # This might process a search query threads = [utils.safe_content(thread) for thread in unsafethreads] except cc.utils.CommentClientMaintenanceError: log.warning("Forum is in maintenance mode") return render_to_response('discussion/maintenance.html', {}) user = cc.User.from_django_user(request.user) user_info = user.to_dict() with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context(threads, course) if request.is_ajax(): return utils.JsonResponse({ 'discussion_data': threads, # TODO: Standardize on 'discussion_data' vs 'threads' 'annotated_content_info': annotated_content_info, 'num_pages': query_params['num_pages'], 'page': query_params['page'], 'corrected_text': query_params['corrected_text'], }) else: with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): user_cohort_id = get_cohort_id(request.user, course_id) context = { 'csrf': csrf(request)['csrf_token'], 'course': course, #'recent_active_threads': recent_active_threads, 'staff_access': has_access(request.user, 'staff', course), 'threads': _attr_safe_json(threads), 'thread_pages': query_params['num_pages'], 'user_info': _attr_safe_json(user_info), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, 'staff', course), 'annotated_content_info': _attr_safe_json(annotated_content_info), 'course_id': course.id.to_deprecated_string(), 'roles': _attr_safe_json(utils.get_role_ids(course_id)), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'cohorts': course_settings["cohorts"], # still needed to render _thread_list_template 'user_cohort': user_cohort_id, # read from container in NewPostView 'is_course_cohorted': is_course_cohorted(course_id), # still needed to render _thread_list_template 'sort_preference': user.default_sort_key, 'category_map': course_settings["category_map"], 'course_settings': _attr_safe_json(course_settings) } # print "start rendering.." return render_to_response('discussion/index.html', context)
def test_is_course_cohorted(self): """ Make sure cohorts.is_course_cohorted() correctly reports if a course is cohorted or not. """ course = modulestore().get_course(self.toy_course_key) self.assertFalse(course.is_cohorted) self.assertFalse(cohorts.is_course_cohorted(course.id)) config_course_cohorts(course, [], cohorted=True) self.assertTrue(course.is_cohorted) self.assertTrue(cohorts.is_course_cohorted(course.id)) # Make sure we get a Http404 if there's no course fake_key = SlashSeparatedCourseKey('a', 'b', 'c') self.assertRaises(Http404, lambda: cohorts.is_course_cohorted(fake_key))
def inline_discussion(request, course_id, discussion_id): """ Renders JSON for DiscussionModules """ nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, course_id, 'load_forum') try: threads, query_params = get_threads(request, course_id, discussion_id, per_page=INLINE_THREADS_PER_PAGE) cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError): log.error("Error loading inline discussion threads.") raise with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) allow_anonymous = course.allow_anonymous allow_anonymous_to_peers = course.allow_anonymous_to_peers #since inline is all one commentable, only show or allow the choice of cohorts #if the commentable is cohorted, otherwise everything is not cohorted #and no one has the option of choosing a cohort is_cohorted = is_course_cohorted(course_id) and is_commentable_cohorted(course_id, discussion_id) is_moderator = cached_has_permission(request.user, "see_all_cohorts", course_id) cohorts_list = list() if is_cohorted: cohorts_list.append({'name': 'All Groups', 'id': None}) #if you're a mod, send all cohorts and let you pick if is_moderator: cohorts = get_course_cohorts(course_id) for cohort in cohorts: cohorts_list.append({'name': cohort.name, 'id': cohort.id}) else: #students don't get to choose cohorts_list = None return utils.JsonResponse({ 'discussion_data': map(utils.safe_content, threads), 'user_info': user_info, 'annotated_content_info': annotated_content_info, 'page': query_params['page'], 'num_pages': query_params['num_pages'], 'roles': utils.get_role_ids(course_id), 'allow_anonymous_to_peers': allow_anonymous_to_peers, 'allow_anonymous': allow_anonymous, 'cohorts': cohorts_list, 'is_moderator': is_moderator, 'is_cohorted': is_cohorted })
def inline_discussion(request, course_id, discussion_id): """ Renders JSON for DiscussionModules """ course = get_course_with_access(request.user, course_id, 'load_forum') try: threads, query_params = get_threads(request, course_id, discussion_id, per_page=INLINE_THREADS_PER_PAGE) cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError): # TODO (vshnayder): since none of this code seems to be aware of the fact that # sometimes things go wrong, I suspect that the js client is also not # checking for errors on request. Check and fix as needed. log.error("Error loading inline discussion threads.") raise Http404 annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) allow_anonymous = course.allow_anonymous allow_anonymous_to_peers = course.allow_anonymous_to_peers #since inline is all one commentable, only show or allow the choice of cohorts #if the commentable is cohorted, otherwise everything is not cohorted #and no one has the option of choosing a cohort is_cohorted = is_course_cohorted(course_id) and is_commentable_cohorted(course_id, discussion_id) is_moderator = cached_has_permission(request.user, "see_all_cohorts", course_id) cohorts_list = list() if is_cohorted: cohorts_list.append({'name': 'All Groups', 'id': None}) #if you're a mod, send all cohorts and let you pick if is_moderator: cohorts = get_course_cohorts(course_id) for cohort in cohorts: cohorts_list.append({'name': cohort.name, 'id': cohort.id}) else: #students don't get to choose cohorts_list = None return utils.JsonResponse({ 'discussion_data': map(utils.safe_content, threads), 'user_info': user_info, 'annotated_content_info': annotated_content_info, 'page': query_params['page'], 'num_pages': query_params['num_pages'], 'roles': utils.get_role_ids(course_id), 'allow_anonymous_to_peers': allow_anonymous_to_peers, 'allow_anonymous': allow_anonymous, 'cohorts': cohorts_list, 'is_moderator': is_moderator, 'is_cohorted': is_cohorted })
def inline_discussion(request, course_id, discussion_id,tags_type): """ Renders JSON for DiscussionModules """ course = get_course_with_access(request.user, course_id, 'load_forum') try: threads, query_params = get_threads_tags(request, course_id, tags_type, discussion_id, per_page=INLINE_THREADS_PER_PAGE) cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError): # TODO (vshnayder): since none of this code seems to be aware of the fact that # sometimes things go wrong, I suspect that the js client is also not # checking for errors on request. Check and fix as needed. log.error("Error loading inline discussion threads.") raise Http404 annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) allow_anonymous = course.allow_anonymous allow_anonymous_to_peers = course.allow_anonymous_to_peers #since inline is all one commentable, only show or allow the choice of cohorts #if the commentable is cohorted, otherwise everything is not cohorted #and no one has the option of choosing a cohort is_cohorted = is_course_cohorted(course_id) and is_commentable_cohorted(course_id, discussion_id) is_moderator = cached_has_permission(request.user, "see_all_cohorts", course_id) cohorts_list = list() if is_cohorted: cohorts_list.append({'name': 'All Groups', 'id': None}) #if you're a mod, send all cohorts and let you pick if is_moderator: cohorts = get_course_cohorts(course_id) for cohort in cohorts: cohorts_list.append({'name': cohort.name, 'id': cohort.id}) else: #students don't get to choose cohorts_list = None return utils.JsonResponse({ 'discussion_data': map(utils.safe_content, threads), 'user_info': user_info, 'annotated_content_info': annotated_content_info, 'page': query_params['page'], 'num_pages': query_params['num_pages'], 'roles': utils.get_role_ids(course_id), 'allow_anonymous_to_peers': allow_anonymous_to_peers, 'allow_anonymous': allow_anonymous, 'cohorts': cohorts_list, 'is_moderator': is_moderator, 'is_cohorted': is_cohorted })
def inline_discussion(request, course_id, discussion_id): """ Renders JSON for DiscussionModules """ nr_transaction = newrelic.agent.current_transaction() course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'load_forum', course_id) threads, query_params = get_threads(request, course_id, discussion_id, per_page=INLINE_THREADS_PER_PAGE) cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) allow_anonymous = course.allow_anonymous allow_anonymous_to_peers = course.allow_anonymous_to_peers #since inline is all one commentable, only show or allow the choice of cohorts #if the commentable is cohorted, otherwise everything is not cohorted #and no one has the option of choosing a cohort is_cohorted = is_course_cohorted(course_id) and is_commentable_cohorted(course_id, discussion_id) is_moderator = cached_has_permission(request.user, "see_all_cohorts", course_id) cohorts_list = list() if is_cohorted: cohorts_list.append({'name': _('All Groups'), 'id': None}) #if you're a mod, send all cohorts and let you pick if is_moderator: cohorts = get_course_cohorts(course_id) for cohort in cohorts: cohorts_list.append({'name': cohort.name, 'id': cohort.id}) else: #students don't get to choose cohorts_list = None return utils.JsonResponse({ 'discussion_data': map(utils.safe_content, threads), 'user_info': user_info, 'annotated_content_info': annotated_content_info, 'page': query_params['page'], 'num_pages': query_params['num_pages'], 'roles': utils.get_role_ids(course_id), 'allow_anonymous_to_peers': allow_anonymous_to_peers, 'allow_anonymous': allow_anonymous, 'cohorts': cohorts_list, 'is_moderator': is_moderator, 'is_cohorted': is_cohorted })
def make_course_settings(course): """ Generate a JSON-serializable model for course settings, which will be used to initialize a DiscussionCourseSettings object on the client. """ obj = { 'is_cohorted': is_course_cohorted(course.id), 'allow_anonymous': course.allow_anonymous, 'allow_anonymous_to_peers': course.allow_anonymous_to_peers, 'cohorts': [{"id": str(g.id), "name": g.name} for g in get_course_cohorts(course)], 'category_map': utils.get_discussion_category_map(course) } return obj
def make_course_settings(course, include_category_map=False): """ Generate a JSON-serializable model for course settings, which will be used to initialize a DiscussionCourseSettings object on the client. """ obj = { 'is_cohorted': is_course_cohorted(course.id), 'allow_anonymous': course.allow_anonymous, 'allow_anonymous_to_peers': course.allow_anonymous_to_peers, 'cohorts': [{"id": str(g.id), "name": g.name} for g in get_course_cohorts(course)], } if include_category_map: obj['category_map'] = utils.get_discussion_category_map(course) return obj
def get_course_info(self, user): cohort_id_map = {cohort.course_id: cohort.id for cohort in user.course_groups.all()} see_all_cohorts_set = { role.course_id for role in user.roles.all() for perm in role.permissions.all() if perm.name == "see_all_cohorts" } return { unicode(enrollment.course_id): { "cohort_id": cohort_id_map.get(enrollment.course_id), "see_all_cohorts": ( enrollment.course_id in see_all_cohorts_set or not is_course_cohorted(enrollment.course_id) ), } for enrollment in user.courseenrollment_set.all() if enrollment.is_active }
def get_course_info(self, user): cohort_id_map = { cohort.course_id: cohort.id for cohort in user.course_groups.all() } see_all_cohorts_set = { role.course_id for role in user.roles.all() for perm in role.permissions.all() if perm.name == "see_all_cohorts" } return { unicode(enrollment.course_id): { "cohort_id": cohort_id_map.get(enrollment.course_id), "see_all_cohorts": (enrollment.course_id in see_all_cohorts_set or not is_course_cohorted(enrollment.course_id)), } for enrollment in user.courseenrollment_set.all() if enrollment.is_active }
def get_course_info(self, user): cohort_id_map = { cohort.course_id: cohort.id for cohort in user.course_groups.all() } see_all_cohorts_set = { role.course_id for role in user.roles.all() for perm in role.permissions.all() if perm.name == "see_all_cohorts" } ret = {} for enrollment in user.courseenrollment_set.all(): if enrollment.is_active: try: ret[unicode(enrollment.course_id)] = { "cohort_id": cohort_id_map.get(enrollment.course_id), "see_all_cohorts": ( enrollment.course_id in see_all_cohorts_set or not is_course_cohorted(enrollment.course_id) ), } except Http404: # is_course_cohorted raises this if course does not exist pass return ret
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 getattr(thread, "group_id", None) is not None 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.prepare_content(thread.to_dict(), course_key, is_staff) 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: #patch for backward compatibility with comments service if not "pinned" 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': 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 forum_form_discussion(request, course_id): """ Renders the main Discussion page, potentially filtered by a search query """ nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, course_id, 'load_forum') with newrelic.agent.FunctionTrace(nr_transaction, "get_discussion_category_map"): category_map = utils.get_discussion_category_map(course) try: unsafethreads, query_params = get_threads( request, course_id) # This might process a search query threads = [utils.safe_content(thread) for thread in unsafethreads] except cc.utils.CommentClientMaintenanceError: log.warning("Forum is in maintenance mode") return render_to_response('discussion/maintenance.html', {}) user = cc.User.from_django_user(request.user) user_info = user.to_dict() with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads( course_id, threads, request.user, user_info) with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context(threads, course) if request.is_ajax(): return utils.JsonResponse({ 'discussion_data': threads, # TODO: Standardize on 'discussion_data' vs 'threads' 'annotated_content_info': annotated_content_info, 'num_pages': query_params['num_pages'], 'page': query_params['page'], }) else: with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): cohorts = get_course_cohorts(course_id) cohorted_commentables = get_cohorted_commentables(course_id) user_cohort_id = get_cohort_id(request.user, course_id) context = { 'csrf': csrf(request)['csrf_token'], 'course': course, #'recent_active_threads': recent_active_threads, 'staff_access': has_access(request.user, course, 'staff'), 'threads': saxutils.escape(json.dumps(threads), escapedict), 'thread_pages': query_params['num_pages'], 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, course, 'staff'), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course_id': course.id, 'category_map': category_map, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'cohorts': cohorts, 'user_cohort': user_cohort_id, 'cohorted_commentables': cohorted_commentables, 'is_course_cohorted': is_course_cohorted(course_id) } # print "start rendering.." return render_to_response('discussion/index.html', context)
def single_thread(request, course_id, discussion_id, thread_id): nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, course_id, 'load_forum') 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 if request.is_ajax(): with newrelic.agent.FunctionTrace(nr_transaction, "get_annotated_content_infos"): annotated_content_info = utils.get_annotated_content_infos( course_id, thread, request.user, user_info=user_info) content = utils.safe_content(thread.to_dict()) 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: with newrelic.agent.FunctionTrace(nr_transaction, "get_discussion_category_map"): category_map = utils.get_discussion_category_map(course) threads, query_params = get_threads(request, course_id) threads.append(thread.to_dict()) course = get_course_with_access(request.user, course_id, 'load_forum') with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context(threads, course) for thread in threads: if thread.get('group_id') and not thread.get('group_name'): thread['group_name'] = get_cohort_by_id( course_id, thread.get('group_id')).name #patch for backward compatibility with comments service if not "pinned" in thread: thread["pinned"] = False threads = [utils.safe_content(thread) for thread in threads] with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads( course_id, threads, request.user, user_info) with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): cohorts = get_course_cohorts(course_id) cohorted_commentables = get_cohorted_commentables(course_id) user_cohort = get_cohort_id(request.user, course_id) context = { 'discussion_id': discussion_id, 'csrf': csrf(request)['csrf_token'], 'init': '', # TODO: What is this? 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course': course, #'recent_active_threads': recent_active_threads, 'course_id': course.id, # TODO: Why pass both course and course.id to template? 'thread_id': thread_id, 'threads': saxutils.escape(json.dumps(threads), escapedict), 'category_map': category_map, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'thread_pages': query_params['num_pages'], 'is_course_cohorted': is_course_cohorted(course_id), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, course, 'staff'), 'cohorts': cohorts, 'user_cohort': get_cohort_id(request.user, course_id), 'cohorted_commentables': cohorted_commentables } return render_to_response('discussion/index.html', context)
def single_thread(request, course_id, discussion_id, thread_id): nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, course_id, 'load_forum') 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. 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") ) if request.is_ajax(): with newrelic.agent.FunctionTrace(nr_transaction, "get_annotated_content_infos"): annotated_content_info = utils.get_annotated_content_infos(course_id, thread, request.user, user_info=user_info) content = utils.safe_content(thread.to_dict()) 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: with newrelic.agent.FunctionTrace(nr_transaction, "get_discussion_category_map"): category_map = utils.get_discussion_category_map(course) threads, query_params = get_threads(request, course_id) threads.append(thread.to_dict()) course = get_course_with_access(request.user, course_id, 'load_forum') with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context(threads, course) for thread in threads: if thread.get('group_id') and not thread.get('group_name'): thread['group_name'] = get_cohort_by_id(course_id, thread.get('group_id')).name #patch for backward compatibility with comments service if not "pinned" in thread: thread["pinned"] = False threads = [utils.safe_content(thread) for thread in threads] #recent_active_threads = cc.search_recent_active_threads( # course_id, # recursive=False, # query_params={'follower_id': request.user.id}, #) with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): cohorts = get_course_cohorts(course_id) cohorted_commentables = get_cohorted_commentables(course_id) user_cohort = get_cohort_id(request.user, course_id) context = { 'discussion_id': discussion_id, 'csrf': csrf(request)['csrf_token'], 'init': '', # TODO: What is this? 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course': course, #'recent_active_threads': recent_active_threads, 'course_id': course.id, # TODO: Why pass both course and course.id to template? 'thread_id': thread_id, 'threads': saxutils.escape(json.dumps(threads), escapedict), 'category_map': category_map, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'thread_pages': query_params['num_pages'], 'is_course_cohorted': is_course_cohorted(course_id), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, course, 'staff'), 'cohorts': cohorts, 'user_cohort': get_cohort_id(request.user, course_id), 'cohorted_commentables': cohorted_commentables } return render_to_response('discussion/index.html', context)
def prepare_content(content, course_key, is_staff=False): """ This function is used to pre-process thread and comment models in various ways before adding them to the HTTP response. This includes fixing empty attribute fields, enforcing author anonymity, and enriching metadata around group ownership and response endorsement. @TODO: not all response pre-processing steps are currently integrated into this function. """ fields = [ 'id', 'title', 'body', 'course_id', 'anonymous', 'anonymous_to_peers', 'endorsed', 'parent_id', 'thread_id', 'votes', 'closed', 'created_at', 'updated_at', 'depth', 'type', 'commentable_id', 'comments_count', 'at_position_list', 'children', 'highlighted_title', 'highlighted_body', 'courseware_title', 'courseware_url', 'unread_comments_count', 'read', 'group_id', 'group_name', 'pinned', 'abuse_flaggers', 'stats', 'resp_skip', 'resp_limit', 'resp_total', 'thread_type', 'endorsed_responses', 'non_endorsed_responses', 'non_endorsed_resp_total', 'endorsement', ] if (content.get('anonymous') is False) and ((content.get('anonymous_to_peers') is False) or is_staff): fields += ['username', 'user_id'] content = strip_none(extract(content, fields)) if content.get("endorsement"): endorsement = content["endorsement"] endorser = None if endorsement["user_id"]: try: endorser = User.objects.get(pk=endorsement["user_id"]) except User.DoesNotExist: log.error("User ID {0} in endorsement for comment {1} but not in our DB.".format( content.get('user_id'), content.get('id')) ) # Only reveal endorser if requester can see author or if endorser is staff if ( endorser and ("username" in fields or cached_has_permission(endorser, "endorse_comment", course_key)) ): endorsement["username"] = endorser.username else: del endorsement["user_id"] for child_content_key in ["children", "endorsed_responses", "non_endorsed_responses"]: if child_content_key in content: children = [ prepare_content(child, course_key, is_staff) for child in content[child_content_key] ] content[child_content_key] = children if is_course_cohorted(course_key): # Augment the specified thread info to include the group name if a group id is present. if content.get('group_id') is not None: content['group_name'] = get_cohort_by_id(course_key, content.get('group_id')).name else: # Remove any cohort information that might remain if the course had previously been cohorted. content.pop('group_id', None) return content
def single_thread(request, course_id, discussion_id, thread_id): nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, course_id, 'load_forum') cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() thread = cc.Thread.find(thread_id).retrieve(recursive=True, user_id=request.user.id) if request.is_ajax(): with newrelic.agent.FunctionTrace(nr_transaction, "get_annotated_content_infos"): annotated_content_info = utils.get_annotated_content_infos( course_id, thread, request.user, user_info=user_info) context = {'thread': thread.to_dict(), 'course_id': course_id} # TODO: Remove completely or switch back to server side rendering # html = render_to_string('discussion/_ajax_single_thread.html', context) content = utils.safe_content(thread.to_dict()) with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context([content], course) return utils.JsonResponse({ #'html': html, 'content': content, 'annotated_content_info': annotated_content_info, }) else: with newrelic.agent.FunctionTrace(nr_transaction, "get_discussion_category_map"): category_map = utils.get_discussion_category_map(course) threads, query_params = get_threads(request, course_id) threads.append(thread.to_dict()) course = get_course_with_access(request.user, course_id, 'load_forum') with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context(threads, course) for thread in threads: if thread.get('group_id') and not thread.get('group_name'): thread['group_name'] = get_cohort_by_id( course_id, thread.get('group_id')).name #patch for backward compatibility with comments service if not "pinned" in thread: thread["pinned"] = False threads = [utils.safe_content(thread) for thread in threads] #recent_active_threads = cc.search_recent_active_threads( # course_id, # recursive=False, # query_params={'follower_id': request.user.id}, #) #trending_tags = cc.search_trending_tags( # course_id, #) with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads( course_id, threads, request.user, user_info) with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): cohorts = get_course_cohorts(course_id) cohorted_commentables = get_cohorted_commentables(course_id) user_cohort = get_cohort_id(request.user, course_id) context = { 'discussion_id': discussion_id, 'csrf': csrf(request)['csrf_token'], 'init': '', # TODO: What is this? 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course': course, #'recent_active_threads': recent_active_threads, #'trending_tags': trending_tags, 'course_id': course.id, # TODO: Why pass both course and course.id to template? 'thread_id': thread_id, 'threads': saxutils.escape(json.dumps(threads), escapedict), 'category_map': category_map, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'thread_pages': query_params['num_pages'], 'is_course_cohorted': is_course_cohorted(course_id), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, course, 'staff'), 'cohorts': cohorts, 'user_cohort': get_cohort_id(request.user, course_id), 'cohorted_commentables': cohorted_commentables } return render_to_response('discussion/single_thread.html', context)
def forum_form_discussion(request, course_id): """ Renders the main Discussion page, potentially filtered by a search query """ nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, course_id, 'load_forum') with newrelic.agent.FunctionTrace(nr_transaction, "get_discussion_category_map"): category_map = utils.get_discussion_category_map(course) try: unsafethreads, query_params = get_threads(request, course_id) # This might process a search query threads = [utils.safe_content(thread) for thread in unsafethreads] except cc.utils.CommentClientMaintenanceError: log.warning("Forum is in maintenance mode") return render_to_response('discussion/maintenance.html', {}) user = cc.User.from_django_user(request.user) user_info = user.to_dict() with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context(threads, course) if request.is_ajax(): return utils.JsonResponse({ 'discussion_data': threads, # TODO: Standardize on 'discussion_data' vs 'threads' 'annotated_content_info': annotated_content_info, 'num_pages': query_params['num_pages'], 'page': query_params['page'], }) else: #recent_active_threads = cc.search_recent_active_threads( # course_id, # recursive=False, # query_params={'follower_id': request.user.id}, #) with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): cohorts = get_course_cohorts(course_id) cohorted_commentables = get_cohorted_commentables(course_id) user_cohort_id = get_cohort_id(request.user, course_id) context = { 'csrf': csrf(request)['csrf_token'], 'course': course, #'recent_active_threads': recent_active_threads, 'staff_access': has_access(request.user, course, 'staff'), 'threads': saxutils.escape(json.dumps(threads), escapedict), 'thread_pages': query_params['num_pages'], 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, course, 'staff'), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course_id': course.id, 'category_map': category_map, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'cohorts': cohorts, 'user_cohort': user_cohort_id, 'cohorted_commentables': cohorted_commentables, 'is_course_cohorted': is_course_cohorted(course_id) } # print "start rendering.." return render_to_response('discussion/index.html', context)
def forum_form_discussion(request, course_id): """ Renders the main Discussion page, potentially filtered by a search query """ course = get_course_with_access(request.user, course_id, 'load') category_map = utils.get_discussion_category_map(course) try: unsafethreads, query_params = get_threads( request, course_id) # This might process a search query threads = [utils.safe_content(thread) for thread in unsafethreads] except cc.utils.CommentClientMaintenanceError: log.warning("Forum is in maintenance mode") return render_to_response('discussion/maintenance.html', {}) except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError) as err: log.error("Error loading forum discussion threads: %s", str(err)) raise Http404 user = cc.User.from_django_user(request.user) user_info = user.to_dict() annotated_content_info = utils.get_metadata_for_threads( course_id, threads, request.user, user_info) for thread in threads: courseware_context = get_courseware_context(thread, course) if courseware_context: thread.update(courseware_context) if request.is_ajax(): return utils.JsonResponse({ 'discussion_data': threads, # TODO: Standardize on 'discussion_data' vs 'threads' 'annotated_content_info': annotated_content_info, 'num_pages': query_params['num_pages'], 'page': query_params['page'], }) else: #recent_active_threads = cc.search_recent_active_threads( # course_id, # recursive=False, # query_params={'follower_id': request.user.id}, #) #trending_tags = cc.search_trending_tags( # course_id, #) cohorts = get_course_cohorts(course_id) cohorted_commentables = get_cohorted_commentables(course_id) user_cohort_id = get_cohort_id(request.user, course_id) context = { 'csrf': csrf(request)['csrf_token'], 'course': course, #'recent_active_threads': recent_active_threads, #'trending_tags': trending_tags, 'staff_access': has_access(request.user, course, 'staff'), 'threads': saxutils.escape(json.dumps(threads), escapedict), 'thread_pages': query_params['num_pages'], 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, course, 'staff'), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course_id': course.id, 'category_map': category_map, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'cohorts': cohorts, 'user_cohort': user_cohort_id, 'cohorted_commentables': cohorted_commentables, 'is_course_cohorted': is_course_cohorted(course_id) } # print "start rendering.." return render_to_response('discussion/index.html', context)
def single_thread(request, course_id, discussion_id, thread_id): nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, course_id, 'load_forum') cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() try: thread = cc.Thread.find(thread_id).retrieve(recursive=True, user_id=request.user.id) except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError): log.error("Error loading single thread.") raise if request.is_ajax(): with newrelic.agent.FunctionTrace(nr_transaction, "get_courseware_context"): courseware_context = get_courseware_context(thread, course) with newrelic.agent.FunctionTrace(nr_transaction, "get_annotated_content_infos"): annotated_content_info = utils.get_annotated_content_infos(course_id, thread, request.user, user_info=user_info) context = {'thread': thread.to_dict(), 'course_id': course_id} # TODO: Remove completely or switch back to server side rendering # html = render_to_string('discussion/_ajax_single_thread.html', context) content = utils.safe_content(thread.to_dict()) if courseware_context: content.update(courseware_context) return utils.JsonResponse({ #'html': html, 'content': content, 'annotated_content_info': annotated_content_info, }) else: with newrelic.agent.FunctionTrace(nr_transaction, "get_discussion_category_map"): category_map = utils.get_discussion_category_map(course) try: threads, query_params = get_threads(request, course_id) threads.append(thread.to_dict()) except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError): log.error("Error loading single thread.") raise course = get_course_with_access(request.user, course_id, 'load_forum') with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context_to_threads"): for thread in threads: courseware_context = get_courseware_context(thread, course) if courseware_context: thread.update(courseware_context) if thread.get('group_id') and not thread.get('group_name'): thread['group_name'] = get_cohort_by_id(course_id, thread.get('group_id')).name #patch for backward compatibility with comments service if not "pinned" in thread: thread["pinned"] = False threads = [utils.safe_content(thread) for thread in threads] #recent_active_threads = cc.search_recent_active_threads( # course_id, # recursive=False, # query_params={'follower_id': request.user.id}, #) #trending_tags = cc.search_trending_tags( # course_id, #) with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): cohorts = get_course_cohorts(course_id) cohorted_commentables = get_cohorted_commentables(course_id) user_cohort = get_cohort_id(request.user, course_id) context = { 'discussion_id': discussion_id, 'csrf': csrf(request)['csrf_token'], 'init': '', # TODO: What is this? 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course': course, #'recent_active_threads': recent_active_threads, #'trending_tags': trending_tags, 'course_id': course.id, # TODO: Why pass both course and course.id to template? 'thread_id': thread_id, 'threads': saxutils.escape(json.dumps(threads), escapedict), 'category_map': category_map, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'thread_pages': query_params['num_pages'], 'is_course_cohorted': is_course_cohorted(course_id), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, course, 'staff'), 'cohorts': cohorts, 'user_cohort': get_cohort_id(request.user, course_id), 'cohorted_commentables': cohorted_commentables } return render_to_response('discussion/single_thread.html', context)
def single_thread(request, course_id, discussion_id, thread_id): course = get_course_with_access(request.user, course_id, 'load_forum') cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() try: thread = cc.Thread.find(thread_id).retrieve(recursive=True, user_id=request.user.id) except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError): log.error("Error loading single thread.") context = {'window_title':'Message', 'error_title':'', 'error_message':"This discussion has been deleted. <a href='/dashboard'>Click here</a> to go back to your dashboard. \ For additional help contact <a href='mailto:[email protected]'>[email protected]</a>."} return render_to_response('error.html', context) # raise Http404 if len(thread.get('children'))<1: create_comment_auto_load(request, course_id, thread_id,User.objects.get(id=thread.get("user_id"))) if request.is_ajax(): annotated_content_info = utils.get_annotated_content_infos(course_id, thread, request.user, user_info=user_info) context = {'thread': thread.to_dict(), 'course_id': course_id} # TODO: Remove completely or switch back to server side rendering # html = render_to_string('discussion/_ajax_single_thread.html', context) content = utils.safe_content(thread.to_dict()) #courseware_context = get_courseware_context(thread, course) #if courseware_context: # content.update(courseware_context) if thread.get('tags')[0]!='portfolio': id_map = get_discussion_id_map(course) id = content['commentable_id'] content_info = None if id in id_map: location = id_map[id]["location"].url() title = id_map[id]["title"] url = reverse('jump_to', kwargs={"course_id": course.location.course_id, "location": location}) content.update({"courseware_url": url, "courseware_title": title}) return utils.JsonResponse({ #'html': html, 'content': content, 'annotated_content_info': annotated_content_info, }) else: category_map = utils.get_discussion_category_map(course) id_map = get_discussion_id_map(course) try: threads, query_params = get_threads_tags(request, course_id) threads.append(thread.to_dict()) except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError): log.error("Error loading single thread.") raise Http404 course = get_course_with_access(request.user, course_id, 'load_forum') thread_output = [] for thread in threads: #courseware_context = get_courseware_context(thread, course) #if courseware_context: # thread.update(courseware_context) id = thread['commentable_id'] content_info = None if id in id_map: location = id_map[id]["location"].url() title = id_map[id]["title"] url = reverse('jump_to', kwargs={"course_id": course.location.course_id, "location": location}) thread.update({"courseware_url": url, "courseware_title": title}) if thread.get('group_id') and not thread.get('group_name'): thread['group_name'] = get_cohort_by_id(course_id, thread.get('group_id')).name #patch for backward compatibility with comments service if not "pinned" in thread: thread["pinned"] = False if len(thread.get('tags'))>0: #if thread.get('tags')[0]!='portfolio' and str(thread.get('courseware_url')).find('__am')<0: if thread.get('tags')[0]!='portfolio' and thread.get('tags')[0]!='aboutme': thread_output.append(thread) thread_output = [utils.safe_content(thread) for thread in thread_output] #recent_active_threads = cc.search_recent_active_threads( # course_id, # recursive=False, # query_params={'follower_id': request.user.id}, #) #trending_tags = cc.search_trending_tags( # course_id, #) annotated_content_info = utils.get_metadata_for_threads(course_id, thread_output, request.user, user_info) cohorts = get_course_cohorts(course_id) cohorted_commentables = get_cohorted_commentables(course_id) user_cohort = get_cohort_id(request.user, course_id) context = { 'discussion_id': discussion_id, 'csrf': csrf(request)['csrf_token'], 'init': '', # TODO: What is this? 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course': course, #'recent_active_threads': recent_active_threads, #'trending_tags': trending_tags, 'course_id': course.id, # TODO: Why pass both course and course.id to template? 'thread_id': thread_id, 'threads': saxutils.escape(json.dumps(thread_output), escapedict), 'category_map': category_map, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'thread_pages': query_params['num_pages'], 'is_course_cohorted': is_course_cohorted(course_id), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, course, 'staff'), 'cohorts': cohorts, 'user_cohort': get_cohort_id(request.user, course_id), 'cohorted_commentables': cohorted_commentables } return render_to_response('discussion/single_thread.html', context)
def forum_form_discussion(request, course_id): """ Renders the main Discussion page, potentially filtered by a search query """ from student.models import UserTestGroup, CourseEnrollment registered=CourseEnrollment.is_enrolled(request.user, course_id) if not registered: return redirect(reverse('cabout', args=[course_id])) course = get_course_with_access(request.user, course_id, 'load_forum') category_map = utils.get_discussion_category_map(course) id_map = get_discussion_id_map(course) try: unsafethreads, query_params = get_threads_tags(request, course_id) # This might process a search query threads = [utils.safe_content(thread) for thread in unsafethreads] except cc.utils.CommentClientMaintenanceError: log.warning("Forum is in maintenance mode") return render_to_response('discussion/maintenance.html', {}) except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError) as err: log.error("Error loading forum discussion threads: %s", str(err)) raise Http404 user = cc.User.from_django_user(request.user) user_info = user.to_dict() thread_output = [] for thread in threads: #courseware_context = get_courseware_context(thread, course) #if courseware_context: # thread.update(courseware_context) id = thread['commentable_id'] content_info = None if id in id_map: location = id_map[id]["location"].url() title = id_map[id]["title"] url = reverse('jump_to', kwargs={"course_id": course.location.course_id, "location": location}) thread.update({"courseware_url": url, "courseware_title": title}) if len(thread.get('tags'))>0: #if thread.get('tags')[0]!='portfolio' and str(thread.get('courseware_url')).find('__am')<0: if thread.get('tags')[0]!='portfolio' and thread.get('tags')[0]!='aboutme': thread_output.append(thread) annotated_content_info = utils.get_metadata_for_threads(course_id, thread_output, request.user, user_info) if request.is_ajax(): return utils.JsonResponse({ 'discussion_data': thread_output, # TODO: Standardize on 'discussion_data' vs 'threads' 'annotated_content_info': annotated_content_info, 'num_pages': query_params['num_pages'], 'page': query_params['page'], }) else: #recent_active_threads = cc.search_recent_active_threads( # course_id, # recursive=False, # query_params={'follower_id': request.user.id}, #) #trending_tags = cc.search_trending_tags( # course_id, #) cohorts = get_course_cohorts(course_id) cohorted_commentables = get_cohorted_commentables(course_id) user_cohort_id = get_cohort_id(request.user, course_id) if request.GET.get('pf_id') != None: curr_user = User.objects.get(id=int(request.GET.get('pf_id'))) else: curr_user = None context = { 'curr_user':curr_user, 'csrf': csrf(request)['csrf_token'], 'course': course, #'recent_active_threads': recent_active_threads, #'trending_tags': trending_tags, 'staff_access': has_access(request.user, course, 'staff'), 'threads': saxutils.escape(json.dumps(thread_output), escapedict), 'thread_pages': query_params['num_pages'], 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, course, 'staff'), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course_id': course.id, 'category_map': category_map, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'cohorts': cohorts, 'user_cohort': user_cohort_id, 'cohorted_commentables': cohorted_commentables, 'is_course_cohorted': is_course_cohorted(course_id) } # print "start rendering.." return render_to_response('discussion/index.html', context)
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 getattr(thread, "group_id", None) is not None 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.prepare_content(thread.to_dict(), course_key, is_staff) 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: #patch for backward compatibility with comments service if not "pinned" 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': 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 prepare_content(content, course_key, is_staff=False): """ This function is used to pre-process thread and comment models in various ways before adding them to the HTTP response. This includes fixing empty attribute fields, enforcing author anonymity, and enriching metadata around group ownership and response endorsement. @TODO: not all response pre-processing steps are currently integrated into this function. """ fields = [ 'id', 'title', 'body', 'course_id', 'anonymous', 'anonymous_to_peers', 'endorsed', 'parent_id', 'thread_id', 'votes', 'closed', 'created_at', 'updated_at', 'depth', 'type', 'commentable_id', 'comments_count', 'at_position_list', 'children', 'highlighted_title', 'highlighted_body', 'courseware_title', 'courseware_url', 'unread_comments_count', 'read', 'group_id', 'group_name', 'pinned', 'abuse_flaggers', 'stats', 'resp_skip', 'resp_limit', 'resp_total', 'thread_type', 'endorsed_responses', 'non_endorsed_responses', 'non_endorsed_resp_total', 'endorsement', ] if (content.get('anonymous') is False) and ( (content.get('anonymous_to_peers') is False) or is_staff): fields += ['username', 'user_id'] content = strip_none(extract(content, fields)) if content.get("endorsement"): endorsement = content["endorsement"] endorser = None if endorsement["user_id"]: try: endorser = User.objects.get(pk=endorsement["user_id"]) except User.DoesNotExist: log.error( "User ID {0} in endorsement for comment {1} but not in our DB." .format(content.get('user_id'), content.get('id'))) # Only reveal endorser if requester can see author or if endorser is staff if (endorser and ("username" in fields or cached_has_permission( endorser, "endorse_comment", course_key))): endorsement["username"] = endorser.username else: del endorsement["user_id"] for child_content_key in [ "children", "endorsed_responses", "non_endorsed_responses" ]: if child_content_key in content: children = [ prepare_content(child, course_key, is_staff) for child in content[child_content_key] ] content[child_content_key] = children if is_course_cohorted(course_key): # Augment the specified thread info to include the group name if a group id is present. if content.get('group_id') is not None: content['group_name'] = get_cohort_by_id( course_key, content.get('group_id')).name else: # Remove any cohort information that might remain if the course had previously been cohorted. content.pop('group_id', None) return content
def forum_form_discussion(request, course_id): """ Renders the main Discussion page, potentially filtered by a search query """ 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, check_if_enrolled=True) course_settings = make_course_settings(course, include_category_map=True) user = cc.User.from_django_user(request.user) user_info = user.to_dict() try: unsafethreads, query_params = get_threads( request, course_key) # This might process a search query is_staff = cached_has_permission(request.user, 'openclose_thread', course.id) threads = [ utils.prepare_content(thread, course_key, is_staff) for thread in unsafethreads ] except cc.utils.CommentClientMaintenanceError: log.warning("Forum is in maintenance mode") return render_to_response('discussion/maintenance.html', {}) except ValueError: return HttpResponseBadRequest("Invalid group_id") 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, "add_courseware_context"): add_courseware_context(threads, course) if request.is_ajax(): return utils.JsonResponse({ 'discussion_data': threads, # TODO: Standardize on 'discussion_data' vs 'threads' 'annotated_content_info': annotated_content_info, 'num_pages': query_params['num_pages'], 'page': query_params['page'], 'corrected_text': query_params['corrected_text'], }) else: with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): user_cohort_id = get_cohort_id(request.user, course_key) context = { 'csrf': csrf(request)['csrf_token'], 'course': course, #'recent_active_threads': recent_active_threads, 'staff_access': has_access(request.user, 'staff', course), 'threads': _attr_safe_json(threads), 'thread_pages': query_params['num_pages'], 'user_info': _attr_safe_json(user_info), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, 'staff', course), 'annotated_content_info': _attr_safe_json(annotated_content_info), 'course_id': course.id.to_deprecated_string(), 'roles': _attr_safe_json(utils.get_role_ids(course_key)), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_key), 'cohorts': course_settings[ "cohorts"], # still needed to render _thread_list_template 'user_cohort': user_cohort_id, # read from container in NewPostView 'is_course_cohorted': is_course_cohorted( course_key), # still needed to render _thread_list_template 'sort_preference': user.default_sort_key, 'category_map': course_settings["category_map"], 'course_settings': _attr_safe_json(course_settings) } # print "start rendering.." return render_to_response('discussion/index.html', context)