예제 #1
0
def users(request, course_id):
    """
    Given a `username` query parameter, find matches for users in the forum for this course.

    Only exact matches are supported here, so the length of the result set will either be 0 or 1.
    """

    course_key = CourseKey.from_string(course_id)
    try:
        get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
    except Http404:
        # course didn't exist, or requesting user does not have access to it.
        return JsonError(status=404)

    try:
        username = request.GET['username']
    except KeyError:
        # 400 is default status for JsonError
        return JsonError(["username parameter is required"])

    user_objs = []
    try:
        matched_user = User.objects.get(username=username)
        cc_user = cc.User.from_django_user(matched_user)
        cc_user.course_id = course_key
        cc_user.retrieve(complete=False)
        if (cc_user['threads_count'] + cc_user['comments_count']) > 0:
            user_objs.append({
                'id': matched_user.id,
                'username': matched_user.username,
            })
    except User.DoesNotExist:
        pass
    return JsonResponse({"users": user_objs})
예제 #2
0
def create_thread(request, course_id, commentable_id):
    """
    Given a course and commentble ID, create the thread
    """

    log.debug("Creating new thread in %r, id %r", course_id, commentable_id)
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)
    post = request.POST

    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers', 'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    if 'title' not in post or not post['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in post or not post['body'].strip():
        return JsonError(_("Body can't be empty"))

    thread = cc.Thread(
        anonymous=anonymous,
        anonymous_to_peers=anonymous_to_peers,
        commentable_id=commentable_id,
        course_id=course_key.to_deprecated_string(),
        user_id=request.user.id,
        thread_type=post["thread_type"],
        body=post["body"],
        title=post["title"]
    )

    # Cohort the thread if required
    try:
        group_id = get_group_id_for_comments_service(request, course_key, commentable_id)
    except ValueError:
        return HttpResponseBadRequest("Invalid cohort id")
    if group_id is not None:
        thread.group_id = group_id

    thread.save()

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

    if post.get('auto_subscribe', 'false').lower() == 'true':
        user = cc.User.from_django_user(request.user)
        user.follow(thread)
    data = thread.to_dict()
    add_thread_group_name(data, course_key)
    add_courseware_context([data], course)
    if request.is_ajax():
        return ajax_content_response(request, course_key, data)
    else:
        return JsonResponse(safe_content(data, course_key))
예제 #3
0
def update_thread(request, course_id, thread_id):
    """
    Given a course id and thread id, update a existing thread, used for both static and ajax submissions
    """
    if 'title' not in request.POST or not request.POST['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in request.POST or not request.POST['body'].strip():
        return JsonError(_("Body can't be empty"))

    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    thread = cc.Thread.find(thread_id)
    thread.body = request.POST["body"]
    thread.title = request.POST["title"]
    # The following checks should avoid issues we've seen during deploys, where end users are hitting an updated server
    # while their browser still has the old client code. This will avoid erasing present values in those cases.
    if "thread_type" in request.POST:
        thread.thread_type = request.POST["thread_type"]
    if "commentable_id" in request.POST:
        course = get_course_with_access(request.user, 'load', course_key)
        if discussion_category_id_access(course, request.user,
                                         request.POST.get("commentable_id")):
            thread.commentable_id = request.POST["commentable_id"]
        else:
            return JsonError(_("Topic doesn't exist"))

    thread.save()
    if request.is_ajax():
        return ajax_content_response(request, course_key, thread.to_dict())
    else:
        return JsonResponse(prepare_content(thread.to_dict(), course_key))
예제 #4
0
 def process_exception(self, request, exception):
     """
     Processes CommentClientErrors in ajax requests. If the request is an ajax request,
     returns a http response that encodes the error as json
     """
     if isinstance(exception, CommentClientError) and request.is_ajax():
         try:
             return JsonError(json.loads(exception.message))
         except ValueError:
             return JsonError(exception.message)
     return None
예제 #5
0
def update_thread(request, course_id, thread_id):
    """
    Given a course id and thread id, update a existing thread, used for both static and ajax submissions
    """
    if 'title' not in request.POST or not request.POST['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in request.POST or not request.POST['body'].strip():
        return JsonError(_("Body can't be empty"))
    thread = cc.Thread.find(thread_id)
    thread.update_attributes(**extract(request.POST, ['body', 'title']))
    thread.save()
    if request.is_ajax():
        return ajax_content_response(request, course_id, thread.to_dict())
    else:
        return JsonResponse(utils.safe_content(thread.to_dict()))
예제 #6
0
def update_moderator_status(request, course_id, user_id):
    """
    given a course id and user id, check if the user has moderator
    and send back a user profile
    """
    is_moderator = request.POST.get('is_moderator', '').lower()
    if is_moderator not in ["true", "false"]:
        return JsonError("Must provide is_moderator as boolean value")
    is_moderator = is_moderator == "true"
    user = User.objects.get(id=user_id)
    role = Role.objects.get(course_id=course_id, name="Moderator")
    if is_moderator:
        user.roles.add(role)
    else:
        user.roles.remove(role)
    if request.is_ajax():
        course = get_course_with_access(request.user, course_id, 'load')
        discussion_user = cc.User(id=user_id, course_id=course_id)
        context = {
            'course': course,
            'course_id': course_id,
            'user': request.user,
            'django_user': user,
            'profiled_user': discussion_user.to_dict(),
        }
        return JsonResponse({
            'html': render_to_string('discussion/ajax_user_profile.html', context)
        })
    else:
        return JsonResponse({})
예제 #7
0
    def wrapper(request, *args, **kwargs):
        """
        Wrapper for the view that only calls the view if the user is authorized.
        """
        def fetch_content():
            """
            Extract the forum object from the keyword arguments to the view.
            """
            if "thread_id" in kwargs:
                content = cc.Thread.find(kwargs["thread_id"]).to_dict()
            elif "comment_id" in kwargs:
                content = cc.Comment.find(kwargs["comment_id"]).to_dict()
            elif "commentable_id" in kwargs:
                content = cc.Commentable.find(
                    kwargs["commentable_id"]).to_dict()
            else:
                content = None
            return content

        course_key = CourseKey.from_string(kwargs['course_id'])
        if check_permissions_by_view(request.user, course_key, fetch_content(),
                                     request.view_name):
            return func(request, *args, **kwargs)
        else:
            return JsonError("unauthorized", status=401)
예제 #8
0
def create_sub_comment(request, course_id, comment_id):
    """
    given a course_id and comment_id, create a response to a comment
    after checking the max depth allowed, if allowed
    """
    if is_comment_too_deep(parent=cc.Comment(comment_id)):
        return JsonError(_("Comment level too deep"))
    return _create_comment(request, CourseKey.from_string(course_id), parent_id=comment_id)
예제 #9
0
def update_thread(request, course_id, thread_id):
    """
    Given a course id and thread id, update a existing thread, used for both static and ajax submissions
    """
    if 'title' not in request.POST or not request.POST['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in request.POST or not request.POST['body'].strip():
        return JsonError(_("Body can't be empty"))
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    thread = cc.Thread.find(thread_id)
    thread.body = request.POST["body"]
    thread.title = request.POST["title"]
    thread.save()
    if request.is_ajax():
        return ajax_content_response(request, course_key, thread.to_dict())
    else:
        return JsonResponse(safe_content(thread.to_dict(), course_key))
예제 #10
0
def create_comment(request, course_id, thread_id):
    """
    given a course_id and thread_id, test for comment depth. if not too deep,
    call _create_comment to create the actual comment.
    """
    if is_comment_too_deep(parent=None):
        return JsonError(_("Comment level too deep"))
    return _create_comment(request, CourseKey.from_string(course_id), thread_id=thread_id)
예제 #11
0
def create_sub_comment(request, course_id, comment_id):
    """
    given a course_id and comment_id, create a response to a comment
    after checking the max depth allowed, if allowed
    """
    if cc_settings.MAX_COMMENT_DEPTH is not None:
        if cc_settings.MAX_COMMENT_DEPTH <= cc.Comment.find(comment_id).depth:
            return JsonError(_("Comment level too deep"))
    return _create_comment(request, course_id, parent_id=comment_id)
예제 #12
0
def create_comment(request, course_id, thread_id):
    """
    given a course_id and thread_id, test for comment depth. if not too deep,
    call _create_comment to create the actual comment.
    """
    if cc_settings.MAX_COMMENT_DEPTH is not None:
        if cc_settings.MAX_COMMENT_DEPTH < 0:
            return JsonError(_("Comment level too deep"))
    return _create_comment(request, course_id, thread_id=thread_id)
예제 #13
0
def _create_comment(request, course_key, thread_id=None, parent_id=None):
    """
    given a course_key, thread_id, and parent_id, create a comment,
    called from create_comment to do the actual creation
    """
    assert isinstance(course_key, CourseKey)
    post = request.POST

    if 'body' not in post or not post['body'].strip():
        return JsonError(_("Body can't be empty"))

    course = get_course_with_access(request.user, 'load', course_key)
    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers', 'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    comment = cc.Comment(
        anonymous=anonymous,
        anonymous_to_peers=anonymous_to_peers,
        user_id=request.user.id,
        course_id=course_key.to_deprecated_string(),
        thread_id=thread_id,
        parent_id=parent_id,
        body=post["body"]
    )
    comment.save()

    followed = post.get('auto_subscribe', 'false').lower() == 'true'

    if followed:
        user = cc.User.from_django_user(request.user)
        user.follow(comment.thread)

    event_data = {'discussion': {'id': comment.thread_id}, 'options': {'followed': followed}}

    if parent_id:
        event_data['response'] = {'id': comment.parent_id}
        event_name = 'edx.forum.comment.created'
    else:
        event_name = 'edx.forum.response.created'

    event_data['commentable_id'] = comment.thread.commentable_id

    track_forum_event(request, event_name, course, comment, event_data)

    if request.is_ajax():
        return ajax_content_response(request, course_key, comment.to_dict())
    else:
        return JsonResponse(prepare_content(comment.to_dict(), course.id))
예제 #14
0
 def wrapper(request, *args, **kwargs):
     def fetch_content():
         if "thread_id" in kwargs:
             content = cc.Thread.find(kwargs["thread_id"]).to_dict()
         elif "comment_id" in kwargs:
             content = cc.Comment.find(kwargs["comment_id"]).to_dict()
         else:
             content = None
         return content
     if check_permissions_by_view(request.user, kwargs['course_id'], fetch_content(), request.view_name):
         return fn(request, *args, **kwargs)
     else:
         return JsonError("unauthorized", status=401)
예제 #15
0
def update_comment(request, course_id, comment_id):
    """
    given a course_id and comment_id, update the comment with payload attributes
    handles static and ajax submissions
    """
    comment = cc.Comment.find(comment_id)
    if 'body' not in request.POST or not request.POST['body'].strip():
        return JsonError(_("Body can't be empty"))
    comment.update_attributes(**extract(request.POST, ['body']))
    comment.save()
    if request.is_ajax():
        return ajax_content_response(request, course_id, comment.to_dict())
    else:
        return JsonResponse(utils.safe_content(comment.to_dict()))
예제 #16
0
 def wrapper(request, *args, **kwargs):
     def fetch_content():
         if "thread_id" in kwargs:
             content = cc.Thread.find(kwargs["thread_id"]).to_dict()
         elif "comment_id" in kwargs:
             content = cc.Comment.find(kwargs["comment_id"]).to_dict()
         else:
             content = None
         return content
     course_key = SlashSeparatedCourseKey.from_deprecated_string(kwargs['course_id'])
     if check_permissions_by_view(request.user, course_key, fetch_content(), request.view_name):
         return fn(request, *args, **kwargs)
     else:
         return JsonError("unauthorized", status=401)
예제 #17
0
def update_thread(request, course_id, thread_id):
    """
    Given a course id and thread id, update a existing thread, used for both static and ajax submissions
    """
    if 'title' not in request.POST or not request.POST['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in request.POST or not request.POST['body'].strip():
        return JsonError(_("Body can't be empty"))

    course_key = CourseKey.from_string(course_id)
    thread = cc.Thread.find(thread_id)
    # Get thread context first in order to be safe from reseting the values of thread object later
    thread_context = getattr(thread, "context", "course")
    thread.body = request.POST["body"]
    thread.title = request.POST["title"]
    user = request.user
    # The following checks should avoid issues we've seen during deploys, where end users are hitting an updated server
    # while their browser still has the old client code. This will avoid erasing present values in those cases.
    if "thread_type" in request.POST:
        thread.thread_type = request.POST["thread_type"]
    if "commentable_id" in request.POST:
        commentable_id = request.POST["commentable_id"]
        course = get_course_with_access(user, 'load', course_key)
        if thread_context == "course" and not discussion_category_id_access(
                course, user, commentable_id):
            return JsonError(_("Topic doesn't exist"))
        else:
            thread.commentable_id = commentable_id

    thread.save()

    thread_edited.send(sender=None, user=user, post=thread)

    if request.is_ajax():
        return ajax_content_response(request, course_key, thread.to_dict())
    else:
        return JsonResponse(prepare_content(thread.to_dict(), course_key))
예제 #18
0
def update_comment(request, course_id, comment_id):
    """
    given a course_id and comment_id, update the comment with payload attributes
    handles static and ajax submissions
    """
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    comment = cc.Comment.find(comment_id)
    if 'body' not in request.POST or not request.POST['body'].strip():
        return JsonError(_("Body can't be empty"))
    comment.body = request.POST["body"]
    comment.save()
    if request.is_ajax():
        return ajax_content_response(request, course_key, comment.to_dict())
    else:
        return JsonResponse(prepare_content(comment.to_dict(), course_key))
예제 #19
0
def _create_comment(request, course_key, thread_id=None, parent_id=None):
    """
    given a course_key, thread_id, and parent_id, create a comment,
    called from create_comment to do the actual creation
    """
    assert isinstance(course_key, CourseKey)
    post = request.POST
    user = request.user

    if 'body' not in post or not post['body'].strip():
        return JsonError(_("Body can't be empty"))

    course = get_course_with_access(user, 'load', course_key)
    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers',
                                      'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    comment = cc.Comment(anonymous=anonymous,
                         anonymous_to_peers=anonymous_to_peers,
                         user_id=user.id,
                         course_id=course_key.to_deprecated_string(),
                         thread_id=thread_id,
                         parent_id=parent_id,
                         body=post["body"])
    comment.save()

    comment_created.send(sender=None, user=user, post=comment)

    followed = post.get('auto_subscribe', 'false').lower() == 'true'

    if followed:
        cc_user = cc.User.from_django_user(request.user)
        cc_user.follow(comment.thread)

    track_comment_created_event(request, course, comment,
                                comment.thread.commentable_id, followed)

    if request.is_ajax():
        return ajax_content_response(request, course_key, comment.to_dict())
    else:
        return JsonResponse(prepare_content(comment.to_dict(), course.id))
예제 #20
0
def _create_comment(request, course_id, thread_id=None, parent_id=None):
    """
    given a course_id, thread_id, and parent_id, create a comment,
    called from create_comment to do the actual creation
    """
    post = request.POST

    if 'body' not in post or not post['body'].strip():
        return JsonError(_("Body can't be empty"))
    comment = cc.Comment(**extract(post, ['body']))

    course = get_course_with_access(request.user, course_id, 'load')
    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers',
                                      'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    comment.update_attributes(
        **{
            'anonymous': anonymous,
            'anonymous_to_peers': anonymous_to_peers,
            'user_id': request.user.id,
            'course_id': course_id,
            'thread_id': thread_id,
            'parent_id': parent_id,
        })
    comment.save()
    if post.get('auto_subscribe', 'false').lower() == 'true':
        user = cc.User.from_django_user(request.user)
        user.follow(comment.thread)
    if request.is_ajax():
        return ajax_content_response(request, course_id, comment.to_dict())
    else:
        return JsonResponse(utils.safe_content(comment.to_dict()))
예제 #21
0
def create_thread(request, course_id, commentable_id):
    """
    Given a course and commentble ID, create the thread
    """

    log.debug("Creating new thread in %r, id %r", course_id, commentable_id)
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)
    post = request.POST
    user = request.user

    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers',
                                      'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    if 'title' not in post or not post['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in post or not post['body'].strip():
        return JsonError(_("Body can't be empty"))

    params = {
        'anonymous': anonymous,
        'anonymous_to_peers': anonymous_to_peers,
        'commentable_id': commentable_id,
        'course_id': course_key.to_deprecated_string(),
        'user_id': user.id,
        'thread_type': post["thread_type"],
        'body': post["body"],
        'title': post["title"],
    }

    # Check for whether this commentable belongs to a team, and add the right context
    if get_team(commentable_id) is not None:
        params['context'] = ThreadContext.STANDALONE
    else:
        params['context'] = ThreadContext.COURSE

    thread = cc.Thread(**params)

    # Cohort the thread if required
    try:
        group_id = get_group_id_for_comments_service(request, course_key,
                                                     commentable_id)
    except ValueError:
        return HttpResponseBadRequest("Invalid cohort id")
    if group_id is not None:
        thread.group_id = group_id

    thread.save()

    thread_created.send(sender=None, user=user, post=thread)

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

    follow = post.get('auto_subscribe', 'false').lower() == 'true'

    if follow:
        cc_user = cc.User.from_django_user(user)
        cc_user.follow(thread)

    event_data = get_thread_created_event_data(thread, follow)
    data = thread.to_dict()

    add_courseware_context([data], course, user)

    track_forum_event(request, THREAD_CREATED_EVENT_NAME, course, thread,
                      event_data)

    if request.is_ajax():
        return ajax_content_response(request, course_key, data)
    else:
        return JsonResponse(prepare_content(data, course_key))
예제 #22
0
def create_thread(request, course_id, commentable_id):
    """
    Given a course and commentble ID, create the thread
    """

    log.debug("Creating new thread in %r, id %r", course_id, commentable_id)
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)
    post = request.POST

    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers',
                                      'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    if 'title' not in post or not post['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in post or not post['body'].strip():
        return JsonError(_("Body can't be empty"))

    thread = cc.Thread(anonymous=anonymous,
                       anonymous_to_peers=anonymous_to_peers,
                       commentable_id=commentable_id,
                       course_id=course_key.to_deprecated_string(),
                       user_id=request.user.id,
                       thread_type=post["thread_type"],
                       body=post["body"],
                       title=post["title"])

    # Cohort the thread if required
    try:
        group_id = get_group_id_for_comments_service(request, course_key,
                                                     commentable_id)
    except ValueError:
        return HttpResponseBadRequest("Invalid cohort id")
    if group_id is not None:
        thread.group_id = group_id

    thread.save()

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

    follow = post.get('auto_subscribe', 'false').lower() == 'true'

    if follow:
        user = cc.User.from_django_user(request.user)
        user.follow(thread)

    event_data = {
        'title': thread.title,
        'commentable_id': commentable_id,
        'options': {
            'followed': follow
        },
        'anonymous': anonymous,
        'thread_type': thread.thread_type,
        'group_id': group_id,
        'anonymous_to_peers': anonymous_to_peers,
        # There is a stated desire for an 'origin' property that will state
        # whether this thread was created via courseware or the forum.
        # However, the view does not contain that data, and including it will
        # likely require changes elsewhere.
    }
    data = thread.to_dict()

    # Calls to id map are expensive, but we need this more than once.
    # Prefetch it.
    id_map = get_discussion_id_map(course, request.user)

    add_courseware_context([data], course, request.user, id_map=id_map)

    track_forum_event(request,
                      'edx.forum.thread.created',
                      course,
                      thread,
                      event_data,
                      id_map=id_map)

    if request.is_ajax():
        return ajax_content_response(request, course_key, data)
    else:
        return JsonResponse(prepare_content(data, course_key))
예제 #23
0
def create_thread(request, course_id, commentable_id):
    """
    Given a course and commentble ID, create the thread
    """

    log.debug("Creating new thread in %r, id %r", course_id, commentable_id)
    course = get_course_with_access(request.user, course_id, 'load')
    post = request.POST

    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers',
                                      'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    if 'title' not in post or not post['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in post or not post['body'].strip():
        return JsonError(_("Body can't be empty"))

    thread = cc.Thread(**extract(post, ['body', 'title']))
    thread.update_attributes(
        **{
            'anonymous': anonymous,
            'anonymous_to_peers': anonymous_to_peers,
            'commentable_id': commentable_id,
            'course_id': course_id,
            'user_id': request.user.id,
        })

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

    #kevinchugh because the new requirement is that all groups will be determined
    #by the group id in the request this all goes away
    #not anymore, only for admins

    # Cohort the thread if the commentable is cohorted.
    if is_commentable_cohorted(course_id, commentable_id):
        user_group_id = get_cohort_id(user, course_id)

        # TODO (vshnayder): once we have more than just cohorts, we'll want to
        # change this to a single get_group_for_user_and_commentable function
        # that can do different things depending on the commentable_id
        if cached_has_permission(request.user, "see_all_cohorts", course_id):
            # admins can optionally choose what group to post as
            group_id = post.get('group_id', user_group_id)
        else:
            # regular users always post with their own id.
            group_id = user_group_id

        if group_id:
            thread.update_attributes(group_id=group_id)

    thread.save()

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

    if post.get('auto_subscribe', 'false').lower() == 'true':
        user = cc.User.from_django_user(request.user)
        user.follow(thread)
    data = thread.to_dict()
    add_courseware_context([data], course)
    if request.is_ajax():
        return ajax_content_response(request, course_id, data)
    else:
        return JsonResponse(utils.safe_content(data))
예제 #24
0
def create_thread(request, course_id, commentable_id):
    """
    Given a course and commentble ID, create the thread
    """
    log.debug("Creating new thread in %r, id %r", course_id, commentable_id)
    course_key = CourseKey.from_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)
    post = request.POST
    user = request.user

    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers',
                                      'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    if 'title' not in post or not post['title'].strip():
        return JsonError(_("Title can't be empty"))
    if 'body' not in post or not post['body'].strip():
        return JsonError(_("Body can't be empty"))

    params = {
        'anonymous': anonymous,
        'anonymous_to_peers': anonymous_to_peers,
        'commentable_id': commentable_id,
        'course_id': course_key.to_deprecated_string(),
        'user_id': user.id,
        'thread_type': post["thread_type"],
        'body': post["body"],
        'title': post["title"],
    }

    # Check for whether this commentable belongs to a team, and add the right context
    if get_team(commentable_id) is not None:
        params['context'] = ThreadContext.STANDALONE
    else:
        params['context'] = ThreadContext.COURSE

    thread = cc.Thread(**params)

    # Divide the thread if required
    try:
        group_id = get_group_id_for_comments_service(request, course_key,
                                                     commentable_id)
    except ValueError:
        return HttpResponseServerError("Invalid group id for commentable")
    if group_id is not None:
        thread.group_id = group_id

    thread.save()

    thread_created.send(sender=None, user=user, post=thread)

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

    follow = post.get('auto_subscribe', 'false').lower() == 'true'

    if follow:
        cc_user = cc.User.from_django_user(user)
        cc_user.follow(thread)
        thread_followed.send(sender=None, user=user, post=thread)

    data = thread.to_dict()

    add_courseware_context([data], course, user)

    track_thread_created_event(request, course, thread, follow)

    if thread.get('group_id'):
        # Send a notification message, if enabled, when anyone posts a new thread on
        # a cohorted/private discussion, except the poster him/herself
        _send_discussion_notification(
            'open-edx.lms.discussions.cohorted-thread-added',
            unicode(course_key),
            thread,
            request.user,
            excerpt=_get_excerpt(thread.body),
            recipient_group_id=thread.get('group_id'),
            recipient_exclude_user_ids=[request.user.id],
            is_anonymous_user=anonymous or anonymous_to_peers)

    add_thread_group_name(data, course_key)
    if thread.get('group_id') and not thread.get('group_name'):
        thread['group_name'] = get_cohort_by_id(course_key,
                                                thread.get('group_id')).name

    data = thread.to_dict()

    if request.is_ajax():
        return ajax_content_response(request, course_key, data)
    else:
        return JsonResponse(prepare_content(data, course_key))
