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
3f1534ea
Commit
3f1534ea
authored
Aug 06, 2012
by
Mike Chen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added permission framework.
parent
30f5bdb4
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
387 additions
and
6 deletions
+387
-6
lms/djangoapps/django_comment_client/__init__.py
+2
-0
lms/djangoapps/django_comment_client/base/urls.py
+1
-0
lms/djangoapps/django_comment_client/base/views.py
+84
-5
lms/djangoapps/django_comment_client/migrations/0001_initial.py
+141
-0
lms/djangoapps/django_comment_client/migrations/__init__.py
+0
-0
lms/djangoapps/django_comment_client/models.py
+36
-0
lms/djangoapps/django_comment_client/permissions.py
+47
-0
lms/djangoapps/django_comment_client/tests.py
+37
-0
lms/djangoapps/django_comment_client/utils.py
+1
-1
lms/static/coffee/src/discussion/content.coffee
+30
-0
lms/static/coffee/src/discussion/utils.coffee
+1
-0
lms/templates/discussion/_thread.html
+7
-0
No files found.
lms/djangoapps/django_comment_client/__init__.py
View file @
3f1534ea
# call some function from permissions so that the post_save hook is imported
from
permissions
import
assign_default_role
lms/djangoapps/django_comment_client/base/urls.py
View file @
3f1534ea
...
...
@@ -13,6 +13,7 @@ urlpatterns = patterns('django_comment_client.base.views',
url
(
r'threads/(?P<thread_id>[\w\-]+)/unvote$'
,
'undo_vote_for_thread'
,
name
=
'undo_vote_for_thread'
),
url
(
r'threads/(?P<thread_id>[\w\-]+)/follow$'
,
'follow_thread'
,
name
=
'follow_thread'
),
url
(
r'threads/(?P<thread_id>[\w\-]+)/unfollow$'
,
'unfollow_thread'
,
name
=
'unfollow_thread'
),
url
(
r'threads/(?P<thread_id>[\w\-]+)/close$'
,
'openclose_thread'
,
name
=
'openclose_thread'
),
url
(
r'comments/(?P<comment_id>[\w\-]+)/update$'
,
'update_comment'
,
name
=
'update_comment'
),
url
(
r'comments/(?P<comment_id>[\w\-]+)/endorse$'
,
'endorse_comment'
,
name
=
'endorse_comment'
),
...
...
lms/djangoapps/django_comment_client/base/views.py
View file @
3f1534ea
...
...
@@ -18,6 +18,64 @@ 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
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
)
else
:
return
JsonError
(
"unauthorized"
)
return
wrapper
return
decorator
def
thread_author_only
(
fn
):
def
verified_fn
(
request
,
*
args
,
**
kwargs
):
thread_id
=
kwargs
.
get
(
'thread_id'
,
False
)
...
...
@@ -48,6 +106,7 @@ def instructor_only(fn):
@require_POST
@login_required
@permitted
(
"create_thread"
)
def
create_thread
(
request
,
course_id
,
commentable_id
):
attributes
=
extract
(
request
.
POST
,
[
'body'
,
'title'
,
'tags'
])
attributes
[
'user_id'
]
=
request
.
user
.
id
...
...
@@ -72,7 +131,7 @@ def create_thread(request, course_id, commentable_id):
@require_POST
@login_required
@
thread_author_only
@
permitted
(
"edit_content"
,
[
"update_thread"
,
"open"
,
"author"
])
def
update_thread
(
request
,
course_id
,
thread_id
):
attributes
=
extract
(
request
.
POST
,
[
'body'
,
'title'
,
'tags'
])
response
=
comment_client
.
update_thread
(
thread_id
,
attributes
)
...
...
@@ -112,6 +171,7 @@ def _create_comment(request, course_id, _response_from_attributes):
@require_POST
@login_required
@permitted
([
"create_comment"
,
"open"
])
def
create_comment
(
request
,
course_id
,
thread_id
):
def
_response_from_attributes
(
attributes
):
return
comment_client
.
create_comment
(
thread_id
,
attributes
)
...
...
@@ -119,14 +179,14 @@ def create_comment(request, course_id, thread_id):
@require_POST
@login_required
@
thread_author_only
@
permitted
(
"delete_thread"
)
def
delete_thread
(
request
,
course_id
,
thread_id
):
response
=
comment_client
.
delete_thread
(
thread_id
)
return
JsonResponse
(
response
)
@require_POST
@login_required
@
comment_author_only
@
permitted
(
"update_comment"
,
[
"update_comment"
,
"open"
,
"author"
])
def
update_comment
(
request
,
course_id
,
comment_id
):
attributes
=
extract
(
request
.
POST
,
[
'body'
])
response
=
comment_client
.
update_comment
(
comment_id
,
attributes
)
...
...
@@ -145,7 +205,7 @@ def update_comment(request, course_id, comment_id):
@require_POST
@login_required
@
instructor_only
@
permitted
(
"endorse_comment"
)
def
endorse_comment
(
request
,
course_id
,
comment_id
):
attributes
=
extract
(
request
.
POST
,
[
'endorsed'
])
response
=
comment_client
.
update_comment
(
comment_id
,
attributes
)
...
...
@@ -153,6 +213,15 @@ def endorse_comment(request, course_id, comment_id):
@require_POST
@login_required
@permitted
(
"openclose_thread"
)
def
openclose_thread
(
request
,
course_id
,
thread_id
):
attributes
=
extract
(
request
.
POST
,
[
'closed'
])
response
=
comment_client
.
update_thread
(
thread_id
,
attributes
)
return
JsonResponse
(
response
)
@require_POST
@login_required
@permitted
([
"create_sub_comment"
,
"open"
])
def
create_sub_comment
(
request
,
course_id
,
comment_id
):
def
_response_from_attributes
(
attributes
):
return
comment_client
.
create_sub_comment
(
comment_id
,
attributes
)
...
...
@@ -160,13 +229,14 @@ def create_sub_comment(request, course_id, comment_id):
@require_POST
@login_required
@
comment_author_only
@
permitted
(
"delete_comment"
)
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"
])
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
)
...
...
@@ -174,6 +244,7 @@ def vote_for_comment(request, course_id, comment_id, value):
@require_POST
@login_required
@permitted
([
"unvote"
,
"open"
])
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
)
...
...
@@ -181,6 +252,7 @@ def undo_vote_for_comment(request, course_id, comment_id):
@require_POST
@login_required
@permitted
([
"vote"
,
"open"
])
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
)
...
...
@@ -188,6 +260,7 @@ def vote_for_thread(request, course_id, thread_id, value):
@require_POST
@login_required
@permitted
([
"unvote"
,
"open"
])
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
)
...
...
@@ -195,6 +268,7 @@ def undo_vote_for_thread(request, course_id, thread_id):
@require_POST
@login_required
@permitted
(
"follow_thread"
)
def
follow_thread
(
request
,
course_id
,
thread_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
subscribe_thread
(
user_id
,
thread_id
)
...
...
@@ -202,6 +276,7 @@ def follow_thread(request, course_id, thread_id):
@require_POST
@login_required
@permitted
(
"follow_commentable"
)
def
follow_commentable
(
request
,
course_id
,
commentable_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
subscribe_commentable
(
user_id
,
commentable_id
)
...
...
@@ -209,6 +284,7 @@ def follow_commentable(request, course_id, commentable_id):
@require_POST
@login_required
@permitted
(
"follow_user"
)
def
follow_user
(
request
,
course_id
,
followed_user_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
follow
(
user_id
,
followed_user_id
)
...
...
@@ -216,6 +292,7 @@ def follow_user(request, course_id, followed_user_id):
@require_POST
@login_required
@permitted
(
"unfollow_thread"
)
def
unfollow_thread
(
request
,
course_id
,
thread_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
unsubscribe_thread
(
user_id
,
thread_id
)
...
...
@@ -223,6 +300,7 @@ def unfollow_thread(request, course_id, thread_id):
@require_POST
@login_required
@permitted
(
"unfollow_commentable"
)
def
unfollow_commentable
(
request
,
course_id
,
commentable_id
):
user_id
=
request
.
user
.
id
response
=
comment_client
.
unsubscribe_commentable
(
user_id
,
commentable_id
)
...
...
@@ -230,6 +308,7 @@ def unfollow_commentable(request, course_id, commentable_id):
@require_POST
@login_required
@permitted
(
"unfollow_user"
)
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/migrations/0001_initial.py
0 → 100644
View file @
3f1534ea
# -*- coding: utf-8 -*-
import
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Adding model 'Role'
db
.
create_table
(
'django_comment_client_role'
,
(
(
'name'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
30
,
primary_key
=
True
)),
))
db
.
send_create_signal
(
'django_comment_client'
,
[
'Role'
])
# Adding M2M table for field users on 'Role'
db
.
create_table
(
'django_comment_client_role_users'
,
(
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
primary_key
=
True
,
auto_created
=
True
)),
(
'role'
,
models
.
ForeignKey
(
orm
[
'django_comment_client.role'
],
null
=
False
)),
(
'user'
,
models
.
ForeignKey
(
orm
[
'auth.user'
],
null
=
False
))
))
db
.
create_unique
(
'django_comment_client_role_users'
,
[
'role_id'
,
'user_id'
])
# Adding model 'Permission'
db
.
create_table
(
'django_comment_client_permission'
,
(
(
'name'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
30
,
primary_key
=
True
)),
))
db
.
send_create_signal
(
'django_comment_client'
,
[
'Permission'
])
# Adding M2M table for field users on 'Permission'
db
.
create_table
(
'django_comment_client_permission_users'
,
(
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
primary_key
=
True
,
auto_created
=
True
)),
(
'permission'
,
models
.
ForeignKey
(
orm
[
'django_comment_client.permission'
],
null
=
False
)),
(
'user'
,
models
.
ForeignKey
(
orm
[
'auth.user'
],
null
=
False
))
))
db
.
create_unique
(
'django_comment_client_permission_users'
,
[
'permission_id'
,
'user_id'
])
# Adding M2M table for field roles on 'Permission'
db
.
create_table
(
'django_comment_client_permission_roles'
,
(
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
primary_key
=
True
,
auto_created
=
True
)),
(
'permission'
,
models
.
ForeignKey
(
orm
[
'django_comment_client.permission'
],
null
=
False
)),
(
'role'
,
models
.
ForeignKey
(
orm
[
'django_comment_client.role'
],
null
=
False
))
))
db
.
create_unique
(
'django_comment_client_permission_roles'
,
[
'permission_id'
,
'role_id'
])
def
backwards
(
self
,
orm
):
# Deleting model 'Role'
db
.
delete_table
(
'django_comment_client_role'
)
# Removing M2M table for field users on 'Role'
db
.
delete_table
(
'django_comment_client_role_users'
)
# Deleting model 'Permission'
db
.
delete_table
(
'django_comment_client_permission'
)
# Removing M2M table for field users on 'Permission'
db
.
delete_table
(
'django_comment_client_permission_users'
)
# Removing M2M table for field roles on 'Permission'
db
.
delete_table
(
'django_comment_client_permission_roles'
)
models
=
{
'auth.group'
:
{
'Meta'
:
{
'object_name'
:
'Group'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'80'
}),
'permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
})
},
'auth.permission'
:
{
'Meta'
:
{
'ordering'
:
"('content_type__app_label', 'content_type__model', 'codename')"
,
'unique_together'
:
"(('content_type', 'codename'),)"
,
'object_name'
:
'Permission'
},
'codename'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['contenttypes.ContentType']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
})
},
'auth.user'
:
{
'Meta'
:
{
'object_name'
:
'User'
},
'about'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'avatar_type'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'n'"
,
'max_length'
:
'1'
}),
'bronze'
:
(
'django.db.models.fields.SmallIntegerField'
,
[],
{
'default'
:
'0'
}),
'consecutive_days_visit_count'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'0'
}),
'country'
:
(
'django_countries.fields.CountryField'
,
[],
{
'max_length'
:
'2'
,
'blank'
:
'True'
}),
'date_joined'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'date_of_birth'
:
(
'django.db.models.fields.DateField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'display_tag_filter_strategy'
:
(
'django.db.models.fields.SmallIntegerField'
,
[],
{
'default'
:
'0'
}),
'email'
:
(
'django.db.models.fields.EmailField'
,
[],
{
'max_length'
:
'75'
,
'blank'
:
'True'
}),
'email_isvalid'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'email_key'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'null'
:
'True'
}),
'email_tag_filter_strategy'
:
(
'django.db.models.fields.SmallIntegerField'
,
[],
{
'default'
:
'1'
}),
'first_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'gold'
:
(
'django.db.models.fields.SmallIntegerField'
,
[],
{
'default'
:
'0'
}),
'gravatar'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
}),
'groups'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'ignored_tags'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'interesting_tags'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'is_staff'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'is_superuser'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'last_login'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'last_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'last_seen'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'location'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
,
'blank'
:
'True'
}),
'new_response_count'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'0'
}),
'password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
}),
'questions_per_page'
:
(
'django.db.models.fields.SmallIntegerField'
,
[],
{
'default'
:
'10'
}),
'real_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
,
'blank'
:
'True'
}),
'reputation'
:
(
'django.db.models.fields.PositiveIntegerField'
,
[],
{
'default'
:
'1'
}),
'seen_response_count'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'0'
}),
'show_country'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'silver'
:
(
'django.db.models.fields.SmallIntegerField'
,
[],
{
'default'
:
'0'
}),
'status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'w'"
,
'max_length'
:
'2'
}),
'user_permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'username'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'30'
}),
'website'
:
(
'django.db.models.fields.URLField'
,
[],
{
'max_length'
:
'200'
,
'blank'
:
'True'
})
},
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
},
'django_comment_client.permission'
:
{
'Meta'
:
{
'object_name'
:
'Permission'
},
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'primary_key'
:
'True'
}),
'roles'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'related_name'
:
"'permissions'"
,
'symmetrical'
:
'False'
,
'to'
:
"orm['django_comment_client.Role']"
}),
'users'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'related_name'
:
"'permissions'"
,
'symmetrical'
:
'False'
,
'to'
:
"orm['auth.User']"
})
},
'django_comment_client.role'
:
{
'Meta'
:
{
'object_name'
:
'Role'
},
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'primary_key'
:
'True'
}),
'users'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'related_name'
:
"'roles'"
,
'symmetrical'
:
'False'
,
'to'
:
"orm['auth.User']"
})
}
}
complete_apps
=
[
'django_comment_client'
]
\ No newline at end of file
lms/djangoapps/django_comment_client/migrations/__init__.py
0 → 100644
View file @
3f1534ea
lms/djangoapps/django_comment_client/models.py
0 → 100644
View file @
3f1534ea
from
django.db
import
models
from
django.contrib.auth.models
import
User
class
Role
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
30
,
null
=
False
,
blank
=
False
,
primary_key
=
True
)
users
=
models
.
ManyToManyField
(
User
,
related_name
=
"roles"
)
def
__unicode__
(
self
):
return
self
.
name
@staticmethod
def
register
(
name
):
return
Role
.
objects
.
get_or_create
(
name
=
name
)[
0
]
def
register_permissions
(
self
,
permissions
):
for
p
in
permissions
:
if
not
self
.
permissions
.
filter
(
name
=
p
):
self
.
permissions
.
add
(
Permission
.
register
(
p
))
def
inherit_permissions
(
self
,
role
):
self
.
register_permissions
(
map
(
lambda
p
:
p
.
name
,
role
.
permissions
.
all
()))
class
Permission
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
30
,
null
=
False
,
blank
=
False
,
primary_key
=
True
)
users
=
models
.
ManyToManyField
(
User
,
related_name
=
"permissions"
)
roles
=
models
.
ManyToManyField
(
Role
,
related_name
=
"permissions"
)
def
__unicode__
(
self
):
return
self
.
name
@staticmethod
def
register
(
name
):
return
Permission
.
objects
.
get_or_create
(
name
=
name
)[
0
]
lms/djangoapps/django_comment_client/permissions.py
0 → 100644
View file @
3f1534ea
from
.models
import
Role
,
Permission
from
django.contrib.auth.models
import
User
from
django.db.models.signals
import
post_save
from
django.dispatch
import
receiver
import
logging
def
has_permission
(
user
,
p
):
if
not
Permission
.
objects
.
filter
(
name
=
p
)
.
exists
():
logging
.
warning
(
"Permission
%
s was not registered. "
%
p
)
if
Permission
.
objects
.
filter
(
users
=
user
,
name
=
p
)
.
exists
():
return
True
if
Permission
.
objects
.
filter
(
roles__in
=
user
.
roles
.
all
(),
name
=
p
)
.
exists
():
return
True
return
False
def
has_permissions
(
user
,
*
args
):
for
p
in
args
:
if
not
has_permission
(
user
,
p
):
return
False
return
True
def
add_permission
(
instance
,
p
):
permission
=
Permission
.
register
(
name
=
p
)
if
isinstance
(
instance
,
User
)
or
isinstance
(
isinstance
,
Role
):
instance
.
permissions
.
add
(
permission
)
else
:
raise
TypeError
(
"Permission can only be added to a role or user"
)
@receiver
(
post_save
,
sender
=
User
)
def
assign_default_role
(
sender
,
instance
,
**
kwargs
):
# if kwargs.get("created", True):
role
=
moderator_role
if
instance
.
is_staff
else
student_role
logging
.
info
(
"assign_default_role: adding
%
s as
%
s"
%
(
instance
,
role
))
instance
.
roles
.
add
(
role
)
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"
])
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"
,
])
moderator_role
.
inherit_permissions
(
student_role
)
\ No newline at end of file
lms/djangoapps/django_comment_client/tests.py
0 → 100644
View file @
3f1534ea
from
django.contrib.auth.models
import
User
from
django.utils
import
unittest
import
string
import
random
from
.permissions
import
student_role
,
moderator_role
,
add_permission
,
has_permission
from
.models
import
Role
,
Permission
class
PermissionsTestCase
(
unittest
.
TestCase
):
def
random_str
(
self
,
length
=
15
,
chars
=
string
.
ascii_uppercase
+
string
.
digits
):
return
''
.
join
(
random
.
choice
(
chars
)
for
x
in
range
(
length
))
def
setUp
(
self
):
self
.
student
=
User
.
objects
.
create
(
username
=
self
.
random_str
(),
password
=
"123456"
,
email
=
"john@yahoo.com"
)
self
.
moderator
=
User
.
objects
.
create
(
username
=
self
.
random_str
(),
password
=
"123456"
,
email
=
"staff@edx.org"
)
self
.
moderator
.
is_staff
=
True
self
.
moderator
.
save
()
def
tearDown
(
self
):
self
.
student
.
delete
()
self
.
moderator
.
delete
()
def
testDefaultRoles
(
self
):
self
.
assertTrue
(
student_role
in
self
.
student
.
roles
.
all
())
self
.
assertTrue
(
moderator_role
in
self
.
moderator
.
roles
.
all
())
def
testPermission
(
self
):
name
=
self
.
random_str
()
Permission
.
register
(
name
)
add_permission
(
moderator_role
,
name
)
self
.
assertTrue
(
has_permission
(
self
.
moderator
,
name
))
add_permission
(
self
.
student
,
name
)
self
.
assertTrue
(
has_permission
(
self
.
student
,
name
))
\ No newline at end of file
lms/djangoapps/django_comment_client/utils.py
View file @
3f1534ea
...
...
@@ -115,7 +115,7 @@ class JsonError(HttpResponse):
indent
=
2
,
ensure_ascii
=
False
)
super
(
JsonError
,
self
)
.
__init__
(
content
,
mimetype
=
'application/json; charset=utf8'
)
mimetype
=
'application/json; charset=utf8'
,
status
=
500
)
class
HtmlResponse
(
HttpResponse
):
def
__init__
(
self
,
html
=
''
):
...
...
lms/static/coffee/src/discussion/content.coffee
View file @
3f1534ea
...
...
@@ -195,6 +195,33 @@ initializeFollowThread = (thread) ->
else
$
(
content
).
removeClass
(
"endorsed"
)
handleOpenClose
=
(
elem
,
text
)
->
url
=
Discussion
.
urlFor
(
'openclose_thread'
,
id
)
closed
=
undefined
if
text
.
match
(
/Close/
)
closed
=
true
else
if
text
.
match
(
/[Oo]pen/
)
closed
=
false
else
return
console
.
log
"Unexpected text "
+
text
+
"for open/close thread."
Discussion
.
safeAjax
$elem
:
$
(
elem
)
url
:
url
type
:
"POST"
dataType
:
"json"
data
:
{
closed
:
closed
}
success
:
(
response
,
textStatus
)
=>
if
textStatus
==
"success"
if
closed
$
(
content
).
addClass
(
"closed"
)
$
(
elem
).
text
"Re-open Thread"
else
$
(
content
).
removeClass
(
"closed"
)
$
(
elem
).
text
"Close Thread"
error
:
(
response
,
textStatus
,
e
)
->
console
.
log
e
handleHideSingleThread
=
(
elem
)
->
$threadTitle
=
$local
(
".thread-title"
)
$showComments
=
$local
(
".discussion-show-comments"
)
...
...
@@ -271,6 +298,9 @@ initializeFollowThread = (thread) ->
"click .discussion-endorse"
:
->
handleEndorse
(
this
,
$
(
this
).
is
(
":checked"
))
"click .discussion-openclose"
:
->
handleOpenClose
(
this
,
$
(
this
).
text
())
"click .discussion-edit"
:
->
if
$content
.
hasClass
(
"thread"
)
handleEditThread
(
this
)
...
...
lms/static/coffee/src/discussion/utils.coffee
View file @
3f1534ea
...
...
@@ -29,6 +29,7 @@ wmdEditors = {}
undo_vote_for_thread
:
"/courses/
#{
$$course_id
}
/discussion/threads/
#{
param
}
/unvote"
follow_thread
:
"/courses/
#{
$$course_id
}
/discussion/threads/
#{
param
}
/follow"
unfollow_thread
:
"/courses/
#{
$$course_id
}
/discussion/threads/
#{
param
}
/unfollow"
openclose_thread
:
"/courses/
#{
$$course_id
}
/discussion/threads/
#{
param
}
/close"
update_comment
:
"/courses/
#{
$$course_id
}
/discussion/comments/
#{
param
}
/update"
endorse_comment
:
"/courses/
#{
$$course_id
}
/discussion/comments/
#{
param
}
/endorse"
create_sub_comment
:
"/courses/
#{
$$course_id
}
/discussion/comments/
#{
param
}
/reply"
...
...
lms/templates/discussion/_thread.html
View file @
3f1534ea
...
...
@@ -86,6 +86,13 @@
% endif
<label
class=
"discussion-link"
for=
"discussion-endorse-${content['id']}"
>
Endorsed
</label>
% endif
% if type == "thread" and request.user.is_staff:
% if content['closed']:
<a
class=
"discussion-openclose"
id=
"discussion-openclose-${content['id']}"
href=
"javascript:void(0);"
>
Re-open thread
</a>
% else:
<a
class=
"discussion-openclose"
id=
"discussion-openclose-${content['id']}"
href=
"javascript:void(0);"
>
Close thread
</a>
% endif
% endif
</div>
</
%
def>
...
...
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