示例#1
0
def _create_base_discussion_view_context(request, course_key):
    """
    Returns the default template context for rendering any discussion view.
    """
    user = request.user
    cc_user = cc.User.from_django_user(user)
    user_info = cc_user.to_dict()
    course = get_course_with_access(user, 'load', course_key, check_if_enrolled=True)
    course_settings = make_course_settings(course, user)
    uses_bootstrap = USE_BOOTSTRAP_FLAG.is_enabled()
    return {
        'csrf': csrf(request)['csrf_token'],
        'course': course,
        'user': user,
        'user_info': user_info,
        'staff_access': bool(has_access(user, 'staff', course)),
        'roles': utils.get_role_ids(course_key),
        'can_create_comment': has_permission(user, "create_comment", course.id),
        'can_create_subcomment': has_permission(user, "create_sub_comment", course.id),
        'can_create_thread': has_permission(user, "create_thread", course.id),
        'flag_moderator': bool(
            has_permission(user, 'openclose_thread', course.id) or
            has_access(user, 'staff', course)
        ),
        'course_settings': course_settings,
        'disable_courseware_js': True,
        'uses_bootstrap': uses_bootstrap,
        'uses_pattern_library': not uses_bootstrap,
    }
示例#2
0
    def testPermission(self):
        name = self.random_str()
        self.moderator_role.add_permission(name)
        self.assertTrue(has_permission(self.moderator, name, self.course_id))

        self.student_role.add_permission(name)
        self.assertTrue(has_permission(self.student, name, self.course_id))
示例#3
0
def _create_discussion_board_context(request, course_key, discussion_id=None, thread_id=None):
    """
    Returns the template context for rendering the discussion board.
    """
    nr_transaction = newrelic.agent.current_transaction()
    context = _create_base_discussion_view_context(request, course_key)
    course = context['course']
    course_settings = context['course_settings']
    user = context['user']
    cc_user = cc.User.from_django_user(user)
    user_info = context['user_info']
    if thread_id:
        thread = _find_thread(request, course, discussion_id=discussion_id, thread_id=thread_id)
        if not thread:
            raise Http404

        # Since we're in page render mode, and the discussions UI will request the thread list itself,
        # we need only return the thread information for this one.
        threads = [thread.to_dict()]

        for thread in threads:
            # patch for backward compatibility with comments service
            if "pinned" not in thread:
                thread["pinned"] = False
        thread_pages = 1
        root_url = reverse('forum_form_discussion', args=[unicode(course.id)])
    else:
        threads, query_params = get_threads(request, course, user_info)   # This might process a search query
        thread_pages = query_params['num_pages']
        root_url = request.path
    is_staff = has_permission(user, 'openclose_thread', course.id)
    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, user, user_info)

    with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
        add_courseware_context(threads, course, user)

    with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"):
        user_cohort_id = get_cohort_id(user, course_key)

    context.update({
        'root_url': root_url,
        'discussion_id': discussion_id,
        'thread_id': thread_id,
        'threads': threads,
        'thread_pages': thread_pages,
        'annotated_content_info': annotated_content_info,
        'is_moderator': has_permission(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
        'sort_preference': cc_user.default_sort_key,
        'category_map': course_settings["category_map"],
        'course_settings': course_settings,
    })
    return context
示例#4
0
def forum_form_discussion(request, course_key):
    """
    Renders the main Discussion page, potentially filtered by a search query
    """
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    if request.is_ajax():
        user = cc.User.from_django_user(request.user)
        user_info = user.to_dict()

        try:
            unsafethreads, query_params = get_threads(request, course, user_info)  # This might process a search query
            is_staff = 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:
            return HttpResponseServerError('Forum is in maintenance mode', status=status.HTTP_503_SERVICE_UNAVAILABLE)
        except ValueError:
            return HttpResponseServerError("Invalid group_id")

        with function_trace("get_metadata_for_threads"):
            annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

        with function_trace("add_courseware_context"):
            add_courseware_context(threads, course, request.user)

        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:
        course_id = unicode(course.id)
        tab_view = CourseTabView()
        return tab_view.get(request, course_id, 'discussion')
示例#5
0
def get_group_id_for_comments_service(request, course_key, commentable_id=None):
    """
    Given a user requesting content within a `commentable_id`, determine the
    group_id which should be passed to the comments service.

    Returns:
        int: the group_id to pass to the comments service or None if nothing
        should be passed

    Raises:
        ValueError if the requested group_id is invalid
    """
    if commentable_id is None or is_commentable_cohorted(course_key, commentable_id):
        if request.method == "GET":
            requested_group_id = request.GET.get('group_id')
        elif request.method == "POST":
            requested_group_id = request.POST.get('group_id')
        if has_permission(request.user, "see_all_cohorts", course_key):
            if not requested_group_id:
                return None
            try:
                group_id = int(requested_group_id)
                get_cohort_by_id(course_key, group_id)
            except CourseUserGroup.DoesNotExist:
                raise ValueError
        else:
            # regular users always query with their own id.
            group_id = get_cohort_id(request.user, course_key)
        return group_id
    else:
        # Never pass a group_id to the comments service for a non-cohorted
        # commentable
        return None
示例#6
0
def get_group_id_for_comments_service(request, course_key, commentable_id=None):
    """
    Given a user requesting content within a `commentable_id`, determine the
    group_id which should be passed to the comments service.

    Returns:
        int: the group_id to pass to the comments service or None if nothing
        should be passed

    Raises:
        ValueError if the requested group_id is invalid
    """
    course_discussion_settings = get_course_discussion_settings(course_key)
    if commentable_id is None or is_commentable_divided(course_key, commentable_id, course_discussion_settings):
        if request.method == "GET":
            requested_group_id = request.GET.get('group_id')
        elif request.method == "POST":
            requested_group_id = request.POST.get('group_id')
        if has_permission(request.user, "see_all_cohorts", course_key):
            if not requested_group_id:
                return None
            group_id = int(requested_group_id)
            _verify_group_exists(group_id, course_discussion_settings)
        else:
            # regular users always query with their own id.
            group_id = get_group_id_for_user(request.user, course_discussion_settings)
        return group_id
    else:
        # Never pass a group_id to the comments service for a non-divided
        # commentable
        return None
示例#7
0
def inline_discussion(request, course_key, discussion_id):
    """
    Renders JSON for DiscussionModules
    """
    nr_transaction = newrelic.agent.current_transaction()

    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    cc_user = cc.User.from_django_user(request.user)
    user_info = cc_user.to_dict()

    try:
        threads, query_params = get_threads(request, course, discussion_id, per_page=INLINE_THREADS_PER_PAGE)
    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)
    is_staff = has_permission(request.user, 'openclose_thread', course.id)
    threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]
    with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
        add_courseware_context(threads, course, request.user)
    return utils.JsonResponse({
        'is_commentable_cohorted': is_commentable_cohorted(course_key, discussion_id),
        'discussion_data': 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_key),
        'course_settings': make_course_settings(course, request.user)
    })
    def has_permission(self, permission):
        """
        Encapsulates lms specific functionality, as `has_permission` is not
        importable outside of lms context, namely in tests.

        :param user:
        :param str permission: Permission
        :rtype: bool
        """
        # normal import causes the xmodule_assets command to fail due to circular import - hence importing locally
        from django_comment_client.permissions import has_permission  # pylint: disable=import-error

        return has_permission(self.django_user, permission, self.course_key)
示例#9
0
def un_flag_abuse_for_comment(request, course_id, comment_id):
    """
    given a course_id and comment id, unflag comment for abuse
    ajax only
    """
    user = cc.User.from_django_user(request.user)
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_by_id(course_key)
    remove_all = bool(
        has_permission(request.user, "openclose_thread", course_key) or has_access(request.user, "staff", course)
    )
    comment = cc.Comment.find(comment_id)
    comment.unFlagAbuse(user, comment, remove_all)
    return JsonResponse(prepare_content(comment.to_dict(), course_key))
示例#10
0
def inline_discussion(request, course_key, discussion_id):
    """
    Renders JSON for DiscussionModules
    """

    with function_trace('get_course_and_user_info'):
        course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
        cc_user = cc.User.from_django_user(request.user)
        user_info = cc_user.to_dict()

    try:
        with function_trace('get_threads'):
            threads, query_params = get_threads(
                request, course, user_info, discussion_id, per_page=INLINE_THREADS_PER_PAGE
            )
    except ValueError:
        return HttpResponseServerError('Invalid group_id')

    with function_trace('get_metadata_for_threads'):
        annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

    with function_trace('determine_group_permissions'):
        is_staff = has_permission(request.user, 'openclose_thread', course.id)
        course_discussion_settings = get_course_discussion_settings(course.id)
        group_names_by_id = get_group_names_by_id(course_discussion_settings)
        course_is_divided = course_discussion_settings.division_scheme is not CourseDiscussionSettings.NONE

    with function_trace('prepare_content'):
        threads = [
            utils.prepare_content(
                thread,
                course_key,
                is_staff,
                course_is_divided,
                group_names_by_id
            ) for thread in threads
        ]

    return utils.JsonResponse({
        'is_commentable_divided': is_commentable_divided(course_key, discussion_id),
        'discussion_data': threads,
        'user_info': user_info,
        'user_group_id': get_group_id_for_user(request.user, course_discussion_settings),
        'annotated_content_info': annotated_content_info,
        'page': query_params['page'],
        'num_pages': query_params['num_pages'],
        'roles': utils.get_role_ids(course_key),
        'course_settings': make_course_settings(course, request.user, False)
    })
示例#11
0
def un_flag_abuse_for_thread(request, course_id, thread_id):
    """
    given a course id and thread id, remove abuse flag for this thread
    ajax only
    """
    user = cc.User.from_django_user(request.user)
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_by_id(course_key)
    thread = cc.Thread.find(thread_id)
    remove_all = bool(
        has_permission(request.user, "openclose_thread", course_key) or has_access(request.user, "staff", course)
    )
    thread.unFlagAbuse(user, thread, remove_all)

    return JsonResponse(prepare_content(thread.to_dict(), course_key))
示例#12
0
def un_flag_abuse_for_comment(request, course_id, comment_id):
    """
    given a course_id and comment id, unflag comment for abuse
    ajax only
    """
    user = cc.User.from_django_user(request.user)
    course_key = CourseKey.from_string(course_id)
    course = get_course_by_id(course_key)
    remove_all = bool(
        has_permission(request.user, 'openclose_thread', course_key) or
        has_access(request.user, 'staff', course)
    )
    comment = cc.Comment.find(comment_id)
    comment.unFlagAbuse(user, comment, remove_all)
    thread_or_comment_flagged.send(sender=None, user=request.user, post=comment, undo=True)

    return JsonResponse(prepare_content(comment.to_dict(), course_key))
示例#13
0
def un_flag_abuse_for_thread(request, course_id, thread_id):
    """
    given a course id and thread id, remove abuse flag for this thread
    ajax only
    """
    user = cc.User.from_django_user(request.user)
    course_key = CourseKey.from_string(course_id)
    course = get_course_by_id(course_key)
    thread = cc.Thread.find(thread_id)
    remove_all = bool(
        has_permission(request.user, 'openclose_thread', course_key) or
        has_access(request.user, 'staff', course)
    )
    thread.unFlagAbuse(user, thread, remove_all)
    thread_or_comment_flagged.send(sender=None, user=request.user, post=thread, undo=True)

    return JsonResponse(prepare_content(thread.to_dict(), course_key))
