Example #1
0
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)
Example #2
0
    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))
Example #3
0
    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))
Example #4
0
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
    })
Example #5
0
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
    })
Example #7
0
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
    })
Example #8
0
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
Example #9
0
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
Example #10
0
 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
     }
Example #11
0
 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
     }
Example #12
0
 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
Example #13
0
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)
Example #14
0
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)
Example #15
0
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)
Example #16
0
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)
Example #17
0
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
Example #18
0
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)
Example #19
0
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)
Example #20
0
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)
Example #21
0
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)
Example #24
0
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)
Example #25
0
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
Example #26
0
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)