예제 #25
0
def _create_comment(request, course_key, thread_id=None, parent_id=None):
    """
    given a course_key, thread_id, and parent_id, create a comment,
    called from create_comment to do the actual creation
    """
    assert isinstance(course_key, CourseKey)
    post = request.POST
    user = request.user

    if 'body' not in post or not post['body'].strip():
        return JsonError(_("Body can't be empty"))

    course = get_course_with_access(user, 'load', course_key)
    if course.allow_anonymous:
        anonymous = post.get('anonymous', 'false').lower() == 'true'
    else:
        anonymous = False

    if course.allow_anonymous_to_peers:
        anonymous_to_peers = post.get('anonymous_to_peers',
                                      'false').lower() == 'true'
    else:
        anonymous_to_peers = False

    comment = cc.Comment(anonymous=anonymous,
                         anonymous_to_peers=anonymous_to_peers,
                         user_id=user.id,
                         course_id=course_key.to_deprecated_string(),
                         thread_id=thread_id,
                         parent_id=parent_id,
                         body=post["body"])
    comment.save()

    comment_created.send(sender=None, user=user, post=comment)

    followed = post.get('auto_subscribe', 'false').lower() == 'true'

    if followed:
        cc_user = cc.User.from_django_user(request.user)
        cc_user.follow(comment.thread)

    track_comment_created_event(request, course, comment,
                                comment.thread.commentable_id, followed)

    #
    # Send notification
    #
    # Feature Flag to check that notifications are enabled or not.
    if settings.FEATURES.get("ENABLE_NOTIFICATIONS", False):

        action_user_id = request.user.id
        is_comment = not thread_id and parent_id

        replying_to_id = None  # keep track of who we are replying to
        if is_comment:
            # If creating a comment, then we don't have the original thread_id
            # so we have to get it from the parent
            comment = cc.Comment.find(parent_id)
            thread_id = comment.thread_id
            replying_to_id = comment.user_id

        thread = cc.Thread.find(thread_id)

        # IMPORTANT: we have to use getattr here as
        # otherwise the property will not get fetched
        # from cs_comment_service
        thread_user_id = int(getattr(thread, 'user_id', 0))

        if not replying_to_id:
            # we must be creating a Reponse on a thread,
            # so the original poster is the author of the thread
            replying_to_id = thread_user_id

        #
        # IMPORTANT: We have to use getattr() here so that the
        # object is fully hydrated. This is a known limitation.
        #
        group_id = getattr(thread, 'group_id')

        if group_id:
            # We always send a notification to the whole cohort
            # when someone posts a comment, except the poster

            _send_discussion_notification(
                'open-edx.lms.discussions.cohorted-comment-added',
                unicode(course_key),
                thread,
                request.user,
                excerpt=_get_excerpt(post["body"]),
                recipient_group_id=thread.get('group_id'),
                recipient_exclude_user_ids=[request.user.id],
                is_anonymous_user=anonymous or anonymous_to_peers)

        elif parent_id is None and action_user_id != replying_to_id:
            # we have to only send the notifications when
            # the user commenting the thread is not
            # the same user who created the thread
            # parent_id is None: publish notification only when creating the comment on
            # the thread not replying on the comment. When the user replied on the comment
            # the parent_id is not None at that time

            _send_discussion_notification(
                'open-edx.lms.discussions.reply-to-thread',
                unicode(course_key),
                thread,
                request.user,
                excerpt=_get_excerpt(post["body"]),
                recipient_user_id=replying_to_id,
                is_anonymous_user=anonymous or anonymous_to_peers)

    if request.is_ajax():
        return ajax_content_response(request, course_key, comment.to_dict())
    else:
        return JsonResponse(prepare_content(comment.to_dict(), course.id))