示例#14
0
def single_thread(request, course_key, discussion_id, thread_id):
    """
    Renders a response to display a single discussion thread.  This could either be a page refresh
    after navigating to a single thread, a direct link to a single thread, or an AJAX call from the
    discussions UI loading the responses/comments for a single thread.

    Depending on the HTTP headers, we'll adjust our response accordingly.
    """
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)

    if request.is_ajax():
        cc_user = cc.User.from_django_user(request.user)
        user_info = cc_user.to_dict()
        is_staff = has_permission(request.user, 'openclose_thread', course.id)
        thread = _load_thread_for_viewing(
            request,
            course,
            discussion_id=discussion_id,
            thread_id=thread_id,
            raise_event=True,
        )

        with function_trace("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 function_trace("add_courseware_context"):
            add_courseware_context([content], course, request.user)

        return utils.JsonResponse({
            'content': content,
            'annotated_content_info': annotated_content_info,
        })
    else:
        course_id = unicode(course.id)
        tab_view = CourseTabView()
        return tab_view.get(request, course_id, 'discussion', discussion_id=discussion_id, thread_id=thread_id)
示例#15
0
def _find_thread(request, course, discussion_id, thread_id):
    """
    Finds the discussion thread with the specified ID.

    Args:
        request: The Django request.
        course_id: The ID of the owning course.
        discussion_id: The ID of the owning discussion.
        thread_id: The ID of the thread.

    Returns:
        The thread in question if the user can see it, else None.
    """
    try:
        thread = cc.Thread.find(thread_id).retrieve(
            with_responses=request.is_ajax(),
            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:
        return None

    # Verify that the student has access to this thread if belongs to a course discussion module
    thread_context = getattr(thread, "context", "course")
    if thread_context == "course" and not utils.discussion_category_id_access(course, request.user, discussion_id):
        return None

    # verify that the thread belongs to the requesting student's group
    is_moderator = has_permission(request.user, "see_all_cohorts", course.id)
    course_discussion_settings = get_course_discussion_settings(course.id)
    if is_commentable_divided(course.id, discussion_id, course_discussion_settings) and not is_moderator:
        user_group_id = get_group_id_for_user(request.user, course_discussion_settings)
        if getattr(thread, "group_id", None) is not None and user_group_id != thread.group_id:
            return None

    return thread
示例#16
0
def _create_discussion_board_context(request, course_key, discussion_id=None, thread_id=None):
    """
    Returns the template context for rendering the discussion board.
    """
    context = _create_base_discussion_view_context(request, course_key)
    course = context['course']
    course_settings = context['course_settings']
    user = context['user']
    cc_user = cc.User.from_django_user(user)
    user_info = context['user_info']
    if thread_id:
        thread = _find_thread(request, course, discussion_id=discussion_id, thread_id=thread_id)
        if not thread:
            raise Http404

        # Since we're in page render mode, and the discussions UI will request the thread list itself,
        # we need only return the thread information for this one.
        threads = [thread.to_dict()]

        for thread in threads:
            # patch for backward compatibility with comments service
            if "pinned" not in thread:
                thread["pinned"] = False
        thread_pages = 1
        root_url = reverse('forum_form_discussion', args=[unicode(course.id)])
    else:
        threads, query_params = get_threads(request, course, user_info)   # This might process a search query
        thread_pages = query_params['num_pages']
        root_url = request.path
    is_staff = has_permission(user, 'openclose_thread', course.id)
    threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]

    with newrelic_function_trace("get_metadata_for_threads"):
        annotated_content_info = utils.get_metadata_for_threads(course_key, threads, user, user_info)

    with newrelic_function_trace("add_courseware_context"):
        add_courseware_context(threads, course, user)

    with newrelic_function_trace("get_cohort_info"):
        course_discussion_settings = get_course_discussion_settings(course_key)
        user_group_id = get_group_id_for_user(user, course_discussion_settings)

    context.update({
        'root_url': root_url,
        'discussion_id': discussion_id,
        'thread_id': thread_id,
        'threads': threads,
        'thread_pages': thread_pages,
        'annotated_content_info': annotated_content_info,
        'is_moderator': has_permission(user, "see_all_cohorts", course_key),
        'groups': course_settings["groups"],  # still needed to render _thread_list_template
        'user_group_id': user_group_id,  # read from container in NewPostView
        'sort_preference': cc_user.default_sort_key,
        'category_map': course_settings["category_map"],
        'course_settings': course_settings,
        'is_commentable_divided': is_commentable_divided(course_key, discussion_id, course_discussion_settings),
        # TODO: (Experimental Code). See https://openedx.atlassian.net/wiki/display/RET/2.+In-course+Verification+Prompts
        'upgrade_link': check_and_get_upgrade_link(request, user, course.id),
        'upgrade_price': get_cosmetic_verified_display_price(course),
        # ENDTODO
    })
    return context
示例#17
0
def prepare_content(content, course_key, is_staff=False, course_is_cohorted=None):
    """
    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.

    Arguments:
        content (dict): A thread or comment.
        course_key (CourseKey): The course key of the course.
        is_staff (bool): Whether the user is a staff member.
        course_is_cohorted (bool): Whether the course is cohorted.
    """
    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', 'context'
    ]

    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 %s in endorsement for comment %s but not in our DB.",
                    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 has_permission(endorser, "endorse_comment", course_key))
        ):
            endorsement["username"] = endorser.username
        else:
            del endorsement["user_id"]

    if course_is_cohorted is None:
        course_is_cohorted = is_course_cohorted(course_key)

    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, course_is_cohorted=course_is_cohorted)
                for child in content[child_content_key]
            ]
            content[child_content_key] = children

    if course_is_cohorted:
        # 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
示例#18
0
def user_profile(request, course_key, user_id):
    """
    Renders a response to display the user profile page (shown after clicking
    on a post author's username).
    """
    user = cc.User.from_django_user(request.user)
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)

    try:
        # If user is not enrolled in the course, do not proceed.
        django_user = User.objects.get(id=user_id)
        if not CourseEnrollment.is_enrolled(django_user, course.id):
            raise Http404

        query_params = {
            'page': request.GET.get('page', 1),
            'per_page': THREADS_PER_PAGE,   # more than threads_per_page to show more activities
        }

        try:
            group_id = get_group_id_for_comments_service(request, course_key)
        except ValueError:
            return HttpResponseServerError("Invalid group_id")
        if group_id is not None:
            query_params['group_id'] = group_id
            profiled_user = cc.User(id=user_id, course_id=course_key, group_id=group_id)
        else:
            profiled_user = cc.User(id=user_id, course_id=course_key)

        threads, page, num_pages = profiled_user.active_threads(query_params)
        query_params['page'] = page
        query_params['num_pages'] = num_pages

        with newrelic_function_trace("get_metadata_for_threads"):
            user_info = cc.User.from_django_user(request.user).to_dict()
            annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

        is_staff = has_permission(request.user, 'openclose_thread', course.id)
        threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]
        with newrelic_function_trace("add_courseware_context"):
            add_courseware_context(threads, course, request.user)
        if request.is_ajax():
            return utils.JsonResponse({
                'discussion_data': threads,
                'page': query_params['page'],
                'num_pages': query_params['num_pages'],
                'annotated_content_info': annotated_content_info,
            })
        else:
            user_roles = django_user.roles.filter(
                course_id=course.id
            ).order_by("name").values_list("name", flat=True).distinct()

            with newrelic_function_trace("get_cohort_info"):
                course_discussion_settings = get_course_discussion_settings(course_key)
                user_group_id = get_group_id_for_user(request.user, course_discussion_settings)

            context = _create_base_discussion_view_context(request, course_key)
            context.update({
                'django_user': django_user,
                'django_user_roles': user_roles,
                'profiled_user': profiled_user.to_dict(),
                'threads': threads,
                'user_group_id': user_group_id,
                'annotated_content_info': annotated_content_info,
                'page': query_params['page'],
                'num_pages': query_params['num_pages'],
                'sort_preference': user.default_sort_key,
                'learner_profile_page_url': reverse('learner_profile', kwargs={'username': django_user.username}),
            })

            return render_to_response('discussion/discussion_profile_page.html', context)
    except User.DoesNotExist:
        raise Http404
示例#19
0
def prepare_content(content,
                    course_key,
                    is_staff=False,
                    course_is_cohorted=None):
    """
    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.

    Arguments:
        content (dict): A thread or comment.
        course_key (CourseKey): The course key of the course.
        is_staff (bool): Whether the user is a staff member.
        course_is_cohorted (bool): Whether the course is cohorted.
    """
    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', 'context'
    ]

    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 %s in endorsement for comment %s but not in our DB.",
                    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 has_permission(endorser, "endorse_comment", course_key))):
            endorsement["username"] = endorser.username
        else:
            del endorsement["user_id"]

    if course_is_cohorted is None:
        course_is_cohorted = is_course_cohorted(course_key)

    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,
                                course_is_cohorted=course_is_cohorted)
                for child in content[child_content_key]
            ]
            content[child_content_key] = children

    if course_is_cohorted:
        # 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
示例#20
0
def single_thread(request, course_key, discussion_id, thread_id):
    """
    Renders a response to display a single discussion thread.  This could either be a page refresh
    after navigating to a single thread, a direct link to a single thread, or an AJAX call from the
    discussions UI loading the responses/comments for a single thread.

    Depending on the HTTP headers, we'll adjust our response accordingly.
    """
    nr_transaction = newrelic.agent.current_transaction()

    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    course_settings = make_course_settings(course, request.user)
    cc_user = cc.User.from_django_user(request.user)
    user_info = cc_user.to_dict()
    is_moderator = has_permission(request.user, "see_all_cohorts", course_key)
    is_staff = has_permission(request.user, 'openclose_thread', course.id)

    try:
        thread = cc.Thread.find(thread_id).retrieve(
            with_responses=request.is_ajax(),
            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 error:
        if error.status_code == 404:
            raise Http404
        raise

    # Verify that the student has access to this thread if belongs to a course discussion module
    thread_context = getattr(thread, "context", "course")
    if thread_context == "course" and not utils.discussion_category_id_access(course, request.user, discussion_id):
        raise Http404

    # 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

    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, request.user)

        return utils.JsonResponse({
            'content': content,
            'annotated_content_info': annotated_content_info,
        })
    else:
        # Since we're in page render mode, and the discussions UI will request the thread list itself,
        # we need only return the thread information for this one.
        threads = [thread.to_dict()]

        with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
            add_courseware_context(threads, course, request.user)

        for thread in threads:
            # patch for backward compatibility with comments service
            if "pinned" not in thread:
                thread["pinned"] = False

        threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]

        with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
            annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

        with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"):
            user_cohort = get_cohort_id(request.user, course_key)

        context = {
            'discussion_id': discussion_id,
            'csrf': csrf(request)['csrf_token'],
            'init': '',   # TODO: What is this?
            'user_info': user_info,
            'can_create_comment': has_permission(request.user, "create_comment", course.id),
            'can_create_subcomment': has_permission(request.user, "create_sub_comment", course.id),
            'can_create_thread': has_permission(request.user, "create_thread", course.id),
            'annotated_content_info': 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': threads,
            'roles': utils.get_role_ids(course_key),
            'is_moderator': is_moderator,
            'thread_pages': 1,
            'is_course_cohorted': is_course_cohorted(course_key),
            'flag_moderator': bool(
                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': course_settings,
            'disable_courseware_js': True,
            'uses_pattern_library': True,
        }
        return render_to_response('discussion/discussion_board.html', context)
示例#21
0
def user_profile(request, course_key, user_id):
    """
    Renders a response to display the user profile page (shown after clicking
    on a post author's username).
    """

    nr_transaction = newrelic.agent.current_transaction()

    #TODO: Allow sorting?
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    try:
        query_params = {
            'page': request.GET.get('page', 1),
            'per_page': THREADS_PER_PAGE,   # more than threads_per_page to show more activities
        }

        try:
            group_id = get_group_id_for_comments_service(request, course_key)
        except ValueError:
            return HttpResponseBadRequest("Invalid group_id")
        if group_id is not None:
            query_params['group_id'] = group_id
            profiled_user = cc.User(id=user_id, course_id=course_key, group_id=group_id)
        else:
            profiled_user = cc.User(id=user_id, course_id=course_key)

        threads, page, num_pages = profiled_user.active_threads(query_params)
        query_params['page'] = page
        query_params['num_pages'] = num_pages
        user_info = cc.User.from_django_user(request.user).to_dict()

        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)

        is_staff = has_permission(request.user, 'openclose_thread', course.id)
        threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]
        if request.is_ajax():
            return utils.JsonResponse({
                'discussion_data': threads,
                'page': query_params['page'],
                'num_pages': query_params['num_pages'],
                'annotated_content_info': annotated_content_info,
            })
        else:
            django_user = User.objects.get(id=user_id)
            context = {
                'course': course,
                'user': request.user,
                'django_user': django_user,
                'profiled_user': profiled_user.to_dict(),
                'threads': threads,
                'user_info': user_info,
                'annotated_content_info': annotated_content_info,
                'page': query_params['page'],
                'num_pages': query_params['num_pages'],
                'learner_profile_page_url': reverse('learner_profile', kwargs={'username': django_user.username}),
                'disable_courseware_js': True,
                'uses_pattern_library': True,
            }

            return render_to_response('discussion/discussion_profile_page.html', context)
    except User.DoesNotExist:
        raise Http404
