Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
a4dd6e56
Commit
a4dd6e56
authored
May 21, 2015
by
Greg Price
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #8132 from edx/gprice/discussion-api-comment-list-endorsed
Add endorsement fields to comment list API
parents
4c9f415b
c65893e9
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
174 additions
and
10 deletions
+174
-10
lms/djangoapps/discussion_api/api.py
+1
-1
lms/djangoapps/discussion_api/serializers.py
+51
-4
lms/djangoapps/discussion_api/tests/test_api.py
+26
-0
lms/djangoapps/discussion_api/tests/test_serializers.py
+79
-4
lms/djangoapps/discussion_api/tests/test_views.py
+5
-0
lms/djangoapps/discussion_api/tests/utils.py
+0
-1
lms/djangoapps/discussion_api/views.py
+12
-0
No files found.
lms/djangoapps/discussion_api/api.py
View file @
a4dd6e56
...
@@ -169,7 +169,7 @@ def get_comment_list(request, thread_id, endorsed, page, page_size):
...
@@ -169,7 +169,7 @@ def get_comment_list(request, thread_id, endorsed, page, page_size):
course_key
=
CourseLocator
.
from_string
(
cc_thread
[
"course_id"
])
course_key
=
CourseLocator
.
from_string
(
cc_thread
[
"course_id"
])
course
=
_get_course_or_404
(
course_key
,
request
.
user
)
course
=
_get_course_or_404
(
course_key
,
request
.
user
)
context
=
get_context
(
course
,
request
.
user
)
context
=
get_context
(
course
,
request
.
user
,
cc_thread
)
# Ensure user has access to the thread
# Ensure user has access to the thread
if
not
context
[
"is_requester_privileged"
]
and
cc_thread
[
"group_id"
]:
if
not
context
[
"is_requester_privileged"
]
and
cc_thread
[
"group_id"
]:
...
...
lms/djangoapps/discussion_api/serializers.py
View file @
a4dd6e56
"""
"""
Discussion API serializers
Discussion API serializers
"""
"""
from
django.contrib.auth.models
import
User
as
DjangoUser
from
rest_framework
import
serializers
from
rest_framework
import
serializers
from
django_comment_common.models
import
(
from
django_comment_common.models
import
(
...
@@ -9,14 +11,14 @@ from django_comment_common.models import (
...
@@ -9,14 +11,14 @@ from django_comment_common.models import (
FORUM_ROLE_MODERATOR
,
FORUM_ROLE_MODERATOR
,
Role
,
Role
,
)
)
from
lms.lib.comment_client.user
import
User
from
lms.lib.comment_client.user
import
User
as
CommentClientUser
from
openedx.core.djangoapps.course_groups.cohorts
import
get_cohort_names
from
openedx.core.djangoapps.course_groups.cohorts
import
get_cohort_names
def
get_context
(
course
,
requester
):
def
get_context
(
course
,
requester
,
thread
=
None
):
"""
"""
Returns a context appropriate for use with ThreadSerializer or
Returns a context appropriate for use with ThreadSerializer or
CommentSerializer.
(if thread is provided)
CommentSerializer.
"""
"""
# TODO: cache staff_user_ids and ta_user_ids if we need to improve perf
# TODO: cache staff_user_ids and ta_user_ids if we need to improve perf
staff_user_ids
=
{
staff_user_ids
=
{
...
@@ -38,7 +40,8 @@ def get_context(course, requester):
...
@@ -38,7 +40,8 @@ def get_context(course, requester):
"is_requester_privileged"
:
requester
.
id
in
staff_user_ids
or
requester
.
id
in
ta_user_ids
,
"is_requester_privileged"
:
requester
.
id
in
staff_user_ids
or
requester
.
id
in
ta_user_ids
,
"staff_user_ids"
:
staff_user_ids
,
"staff_user_ids"
:
staff_user_ids
,
"ta_user_ids"
:
ta_user_ids
,
"ta_user_ids"
:
ta_user_ids
,
"cc_requester"
:
User
.
from_django_user
(
requester
)
.
retrieve
(),
"cc_requester"
:
CommentClientUser
.
from_django_user
(
requester
)
.
retrieve
(),
"thread"
:
thread
,
}
}
...
@@ -60,6 +63,13 @@ class _ContentSerializer(serializers.Serializer):
...
@@ -60,6 +63,13 @@ class _ContentSerializer(serializers.Serializer):
# name above and modify it here
# name above and modify it here
self
.
fields
[
"id"
]
=
self
.
fields
.
pop
(
"id_"
)
self
.
fields
[
"id"
]
=
self
.
fields
.
pop
(
"id_"
)
def
_is_user_privileged
(
self
,
user_id
):
"""
Returns a boolean indicating whether the given user_id identifies a
privileged user.
"""
return
user_id
in
self
.
context
[
"staff_user_ids"
]
or
user_id
in
self
.
context
[
"ta_user_ids"
]
def
_is_anonymous
(
self
,
obj
):
def
_is_anonymous
(
self
,
obj
):
"""
"""
Returns a boolean indicating whether the content should be anonymous to
Returns a boolean indicating whether the content should be anonymous to
...
@@ -156,12 +166,49 @@ class CommentSerializer(_ContentSerializer):
...
@@ -156,12 +166,49 @@ class CommentSerializer(_ContentSerializer):
"""
"""
thread_id
=
serializers
.
CharField
()
thread_id
=
serializers
.
CharField
()
parent_id
=
serializers
.
SerializerMethodField
(
"get_parent_id"
)
parent_id
=
serializers
.
SerializerMethodField
(
"get_parent_id"
)
endorsed
=
serializers
.
BooleanField
()
endorsed_by
=
serializers
.
SerializerMethodField
(
"get_endorsed_by"
)
endorsed_by_label
=
serializers
.
SerializerMethodField
(
"get_endorsed_by_label"
)
endorsed_at
=
serializers
.
SerializerMethodField
(
"get_endorsed_at"
)
children
=
serializers
.
SerializerMethodField
(
"get_children"
)
children
=
serializers
.
SerializerMethodField
(
"get_children"
)
def
get_parent_id
(
self
,
_obj
):
def
get_parent_id
(
self
,
_obj
):
"""Returns the comment's parent's id (taken from the context)."""
"""Returns the comment's parent's id (taken from the context)."""
return
self
.
context
.
get
(
"parent_id"
)
return
self
.
context
.
get
(
"parent_id"
)
def
get_endorsed_by
(
self
,
obj
):
"""
Returns the username of the endorsing user, if the information is
available and would not identify the author of an anonymous thread.
"""
endorsement
=
obj
.
get
(
"endorsement"
)
if
endorsement
:
endorser_id
=
int
(
endorsement
[
"user_id"
])
# Avoid revealing the identity of an anonymous non-staff question
# author who has endorsed a comment in the thread
if
not
(
self
.
_is_anonymous
(
self
.
context
[
"thread"
])
and
not
self
.
_is_user_privileged
(
endorser_id
)
):
return
DjangoUser
.
objects
.
get
(
id
=
endorser_id
)
.
username
return
None
def
get_endorsed_by_label
(
self
,
obj
):
"""
Returns the role label (i.e. "staff" or "community_ta") for the
endorsing user
"""
endorsement
=
obj
.
get
(
"endorsement"
)
if
endorsement
:
return
self
.
_get_user_label
(
int
(
endorsement
[
"user_id"
]))
else
:
return
None
def
get_endorsed_at
(
self
,
obj
):
"""Returns the timestamp for the endorsement, if available."""
endorsement
=
obj
.
get
(
"endorsement"
)
return
endorsement
[
"time"
]
if
endorsement
else
None
def
get_children
(
self
,
obj
):
def
get_children
(
self
,
obj
):
"""Returns the list of the comment's children, serialized."""
"""Returns the list of the comment's children, serialized."""
child_context
=
dict
(
self
.
context
)
child_context
=
dict
(
self
.
context
)
...
...
lms/djangoapps/discussion_api/tests/test_api.py
View file @
a4dd6e56
...
@@ -745,6 +745,7 @@ class GetCommentListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
...
@@ -745,6 +745,7 @@ class GetCommentListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
"created_at"
:
"2015-05-11T00:00:00Z"
,
"created_at"
:
"2015-05-11T00:00:00Z"
,
"updated_at"
:
"2015-05-11T11:11:11Z"
,
"updated_at"
:
"2015-05-11T11:11:11Z"
,
"body"
:
"Test body"
,
"body"
:
"Test body"
,
"endorsed"
:
False
,
"abuse_flaggers"
:
[],
"abuse_flaggers"
:
[],
"votes"
:
{
"up_count"
:
4
},
"votes"
:
{
"up_count"
:
4
},
"children"
:
[],
"children"
:
[],
...
@@ -759,6 +760,7 @@ class GetCommentListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
...
@@ -759,6 +760,7 @@ class GetCommentListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
"created_at"
:
"2015-05-11T22:22:22Z"
,
"created_at"
:
"2015-05-11T22:22:22Z"
,
"updated_at"
:
"2015-05-11T33:33:33Z"
,
"updated_at"
:
"2015-05-11T33:33:33Z"
,
"body"
:
"More content"
,
"body"
:
"More content"
,
"endorsed"
:
False
,
"abuse_flaggers"
:
[
str
(
self
.
user
.
id
)],
"abuse_flaggers"
:
[
str
(
self
.
user
.
id
)],
"votes"
:
{
"up_count"
:
7
},
"votes"
:
{
"up_count"
:
7
},
"children"
:
[],
"children"
:
[],
...
@@ -774,6 +776,10 @@ class GetCommentListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
...
@@ -774,6 +776,10 @@ class GetCommentListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
"created_at"
:
"2015-05-11T00:00:00Z"
,
"created_at"
:
"2015-05-11T00:00:00Z"
,
"updated_at"
:
"2015-05-11T11:11:11Z"
,
"updated_at"
:
"2015-05-11T11:11:11Z"
,
"raw_body"
:
"Test body"
,
"raw_body"
:
"Test body"
,
"endorsed"
:
False
,
"endorsed_by"
:
None
,
"endorsed_by_label"
:
None
,
"endorsed_at"
:
None
,
"abuse_flagged"
:
False
,
"abuse_flagged"
:
False
,
"voted"
:
False
,
"voted"
:
False
,
"vote_count"
:
4
,
"vote_count"
:
4
,
...
@@ -788,6 +794,10 @@ class GetCommentListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
...
@@ -788,6 +794,10 @@ class GetCommentListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
"created_at"
:
"2015-05-11T22:22:22Z"
,
"created_at"
:
"2015-05-11T22:22:22Z"
,
"updated_at"
:
"2015-05-11T33:33:33Z"
,
"updated_at"
:
"2015-05-11T33:33:33Z"
,
"raw_body"
:
"More content"
,
"raw_body"
:
"More content"
,
"endorsed"
:
False
,
"endorsed_by"
:
None
,
"endorsed_by_label"
:
None
,
"endorsed_at"
:
None
,
"abuse_flagged"
:
True
,
"abuse_flagged"
:
True
,
"voted"
:
False
,
"voted"
:
False
,
"vote_count"
:
7
,
"vote_count"
:
7
,
...
@@ -813,6 +823,22 @@ class GetCommentListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
...
@@ -813,6 +823,22 @@ class GetCommentListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
non_endorsed_actual
=
self
.
get_comment_list
(
thread
,
endorsed
=
False
)
non_endorsed_actual
=
self
.
get_comment_list
(
thread
,
endorsed
=
False
)
self
.
assertEqual
(
non_endorsed_actual
[
"results"
][
0
][
"id"
],
"non_endorsed_comment"
)
self
.
assertEqual
(
non_endorsed_actual
[
"results"
][
0
][
"id"
],
"non_endorsed_comment"
)
def
test_endorsed_by_anonymity
(
self
):
"""
Ensure thread anonymity is properly considered in serializing
endorsed_by.
"""
thread
=
self
.
make_minimal_cs_thread
({
"anonymous"
:
True
,
"children"
:
[
make_minimal_cs_comment
({
"endorsement"
:
{
"user_id"
:
str
(
self
.
author
.
id
),
"time"
:
"2015-05-18T12:34:56Z"
}
})
]
})
actual_comments
=
self
.
get_comment_list
(
thread
)[
"results"
]
self
.
assertIsNone
(
actual_comments
[
0
][
"endorsed_by"
])
@ddt.data
(
@ddt.data
(
(
"discussion"
,
None
,
"children"
,
"resp_total"
),
(
"discussion"
,
None
,
"children"
,
"resp_total"
),
(
"question"
,
False
,
"non_endorsed_responses"
,
"non_endorsed_resp_total"
),
(
"question"
,
False
,
"non_endorsed_responses"
,
"non_endorsed_resp_total"
),
...
...
lms/djangoapps/discussion_api/tests/test_serializers.py
View file @
a4dd6e56
"""
"""
Tests for Discussion API serializers
Tests for Discussion API serializers
"""
"""
import
itertools
import
ddt
import
ddt
import
httpretty
import
httpretty
...
@@ -199,7 +201,12 @@ class ThreadSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
...
@@ -199,7 +201,12 @@ class ThreadSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
@ddt.ddt
@ddt.ddt
class
CommentSerializerTest
(
SerializerTestMixin
,
ModuleStoreTestCase
):
class
CommentSerializerTest
(
SerializerTestMixin
,
ModuleStoreTestCase
):
"""Tests for CommentSerializer."""
"""Tests for CommentSerializer."""
def
make_cs_content
(
self
,
overrides
):
def
setUp
(
self
):
super
(
CommentSerializerTest
,
self
)
.
setUp
()
self
.
endorser
=
UserFactory
.
create
()
self
.
endorsed_at
=
"2015-05-18T12:34:56Z"
def
make_cs_content
(
self
,
overrides
=
None
,
with_endorsement
=
False
):
"""
"""
Create a comment with the given overrides, plus some useful test data.
Create a comment with the given overrides, plus some useful test data.
"""
"""
...
@@ -207,15 +214,21 @@ class CommentSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
...
@@ -207,15 +214,21 @@ class CommentSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
"user_id"
:
str
(
self
.
author
.
id
),
"user_id"
:
str
(
self
.
author
.
id
),
"username"
:
self
.
author
.
username
"username"
:
self
.
author
.
username
}
}
merged_overrides
.
update
(
overrides
)
if
with_endorsement
:
merged_overrides
[
"endorsement"
]
=
{
"user_id"
:
str
(
self
.
endorser
.
id
),
"time"
:
self
.
endorsed_at
}
merged_overrides
.
update
(
overrides
or
{})
return
make_minimal_cs_comment
(
merged_overrides
)
return
make_minimal_cs_comment
(
merged_overrides
)
def
serialize
(
self
,
comment
):
def
serialize
(
self
,
comment
,
thread_data
=
None
):
"""
"""
Create a serializer with an appropriate context and use it to serialize
Create a serializer with an appropriate context and use it to serialize
the given comment, returning the result.
the given comment, returning the result.
"""
"""
return
CommentSerializer
(
comment
,
context
=
get_context
(
self
.
course
,
self
.
user
))
.
data
context
=
get_context
(
self
.
course
,
self
.
user
,
make_minimal_cs_thread
(
thread_data
))
return
CommentSerializer
(
comment
,
context
=
context
)
.
data
def
test_basic
(
self
):
def
test_basic
(
self
):
comment
=
{
comment
=
{
...
@@ -228,6 +241,7 @@ class CommentSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
...
@@ -228,6 +241,7 @@ class CommentSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
"created_at"
:
"2015-04-28T00:00:00Z"
,
"created_at"
:
"2015-04-28T00:00:00Z"
,
"updated_at"
:
"2015-04-28T11:11:11Z"
,
"updated_at"
:
"2015-04-28T11:11:11Z"
,
"body"
:
"Test body"
,
"body"
:
"Test body"
,
"endorsed"
:
False
,
"abuse_flaggers"
:
[],
"abuse_flaggers"
:
[],
"votes"
:
{
"up_count"
:
4
},
"votes"
:
{
"up_count"
:
4
},
"children"
:
[],
"children"
:
[],
...
@@ -241,6 +255,10 @@ class CommentSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
...
@@ -241,6 +255,10 @@ class CommentSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
"created_at"
:
"2015-04-28T00:00:00Z"
,
"created_at"
:
"2015-04-28T00:00:00Z"
,
"updated_at"
:
"2015-04-28T11:11:11Z"
,
"updated_at"
:
"2015-04-28T11:11:11Z"
,
"raw_body"
:
"Test body"
,
"raw_body"
:
"Test body"
,
"endorsed"
:
False
,
"endorsed_by"
:
None
,
"endorsed_by_label"
:
None
,
"endorsed_at"
:
None
,
"abuse_flagged"
:
False
,
"abuse_flagged"
:
False
,
"voted"
:
False
,
"voted"
:
False
,
"vote_count"
:
4
,
"vote_count"
:
4
,
...
@@ -248,6 +266,63 @@ class CommentSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
...
@@ -248,6 +266,63 @@ class CommentSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
}
}
self
.
assertEqual
(
self
.
serialize
(
comment
),
expected
)
self
.
assertEqual
(
self
.
serialize
(
comment
),
expected
)
@ddt.data
(
*
itertools
.
product
(
[
FORUM_ROLE_ADMINISTRATOR
,
FORUM_ROLE_MODERATOR
,
FORUM_ROLE_COMMUNITY_TA
,
FORUM_ROLE_STUDENT
,
],
[
True
,
False
]
)
)
@ddt.unpack
def
test_endorsed_by
(
self
,
endorser_role_name
,
thread_anonymous
):
"""
Test correctness of the endorsed_by field.
The endorser should be anonymous iff the thread is anonymous to the
requester, and the endorser is not a privileged user.
endorser_role_name is the name of the endorser's role.
thread_anonymous is the value of the anonymous field in the thread.
"""
self
.
create_role
(
endorser_role_name
,
[
self
.
endorser
])
serialized
=
self
.
serialize
(
self
.
make_cs_content
(
with_endorsement
=
True
),
thread_data
=
{
"anonymous"
:
thread_anonymous
}
)
actual_endorser_anonymous
=
serialized
[
"endorsed_by"
]
is
None
expected_endorser_anonymous
=
endorser_role_name
==
FORUM_ROLE_STUDENT
and
thread_anonymous
self
.
assertEqual
(
actual_endorser_anonymous
,
expected_endorser_anonymous
)
@ddt.data
(
(
FORUM_ROLE_ADMINISTRATOR
,
"staff"
),
(
FORUM_ROLE_MODERATOR
,
"staff"
),
(
FORUM_ROLE_COMMUNITY_TA
,
"community_ta"
),
(
FORUM_ROLE_STUDENT
,
None
),
)
@ddt.unpack
def
test_endorsed_by_labels
(
self
,
role_name
,
expected_label
):
"""
Test correctness of the endorsed_by_label field.
The label should be "staff", "staff", or "community_ta" for the
Administrator, Moderator, and Community TA roles, respectively.
role_name is the name of the author's role.
expected_label is the expected value of the author_label field in the
API output.
"""
self
.
create_role
(
role_name
,
[
self
.
endorser
])
serialized
=
self
.
serialize
(
self
.
make_cs_content
(
with_endorsement
=
True
))
self
.
assertEqual
(
serialized
[
"endorsed_by_label"
],
expected_label
)
def
test_endorsed_at
(
self
):
serialized
=
self
.
serialize
(
self
.
make_cs_content
(
with_endorsement
=
True
))
self
.
assertEqual
(
serialized
[
"endorsed_at"
],
self
.
endorsed_at
)
def
test_children
(
self
):
def
test_children
(
self
):
comment
=
self
.
make_cs_content
({
comment
=
self
.
make_cs_content
({
"id"
:
"test_root"
,
"id"
:
"test_root"
,
...
...
lms/djangoapps/discussion_api/tests/test_views.py
View file @
a4dd6e56
...
@@ -240,6 +240,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
...
@@ -240,6 +240,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"created_at"
:
"2015-05-11T00:00:00Z"
,
"created_at"
:
"2015-05-11T00:00:00Z"
,
"updated_at"
:
"2015-05-11T11:11:11Z"
,
"updated_at"
:
"2015-05-11T11:11:11Z"
,
"body"
:
"Test body"
,
"body"
:
"Test body"
,
"endorsed"
:
False
,
"abuse_flaggers"
:
[],
"abuse_flaggers"
:
[],
"votes"
:
{
"up_count"
:
4
},
"votes"
:
{
"up_count"
:
4
},
"children"
:
[],
"children"
:
[],
...
@@ -253,6 +254,10 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
...
@@ -253,6 +254,10 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"created_at"
:
"2015-05-11T00:00:00Z"
,
"created_at"
:
"2015-05-11T00:00:00Z"
,
"updated_at"
:
"2015-05-11T11:11:11Z"
,
"updated_at"
:
"2015-05-11T11:11:11Z"
,
"raw_body"
:
"Test body"
,
"raw_body"
:
"Test body"
,
"endorsed"
:
False
,
"endorsed_by"
:
None
,
"endorsed_by_label"
:
None
,
"endorsed_at"
:
None
,
"abuse_flagged"
:
False
,
"abuse_flagged"
:
False
,
"voted"
:
True
,
"voted"
:
True
,
"vote_count"
:
4
,
"vote_count"
:
4
,
...
...
lms/djangoapps/discussion_api/tests/utils.py
View file @
a4dd6e56
...
@@ -119,7 +119,6 @@ def make_minimal_cs_comment(overrides=None):
...
@@ -119,7 +119,6 @@ def make_minimal_cs_comment(overrides=None):
"abuse_flaggers"
:
[],
"abuse_flaggers"
:
[],
"votes"
:
{
"up_count"
:
0
},
"votes"
:
{
"up_count"
:
0
},
"endorsed"
:
False
,
"endorsed"
:
False
,
"endorsement"
:
None
,
"children"
:
[],
"children"
:
[],
}
}
ret
.
update
(
overrides
or
{})
ret
.
update
(
overrides
or
{})
...
...
lms/djangoapps/discussion_api/views.py
View file @
a4dd6e56
...
@@ -174,6 +174,18 @@ class CommentViewSet(_ViewMixin, DeveloperErrorViewMixin, ViewSet):
...
@@ -174,6 +174,18 @@ class CommentViewSet(_ViewMixin, DeveloperErrorViewMixin, ViewSet):
* raw_body: The comment's raw body text without any rendering applied
* raw_body: The comment's raw body text without any rendering applied
* endorsed: Boolean indicating whether the comment has been endorsed
(by a privileged user or, for a question thread, the thread
author)
* endorsed_by: The username of the endorsing user, if available
* endorsed_by_label: A label indicating whether the endorsing user
has a special role in the course (see author_label)
* endorsed_at: The ISO 8601 timestamp for the endorsement, if
available
* abuse_flagged: Boolean indicating whether the requesting user has
* abuse_flagged: Boolean indicating whether the requesting user has
flagged the comment for abuse
flagged the comment for abuse
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment