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
af656fcf
Commit
af656fcf
authored
Aug 06, 2012
by
Mike Chen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
use permissions to check if one can edit or relp
parent
3f1534ea
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
121 additions
and
81 deletions
+121
-81
lms/djangoapps/django_comment_client/base/views.py
+35
-70
lms/djangoapps/django_comment_client/forum/views.py
+11
-10
lms/djangoapps/django_comment_client/permissions.py
+66
-1
lms/djangoapps/django_comment_client/utils.py
+4
-0
lms/envs/common.py
+2
-0
lms/static/coffee/src/discussion/content.coffee
+3
-0
No files found.
lms/djangoapps/django_comment_client/base/views.py
View file @
af656fcf
...
...
@@ -18,62 +18,27 @@ from django.conf import settings
from
mitxmako.shortcuts
import
render_to_response
,
render_to_string
from
django_comment_client.utils
import
JsonResponse
,
JsonError
,
extract
from
django_comment_client.permissions
import
has_permission
,
has_permission
from
django_comment_client.permissions
import
check_permissions_by_view
import
functools
#
def
permitted
(
*
per
):
"""
Accepts a list of permissions and proceed if any of the permission is valid.
Note that @permitted("can_view", "can_edit") will proceed if the user has either
"can_view" or "can_edit" permission. To use AND operator in between, wrap them in
a list:
@permitted(["can_view", "can_edit"])
Special conditions can be used like permissions, e.g.
@permitted(["can_vote", "open"]) # where open is True if not content['closed']
"""
def
decorator
(
fn
):
@functools.wraps
(
fn
)
def
wrapper
(
request
,
*
args
,
**
kwargs
):
permissions
=
filter
(
lambda
x
:
len
(
x
),
list
(
per
))
user
=
request
.
user
import
pdb
;
pdb
.
set_trace
()
def
fetch_content
():
if
"thread_id"
in
kwargs
:
content
=
comment_client
.
get_thread
(
kwargs
[
"thread_id"
])
elif
"comment_id"
in
kwargs
:
content
=
comment_client
.
get_comment
(
kwargs
[
"comment_id"
])
else
:
logging
.
warning
(
"missing thread_id or comment_id"
)
return
None
return
content
def
test_permission
(
user
,
permission
,
operator
=
"or"
):
if
isinstance
(
permission
,
basestring
):
if
permission
==
""
:
return
True
elif
permission
==
"author"
:
return
fetch_content
()[
"user_id"
]
==
request
.
user
.
id
elif
permission
==
"open"
:
return
not
fetch_content
()[
"closed"
]
return
has_permission
(
user
,
permission
)
elif
isinstance
(
permission
,
list
)
and
operator
in
[
"and"
,
"or"
]:
results
=
[
test_permission
(
user
,
x
,
operator
=
"and"
)
for
x
in
permission
]
if
operator
==
"or"
:
return
True
in
results
elif
operator
==
"and"
:
return
not
False
in
results
if
test_permission
(
user
,
permissions
,
operator
=
"or"
):
return
fn
(
request
,
*
args
,
**
kwargs
)
def
permitted
(
fn
):
@functools.wraps
(
fn
)
def
wrapper
(
request
,
*
args
,
**
kwargs
):
def
fetch_content
():
if
"thread_id"
in
kwargs
:
content
=
comment_client
.
get_thread
(
kwargs
[
"thread_id"
])
elif
"comment_id"
in
kwargs
:
content
=
comment_client
.
get_comment
(
kwargs
[
"comment_id"
])
else
:
return
JsonError
(
"unauthorized"
)
content
=
None
return
content
return
wrapper
return
decorator
if
check_permissions_by_view
(
request
.
user
,
fetch_content
(),
request
.
view_name
):
return
fn
(
request
,
*
args
,
**
kwargs
)
else
:
return
JsonError
(
"unauthorized"
)
return
wrapper
def
thread_author_only
(
fn
):
...
...
@@ -106,7 +71,7 @@ def instructor_only(fn):
@require_POST
@login_required
@permitted
(
"create_thread"
)
@permitted
def
create_thread
(
request
,
course_id
,
commentable_id
):
attributes
=
extract
(
request
.
POST
,
[
'body'
,
'title'
,
'tags'
])
attributes
[
'user_id'
]
=
request
.
user
.
id
...
...
@@ -131,7 +96,7 @@ def create_thread(request, course_id, commentable_id):
@require_POST
@login_required
@permitted
(
"edit_content"
,
[
"update_thread"
,
"open"
,
"author"
])
@permitted
def
update_thread
(
request
,
course_id
,
thread_id
):
attributes
=
extract
(
request
.
POST
,
[
'body'
,
'title'
,
'tags'
])
response
=
comment_client
.
update_thread
(
thread_id
,
attributes
)
...
...
@@ -171,7 +136,7 @@ def _create_comment(request, course_id, _response_from_attributes):
@require_POST
@login_required
@permitted
([
"create_comment"
,
"open"
])
@permitted
def
create_comment
(
request
,
course_id
,
thread_id
):
def
_response_from_attributes
(
attributes
):
return
comment_client
.
create_comment
(
thread_id
,
attributes
)
...
...
@@ -179,14 +144,14 @@ def create_comment(request, course_id, thread_id):
@require_POST
@login_required
@permitted
(
"delete_thread"
)
@permitted
def
delete_thread
(
request
,
course_id
,
thread_id
):
response
=
comment_client
.
delete_thread
(
thread_id
)
return
JsonResponse
(
response
)
@require_POST
@login_required
@permitted
(
"update_comment"
,
[
"update_comment"
,
"open"
,
"author"
])
@permitted
def
update_comment
(
request
,
course_id
,
comment_id
):
attributes
=
extract
(
request
.
POST
,
[
'body'
])
response
=
comment_client
.
update_comment
(
comment_id
,
attributes
)
...
...
@@ -205,7 +170,7 @@ def update_comment(request, course_id, comment_id):
@require_POST
@login_required
@permitted
(
"endorse_comment"
)
@permitted
def
endorse_comment
(
request
,
course_id
,
comment_id
):
attributes
=
extract
(
request
.
POST
,
[
'endorsed'
])
response
=
comment_client
.
update_comment
(
comment_id
,
attributes
)
...
...
@@ -213,7 +178,7 @@ def endorse_comment(request, course_id, comment_id):
@require_POST
@login_required
@permitted
(
"openclose_thread"
)
@permitted
def
openclose_thread
(
request
,
course_id
,
thread_id
):
attributes
=
extract
(
request
.
POST
,
[
'closed'
])
response
=
comment_client
.
update_thread
(
thread_id
,
attributes
)
...
...
@@ -221,7 +186,7 @@ def openclose_thread(request, course_id, thread_id):
@require_POST
@login_required
@permitted
([
"create_sub_comment"
,
"open"
])
@permitted
def
create_sub_comment
(
request
,
course_id
,
comment_id
):
def
_response_from_attributes
(
attributes
):
return
comment_client
.
create_sub_comment
(
comment_id
,
attributes
)
...
...
@@ -229,14 +194,14 @@ def create_sub_comment(request, course_id, comment_id):
@require_POST
@login_required
@permitted
(
"delete_comment"
)
@permitted
def
delete_comment
(
request
,
course_id
,
comment_id
):
response
=
comment_client
.
delete_comment
(
comment_id
)
return
JsonResponse
(
response
)
@require_POST
@login_required
@permitted
([
"vote"
,
"open"
])
@permitted
def
vote_for_comment
(
request
,
course_id
,
comment_id
,
value
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
vote_for_comment
(
comment_id
,
user_id
,
value
)
...
...
@@ -244,7 +209,7 @@ def vote_for_comment(request, course_id, comment_id, value):
@require_POST
@login_required
@permitted
([
"unvote"
,
"open"
])
@permitted
def
undo_vote_for_comment
(
request
,
course_id
,
comment_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
undo_vote_for_comment
(
comment_id
,
user_id
)
...
...
@@ -252,7 +217,7 @@ def undo_vote_for_comment(request, course_id, comment_id):
@require_POST
@login_required
@permitted
([
"vote"
,
"open"
])
@permitted
def
vote_for_thread
(
request
,
course_id
,
thread_id
,
value
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
vote_for_thread
(
thread_id
,
user_id
,
value
)
...
...
@@ -260,7 +225,7 @@ def vote_for_thread(request, course_id, thread_id, value):
@require_POST
@login_required
@permitted
([
"unvote"
,
"open"
])
@permitted
def
undo_vote_for_thread
(
request
,
course_id
,
thread_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
undo_vote_for_thread
(
thread_id
,
user_id
)
...
...
@@ -268,7 +233,7 @@ def undo_vote_for_thread(request, course_id, thread_id):
@require_POST
@login_required
@permitted
(
"follow_thread"
)
@permitted
def
follow_thread
(
request
,
course_id
,
thread_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
subscribe_thread
(
user_id
,
thread_id
)
...
...
@@ -276,7 +241,7 @@ def follow_thread(request, course_id, thread_id):
@require_POST
@login_required
@permitted
(
"follow_commentable"
)
@permitted
def
follow_commentable
(
request
,
course_id
,
commentable_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
subscribe_commentable
(
user_id
,
commentable_id
)
...
...
@@ -284,7 +249,7 @@ def follow_commentable(request, course_id, commentable_id):
@require_POST
@login_required
@permitted
(
"follow_user"
)
@permitted
def
follow_user
(
request
,
course_id
,
followed_user_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
follow
(
user_id
,
followed_user_id
)
...
...
@@ -292,7 +257,7 @@ def follow_user(request, course_id, followed_user_id):
@require_POST
@login_required
@permitted
(
"unfollow_thread"
)
@permitted
def
unfollow_thread
(
request
,
course_id
,
thread_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
unsubscribe_thread
(
user_id
,
thread_id
)
...
...
@@ -300,7 +265,7 @@ def unfollow_thread(request, course_id, thread_id):
@require_POST
@login_required
@permitted
(
"unfollow_commentable"
)
@permitted
def
unfollow_commentable
(
request
,
course_id
,
commentable_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
unsubscribe_commentable
(
user_id
,
commentable_id
)
...
...
@@ -308,7 +273,7 @@ def unfollow_commentable(request, course_id, commentable_id):
@require_POST
@login_required
@permitted
(
"unfollow_user"
)
@permitted
def
unfollow_user
(
request
,
course_id
,
followed_user_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
unfollow
(
user_id
,
followed_user_id
)
...
...
lms/djangoapps/django_comment_client/forum/views.py
View file @
af656fcf
...
...
@@ -18,6 +18,7 @@ import json
import
comment_client
import
dateutil
from
django_comment_client.permissions
import
check_permissions_by_view
THREADS_PER_PAGE
=
5
PAGES_NEARBY_DELTA
=
2
...
...
@@ -48,7 +49,7 @@ def render_discussion(request, course_id, threads, discussion_id=None, \
'forum'
:
(
lambda
:
reverse
(
'django_comment_client.forum.views.forum_form_discussion'
,
args
=
[
course_id
,
discussion_id
])),
}[
discussion_type
]()
annotated_content_info
=
{
thread
[
'id'
]:
get_annotated_content_info
(
thread
,
request
.
user
.
id
)
for
thread
in
threads
}
annotated_content_info
=
{
thread
[
'id'
]:
get_annotated_content_info
(
thread
,
request
.
user
,
is_thread
=
True
)
for
thread
in
threads
}
context
=
{
'threads'
:
threads
,
...
...
@@ -127,17 +128,18 @@ def forum_form_discussion(request, course_id, discussion_id):
return
render_to_response
(
'discussion/index.html'
,
context
)
def
get_annotated_content_info
(
content
,
user
_i
d
):
def
get_annotated_content_info
(
content
,
user
,
is_threa
d
):
return
{
'editable'
:
str
(
content
[
'user_id'
])
==
str
(
user_id
),
# TODO may relax this to instructors
'editable'
:
check_permissions_by_view
(
user
,
content
,
"update_thread"
if
is_thread
else
"update_comment"
),
'can_reply'
:
check_permissions_by_view
(
user
,
content
,
"create_comment"
if
is_thread
else
"create_sub_comment"
),
}
def
get_annotated_content_infos
(
thread
,
user
_id
):
def
get_annotated_content_infos
(
thread
,
user
):
infos
=
{}
def
_annotate
(
content
):
infos
[
str
(
content
[
'id'
])]
=
get_annotated_content_info
(
content
,
user
_i
d
)
def
_annotate
(
content
,
is_thread
=
True
):
infos
[
str
(
content
[
'id'
])]
=
get_annotated_content_info
(
content
,
user
,
is_threa
d
)
for
child
in
content
.
get
(
'children'
,
[]):
_annotate
(
child
)
_annotate
(
child
,
is_thread
=
False
)
_annotate
(
thread
)
return
infos
...
...
@@ -146,7 +148,7 @@ def render_single_thread(request, course_id, thread_id):
thread
=
comment_client
.
get_thread
(
thread_id
,
recursive
=
True
)
annotated_content_info
=
get_annotated_content_infos
(
thread
=
thread
,
\
user
_id
=
request
.
user
.
id
)
user
=
request
.
user
,
is_thread
=
True
)
context
=
{
'thread'
:
thread
,
...
...
@@ -162,8 +164,7 @@ def single_thread(request, course_id, discussion_id, thread_id):
if
request
.
is_ajax
():
thread
=
comment_client
.
get_thread
(
thread_id
,
recursive
=
True
)
annotated_content_info
=
get_annotated_content_infos
(
thread
=
thread
,
\
user_id
=
request
.
user
.
id
)
annotated_content_info
=
get_annotated_content_infos
(
thread
,
request
.
user
)
context
=
{
'thread'
:
thread
}
html
=
render_to_string
(
'discussion/_ajax_single_thread.html'
,
context
)
...
...
lms/djangoapps/django_comment_client/permissions.py
View file @
af656fcf
...
...
@@ -34,11 +34,76 @@ def assign_default_role(sender, instance, **kwargs):
logging
.
info
(
"assign_default_role: adding
%
s as
%
s"
%
(
instance
,
role
))
instance
.
roles
.
add
(
role
)
def
check_permissions
(
user
,
content
,
per
):
"""
Accepts a list of permissions and proceed if any of the permission is valid.
Note that check_permissions("can_view", "can_edit") will proceed if the user has either
"can_view" or "can_edit" permission. To use AND operator in between, wrap them in
a list:
check_permissions(["can_view", "can_edit"])
Special conditions can be used like permissions, e.g.
(["can_vote", "open"]) # where open is True if not content['closed']
"""
permissions
=
filter
(
lambda
x
:
len
(
x
),
list
(
per
))
def
test_permission
(
user
,
permission
,
operator
=
"or"
):
if
isinstance
(
permission
,
basestring
):
# import pdb; pdb.set_trace()
if
permission
==
""
:
return
True
elif
permission
==
"author"
:
return
content
[
"user_id"
]
==
str
(
user
.
id
)
elif
permission
==
"open"
:
return
not
content
[
"closed"
]
return
has_permission
(
user
,
permission
)
elif
isinstance
(
permission
,
list
)
and
operator
in
[
"and"
,
"or"
]:
results
=
[
test_permission
(
user
,
x
,
operator
=
"and"
)
for
x
in
permission
]
if
operator
==
"or"
:
return
True
in
results
elif
operator
==
"and"
:
return
not
False
in
results
return
test_permission
(
user
,
permissions
,
operator
=
"or"
)
VIEW_PERMISSIONS
=
{
'update_thread'
:
(
'edit_content'
,
[
'update_thread'
,
'open'
,
'author'
]),
'create_comment'
:
([
"create_comment"
,
"open"
]),
'delete_thread'
:
(
'delete_thread'
),
'update_comment'
:
(
'edit_content'
,
[
'update_comment'
,
'open'
,
'author'
]),
'endorse_comment'
:
(
'endorse_comment'
),
'openclose_thread'
:
(
'openclose_thread'
),
'create_sub_comment'
:
([
'create_sub_comment'
,
'open'
]),
'delete_comment'
:
(
'delete_comment'
),
'vote_for_commend'
:
([
'vote'
,
'open'
]),
'undo_vote_for_comment'
:
([
'unvote'
,
'open'
]),
'vote_for_thread'
:
([
'vote'
,
'open'
]),
'undo_vote_for_thread'
:
([
'unvote'
,
'open'
]),
'follow_thread'
:
(
'follow_thread'
),
'follow_commentable'
:
(
'follow_commentable'
),
'follow_user'
:
(
'follow_user'
),
'unfollow_thread'
:
(
'unfollow_thread'
),
'unfollow_commentable'
:
(
'unfollow_commentable'
),
'unfollow_user'
:
(
'unfollow_user'
),
'create_thread'
:
(
'create_thread'
),
}
def
check_permissions_by_view
(
user
,
content
,
name
):
try
:
p
=
VIEW_PERMISSIONS
[
name
]
except
KeyError
:
logging
.
warning
(
"Permission for view named
%
s does not exist in permissions.py"
%
name
)
permissions
=
list
((
p
,
)
if
isinstance
(
p
,
basestring
)
else
p
)
return
check_permissions
(
user
,
content
,
permissions
)
moderator_role
=
Role
.
register
(
"Moderator"
)
student_role
=
Role
.
register
(
"Student"
)
moderator_role
.
register_permissions
([
"edit_content"
,
"delete_thread"
,
"openclose_thread"
,
"update_thread"
,
"endorse_comment"
,
"delete_comment"
])
"endorse_comment"
,
"delete_comment"
])
student_role
.
register_permissions
([
"vote"
,
"update_thread"
,
"follow_thread"
,
"unfollow_thread"
,
"update_comment"
,
"create_sub_comment"
,
"unvote"
,
"create_thread"
,
"follow_commentable"
,
"unfollow_commentable"
,
"create_comment"
,
])
...
...
lms/djangoapps/django_comment_client/utils.py
View file @
af656fcf
...
...
@@ -120,3 +120,7 @@ class JsonError(HttpResponse):
class
HtmlResponse
(
HttpResponse
):
def
__init__
(
self
,
html
=
''
):
super
(
HtmlResponse
,
self
)
.
__init__
(
html
,
content_type
=
'text/plain'
)
class
ViewNameMiddleware
(
object
):
def
process_view
(
self
,
request
,
view_func
,
view_args
,
view_kwargs
):
request
.
view_name
=
view_func
.
__name__
lms/envs/common.py
View file @
af656fcf
...
...
@@ -294,6 +294,8 @@ MIDDLEWARE_CLASSES = (
'askbot.middleware.spaceless.SpacelessMiddleware'
,
# 'askbot.middleware.pagesize.QuestionsPageSizeMiddleware',
# 'debug_toolbar.middleware.DebugToolbarMiddleware',
'django_comment_client.utils.ViewNameMiddleware'
,
)
############################### Pipeline #######################################
...
...
lms/static/coffee/src/discussion/content.coffee
View file @
af656fcf
...
...
@@ -78,6 +78,7 @@ initializeFollowThread = (thread) ->
$comment
=
$
(
response
.
html
)
$content
.
children
(
".comments"
).
prepend
(
$comment
)
Discussion
.
setWmdContent
$content
,
$local
,
"reply-body"
,
""
Discussion
.
setContentInfo
response
.
content
[
'id'
],
'can_reply'
,
true
Discussion
.
setContentInfo
response
.
content
[
'id'
],
'editable'
,
true
Discussion
.
initializeContent
(
$comment
)
Discussion
.
bindContentEvents
(
$comment
)
...
...
@@ -321,3 +322,5 @@ initializeFollowThread = (thread) ->
id
=
$content
.
attr
(
"_id"
)
if
not
Discussion
.
getContentInfo
id
,
'editable'
$local
(
".discussion-edit"
).
remove
()
if
not
Discussion
.
getContentInfo
id
,
'can_reply'
$local
(
".discussion-reply"
).
remove
()
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