示例#22
0
def forum_form_discussion(request, course_key):
    """
    Renders the main Discussion page, potentially filtered by a search query
    """
    nr_transaction = newrelic.agent.current_transaction()

    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    course_settings = make_course_settings(course, request.user)

    user = cc.User.from_django_user(request.user)
    user_info = user.to_dict()

    try:
        unsafethreads, query_params = get_threads(request, course)   # This might process a search query
        is_staff = 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', {
            'disable_courseware_js': True,
            'uses_pattern_library': True,
        })
    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, request.user)

    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': bool(has_access(request.user, 'staff', course)),
            'threads': threads,
            'thread_pages': query_params['num_pages'],
            'user_info': user_info,
            'can_create_comment': has_permission(request.user, "create_comment", course.id),
            'can_create_subcomment': has_permission(request.user, "create_sub_comment", course.id),
            'can_create_thread': has_permission(request.user, "create_thread", course.id),
            'flag_moderator': bool(
                has_permission(request.user, 'openclose_thread', course.id) or
                has_access(request.user, 'staff', course)
            ),
            'annotated_content_info': annotated_content_info,
            'course_id': course.id.to_deprecated_string(),
            'roles': utils.get_role_ids(course_key),
            'is_moderator': 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': course_settings,
            'disable_courseware_js': True,
            'uses_pattern_library': True,
        }
        # print "start rendering.."
        return render_to_response('discussion/discussion_board.html', context)
示例#23
0
def get_threads(request, course, discussion_id=None, per_page=THREADS_PER_PAGE):
    """
    This may raise an appropriate subclass of cc.utils.CommentClientError
    if something goes wrong. It may also raise ValueError if the group_id is invalid.
    """
    default_query_params = {
        'page': 1,
        'per_page': per_page,
        'sort_key': 'date',
        'sort_order': 'desc',
        'text': '',
        'course_id': unicode(course.id),
        'commentable_id': discussion_id,
        'user_id': request.user.id,
        'group_id': get_group_id_for_comments_service(request, course.id, discussion_id),  # may raise ValueError
    }

    # If provided with a discussion id, filter by discussion id in the
    # comments_service.
    if discussion_id is not None:
        default_query_params['commentable_id'] = discussion_id

    if not request.GET.get('sort_key'):
        # If the user did not select a sort key, use their last used sort key
        cc_user = cc.User.from_django_user(request.user)
        cc_user.retrieve()
        # TODO: After the comment service is updated this can just be user.default_sort_key because the service returns the default value
        default_query_params['sort_key'] = cc_user.get('default_sort_key') or default_query_params['sort_key']
    else:
        # If the user clicked a sort key, update their default sort key
        cc_user = cc.User.from_django_user(request.user)
        cc_user.default_sort_key = request.GET.get('sort_key')
        cc_user.save()

    #there are 2 dimensions to consider when executing a search with respect to group id
    #is user a moderator
    #did the user request a group

    #if the user requested a group explicitly, give them that group, otherwise, if mod, show all, else if student, use cohort

    is_cohorted = is_commentable_cohorted(course.id, discussion_id)

    if has_permission(request.user, "see_all_cohorts", course.id):
        group_id = request.GET.get('group_id')
        if group_id in ("all", "None"):
            group_id = None
    else:
        group_id = get_cohort_id(request.user, course.id)
        if not group_id:
            default_query_params['exclude_groups'] = True

    if group_id:
        group_id = int(group_id)
        try:
            CourseUserGroup.objects.get(course_id=course.id, id=group_id)
        except CourseUserGroup.DoesNotExist:
            if not is_cohorted:
                group_id = None
            else:
                raise ValueError("Invalid Group ID")
        default_query_params["group_id"] = group_id

    #so by default, a moderator sees all items, and a student sees his cohort

    query_params = merge_dict(
        default_query_params,
        strip_none(
            extract(
                request.GET,
                [
                    'page',
                    'sort_key',
                    'sort_order',
                    'text',
                    'commentable_ids',
                    'flagged',
                    'unread',
                    'unanswered',
                ]
            )
        )
    )

    if not is_cohorted:
        query_params.pop('group_id', None)

    threads, page, num_pages, corrected_text = cc.Thread.search(query_params)
    threads = _set_group_names(course.id, threads)

    query_params['page'] = page
    query_params['num_pages'] = num_pages
    query_params['corrected_text'] = corrected_text

    return threads, query_params
示例#24
0
def _create_discussion_board_context(request,
                                     course_key,
                                     discussion_id=None,
                                     thread_id=None):
    """
    Returns the template context for rendering the discussion board.
    """
    nr_transaction = newrelic.agent.current_transaction()
    context = _create_base_discussion_view_context(request, course_key)
    course = context['course']
    course_settings = context['course_settings']
    user = context['user']
    cc_user = cc.User.from_django_user(user)
    user_info = context['user_info']
    if thread_id:
        thread = _find_thread(request,
                              course,
                              discussion_id=discussion_id,
                              thread_id=thread_id)
        if not thread:
            raise Http404

        # Since we're in page render mode, and the discussions UI will request the thread list itself,
        # we need only return the thread information for this one.
        threads = [thread.to_dict()]

        for thread in threads:
            # patch for backward compatibility with comments service
            if "pinned" not in thread:
                thread["pinned"] = False
        thread_pages = 1
        root_url = reverse('forum_form_discussion', args=[unicode(course.id)])
    else:
        threads, query_params = get_threads(
            request, course, user_info)  # This might process a search query
        thread_pages = query_params['num_pages']
        root_url = request.path
    is_staff = has_permission(user, 'openclose_thread', course.id)
    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, user, user_info)

    with newrelic.agent.FunctionTrace(nr_transaction,
                                      "add_courseware_context"):
        add_courseware_context(threads, course, user)

    with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"):
        user_cohort_id = get_cohort_id(user, course_key)

    context.update({
        'root_url':
        root_url,
        'discussion_id':
        discussion_id,
        'thread_id':
        thread_id,
        'threads':
        threads,
        'thread_pages':
        thread_pages,
        'annotated_content_info':
        annotated_content_info,
        'is_moderator':
        has_permission(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
        'sort_preference':
        cc_user.default_sort_key,
        'category_map':
        course_settings["category_map"],
        'course_settings':
        course_settings,
    })
    return context
示例#25
0
def followed_threads(request, course_key, user_id):
    """
    Ajax-only endpoint retrieving the threads followed by a specific user.
    """

    nr_transaction = newrelic.agent.current_transaction()

    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    try:
        profiled_user = cc.User(id=user_id, course_id=course_key)

        default_query_params = {
            'page': 1,
            'per_page': THREADS_PER_PAGE,   # more than threads_per_page to show more activities
            'sort_key': 'date',
            'sort_order': 'desc',
        }

        query_params = merge_dict(
            default_query_params,
            strip_none(
                extract(
                    request.GET,
                    [
                        'page',
                        'sort_key',
                        'sort_order',
                        'flagged',
                        'unread',
                        'unanswered',
                    ]
                )
            )
        )

        try:
            group_id = get_group_id_for_comments_service(request, course_key)
        except ValueError:
            return HttpResponseBadRequest("Invalid group_id")
        if group_id is not None:
            query_params['group_id'] = group_id

        threads, page, num_pages = profiled_user.subscribed_threads(query_params)
        threads = _set_group_names(course.id, threads)
        query_params['page'] = page
        query_params['num_pages'] = num_pages
        user_info = cc.User.from_django_user(request.user).to_dict()

        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)
        if request.is_ajax():
            is_staff = has_permission(request.user, 'openclose_thread', course.id)
            return utils.JsonResponse({
                'annotated_content_info': annotated_content_info,
                'discussion_data': [utils.prepare_content(thread, course_key, is_staff) for thread in threads],
                'page': query_params['page'],
                'num_pages': query_params['num_pages'],
            })
        #TODO remove non-AJAX support, it does not appear to be used and does not appear to work.
        else:
            context = {
                'course': course,
                'user': request.user,
                'django_user': User.objects.get(id=user_id),
                'profiled_user': profiled_user.to_dict(),
                'threads': _attr_safe_json(threads),
                'user_info': _attr_safe_json(user_info),
                'annotated_content_info': _attr_safe_json(annotated_content_info),
                #                'content': content,
            }

            return render_to_response('discussion/user_profile.html', context)
    except User.DoesNotExist:
        raise Http404
示例#26
0
def user_profile(request, course_key, user_id):
    """
    Renders a response to display the user profile page (shown after clicking
    on a post author's username).
    """

    nr_transaction = newrelic.agent.current_transaction()

    #TODO: Allow sorting?
    course = get_course_with_access(request.user,
                                    'load',
                                    course_key,
                                    check_if_enrolled=True)
    try:
        query_params = {
            'page': request.GET.get('page', 1),
            'per_page':
            THREADS_PER_PAGE,  # more than threads_per_page to show more activities
        }

        try:
            group_id = get_group_id_for_comments_service(request, course_key)
        except ValueError:
            return HttpResponseBadRequest("Invalid group_id")
        if group_id is not None:
            query_params['group_id'] = group_id
            profiled_user = cc.User(id=user_id,
                                    course_id=course_key,
                                    group_id=group_id)
        else:
            profiled_user = cc.User(id=user_id, course_id=course_key)

        threads, page, num_pages = profiled_user.active_threads(query_params)
        query_params['page'] = page
        query_params['num_pages'] = num_pages
        user_info = cc.User.from_django_user(request.user).to_dict()

        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)

        is_staff = has_permission(request.user, 'openclose_thread', course.id)
        threads = [
            utils.prepare_content(thread, course_key, is_staff)
            for thread in threads
        ]
        if request.is_ajax():
            return utils.JsonResponse({
                'discussion_data':
                threads,
                'page':
                query_params['page'],
                'num_pages':
                query_params['num_pages'],
                'annotated_content_info':
                json.dumps(annotated_content_info),
            })
        else:
            django_user = User.objects.get(id=user_id)
            context = {
                'course':
                course,
                'user':
                request.user,
                'django_user':
                django_user,
                'profiled_user':
                profiled_user.to_dict(),
                'threads':
                json.dumps(threads),
                'user_info':
                json.dumps(user_info, default=lambda x: None),
                'annotated_content_info':
                json.dumps(annotated_content_info),
                'page':
                query_params['page'],
                'num_pages':
                query_params['num_pages'],
                'learner_profile_page_url':
                reverse('learner_profile',
                        kwargs={'username': django_user.username})
            }

            return render_to_response('discussion/user_profile.html', context)
    except User.DoesNotExist:
        raise Http404
