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
b99ef48b
Commit
b99ef48b
authored
Sep 04, 2012
by
Rocky Duan
Committed by
Matthew Mongeau
Sep 10, 2012
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fit permission into new discussion view structure; admin actions not working yet
parent
68baafb8
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
234 additions
and
30 deletions
+234
-30
lms/djangoapps/django_comment_client/forum/views.py
+64
-0
lms/static/coffee/src/discussion/content.coffee
+28
-6
lms/static/coffee/src/discussion/main.coffee
+2
-0
lms/static/coffee/src/discussion/views/discussion_content_view.coffee
+51
-0
lms/static/coffee/src/discussion/views/discussion_thread_view.coffee
+47
-12
lms/static/coffee/src/discussion/views/response_comment_view.coffee
+1
-1
lms/static/coffee/src/discussion/views/thread_list_item_view.coffee
+2
-2
lms/static/coffee/src/discussion/views/thread_response_view.coffee
+22
-6
lms/static/sass/_discussion.scss
+4
-0
lms/templates/discussion/_underscore_templates.html
+12
-2
lms/templates/discussion/single_thread.html
+1
-1
No files found.
lms/djangoapps/django_comment_client/forum/views.py
View file @
b99ef48b
...
...
@@ -179,7 +179,14 @@ def forum_form_discussion(request, course_id):
category_map
=
utils
.
get_discussion_category_map
(
course
)
threads
,
query_params
=
get_threads
(
request
,
course_id
)
content
=
render_forum_discussion
(
request
,
course_id
,
threads
,
discussion_id
=
_general_discussion_id
(
course_id
),
query_params
=
query_params
)
user_info
=
cc
.
User
.
from_django_user
(
request
.
user
)
.
to_dict
()
def
infogetter
(
thread
):
return
utils
.
get_annotated_content_infos
(
course_id
,
thread
,
request
.
user
,
user_info
)
annotated_content_info
=
reduce
(
merge_dict
,
map
(
infogetter
,
threads
),
{})
if
request
.
is_ajax
():
return
utils
.
JsonResponse
({
'html'
:
content
,
...
...
@@ -205,6 +212,7 @@ def forum_form_discussion(request, course_id):
'staff_access'
:
has_access
(
request
.
user
,
course
,
'staff'
),
'threads'
:
saxutils
.
escape
(
json
.
dumps
(
threads
),
escapedict
),
'user_info'
:
saxutils
.
escape
(
json
.
dumps
(
user_info
),
escapedict
),
'annotated_content_info'
:
saxutils
.
escape
(
json
.
dumps
(
annotated_content_info
),
escapedict
),
'course_id'
:
course
.
id
,
'category_map'
:
category_map
,
}
...
...
@@ -236,6 +244,7 @@ def render_single_thread(request, discussion_id, course_id, thread_id):
def
single_thread
(
request
,
course_id
,
discussion_id
,
thread_id
):
<<<<<<<
HEAD
try
:
if
request
.
is_ajax
():
...
...
@@ -286,6 +295,61 @@ def single_thread(request, course_id, discussion_id, thread_id):
return
render_to_response
(
'discussion/single_thread.html'
,
context
)
except
(
cc
.
utils
.
CommentClientError
,
cc
.
utils
.
CommentClientUnknownError
)
as
err
:
raise
Http404
=======
if
request
.
is_ajax
():
user_info
=
cc
.
User
.
from_django_user
(
request
.
user
)
.
to_dict
()
thread
=
cc
.
Thread
.
find
(
thread_id
)
.
retrieve
(
recursive
=
True
)
annotated_content_info
=
utils
.
get_annotated_content_infos
(
course_id
,
thread
,
request
.
user
,
user_info
=
user_info
)
context
=
{
'thread'
:
thread
.
to_dict
(),
'course_id'
:
course_id
}
html
=
render_to_string
(
'discussion/_ajax_single_thread.html'
,
context
)
return
utils
.
JsonResponse
({
'html'
:
html
,
'content'
:
utils
.
safe_content
(
thread
.
to_dict
()),
'annotated_content_info'
:
annotated_content_info
,
})
else
:
course
=
get_course_with_access
(
request
.
user
,
course_id
,
'load'
)
category_map
=
utils
.
get_discussion_category_map
(
course
)
threads
,
query_params
=
get_threads
(
request
,
course_id
)
recent_active_threads
=
cc
.
search_recent_active_threads
(
course_id
,
recursive
=
False
,
query_params
=
{
'follower_id'
:
request
.
user
.
id
},
)
trending_tags
=
cc
.
search_trending_tags
(
course_id
,
)
user_info
=
cc
.
User
.
from_django_user
(
request
.
user
)
.
to_dict
()
escapedict
=
{
'"'
:
'"'
}
def
infogetter
(
thread
):
return
utils
.
get_annotated_content_infos
(
course_id
,
thread
,
request
.
user
,
user_info
)
annotated_content_info
=
reduce
(
merge_dict
,
map
(
infogetter
,
threads
),
{})
context
=
{
'discussion_id'
:
discussion_id
,
'csrf'
:
csrf
(
request
)[
'csrf_token'
],
'init'
:
''
,
'user_info'
:
saxutils
.
escape
(
json
.
dumps
(
user_info
),
escapedict
),
'annotated_content_info'
:
saxutils
.
escape
(
json
.
dumps
(
annotated_content_info
),
escapedict
),
'course'
:
course
,
'recent_active_threads'
:
recent_active_threads
,
'trending_tags'
:
trending_tags
,
'course_id'
:
course
.
id
,
'thread_id'
:
thread_id
,
'threads'
:
saxutils
.
escape
(
json
.
dumps
(
threads
),
escapedict
),
'category_map'
:
category_map
,
}
return
render_to_response
(
'discussion/single_thread.html'
,
context
)
>>>>>>>
fit
permission
into
new
discussion
view
structure
;
admin
actions
not
working
yet
def
user_profile
(
request
,
course_id
,
user_id
):
...
...
lms/static/coffee/src/discussion/content.coffee
View file @
b99ef48b
if
Backbone
?
class
@
Content
extends
Backbone
.
Model
@
contents
:
{}
@
contentInfos
:
{}
template
:
->
DiscussionUtil
.
getTemplate
(
'_content'
)
actions
:
...
...
@@ -13,15 +16,18 @@ if Backbone?
urlMappers
:
{}
urlFor
:
(
name
)
->
console
.
log
@
@
urlMappers
[
name
].
apply
(
@
)
can
:
(
action
)
->
DiscussionUtil
.
getContentInfo
@
id
,
action
(
@
get
(
'ability'
)
||
{})[
action
]
updateInfo
:
(
info
)
->
@
set
(
'ability'
,
info
.
ability
)
@
set
(
'voted'
,
info
.
voted
)
@
set
(
'subscribed'
,
info
.
subscribed
)
if
info
console
.
log
info
.
ability
@
set
(
'ability'
,
info
.
ability
)
@
set
(
'voted'
,
info
.
voted
)
@
set
(
'subscribed'
,
info
.
subscribed
)
addComment
:
(
comment
,
options
)
->
options
||=
{}
...
...
@@ -46,10 +52,24 @@ if Backbone?
@
addComment
comment
,
{
silent
:
true
}
initialize
:
->
DiscussionUtil
.
addContent
@
id
,
@
Content
.
addContent
@
id
,
@
if
Content
.
getInfo
(
@
id
)
@
updateInfo
(
Content
.
getInfo
(
@
id
))
@
set
'user_url'
,
DiscussionUtil
.
urlFor
(
'user_profile'
,
@
get
(
'user_id'
))
@
resetComments
(
@
get
(
'children'
))
@
addContent
:
(
id
,
content
)
->
@
contents
[
id
]
=
content
@
getContent
:
(
id
)
->
@
contents
[
id
]
@
getInfo
:
(
id
)
->
@
contentInfos
[
id
]
@
loadContentInfos
:
(
infos
)
->
for
id
,
info
of
infos
if
@
getContent
(
id
)
@
getContent
(
id
).
updateInfo
(
info
)
$
.
extend
@
contentInfos
,
infos
class
@
ContentView
extends
Backbone
.
View
...
...
@@ -426,9 +446,11 @@ if Backbone?
super
()
follow
:
->
@
set
(
'subscribed'
,
true
)
@
trigger
"thread:follow"
unfollow
:
->
@
set
(
'subscribed'
,
false
)
@
trigger
"thread:unfollow"
display_body
:
->
...
...
lms/static/coffee/src/discussion/main.coffee
View file @
b99ef48b
...
...
@@ -6,7 +6,9 @@ DiscussionApp =
window
.
$
$course_id
=
element
.
data
(
"course-id"
)
user_info
=
element
.
data
(
"user-info"
)
threads
=
element
.
data
(
"threads"
)
content_info
=
element
.
data
(
"content-info"
)
window
.
user
=
new
DiscussionUser
(
user_info
)
Content
.
loadContentInfos
(
content_info
)
discussion
=
new
Discussion
(
threads
)
new
DiscussionRouter
({
discussion
:
discussion
})
Backbone
.
history
.
start
({
pushState
:
true
,
root
:
"/courses/
#{
$$course_id
}
/discussion/forum/"
})
...
...
lms/static/coffee/src/discussion/views/discussion_content_view.coffee
0 → 100644
View file @
b99ef48b
class
@
DiscussionContentView
extends
Backbone
.
View
partialRenderer
:
endorsed
:
(
endorsed
)
->
closed
:
(
closed
)
->
# we should just re-render the whole thread, or update according to new abilities
voted
:
(
voted
)
->
if
voted
@
$
(
".discussion-vote"
).
addClass
(
"is-cast"
)
else
@
$
(
".discussion-vote"
).
removeClass
(
"is-cast"
)
votes_point
:
(
votes_point
)
->
@
$
(
".discussion-vote .votes-count-number"
).
html
(
votes_point
)
comments_count
:
(
comments_count
)
->
subscribed
:
(
subscribed
)
->
if
subscribed
@
$
(
".dogear"
).
addClass
(
"is-followed"
)
else
@
$
(
".dogear"
).
removeClass
(
"is-followed"
)
ability
:
(
ability
)
->
console
.
log
"ability changed"
for
action
,
selector
of
@
abilityRenderer
if
not
ability
[
action
]
selector
.
disable
.
apply
(
@
)
else
selector
.
enable
.
apply
(
@
)
abilityRenderer
:
editable
:
enable
:
->
@
$
(
".action-edit"
).
closest
(
"li"
).
show
()
disable
:
->
@
$
(
".action-edit"
).
closest
(
"li"
).
hide
()
can_delete
:
enable
:
->
@
$
(
".action-delete"
).
closest
(
"li"
).
show
()
disable
:
->
@
$
(
".action-delete"
).
closest
(
"li"
).
hide
()
can_endorse
:
enable
:
->
@
$
(
".action-endorse"
).
css
(
"cursor"
,
"auto"
)
disable
:
->
@
$
(
".action-endorse"
).
css
(
"cursor"
,
"default"
)
renderPartial
:
->
console
.
log
"changed"
for
attr
,
value
of
@
model
.
changedAttributes
()
if
@
partialRenderer
[
attr
]
@
partialRenderer
[
attr
].
apply
(
@
,
[
value
])
initialize
:
->
@
model
.
bind
(
'change'
,
@
renderPartial
,
@
)
lms/static/coffee/src/discussion/views/discussion_thread_view.coffee
View file @
b99ef48b
class
@
DiscussionThreadView
extends
Backbone
.
View
class
@
DiscussionThreadView
extends
DiscussionContentView
abilityRenderer
:
editable
:
enable
:
->
@
$
(
".action-edit"
).
closest
(
"li"
).
show
()
disable
:
->
@
$
(
".action-edit"
).
closest
(
"li"
).
hide
()
can_delete
:
enable
:
->
@
$
(
".action-delete"
).
closest
(
"li"
).
show
()
disable
:
->
@
$
(
".action-delete"
).
closest
(
"li"
).
hide
()
can_endorse
:
enable
:
->
@
$
(
".action-endorse"
).
css
(
"cursor"
,
"auto"
)
disable
:
->
@
$
(
".action-endorse"
).
css
(
"cursor"
,
"default"
)
events
:
"click .discussion-vote"
:
"toggleVote"
"click .
dogear
"
:
"toggleFollowing"
"click .
action-follow
"
:
"toggleFollowing"
"click .discussion-submit-post"
:
"submitComment"
"click .action-edit"
:
"edit"
"click .action-delete"
:
"delete"
template
:
_
.
template
(
$
(
"#thread-template"
).
html
())
render
:
->
...
...
@@ -32,6 +49,7 @@ class @DiscussionThreadView extends Backbone.View
url
:
"/courses/
#{
$$course_id
}
/discussion/forum/
#{
@
model
.
get
(
'commentable_id'
)
}
/threads/
#{
@
model
.
id
}
"
success
:
(
data
,
textStatus
,
xhr
)
=>
@
$
(
".loading"
).
remove
()
Content
.
loadContentInfos
(
data
[
'annotated_content_info'
])
comments
=
new
Comments
(
data
[
'content'
][
'children'
])
comments
.
each
@
renderResponse
...
...
@@ -44,19 +62,17 @@ class @DiscussionThreadView extends Backbone.View
addComment
:
=>
@
model
.
trigger
"comment:add"
toggleVote
:
->
@
$
(
".discussion-vote"
).
toggleClass
(
"is-cast"
)
if
@
$
(
".discussion-vote"
).
hasClass
(
"is-cast"
)
toggleVote
:
(
event
)
->
event
.
preventDefault
(
)
if
not
@
model
.
get
(
'voted'
)
#
@$(".discussion-vote").hasClass("is-cast")
@
vote
()
else
@
unvote
()
false
toggleFollowing
:
(
event
)
->
$elem
=
$
(
event
.
target
)
@
$
(
".dogear"
).
toggleClass
(
"is-followed"
)
url
=
null
if
@
$
(
".dogear"
).
hasClass
(
"is-followed"
)
if
not
@
model
.
get
(
'subscribed'
)
@
model
.
follow
()
url
=
@
model
.
urlFor
(
"follow"
)
else
...
...
@@ -69,7 +85,8 @@ class @DiscussionThreadView extends Backbone.View
vote
:
->
url
=
@
model
.
urlFor
(
"upvote"
)
@
$
(
".discussion-vote .votes-count-number"
).
html
(
parseInt
(
@
$
(
".discussion-vote .votes-count-number"
).
html
())
+
1
)
@
model
.
set
(
'votes_point'
,
parseInt
(
@
model
.
get
(
'votes_point'
))
+
1
)
#@$(".discussion-vote .votes-count-number").html(parseInt(@$(".discussion-vote .votes-count-number").html()) + 1)
DiscussionUtil
.
safeAjax
$elem
:
@
$
(
".discussion-vote"
)
url
:
url
...
...
@@ -80,7 +97,8 @@ class @DiscussionThreadView extends Backbone.View
unvote
:
->
url
=
@
model
.
urlFor
(
"unvote"
)
@
$
(
".discussion-vote .votes-count-number"
).
html
(
parseInt
(
@
$
(
".discussion-vote .votes-count-number"
).
html
())
-
1
)
@
model
.
set
(
'votes_point'
,
parseInt
(
@
model
.
get
(
'votes_point'
))
-
1
)
#@$(".discussion-vote .votes-count-number").html(parseInt(@$(".discussion-vote .votes-count-number").html()) - 1)
DiscussionUtil
.
safeAjax
$elem
:
@
$
(
".discussion-vote"
)
url
:
url
...
...
@@ -89,7 +107,8 @@ class @DiscussionThreadView extends Backbone.View
if
textStatus
==
'success'
@
model
.
set
(
response
)
submitComment
:
->
submitComment
:
(
event
)
->
event
.
preventDefault
()
url
=
@
model
.
urlFor
(
'reply'
)
body
=
@
$
(
"#wmd-input"
).
val
()
response
=
new
Comment
(
body
:
body
,
created_at
:
(
new
Date
()).
toISOString
(),
username
:
window
.
user
.
get
(
"username"
),
votes
:
{
up_count
:
0
})
...
...
@@ -103,4 +122,20 @@ class @DiscussionThreadView extends Backbone.View
dataType
:
'json'
data
:
body
:
body
false
edit
:
->
delete
:
->
toggleEndorse
:
->
$elem
=
$
(
event
.
target
)
url
=
@
model
.
urlFor
(
'endorse'
)
endorsed
=
@
model
.
get
(
'endorsed'
)
data
=
{
endorsed
:
not
endorsed
}
DiscussionUtil
.
safeAjax
$elem
:
$elem
url
:
url
data
:
data
type
:
"POST"
success
:
(
response
,
textStatus
)
=>
@
model
.
set
(
'endorsed'
,
not
endorsed
)
lms/static/coffee/src/discussion/views/response_comment_view.coffee
View file @
b99ef48b
class
@
ResponseCommentView
extends
Backbone
.
View
class
@
ResponseCommentView
extends
DiscussionContent
View
tagName
:
"li"
template
:
_
.
template
(
$
(
"#response-comment-template"
).
html
())
render
:
->
...
...
lms/static/coffee/src/discussion/views/thread_list_item_view.coffee
View file @
b99ef48b
...
...
@@ -13,9 +13,9 @@ class @ThreadListItemView extends Backbone.View
if
window
.
user
.
following
(
@
model
)
@
follow
()
@
threadSelected
:
->
threadSelected
:
(
event
)
->
event
.
preventDefault
()
@
trigger
(
"thread:selected"
,
@
model
.
id
)
false
follow
:
=>
@
$
(
"a"
).
addClass
(
"followed"
)
...
...
lms/static/coffee/src/discussion/views/thread_response_view.coffee
View file @
b99ef48b
class
@
ThreadResponseView
extends
Backbone
.
View
class
@
ThreadResponseView
extends
DiscussionContent
View
tagName
:
"li"
template
:
_
.
template
(
$
(
"#thread-response-template"
).
html
())
events
:
"click .vote-btn"
:
"toggleVote"
"submit form"
:
"submitComment"
"click .action-endorse"
:
"toggleEndorse"
render
:
->
@
$el
.
html
(
@
template
(
@
model
.
toJSON
()))
...
...
@@ -27,13 +29,13 @@ class @ThreadResponseView extends Backbone.View
view
.
render
()
@
$
(
".comments li:last"
).
before
(
view
.
el
)
toggleVote
:
->
toggleVote
:
(
event
)
->
event
.
preventDefault
()
@
$
(
".vote-btn"
).
toggleClass
(
"is-cast"
)
if
@
$
(
".vote-btn"
).
hasClass
(
"is-cast"
)
@
vote
()
else
@
unvote
()
false
vote
:
->
url
=
@
model
.
urlFor
(
"upvote"
)
...
...
@@ -58,10 +60,11 @@ class @ThreadResponseView extends Backbone.View
@
model
.
set
(
response
)
submitComment
:
(
event
)
->
event
.
preventDefault
()
url
=
@
model
.
urlFor
(
'reply'
)
body
=
@
$
(
".comment-form-input"
).
val
()
if
not
body
.
trim
().
length
return
false
return
comment
=
new
Comment
(
body
:
body
,
created_at
:
(
new
Date
()).
toISOString
(),
username
:
window
.
user
.
get
(
"username"
))
@
renderComment
(
comment
)
@
trigger
"comment:add"
...
...
@@ -74,5 +77,18 @@ class @ThreadResponseView extends Backbone.View
dataType
:
'json'
data
:
body
:
body
false
toggleEndorse
:
(
event
)
->
event
.
preventDefault
()
if
not
@
model
.
can
(
'can_endorse'
)
return
$elem
=
$
(
event
.
target
)
url
=
@
model
.
urlFor
(
'endorse'
)
endorsed
=
@
model
.
get
(
'endorsed'
)
data
=
{
endorsed
:
not
endorsed
}
@
model
.
set
(
'endorsed'
,
not
endorsed
)
DiscussionUtil
.
safeAjax
$elem
:
$elem
url
:
url
data
:
data
type
:
"POST"
lms/static/sass/_discussion.scss
View file @
b99ef48b
...
...
@@ -1249,12 +1249,16 @@ body.discussion {
}
.moderator-actions
{
margin
:
0
;
margin-top
:
20px
;
padding
:
0
;
@include
clearfix
;
li
{
float
:
left
;
margin-right
:
8px
;
list-style
:
none
;
}
a
{
...
...
lms/templates/discussion/_underscore_templates.html
View file @
b99ef48b
<script
type=
"text/template"
id=
"thread-template"
>
<
article
class
=
"discussion-article"
data
-
id
=
"${'<%- id %>'}"
>
<
a
href
=
"
#"
class
=
"dogear
"
><
/a
>
<
a
href
=
"
javascript:void(0)"
class
=
"dogear action-follow
"
><
/a
>
<
div
class
=
"discussion-post"
>
<
header
>
<
a
href
=
"#"
class
=
"vote-btn discussion-vote discussion-vote-up"
><
span
class
=
"plus-icon"
>+<
/span> <span class='votes-count-number'>${'<%- votes
[
"up_count"
]
%>'}</
span
><
/a
>
...
...
@@ -13,6 +13,15 @@
<
div
class
=
"post-body"
>
$
{
'<%- body %>'
}
<
/div
>
<
div
class
=
"post-status"
>
$
{
'<% if (closed) { %>'
}
This
thread
is
closed
.
$
{
'<% } %>'
}
<
/div
>
<
ul
class
=
"moderator-actions"
>
<
li
style
=
"display: none"
><
a
class
=
"action-edit"
href
=
"javascript:void(0)"
><
span
class
=
"edit-icon"
><
/span> Edit</
a
><
/li
>
<
li
style
=
"display: none"
><
a
class
=
"action-delete"
href
=
"javascript:void(0)"
><
span
class
=
"delete-icon"
><
/span> Delete</
a
><
/li
>
<
/ul
>
<
/div
>
<
ol
class
=
"responses"
>
<
li
class
=
"loading"
><
div
class
=
"loading-animation"
><
/div></
li
>
...
...
@@ -30,7 +39,8 @@
<script
type=
"text/template"
id=
"thread-response-template"
>
<
header
>
<
a
href
=
"#"
class
=
"vote-btn"
data
-
tooltip
=
"vote"
><
span
class
=
"plus-icon"
><
/span><span class="votes-count-number">${"<%- votes
[
'up_count'
]
%>"}</
span
><
/a
>
<
a
href
=
"javascript:void(0)"
class
=
"vote-btn"
data
-
tooltip
=
"vote"
><
span
class
=
"plus-icon"
><
/span><span class="votes-count-number">${"<%- votes
[
'up_count'
]
%>"}</
span
><
/a
>
<
a
href
=
"javascript:void(0)"
class
=
"endorse-btn${'<% if (endorsed) { %> is-endorsed<% } %>'} action-endorse"
style
=
"cursor: default"
data
-
tooltip
=
"endorse"
><
span
class
=
"check-icon"
style
=
"pointer-events: none; "
><
/span></
a
>
<
a
href
=
"${'<%- user_url %>'}"
class
=
"posted-by"
>
$
{
"<%- username %>"
}
<
/a
>
<
p
class
=
"posted-details"
title
=
"${'<%- created_at %>'}"
>
Sometime
<
/p
>
<
/header
>
...
...
lms/templates/discussion/single_thread.html
View file @
b99ef48b
...
...
@@ -24,7 +24,7 @@
<
%
include
file=
"_new_post.html"
/>
<section
class=
"discussion container"
id=
"discussion-container"
data-course-id=
"${course_id}"
data-user-info=
"${user_info}"
data-threads=
"${threads}"
>
<section
class=
"discussion container"
id=
"discussion-container"
data-course-id=
"${course_id}"
data-user-info=
"${user_info}"
data-threads=
"${threads}"
data-content-info=
"${annotated_content_info}"
>
<div
class=
"discussion-body"
>
<div
class=
"sidebar"
></div>
<div
class=
"discussion-column"
></div>
...
...
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