示例#27
0
def single_thread(request, course_key, discussion_id, thread_id):
    """
    Renders a response to display a single discussion thread.
    """
    nr_transaction = newrelic.agent.current_transaction()

    course = get_course_with_access(request.user,
                                    'load',
                                    course_key,
                                    check_if_enrolled=True)
    course_settings = make_course_settings(course, request.user)
    cc_user = cc.User.from_django_user(request.user)
    user_info = cc_user.to_dict()
    is_moderator = has_permission(request.user, "see_all_cohorts", course_key)

    # 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 student has access to this thread if belongs to a course discussion module
    thread_context = getattr(thread, "context", "course")
    if thread_context == "course" and not utils.discussion_category_id_access(
            course, request.user, discussion_id):
        raise Http404

    # verify that the thread belongs to the requesting student's cohort
    if is_commentable_cohorted(course_key, discussion_id) and not is_moderator:
        user_group_id = get_cohort_id(request.user, course_key)
        if getattr(thread, "group_id",
                   None) is not None and user_group_id != thread.group_id:
            raise Http404

    is_staff = has_permission(request.user, 'openclose_thread', course.id)
    if request.is_ajax():
        with newrelic.agent.FunctionTrace(nr_transaction,
                                          "get_annotated_content_infos"):
            annotated_content_info = utils.get_annotated_content_infos(
                course_key, thread, request.user, user_info=user_info)
        content = utils.prepare_content(thread.to_dict(), course_key, is_staff)
        with newrelic.agent.FunctionTrace(nr_transaction,
                                          "add_courseware_context"):
            add_courseware_context([content], course, request.user)
        return utils.JsonResponse({
            'content':
            content,
            'annotated_content_info':
            annotated_content_info,
        })

    else:
        try:
            threads, query_params = get_threads(request, course)
        except ValueError:
            return HttpResponseBadRequest("Invalid group_id")
        threads.append(thread.to_dict())

        with newrelic.agent.FunctionTrace(nr_transaction,
                                          "add_courseware_context"):
            add_courseware_context(threads, course, request.user)

        for thread in threads:
            # patch for backward compatibility with comments service
            if "pinned" not in thread:
                thread["pinned"] = False

        threads = [
            utils.prepare_content(thread, course_key, is_staff)
            for thread in threads
        ]

        with newrelic.agent.FunctionTrace(nr_transaction,
                                          "get_metadata_for_threads"):
            annotated_content_info = utils.get_metadata_for_threads(
                course_key, threads, request.user, user_info)

        with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"):
            user_cohort = get_cohort_id(request.user, course_key)

        context = {
            'discussion_id':
            discussion_id,
            'csrf':
            csrf(request)['csrf_token'],
            'init':
            '',  # TODO: What is this?
            'user_info':
            json.dumps(user_info),
            'can_create_comment':
            json.dumps(
                has_permission(request.user, "create_comment", course.id)),
            'can_create_subcomment':
            json.dumps(
                has_permission(request.user, "create_sub_comment", course.id)),
            'can_create_thread':
            has_permission(request.user, "create_thread", course.id),
            'annotated_content_info':
            json.dumps(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':
            json.dumps(threads),
            'roles':
            json.dumps(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':
            bool(
                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':
            json.dumps(course_settings)
        }
        return render_to_response('discussion/index.html', context)
示例#28
0
def user_profile(request, course_key, user_id):
    """
    Renders a response to display the user profile page (shown after clicking
    on a post author's username).
    """
    user = cc.User.from_django_user(request.user)
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)

    try:
        # If user is not enrolled in the course, do not proceed.
        django_user = User.objects.get(id=user_id)
        if not CourseEnrollment.is_enrolled(django_user, course.id):
            raise Http404

        query_params = {
            'page': request.GET.get('page', 1),
            'per_page': THREADS_PER_PAGE,   # more than threads_per_page to show more activities
        }

        try:
            group_id = get_group_id_for_comments_service(request, course_key)
        except ValueError:
            return HttpResponseServerError("Invalid group_id")
        if group_id is not None:
            query_params['group_id'] = group_id
            profiled_user = cc.User(id=user_id, course_id=course_key, group_id=group_id)
        else:
            profiled_user = cc.User(id=user_id, course_id=course_key)

        threads, page, num_pages = profiled_user.active_threads(query_params)
        query_params['page'] = page
        query_params['num_pages'] = num_pages

        with newrelic_function_trace("get_metadata_for_threads"):
            user_info = cc.User.from_django_user(request.user).to_dict()
            annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

        is_staff = has_permission(request.user, 'openclose_thread', course.id)
        threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]
        with newrelic_function_trace("add_courseware_context"):
            add_courseware_context(threads, course, request.user)
        if request.is_ajax():
            return utils.JsonResponse({
                'discussion_data': threads,
                'page': query_params['page'],
                'num_pages': query_params['num_pages'],
                'annotated_content_info': annotated_content_info,
            })
        else:
            user_roles = django_user.roles.filter(
                course_id=course.id
            ).order_by("name").values_list("name", flat=True).distinct()

            with newrelic_function_trace("get_cohort_info"):
                course_discussion_settings = get_course_discussion_settings(course_key)
                user_group_id = get_group_id_for_user(request.user, course_discussion_settings)

            context = _create_base_discussion_view_context(request, course_key)
            context.update({
                'django_user': django_user,
                'django_user_roles': user_roles,
                'profiled_user': profiled_user.to_dict(),
                'threads': threads,
                'user_group_id': user_group_id,
                'annotated_content_info': annotated_content_info,
                'page': query_params['page'],
                'num_pages': query_params['num_pages'],
                'sort_preference': user.default_sort_key,
                'learner_profile_page_url': reverse('learner_profile', kwargs={'username': django_user.username}),
            })

            return render_to_response('discussion/discussion_profile_page.html', context)
    except User.DoesNotExist:
        raise Http404
示例#29
0
文件: views.py 项目: gopinath81/vmss
def followed_threads(request, course_key, user_id):
    """
    Ajax-only endpoint retrieving the threads followed by a specific user.
    """

    nr_transaction = newrelic.agent.current_transaction()

    course = get_course_with_access(request.user,
                                    'load',
                                    course_key,
                                    check_if_enrolled=True)
    try:
        profiled_user = cc.User(id=user_id, course_id=course_key)

        default_query_params = {
            'page': 1,
            'per_page':
            THREADS_PER_PAGE,  # more than threads_per_page to show more activities
            'sort_key': 'date',
        }

        query_params = merge_dict(
            default_query_params,
            strip_none(
                extract(request.GET, [
                    'page',
                    'sort_key',
                    'flagged',
                    'unread',
                    'unanswered',
                ])))

        try:
            group_id = get_group_id_for_comments_service(request, course_key)
        except ValueError:
            return HttpResponseBadRequest("Invalid group_id")
        if group_id is not None:
            query_params['group_id'] = group_id

        paginated_results = profiled_user.subscribed_threads(query_params)
        print "\n \n \n paginated results \n \n \n "
        print paginated_results
        query_params['page'] = paginated_results.page
        query_params['num_pages'] = paginated_results.num_pages
        user_info = cc.User.from_django_user(request.user).to_dict()

        with newrelic.agent.FunctionTrace(nr_transaction,
                                          "get_metadata_for_threads"):
            annotated_content_info = utils.get_metadata_for_threads(
                course_key, paginated_results.collection, request.user,
                user_info)
        if request.is_ajax():
            is_staff = has_permission(request.user, 'openclose_thread',
                                      course.id)
            return utils.JsonResponse({
                'annotated_content_info':
                annotated_content_info,
                'discussion_data': [
                    utils.prepare_content(thread, course_key, is_staff)
                    for thread in paginated_results.collection
                ],
                'page':
                query_params['page'],
                'num_pages':
                query_params['num_pages'],
            })
        #TODO remove non-AJAX support, it does not appear to be used and does not appear to work.
        else:
            context = {
                'course': course,
                'user': request.user,
                'django_user': User.objects.get(id=user_id),
                'profiled_user': profiled_user.to_dict(),
                'threads': paginated_results.collection,
                'user_info': user_info,
                'annotated_content_info': annotated_content_info,
                #                'content': content,
            }

            return render_to_response('discussion/user_profile.html', context)
    except User.DoesNotExist:
        raise Http404
示例#30
0
def get_threads(request, course, user_info, discussion_id=None, per_page=THREADS_PER_PAGE):
    """
    This may raise an appropriate subclass of cc.utils.CommentClientError
    if something goes wrong, or ValueError if the group_id is invalid.

    Arguments:
        request (WSGIRequest): The user request.
        course (CourseDescriptorWithMixins): The course object.
        user_info (dict): The comment client User object as a dict.
        discussion_id (unicode): Optional discussion id/commentable id for context.
        per_page (int): Optional number of threads per page.

    Returns:
        (tuple of list, dict): A tuple of the list of threads and a dict of the
            query parameters used for the search.

    """
    default_query_params = {
        'page': 1,
        'per_page': per_page,
        'sort_key': 'activity',
        'text': '',
        'course_id': unicode(course.id),
        'user_id': request.user.id,
        'context': ThreadContext.COURSE,
        'group_id': get_group_id_for_comments_service(request, course.id, discussion_id),  # may raise ValueError
    }

    # If provided with a discussion id, filter by discussion id in the
    # comments_service.
    if discussion_id is not None:
        default_query_params['commentable_id'] = discussion_id
        # Use the discussion id/commentable id to determine the context we are going to pass through to the backend.
        if get_team(discussion_id) is not None:
            default_query_params['context'] = ThreadContext.STANDALONE

    if not request.GET.get('sort_key'):
        # If the user did not select a sort key, use their last used sort key
        default_query_params['sort_key'] = user_info.get('default_sort_key') or default_query_params['sort_key']

    elif request.GET.get('sort_key') != user_info.get('default_sort_key'):
        # If the user clicked a sort key, update their default sort key
        cc_user = cc.User.from_django_user(request.user)
        cc_user.default_sort_key = request.GET.get('sort_key')
        cc_user.save()

    # there are 2 dimensions to consider when executing a search with respect to group id
    # is user a moderator
    # did the user request a group

    # if the user requested a group explicitly, give them that group, otherwise, if mod, show all, else if student, use cohort

    if discussion_id:
        is_cohorted = is_commentable_divided(course.id, discussion_id)
    else:
        is_cohorted = is_course_cohorted(course.id)

    if has_permission(request.user, "see_all_cohorts", course.id):
        group_id = request.GET.get('group_id')
        if group_id in ("all", "None"):
            group_id = None
    else:
        group_id = get_cohort_id(request.user, course.id)
        if not group_id:
            default_query_params['exclude_groups'] = True

    if group_id:
        group_id = int(group_id)
        try:
            CourseUserGroup.objects.get(course_id=course.id, id=group_id)
        except CourseUserGroup.DoesNotExist:
            if not is_cohorted:
                group_id = None
            else:
                raise ValueError("Invalid Group ID")
        default_query_params["group_id"] = group_id

    #so by default, a moderator sees all items, and a student sees his cohort

    query_params = merge_dict(
        default_query_params,
        strip_none(
            extract(
                request.GET,
                [
                    'page',
                    'sort_key',
                    'text',
                    'commentable_ids',
                    'flagged',
                    'unread',
                    'unanswered',
                ]
            )
        )
    )

    if not is_cohorted:
        query_params.pop('group_id', None)

    paginated_results = cc.Thread.search(query_params)
    threads = paginated_results.collection

    # If not provided with a discussion id, filter threads by commentable ids
    # which are accessible to the current user.
    if discussion_id is None:
        discussion_category_ids = set(utils.get_discussion_categories_ids(course, request.user))
        threads = [
            thread for thread in threads
            if thread.get('commentable_id') in discussion_category_ids
        ]

    for thread in threads:
        # patch for backward compatibility to comments service
        if 'pinned' not in thread:
            thread['pinned'] = False

    query_params['page'] = paginated_results.page
    query_params['num_pages'] = paginated_results.num_pages
    query_params['corrected_text'] = paginated_results.corrected_text

    return threads, query_params
def render_body(context,**pageargs):
    __M_caller = context.caller_stack._push_frame()
    try:
        __M_locals = __M_dict_builtin(pageargs=pageargs)
        course = context.get('course', UNDEFINED)
        user = context.get('user', UNDEFINED)
        settings = context.get('settings', UNDEFINED)
        __M_writer = context.writer()
        # SOURCE LINE 1
        __M_writer(u'\n')
        # SOURCE LINE 2
        __M_writer(u'\n\n<script type="text/template" id="thread-template">\n    <article class="discussion-article" data-id="')
        # SOURCE LINE 5
        __M_writer(filters.decode.utf8('<%- id %>'))
        __M_writer(u'">\n        <div class="thread-content-wrapper"></div>\n        \n        <ol class="responses">\n            <li class="loading"><div class="loading-animation"></div></li>\n        </ol>\n        <div class="post-status-closed bottom-post-status" style="display: none">\n          ')
        # SOURCE LINE 12
        __M_writer(filters.decode.utf8(_("This thread is closed.")))
        __M_writer(u'\n        </div>\n        <!--\n')
        # SOURCE LINE 15
        if course is UNDEFINED or has_permission(user, 'create_comment', course.id):
            # SOURCE LINE 16
            __M_writer(u'        <form class="discussion-reply-new" data-id="')
            __M_writer(filters.decode.utf8('<%- id %>'))
            __M_writer(u'">\n            <h4>')
            # SOURCE LINE 17
            __M_writer(filters.decode.utf8(_("Start a new discussion:")))
            __M_writer(u'</h4>\n            <ul class="discussion-errors"></ul>\n            <div class="reply-body" data-id="')
            # SOURCE LINE 19
            __M_writer(filters.decode.utf8('<%- id %>'))
            __M_writer(u'"></div>\n            <div class="reply-post-control">\n                <a class="discussion-submit-post control-button" href="#">')
            # SOURCE LINE 21
            __M_writer(filters.decode.utf8(_("Submit")))
            __M_writer(u'</a>\n            </div>\n        </form>\n')
        # SOURCE LINE 25
        __M_writer(u'        -->\n    </article>\n</script>\n\n<script type="text/template" id="thread-show-template">\n  <div class="discussion-post">\n      <div><a href="javascript:void(0)" class="dogear action-follow" data-tooltip="follow"></a></div>\n      <header>\n      ')
        # SOURCE LINE 33
        __M_writer(filters.decode.utf8("<% if (obj.group_id) { %>"))
        __M_writer(u'\n      <div class="group-visibility-label">')
        # SOURCE LINE 34
        __M_writer(filters.decode.utf8("<%- obj.group_string%>"))
        __M_writer(u'</div>\n              ')
        # SOURCE LINE 35
        __M_writer(filters.decode.utf8("<% }  %>"))
        __M_writer(u'      \n      \n          <a href="#" class="vote-btn discussion-vote discussion-vote-up" data-role="discussion-vote" data-tooltip="vote">\n          <span class="plus-icon">+</span> <span class=\'votes-count-number\'>')
        # SOURCE LINE 38
        __M_writer(filters.decode.utf8('<%- votes["up_count"] %>'))
        __M_writer(u'</span></a>\n          <h1>')
        # SOURCE LINE 39
        __M_writer(filters.decode.utf8('<%- title %>'))
        __M_writer(u'</h1>\n          <p class="posted-details">\n              ')
        # SOURCE LINE 41
        __M_writer(filters.decode.utf8("<% if (obj.username) { %>"))
        __M_writer(u'\n              <a href="')
        # SOURCE LINE 42
        __M_writer(filters.decode.utf8('<%- user_url %>'))
        __M_writer(u'" class="username">')
        __M_writer(filters.decode.utf8('<%- username %>'))
        __M_writer(u'</a>\n              ')
        # SOURCE LINE 43
        __M_writer(filters.decode.utf8("<% } else {print('anonymous');} %>"))
        __M_writer(u'\n              <span class="timeago" title="')
        # SOURCE LINE 44
        __M_writer(filters.decode.utf8('<%- created_at %>'))
        __M_writer(u'">')
        __M_writer(filters.decode.utf8('<%- created_at %>'))
        __M_writer(u'</span>\n\n              <span class="post-status-closed top-post-status" style="display: none">\n                ')
        # SOURCE LINE 47
        __M_writer(filters.decode.utf8(_("&bull; This thread is closed.")))
        __M_writer(u'\n              </span>\n          </p>\n      </header>\n\n      <div class="post-body">')
        # SOURCE LINE 52
        __M_writer(filters.decode.utf8('<%- body %>'))
        __M_writer(u'</div>\n      <div class="discussion-flag-abuse notflagged" data-role="thread-flag" data-tooltip="Report Misuse">\n      <i class="icon icon-flag"></i><span class="flag-label">')
        # SOURCE LINE 54
        __M_writer(filters.decode.utf8(_("Report Misuse")))
        __M_writer(u'</span></div>\n        \n        \n')
        # SOURCE LINE 57
        if course and has_permission(user, 'openclose_thread', course.id):
            # SOURCE LINE 58
            __M_writer(u'      <div class="admin-pin discussion-pin notpinned" data-role="thread-pin" data-tooltip="pin this thread">\n      <i class="icon icon-pushpin"></i><span class="pin-label">')
            # SOURCE LINE 59
            __M_writer(filters.decode.utf8(_("Pin Thread")))
            __M_writer(u'</span></div>\n\n')
            # SOURCE LINE 61
        else:
            # SOURCE LINE 62
            __M_writer(u'      ')
            __M_writer(filters.decode.utf8("<% if (pinned) { %>"))
            __M_writer(u'\n      <div class="discussion-pin notpinned" data-role="thread-pin" data-tooltip="pin this thread">\n      <i class="icon icon-pushpin"></i><span class="pin-label">')
            # SOURCE LINE 64
            __M_writer(filters.decode.utf8(_("Pin Thread")))
            __M_writer(u'</span></div>\n      ')
            # SOURCE LINE 65
            __M_writer(filters.decode.utf8("<% }  %>"))
            __M_writer(u'  \n')
        # SOURCE LINE 67
        __M_writer(u'      \n      \n      ')
        # SOURCE LINE 69
        __M_writer(filters.decode.utf8('<% if (obj.courseware_url) { %>'))
        __M_writer(u'\n      <div class="post-context">\n          (this topic is about <a href="')
        # SOURCE LINE 71
        __M_writer(filters.decode.utf8('<%- courseware_url%>'))
        __M_writer(u'">')
        __M_writer(filters.decode.utf8('<%- courseware_title %>'))
        __M_writer(u' Welcome to Pepper</a>)\n      </div>\n      ')
        # SOURCE LINE 73
        __M_writer(filters.decode.utf8('<% } %>'))
        __M_writer(u'\n\n      <ul class="moderator-actions">\n          <li style="display: none"><a class="action-edit" href="javascript:void(0)"><span class="edit-icon"></span> ')
        # SOURCE LINE 76
        __M_writer(filters.decode.utf8(_("Edit")))
        __M_writer(u'</a></li>\n          <li style="display: none"><a class="action-delete" href="javascript:void(0)"><span class="delete-icon"></span> ')
        # SOURCE LINE 77
        __M_writer(filters.decode.utf8(_("Delete")))
        __M_writer(u'</a></li>\n          <li style="display: none"><a class="action-openclose" href="javascript:void(0)"><span class="edit-icon"></span> ')
        # SOURCE LINE 78
        __M_writer(filters.decode.utf8(_("Close")))
        __M_writer(u'</a></li>\n      </ul>\n  </div>\n</script>\n\n<script type="text/template" id="thread-edit-template">\n  <div class="discussion-post edit-post-form">\n    <h1>')
        # SOURCE LINE 85
        __M_writer(filters.decode.utf8(_("Editing topic")))
        __M_writer(u'</h1>\n    <ul class="edit-post-form-errors"></ul>\n    <div class="form-row">\n      <input type="text" class="edit-post-title" name="title" value="')
        # SOURCE LINE 88
        __M_writer(filters.decode.utf8("<%-title %>"))
        __M_writer(u'" placeholder="Title">\n    </div>\n    <div class="form-row">\n      <div class="edit-post-body" name="body">')
        # SOURCE LINE 91
        __M_writer(filters.decode.utf8("<%- body %>"))
        __M_writer(u'</div>\n    </div>\n')
        # SOURCE LINE 98
        __M_writer(u'    <input type="submit" class="post-update" value="')
        __M_writer(filters.decode.utf8(_("Update topic")))
        __M_writer(u'">\n    <a href="#" class="post-cancel">')
        # SOURCE LINE 99
        __M_writer(filters.decode.utf8(_("Cancel")))
        __M_writer(u'</a>\n  </div>\n</script>\n\n<script type="text/template" id="thread-response-template">\n    <div class="discussion-response"></div>\n    <ol class="comments">\n        <li class="new-comment response-local">\n            <form class="comment-form" data-id="')
        # SOURCE LINE 107
        __M_writer(filters.decode.utf8('<%- wmdId %>'))
        __M_writer(u'">\n                <ul class="discussion-errors"></ul>\n                <div class="comment-body" data-id="')
        # SOURCE LINE 109
        __M_writer(filters.decode.utf8('<%- wmdId %>'))
        __M_writer(u'"\n                data-placeholder="Add a comment..."></div>\n                <div class="comment-post-control">\n                    <a class="discussion-submit-comment control-button" href="#">')
        # SOURCE LINE 112
        __M_writer(filters.decode.utf8(_("Submit")))
        __M_writer(u'</a>\n                </div>\n            </form>\n        \n        </li>\n    </ol>\n</script>\n\n<script type="text/template" id="thread-response-show-template">\n    <header class="response-local">\n        <a href="javascript:void(0)" class="vote-btn" data-tooltip="vote"><span class="plus-icon"></span><span class="votes-count-number">')
        # SOURCE LINE 122
        __M_writer(filters.decode.utf8("<%- votes['up_count'] %>"))
        __M_writer(u'</span></a>\n        <a href="javascript:void(0)" class="endorse-btn')
        # SOURCE LINE 123
        __M_writer(filters.decode.utf8('<% if (endorsed) { %> is-endorsed<% } %>'))
        __M_writer(u' action-endorse" style="cursor: default; display: none;" data-tooltip="endorse"><span class="check-icon" style="pointer-events: none; "></span></a>\n        ')
        # SOURCE LINE 124
        __M_writer(filters.decode.utf8("<% if (obj.username) { %>"))
        __M_writer(u'\n        <a href="')
        # SOURCE LINE 125
        __M_writer(filters.decode.utf8('<%- user_url %>'))
        __M_writer(u'" class="posted-by" posted_by_id="')
        __M_writer(filters.decode.utf8('<%- user_id %>'))
        __M_writer(u'">')
        __M_writer(filters.decode.utf8('<%- username %>'))
        __M_writer(u'</a>\n        ')
        # SOURCE LINE 126
        __M_writer(filters.decode.utf8("<% } else {print('<span class=\"anonymous\"><em>anonymous</em></span>');} %>"))
        __M_writer(u'\n        <p class="posted-details" title="')
        # SOURCE LINE 127
        __M_writer(filters.decode.utf8('<%- created_at %>'))
        __M_writer(u'">')
        __M_writer(filters.decode.utf8('<%- created_at %>'))
        __M_writer(u'</p>\n    </header>\n    <div class="response-local"><div class="response-body">')
        # SOURCE LINE 129
        __M_writer(filters.decode.utf8("<%- body %>"))
        __M_writer(u'</div>\n    <div class="discussion-flag-abuse notflagged" data-role="thread-flag" data-tooltip="report misuse">                \n      <i class="icon icon-flag"></i><span class="flag-label">Report Misuse</span></div>\n    </div>\n    <ul class="moderator-actions response-local">\n        <li style="display: none"><a class="action-edit" href="javascript:void(0)"><span class="edit-icon"></span> ')
        # SOURCE LINE 134
        __M_writer(filters.decode.utf8(_("Edit")))
        __M_writer(u'</a></li>\n        <li style="display: none"><a class="action-delete" href="javascript:void(0)"><span class="delete-icon"></span> ')
        # SOURCE LINE 135
        __M_writer(filters.decode.utf8(_("Delete")))
        __M_writer(u'</a></li>\n        <li style="display: none"><a class="action-openclose" href="javascript:void(0)"><span class="edit-icon"></span> ')
        # SOURCE LINE 136
        __M_writer(filters.decode.utf8(_("Close")))
        __M_writer(u'</a></li>\n    </ul>\n</script>\n\n<script type="text/template" id="thread-response-edit-template">\n  <div class="edit-post-form">\n    <h1>')
        # SOURCE LINE 142
        __M_writer(filters.decode.utf8(_("Editing response")))
        __M_writer(u'</h1>\n    <ul class="edit-post-form-errors"></ul>\n    <div class="form-row">\n      <div class="edit-post-body" name="body">')
        # SOURCE LINE 145
        __M_writer(filters.decode.utf8("<%- body %>"))
        __M_writer(u'</div>\n    </div>\n    <input type="submit" class="post-update" value="')
        # SOURCE LINE 147
        __M_writer(filters.decode.utf8(_("Update response")))
        __M_writer(u'">\n    <a href="#" class="post-cancel">')
        # SOURCE LINE 148
        __M_writer(filters.decode.utf8(_("Cancel")))
        __M_writer(u'</a>\n  </div>\n</script>\n\n<script type="text/template" id="response-comment-show-template">\n  <a id="a')
        # SOURCE LINE 153
        __M_writer(filters.decode.utf8('<%- id %>'))
        __M_writer(u'"></a>\n  <div id="comment_')
        # SOURCE LINE 154
        __M_writer(filters.decode.utf8('<%- id %>'))
        __M_writer(u'">\n    <div class="response-body">')
        # SOURCE LINE 155
        __M_writer(filters.decode.utf8('<%- body %>'))
        __M_writer(u'</div>\n    <div class="discussion-flag-abuse notflagged" data-role="thread-flag" data-tooltip="Report Misuse">                \n      <i class="icon icon-flag"></i><span class="flag-label"></span></div> \n    <p class="posted-details">&ndash;posted <span class="timeago" title="')
        # SOURCE LINE 158
        __M_writer(filters.decode.utf8('<%- created_at %>'))
        __M_writer(u'">')
        __M_writer(filters.decode.utf8('<%- created_at %>'))
        __M_writer(u'</span> by\n        ')
        # SOURCE LINE 159
        __M_writer(filters.decode.utf8("<% if (obj.username) { %>"))
        __M_writer(u'\n        <a href="')
        # SOURCE LINE 160
        __M_writer(filters.decode.utf8('<%- user_url %>'))
        __M_writer(u'" class="profile-link">')
        __M_writer(filters.decode.utf8('<%- username %>'))
        __M_writer(u'</a>\n        ')
        # SOURCE LINE 161
        __M_writer(filters.decode.utf8("<% } else {print('anonymous');} %>"))
        __M_writer(u'\n    </p>\n    <div class="discussion-response')
        # SOURCE LINE 163
        __M_writer(filters.decode.utf8('<%- id %>'))
        __M_writer(u'"></div>\n    <div style="padding-left:20px;">\n     <ul class="moderator-actions response-local">\n          <li style="display: none"><a class="action-edit" href="javascript:void(0)"><span class="edit-icon"></span> ')
        # SOURCE LINE 166
        __M_writer(filters.decode.utf8(_("Edit")))
        __M_writer(u'</a></li>\n          <li style="display: none"><a class="action-delete" href="javascript:void(0)"><span class="delete-icon"></span> ')
        # SOURCE LINE 167
        __M_writer(filters.decode.utf8(_("Delete")))
        __M_writer(u'</a></li>\n          <!--<li style="display: none"><a class="action-openclose" href="javascript:void(0)"><span class="edit-icon"></span> ')
        # SOURCE LINE 168
        __M_writer(filters.decode.utf8(_("Close")))
        __M_writer(u'</a></li>\n          -->\n      </ul>\n     </div>\n  </div>\n</script>\n\n<script type="text/template" id="thread-list-item-template">\n    <a href="')
        # SOURCE LINE 176
        __M_writer(filters.decode.utf8('<%- id %>'))
        __M_writer(u'" data-id="')
        __M_writer(filters.decode.utf8('<%- id %>'))
        __M_writer(u'">\n        <span class="title">')
        # SOURCE LINE 177
        __M_writer(filters.decode.utf8("<%- title %>"))
        __M_writer(u'</span>\n        ')
        # SOURCE LINE 178
        __M_writer(filters.decode.utf8("<% if (unread_comments_count > 0) { %>"))
        __M_writer(u'\n            <span class="comments-count unread" data-tooltip="')
        # SOURCE LINE 179
        __M_writer(filters.decode.utf8("<%- unread_comments_count %>"))
        __M_writer(u' new comment')
        __M_writer(filters.decode.utf8("<%- unread_comments_count > 1 ? 's' : '' %>"))
        __M_writer(u'">')
        __M_writer(filters.decode.utf8("<%- comments_count %>"))
        __M_writer(u'</span>\n        ')
        # SOURCE LINE 180
        __M_writer(filters.decode.utf8("<% } else { %>"))
        __M_writer(u'\n            <span class="comments-count">')
        # SOURCE LINE 181
        __M_writer(filters.decode.utf8("<%- comments_count %>"))
        __M_writer(u'</span>\n        ')
        # SOURCE LINE 182
        __M_writer(filters.decode.utf8("<% } %>"))
        __M_writer(u'\n        <span class="votes-count">+')
        # SOURCE LINE 183
        __M_writer(filters.decode.utf8("<%- votes['up_count'] %>"))
        __M_writer(u'</span>\n    </a>\n</script>\n<script type="text/template" id="discussion-home">\n  <style>\n    [class^="icon-"], [class*=" icon-"] {font-family: "FontAwesome" !important;}\n  </style>\n  <div class="discussion-article blank-slate">\n  <section class="home-header">\n  <span class="label">DISCUSSION HOME:</span>\n')
        # SOURCE LINE 193
        if course and course.display_name_with_default:
            # SOURCE LINE 194
            __M_writer(u'  <h1 class="home-title">')
            __M_writer(filters.decode.utf8(course.display_name_with_default))
            __M_writer(u'</h1>\n  </section>\n  \n')
            # SOURCE LINE 197
            if settings.MITX_FEATURES.get('ENABLE_DISCUSSION_HOME_PANEL'):
                # SOURCE LINE 198
                __M_writer(u'  <span class="label label-settings">HOW TO USE PEPPER DISCUSSIONS</span>\n  <table class="home-helpgrid">\n  <tr class="helpgrid-row helpgrid-row-navigation">\n  <td class="row-title">Find discussions</td>\n  <td class="row-item">\n  <i class="icon icon-reorder"></i>\n  <span class="row-description">Focus in on specific topics</span>\n  </td>\n  <td class="row-item">\n  <i class="icon icon-search"></i>\n  <span class="row-description">Search for specific topics </span>\n  </td>\n  <td class="row-item">\n  <i class="icon icon-sort"></i>\n  <span class="row-description">Sort by date, vote, or comments </span>\n  </td>\n  </tr>\n  <tr class="helpgrid-row helpgrid-row-participation">\n  <td class="row-title">Engage with topics</td>\n  <td class="row-item">\n  <i class="icon icon-plus"></i>\n  <span class="row-description">Upvote topics and good responses</span>\n  </td>\n  <td class="row-item">\n  <i class="icon icon-flag"></i>\n  <span class="row-description">Report Forum Misuse</span>\n  </td>\n  <td class="row-item">\n  <i class="icon icon-star"></i>\n  <span class="row-description">Follow topics for updates</span>\n  </td>\n  </tr>\n  <!--\n  <tr class="helpgrid-row helpgrid-row-notification">\n  <td class="row-title">Receive updates</td>\n  <td class="row-item-full" colspan="3">\n  <input type="checkbox" class="email-setting" name="email-notification"></input>\n  <i class="icon icon-envelope"></i>\n  <span class="row-description"> If enabled, you will receive an email digest once a day notifying you about new, unread activity from posts you are following. </span>\n  </td>\n  </tr>\n  -->\n  </table>\n  <!--20151124 add new information-->\n  <!--begin-->\n  </br>\n  <table class="home-helpgrid">\n  <tr>\n  <td style="background-color:#b0e0e6;">\n    <p style="padding:8px;">Let\'s use the discussion forum to coach and collaborate with your fellow Pepper</br>educators. <b>If you have a support issue, please contact us at </b><a href="mailto:[email protected]" style="font-color:#1ea9dc;">[email protected]</a>. Support-related items will be removed from the board.</p>\n  </td>\n  </tr>\n  </table>\n  <!--end-->\n')
        # SOURCE LINE 254
        __M_writer(u'\n  </div>    \n</script>\n')
        return ''
    finally:
        context.caller_stack._pop_frame()
示例#32
0
def single_thread(request, course_key, discussion_id, thread_id):
    """
    Renders a response to display a single discussion thread.
    """
    nr_transaction = newrelic.agent.current_transaction()

    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    course_settings = make_course_settings(course, request.user)
    cc_user = cc.User.from_django_user(request.user)
    user_info = cc_user.to_dict()
    is_moderator = has_permission(request.user, "see_all_cohorts", course_key)

    # 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 error:
        if error.status_code == 404:
            raise Http404
        raise

    # Verify that the student has access to this thread if belongs to a course discussion module
    thread_context = getattr(thread, "context", "course")
    if thread_context == "course" and not utils.discussion_category_id_access(course, request.user, discussion_id):
        raise Http404

    # verify that the thread belongs to the requesting student's cohort
    if is_commentable_cohorted(course_key, discussion_id) and not is_moderator:
        user_group_id = get_cohort_id(request.user, course_key)
        if getattr(thread, "group_id", None) is not None and user_group_id != thread.group_id:
            raise Http404

    is_staff = has_permission(request.user, 'openclose_thread', course.id)
    if request.is_ajax():
        with newrelic.agent.FunctionTrace(nr_transaction, "get_annotated_content_infos"):
            annotated_content_info = utils.get_annotated_content_infos(
                course_key,
                thread,
                request.user,
                user_info=user_info
            )
        content = utils.prepare_content(thread.to_dict(), course_key, is_staff)
        with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
            add_courseware_context([content], course, request.user)
        return utils.JsonResponse({
            'content': content,
            'annotated_content_info': annotated_content_info,
        })

    else:
        try:
            threads, query_params = get_threads(request, course)
        except ValueError:
            return HttpResponseBadRequest("Invalid group_id")
        threads.append(thread.to_dict())

        with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
            add_courseware_context(threads, course, request.user)

        for thread in threads:
            # patch for backward compatibility with comments service
            if "pinned" not in thread:
                thread["pinned"] = False

        threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]

        with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
            annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

        with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"):
            user_cohort = get_cohort_id(request.user, course_key)

        context = {
            'discussion_id': discussion_id,
            'csrf': csrf(request)['csrf_token'],
            'init': '',   # TODO: What is this?
            'user_info': user_info,
            'can_create_comment': has_permission(request.user, "create_comment", course.id),
            'can_create_subcomment': has_permission(request.user, "create_sub_comment", course.id),
            'can_create_thread': has_permission(request.user, "create_thread", course.id),
            'annotated_content_info': 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': threads,
            'roles': 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': bool(
                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': course_settings,
            'disable_courseware_js': True,
            'uses_pattern_library': True,
        }
        return render_to_response('discussion/discussion_board.html', context)
示例#33
0
def get_threads(request,
                course,
                user_info,
                discussion_id=None,
                per_page=THREADS_PER_PAGE):
    """
    This may raise an appropriate subclass of cc.utils.CommentClientError
    if something goes wrong, or ValueError if the group_id is invalid.

    Arguments:
        request (WSGIRequest): The user request.
        course (CourseDescriptorWithMixins): The course object.
        user_info (dict): The comment client User object as a dict.
        discussion_id (unicode): Optional discussion id/commentable id for context.
        per_page (int): Optional number of threads per page.

    Returns:
        (tuple of list, dict): A tuple of the list of threads and a dict of the
            query parameters used for the search.

    """
    default_query_params = {
        'page':
        1,
        'per_page':
        per_page,
        'sort_key':
        'activity',
        'text':
        '',
        'course_id':
        unicode(course.id),
        'user_id':
        request.user.id,
        'context':
        ThreadContext.COURSE,
        'group_id':
        get_group_id_for_comments_service(
            request, course.id, discussion_id),  # may raise ValueError
    }

    # If provided with a discussion id, filter by discussion id in the
    # comments_service.
    if discussion_id is not None:
        default_query_params['commentable_id'] = discussion_id
        # Use the discussion id/commentable id to determine the context we are going to pass through to the backend.
        if get_team(discussion_id) is not None:
            default_query_params['context'] = ThreadContext.STANDALONE

    if not request.GET.get('sort_key'):
        # If the user did not select a sort key, use their last used sort key
        default_query_params['sort_key'] = user_info.get(
            'default_sort_key') or default_query_params['sort_key']

    elif request.GET.get('sort_key') != user_info.get('default_sort_key'):
        # If the user clicked a sort key, update their default sort key
        cc_user = cc.User.from_django_user(request.user)
        cc_user.default_sort_key = request.GET.get('sort_key')
        cc_user.save()

    #there are 2 dimensions to consider when executing a search with respect to group id
    #is user a moderator
    #did the user request a group

    # if the user requested a group explicitly, give them that group, otherwise, if mod, show all, else if student, use cohort

    if discussion_id:
        is_cohorted = is_commentable_divided(course.id, discussion_id)
    else:
        is_cohorted = is_course_cohorted(course.id)

    if has_permission(request.user, "see_all_cohorts", course.id):
        group_id = request.GET.get('group_id')
        if group_id in ("all", "None"):
            group_id = None
    else:
        group_id = get_cohort_id(request.user, course.id)
        if not group_id:
            default_query_params['exclude_groups'] = True

    if group_id:
        group_id = int(group_id)
        try:
            CourseUserGroup.objects.get(course_id=course.id, id=group_id)
        except CourseUserGroup.DoesNotExist:
            if not is_cohorted:
                group_id = None
            else:
                raise ValueError("Invalid Group ID")
        default_query_params["group_id"] = group_id

    #so by default, a moderator sees all items, and a student sees his cohort

    query_params = default_query_params.copy()
    query_params.update(
        strip_none(
            extract(request.GET, [
                'page',
                'sort_key',
                'text',
                'commentable_ids',
                'flagged',
                'unread',
                'unanswered',
            ])))

    if not is_cohorted:
        query_params.pop('group_id', None)

    paginated_results = cc.Thread.search(query_params)
    threads = paginated_results.collection

    # If not provided with a discussion id, filter threads by commentable ids
    # which are accessible to the current user.
    if discussion_id is None:
        discussion_category_ids = set(
            utils.get_discussion_categories_ids(course, request.user))
        threads = [
            thread for thread in threads
            if thread.get('commentable_id') in discussion_category_ids
        ]

    for thread in threads:
        # patch for backward compatibility to comments service
        if 'pinned' not in thread:
            thread['pinned'] = False

    query_params['page'] = paginated_results.page
    query_params['num_pages'] = paginated_results.num_pages
    query_params['corrected_text'] = paginated_results.corrected_text

    return threads, query_params
示例#34
0
def create_user_profile_context(request, course_key, user_id):
    """ Generate a context dictionary for the user profile. """
    user = cc.User.from_django_user(request.user)
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)

    # If user is not enrolled in the course, do not proceed.
    django_user = User.objects.get(id=user_id)
    if not CourseEnrollment.is_enrolled(django_user, course.id):
        raise Http404

    query_params = {
        'page': request.GET.get('page', 1),
        'per_page': THREADS_PER_PAGE,   # more than threads_per_page to show more activities
    }

    group_id = get_group_id_for_comments_service(request, course_key)
    if group_id is not None:
        query_params['group_id'] = group_id
        profiled_user = cc.User(id=user_id, course_id=course_key, group_id=group_id)
    else:
        profiled_user = cc.User(id=user_id, course_id=course_key)

    threads, page, num_pages = profiled_user.active_threads(query_params)
    query_params['page'] = page
    query_params['num_pages'] = num_pages

    with function_trace("get_metadata_for_threads"):
        user_info = cc.User.from_django_user(request.user).to_dict()
        annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

    is_staff = has_permission(request.user, 'openclose_thread', course.id)
    threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]
    with function_trace("add_courseware_context"):
        add_courseware_context(threads, course, request.user)

        # TODO: LEARNER-3854: If we actually implement Learner Analytics code, this
        #   code was original protected to not run in user_profile() if is_ajax().
        #   Someone should determine if that is still necessary (i.e. was that ever
        #   called as is_ajax()) and clean this up as necessary.
        user_roles = django_user.roles.filter(
            course_id=course.id
        ).order_by("name").values_list("name", flat=True).distinct()

        with function_trace("get_cohort_info"):
            course_discussion_settings = get_course_discussion_settings(course_key)
            user_group_id = get_group_id_for_user(request.user, course_discussion_settings)

        context = _create_base_discussion_view_context(request, course_key)
        context.update({
            'django_user': django_user,
            'django_user_roles': user_roles,
            'profiled_user': profiled_user.to_dict(),
            'threads': threads,
            'user_group_id': user_group_id,
            'annotated_content_info': annotated_content_info,
            'page': query_params['page'],
            'num_pages': query_params['num_pages'],
            'sort_preference': user.default_sort_key,
            'learner_profile_page_url': reverse('learner_profile', kwargs={'username': django_user.username}),
        })
        return context
示例#35
0
def inline_discussion(request, course_key, discussion_id):
    """
    Renders JSON for DiscussionModules
    """

    with function_trace('get_course_and_user_info'):
        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=True)
        cc_user = cc.User.from_django_user(request.user)
        user_info = cc_user.to_dict()

    try:
        with function_trace('get_threads'):
            threads, query_params = get_threads(
                request,
                course,
                user_info,
                discussion_id,
                per_page=INLINE_THREADS_PER_PAGE)
    except ValueError:
        return HttpResponseServerError('Invalid group_id')

    with function_trace('get_metadata_for_threads'):
        annotated_content_info = utils.get_metadata_for_threads(
            course_key, threads, request.user, user_info)

    with function_trace('determine_group_permissions'):
        is_staff = has_permission(request.user, 'openclose_thread', course.id)
        course_discussion_settings = get_course_discussion_settings(course.id)
        group_names_by_id = get_group_names_by_id(course_discussion_settings)
        course_is_divided = course_discussion_settings.division_scheme is not CourseDiscussionSettings.NONE

    with function_trace('prepare_content'):
        threads = [
            utils.prepare_content(thread, course_key, is_staff,
                                  course_is_divided, group_names_by_id)
            for thread in threads
        ]

    return utils.JsonResponse({
        'is_commentable_divided':
        is_commentable_divided(course_key, discussion_id),
        'discussion_data':
        threads,
        'user_info':
        user_info,
        'user_group_id':
        get_group_id_for_user(request.user, course_discussion_settings),
        'annotated_content_info':
        annotated_content_info,
        'page':
        query_params['page'],
        'num_pages':
        query_params['num_pages'],
        'roles':
        utils.get_role_ids(course_key),
        'course_settings':
        make_course_settings(course, request.user, False)
    })
示例#36
0
def _create_discussion_board_context(request, base_context, thread=None):
    """
    Returns the template context for rendering the discussion board.
    """
    context = base_context.copy()
    course = context['course']
    course_key = course.id
    thread_id = thread.id if thread else None
    discussion_id = thread.commentable_id if thread else None
    course_settings = context['course_settings']
    user = context['user']
    cc_user = cc.User.from_django_user(user)
    user_info = context['user_info']
    if thread:

        # Since we're in page render mode, and the discussions UI will request the thread list itself,
        # we need only return the thread information for this one.
        threads = [thread.to_dict()]

        for thread in threads:
            # patch for backward compatibility with comments service
            if "pinned" not in thread:
                thread["pinned"] = False
        thread_pages = 1
        root_url = reverse('forum_form_discussion', args=[unicode(course.id)])
    else:
        threads, query_params = get_threads(request, course, user_info)   # This might process a search query
        thread_pages = query_params['num_pages']
        root_url = request.path
    is_staff = has_permission(user, 'openclose_thread', course.id)
    threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]

    with function_trace("get_metadata_for_threads"):
        annotated_content_info = utils.get_metadata_for_threads(course_key, threads, user, user_info)

    with function_trace("add_courseware_context"):
        add_courseware_context(threads, course, user)

    with function_trace("get_cohort_info"):
        course_discussion_settings = get_course_discussion_settings(course_key)
        user_group_id = get_group_id_for_user(user, course_discussion_settings)

    context.update({
        'root_url': root_url,
        'discussion_id': discussion_id,
        'thread_id': thread_id,
        'threads': threads,
        'thread_pages': thread_pages,
        'annotated_content_info': annotated_content_info,
        'is_moderator': has_permission(user, "see_all_cohorts", course_key),
        'groups': course_settings["groups"],  # still needed to render _thread_list_template
        'user_group_id': user_group_id,  # read from container in NewPostView
        'sort_preference': cc_user.default_sort_key,
        'category_map': course_settings["category_map"],
        'course_settings': course_settings,
        'is_commentable_divided': is_commentable_divided(course_key, discussion_id, course_discussion_settings),
        # If the default topic id is None the front-end code will look for a topic that contains "General"
        'discussion_default_topic_id': _get_discussion_default_topic_id(course),
    })
    context.update(
        get_experiment_user_metadata_context(
            course,
            user,
        )
    )
    return context
示例#37
0
def followed_threads(request, course_key, user_id):
    """
    Ajax-only endpoint retrieving the threads followed by a specific user.
    """
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    try:
        profiled_user = cc.User(id=user_id, course_id=course_key)

        query_params = {
            'page': 1,
            'per_page': THREADS_PER_PAGE,   # more than threads_per_page to show more activities
            'sort_key': 'date',
        }
        query_params.update(
            strip_none(
                extract(
                    request.GET,
                    [
                        'page',
                        'sort_key',
                        'flagged',
                        'unread',
                        'unanswered',
                    ]
                )
            )
        )

        try:
            group_id = get_group_id_for_comments_service(request, course_key)
        except ValueError:
            return HttpResponseServerError("Invalid group_id")
        if group_id is not None:
            query_params['group_id'] = group_id

        paginated_results = profiled_user.subscribed_threads(query_params)
        print "\n \n \n paginated results \n \n \n "
        print paginated_results
        query_params['page'] = paginated_results.page
        query_params['num_pages'] = paginated_results.num_pages
        user_info = cc.User.from_django_user(request.user).to_dict()

        with function_trace("get_metadata_for_threads"):
            annotated_content_info = utils.get_metadata_for_threads(
                course_key,
                paginated_results.collection,
                request.user, user_info
            )
        if request.is_ajax():
            is_staff = has_permission(request.user, 'openclose_thread', course.id)
            return utils.JsonResponse({
                'annotated_content_info': annotated_content_info,
                'discussion_data': [
                    utils.prepare_content(thread, course_key, is_staff) for thread in paginated_results.collection
                ],
                'page': query_params['page'],
                'num_pages': query_params['num_pages'],
            })
        #TODO remove non-AJAX support, it does not appear to be used and does not appear to work.
        else:
            context = {
                'course': course,
                'user': request.user,
                'django_user': User.objects.get(id=user_id),
                'profiled_user': profiled_user.to_dict(),
                'threads': paginated_results.collection,
                'user_info': user_info,
                'annotated_content_info': annotated_content_info,
                #                'content': content,
            }

            return render_to_response('discussion/user_profile.html', context)
    except User.DoesNotExist:
        raise Http404
示例#38
0
def _create_discussion_board_context(request, base_context, thread=None):
    """
    Returns the template context for rendering the discussion board.
    """
    context = base_context.copy()
    course = context['course']
    course_key = course.id
    thread_id = thread.id if thread else None
    discussion_id = thread.commentable_id if thread else None
    course_settings = context['course_settings']
    user = context['user']
    cc_user = cc.User.from_django_user(user)
    user_info = context['user_info']
    if thread:

        # Since we're in page render mode, and the discussions UI will request the thread list itself,
        # we need only return the thread information for this one.
        threads = [thread.to_dict()]

        for thread in threads:
            # patch for backward compatibility with comments service
            if "pinned" not in thread:
                thread["pinned"] = False
        thread_pages = 1
        root_url = reverse('forum_form_discussion', args=[unicode(course.id)])
    else:
        threads, query_params = get_threads(
            request, course, user_info)  # This might process a search query
        thread_pages = query_params['num_pages']
        root_url = request.path
    is_staff = has_permission(user, 'openclose_thread', course.id)
    threads = [
        utils.prepare_content(thread, course_key, is_staff)
        for thread in threads
    ]

    with newrelic_function_trace("get_metadata_for_threads"):
        annotated_content_info = utils.get_metadata_for_threads(
            course_key, threads, user, user_info)

    with newrelic_function_trace("add_courseware_context"):
        add_courseware_context(threads, course, user)

    with newrelic_function_trace("get_cohort_info"):
        course_discussion_settings = get_course_discussion_settings(course_key)
        user_group_id = get_group_id_for_user(user, course_discussion_settings)

    context.update({
        'root_url':
        root_url,
        'discussion_id':
        discussion_id,
        'thread_id':
        thread_id,
        'threads':
        threads,
        'thread_pages':
        thread_pages,
        'annotated_content_info':
        annotated_content_info,
        'is_moderator':
        has_permission(user, "see_all_cohorts", course_key),
        'groups':
        course_settings[
            "groups"],  # still needed to render _thread_list_template
        'user_group_id':
        user_group_id,  # read from container in NewPostView
        'sort_preference':
        cc_user.default_sort_key,
        'category_map':
        course_settings["category_map"],
        'course_settings':
        course_settings,
        'is_commentable_divided':
        is_commentable_divided(course_key, discussion_id,
                               course_discussion_settings),
        # TODO: (Experimental Code). See https://openedx.atlassian.net/wiki/display/RET/2.+In-course+Verification+Prompts
        'upgrade_link':
        check_and_get_upgrade_link(request, user, course.id),
        'upgrade_price':
        get_cosmetic_verified_display_price(course),
        # ENDTODO
        # If the default topic id is None the front-end code will look for a topic that contains "General"
        'discussion_default_topic_id':
        _get_discussion_default_topic_id(course),
    })
    return context
def render_body(context, **pageargs):
    __M_caller = context.caller_stack._push_frame()
    try:
        __M_locals = __M_dict_builtin(pageargs=pageargs)
        can_create_subcomment = context.get('can_create_subcomment', UNDEFINED)
        sort_preference = context.get('sort_preference', UNDEFINED)
        course = context.get('course', UNDEFINED)
        getattr = context.get('getattr', UNDEFINED)
        can_create_comment = context.get('can_create_comment', UNDEFINED)
        flag_moderator = context.get('flag_moderator', UNDEFINED)
        user_group_id = context.get('user_group_id', UNDEFINED)
        course_expiration_fragment = context.get('course_expiration_fragment',
                                                 UNDEFINED)
        user = context.get('user', UNDEFINED)
        __M_writer = context.writer()
        __M_writer(u'\n')
        __M_writer(u'\n\n')
        __M_writer(u'\n\n')
        __M_writer(
            u'\n\n<section class="discussion discussion-board page-content-container" id="discussion-container"\n         data-course-id="'
        )
        __M_writer(filters.html_escape(filters.decode.utf8(course.id)))
        __M_writer(u'"\n         data-user-create-comment="')
        __M_writer(
            filters.html_escape(
                filters.decode.utf8(json.dumps(can_create_comment))))
        __M_writer(u'"\n         data-user-create-subcomment="')
        __M_writer(
            filters.html_escape(
                filters.decode.utf8(json.dumps(can_create_subcomment))))
        __M_writer(
            u'"\n         data-read-only="false"\n         data-sort-preference="'
        )
        __M_writer(filters.html_escape(filters.decode.utf8(sort_preference)))
        __M_writer(u'"\n         data-flag-moderator="')
        __M_writer(
            filters.html_escape(filters.decode.utf8(
                json.dumps(flag_moderator))))
        __M_writer(u'"\n         data-user-group-id="')
        __M_writer(filters.html_escape(filters.decode.utf8(user_group_id)))
        __M_writer(u'">\n    <header class="page-header has-secondary">\n')
        __M_writer(
            u'        <div class="page-header-main">\n            <nav aria-label="'
        )
        __M_writer(filters.html_escape(filters.decode.utf8(_('Discussions'))))
        __M_writer(
            u'" class="sr-is-focusable" tabindex="-1">\n                <div class="has-breadcrumbs"></div>\n            </nav>\n        </div>\n        <div class="page-header-secondary">\n'
        )
        if has_permission(user, 'create_thread', course.id):
            __M_writer(
                u'            <div class="forum-actions">\n                <button class="btn btn-outline-primary btn-small new-post-btn">'
            )
            __M_writer(
                filters.html_escape(filters.decode.utf8(_("Add a Post"))))
            __M_writer(u'</button>\n            </div>\n')
        __M_writer(
            u'            <div class="forum-search"></div>\n        </div>\n    </header>\n'
        )
        if course_expiration_fragment:
            __M_writer(u'        ')
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        HTML(course_expiration_fragment.content))))
            __M_writer(u'\n')
        __M_writer(u'    <div class="page-content"\n')
        if getattr(course, 'language'):
            __M_writer(u'        lang="')
            __M_writer(
                filters.html_escape(filters.decode.utf8(course.language)))
            __M_writer(u'"\n')
        __M_writer(
            u'    >\n        <div class="discussion-body">\n            <main id="main" class="discussion-column" aria-label="Content" tabindex="-1">\n                <article class="new-post-article is-hidden" style="display: none" tabindex="-1" aria-label="'
        )
        __M_writer(
            filters.html_escape(filters.decode.utf8(_("New topic form"))))
        __M_writer(
            u'"></article>\n                <div class="forum-content"></div>\n            </main>\n            <aside class="forum-nav" role="complementary" aria-label="'
        )
        __M_writer(
            filters.html_escape(
                filters.decode.utf8(_("Discussion thread list"))))
        __M_writer(u'">\n                ')
        runtime._include_file(context, u'_filter_dropdown.html', _template_uri)
        __M_writer(
            u'\n                <div class="discussion-thread-list-container"></div>\n            </aside>\n        </div>\n    </div>\n</section>\n\n'
        )
        runtime._include_file(context, u'_underscore_templates.html',
                              _template_uri)
        __M_writer(u'\n')
        runtime._include_file(context, u'_thread_list_template.html',
                              _template_uri)
        __M_writer(u'\n')
        return ''
    finally:
        context.caller_stack._pop_frame()
示例#40
0
def create_user_profile_context(request, course_key, user_id):
    """ Generate a context dictionary for the user profile. """
    user = cc.User.from_django_user(request.user)
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)

    # If user is not enrolled in the course, do not proceed.
    django_user = User.objects.get(id=user_id)
    if not CourseEnrollment.is_enrolled(django_user, course.id):
        raise Http404

    query_params = {
        'page': request.GET.get('page', 1),
        'per_page': THREADS_PER_PAGE,   # more than threads_per_page to show more activities
    }

    group_id = get_group_id_for_comments_service(request, course_key)
    if group_id is not None:
        query_params['group_id'] = group_id
        profiled_user = cc.User(id=user_id, course_id=course_key, group_id=group_id)
    else:
        profiled_user = cc.User(id=user_id, course_id=course_key)

    threads, page, num_pages = profiled_user.active_threads(query_params)
    query_params['page'] = page
    query_params['num_pages'] = num_pages

    with function_trace("get_metadata_for_threads"):
        user_info = cc.User.from_django_user(request.user).to_dict()
        annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

    is_staff = has_permission(request.user, 'openclose_thread', course.id)
    threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]
    with function_trace("add_courseware_context"):
        add_courseware_context(threads, course, request.user)

        # TODO: LEARNER-3854: If we actually implement Learner Analytics code, this
        #   code was original protected to not run in user_profile() if is_ajax().
        #   Someone should determine if that is still necessary (i.e. was that ever
        #   called as is_ajax()) and clean this up as necessary.
        user_roles = django_user.roles.filter(
            course_id=course.id
        ).order_by("name").values_list("name", flat=True).distinct()

        with function_trace("get_cohort_info"):
            course_discussion_settings = get_course_discussion_settings(course_key)
            user_group_id = get_group_id_for_user(request.user, course_discussion_settings)

        context = _create_base_discussion_view_context(request, course_key)
        context.update({
            'django_user': django_user,
            'django_user_roles': user_roles,
            'profiled_user': profiled_user.to_dict(),
            'threads': threads,
            'user_group_id': user_group_id,
            'annotated_content_info': annotated_content_info,
            'page': query_params['page'],
            'num_pages': query_params['num_pages'],
            'sort_preference': user.default_sort_key,
            'learner_profile_page_url': reverse('learner_profile', kwargs={'username': django_user.username}),
        })
        return context
示例#41
0
文件: views.py 项目: gopinath81/vmss
def forum_form_discussion(request, course_key):
    """
    Renders the main Discussion page, potentially filtered by a search query
    """
    nr_transaction = newrelic.agent.current_transaction()

    course = get_course_with_access(request.user,
                                    'load',
                                    course_key,
                                    check_if_enrolled=True)
    course_settings = make_course_settings(course, request.user)

    user = cc.User.from_django_user(request.user)
    user_info = user.to_dict()

    try:
        unsafethreads, query_params = get_threads(
            request, course, user_info)  # This might process a search query
        is_staff = 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', {
            'disable_courseware_js': True,
            'uses_pattern_library': True,
        })
    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, request.user)

    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':
            bool(has_access(request.user, 'staff', course)),
            'threads':
            threads,
            'thread_pages':
            query_params['num_pages'],
            'user_info':
            user_info,
            'can_create_comment':
            has_permission(request.user, "create_comment", course.id),
            'can_create_subcomment':
            has_permission(request.user, "create_sub_comment", course.id),
            'can_create_thread':
            has_permission(request.user, "create_thread", course.id),
            'flag_moderator':
            bool(
                has_permission(request.user, 'openclose_thread', course.id)
                or has_access(request.user, 'staff', course)),
            'annotated_content_info':
            annotated_content_info,
            'course_id':
            course.id.to_deprecated_string(),
            'roles':
            utils.get_role_ids(course_key),
            'is_moderator':
            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':
            course_settings,
            'disable_courseware_js':
            True,
            'uses_pattern_library':
            True,
        }
        # print "start rendering.."
        return render_to_response('discussion/discussion_board.html', context)
示例#42
0
文件: views.py 项目: gopinath81/vmss
def single_thread(request, course_key, discussion_id, thread_id):
    """
    Renders a response to display a single discussion thread.  This could either be a page refresh
    after navigating to a single thread, a direct link to a single thread, or an AJAX call from the
    discussions UI loading the responses/comments for a single thread.

    Depending on the HTTP headers, we'll adjust our response accordingly.
    """
    nr_transaction = newrelic.agent.current_transaction()

    course = get_course_with_access(request.user,
                                    'load',
                                    course_key,
                                    check_if_enrolled=True)
    course_settings = make_course_settings(course, request.user)
    cc_user = cc.User.from_django_user(request.user)
    user_info = cc_user.to_dict()
    is_moderator = has_permission(request.user, "see_all_cohorts", course_key)
    is_staff = has_permission(request.user, 'openclose_thread', course.id)

    try:
        thread = cc.Thread.find(thread_id).retrieve(
            with_responses=request.is_ajax(),
            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 error:
        if error.status_code == 404:
            raise Http404
        raise

    # Verify that the student has access to this thread if belongs to a course discussion module
    thread_context = getattr(thread, "context", "course")
    if thread_context == "course" and not utils.discussion_category_id_access(
            course, request.user, discussion_id):
        raise Http404

    # 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

    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, request.user)

        return utils.JsonResponse({
            'content':
            content,
            'annotated_content_info':
            annotated_content_info,
        })
    else:
        # Since we're in page render mode, and the discussions UI will request the thread list itself,
        # we need only return the thread information for this one.
        threads = [thread.to_dict()]

        with newrelic.agent.FunctionTrace(nr_transaction,
                                          "add_courseware_context"):
            add_courseware_context(threads, course, request.user)

        for thread in threads:
            # patch for backward compatibility with comments service
            if "pinned" not in thread:
                thread["pinned"] = False

        threads = [
            utils.prepare_content(thread, course_key, is_staff)
            for thread in threads
        ]

        with newrelic.agent.FunctionTrace(nr_transaction,
                                          "get_metadata_for_threads"):
            annotated_content_info = utils.get_metadata_for_threads(
                course_key, threads, request.user, user_info)

        with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"):
            user_cohort = get_cohort_id(request.user, course_key)

        context = {
            'discussion_id':
            discussion_id,
            'csrf':
            csrf(request)['csrf_token'],
            'init':
            '',  # TODO: What is this?
            'user_info':
            user_info,
            'can_create_comment':
            has_permission(request.user, "create_comment", course.id),
            'can_create_subcomment':
            has_permission(request.user, "create_sub_comment", course.id),
            'can_create_thread':
            has_permission(request.user, "create_thread", course.id),
            'annotated_content_info':
            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':
            threads,
            'roles':
            utils.get_role_ids(course_key),
            'is_moderator':
            is_moderator,
            'thread_pages':
            1,
            'is_course_cohorted':
            is_course_cohorted(course_key),
            'flag_moderator':
            bool(
                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':
            course_settings,
            'disable_courseware_js':
            True,
            'uses_pattern_library':
            True,
        }
        return render_to_response('discussion/discussion_board.html', context)
示例#43
0
文件: views.py 项目: GbalsaC/bitnamiP
def followed_threads(request, course_key, user_id):
    """
    Ajax-only endpoint retrieving the threads followed by a specific user.
    """

    #    nr_transaction = newrelic.agent.current_transaction()

    course = get_course_with_access(request.user, "load", course_key, check_if_enrolled=True)
    try:
        profiled_user = cc.User(id=user_id, course_id=course_key)

        default_query_params = {
            "page": 1,
            "per_page": THREADS_PER_PAGE,  # more than threads_per_page to show more activities
            "sort_key": "date",
            "sort_order": "desc",
        }

        query_params = merge_dict(
            default_query_params,
            strip_none(extract(request.GET, ["page", "sort_key", "sort_order", "flagged", "unread", "unanswered"])),
        )

        try:
            group_id = get_group_id_for_comments_service(request, course_key)
        except ValueError:
            return HttpResponseBadRequest("Invalid group_id")
        if group_id is not None:
            query_params["group_id"] = group_id

        threads, page, num_pages = profiled_user.subscribed_threads(query_params)
        query_params["page"] = page
        query_params["num_pages"] = num_pages
        user_info = cc.User.from_django_user(request.user).to_dict()

        #        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)
        if request.is_ajax():
            is_staff = has_permission(request.user, "openclose_thread", course.id)
            return utils.JsonResponse(
                {
                    "annotated_content_info": annotated_content_info,
                    "discussion_data": [utils.prepare_content(thread, course_key, is_staff) for thread in threads],
                    "page": query_params["page"],
                    "num_pages": query_params["num_pages"],
                }
            )
        # TODO remove non-AJAX support, it does not appear to be used and does not appear to work.
        else:
            context = {
                "course": course,
                "user": request.user,
                "django_user": User.objects.get(id=user_id),
                "profiled_user": profiled_user.to_dict(),
                "threads": _attr_safe_json(threads),
                "user_info": _attr_safe_json(user_info),
                "annotated_content_info": _attr_safe_json(annotated_content_info),
                #                'content': content,
            }

            return render_to_response("discussion/user_profile.html", context)
    except User.DoesNotExist:
        raise Http404