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
cf2f5d2a
Commit
cf2f5d2a
authored
Aug 23, 2012
by
Tom Giannattasio
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into feature/tomg/fall-design
parents
163eb9d4
d084506d
Show whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
277 additions
and
134 deletions
+277
-134
common/lib/capa/capa/inputtypes.py
+3
-3
common/lib/capa/capa/templates/choicegroup.html
+0
-3
lms/djangoapps/django_comment_client/base/views.py
+20
-10
lms/djangoapps/django_comment_client/forum/views.py
+5
-2
lms/djangoapps/django_comment_client/helpers.py
+10
-16
lms/djangoapps/django_comment_client/mustache_helpers.py
+2
-1
lms/djangoapps/django_comment_client/settings.py
+10
-0
lms/djangoapps/django_comment_client/utils.py
+19
-0
lms/envs/aws.py
+1
-1
lms/envs/common.py
+4
-0
lms/envs/dev.py
+2
-0
lms/lib/comment_client/settings.py
+4
-1
lms/static/coffee/src/discussion/content.coffee
+48
-27
lms/static/coffee/src/discussion/discussion.coffee
+24
-13
lms/static/coffee/src/discussion/discussion_module.coffee
+4
-2
lms/static/coffee/src/discussion/main.coffee
+6
-0
lms/static/coffee/src/discussion/user_profile.coffee
+19
-24
lms/static/coffee/src/discussion/utils.coffee
+14
-0
lms/static/images/discussion/loading.gif
+0
-0
lms/static/sass/_discussion.scss
+7
-3
lms/static/sass/course.scss
+6
-0
lms/static/sass/course/courseware/courses/_cs188.scss
+32
-0
lms/templates/courseware/courseware.html
+1
-0
lms/templates/discussion/_content_renderer.html
+7
-7
lms/templates/discussion/_similar_posts.html
+9
-0
lms/templates/discussion/_user_active_threads.html
+4
-4
lms/templates/discussion/_user_profile.html
+3
-3
lms/templates/discussion/index.html
+2
-2
lms/templates/discussion/mustache/_content.mustache
+3
-1
lms/templates/discussion/mustache/_new_post.mustache
+1
-5
lms/templates/discussion/single_thread.html
+2
-2
lms/templates/discussion/user_profile.html
+5
-4
No files found.
common/lib/capa/capa/inputtypes.py
View file @
cf2f5d2a
...
...
@@ -185,7 +185,7 @@ def choicegroup(element, value, status, render_template, msg=''):
if
choice
.
text
is
not
None
:
ctext
+=
choice
.
text
# TODO: fix order?
choices
.
append
((
choice
.
get
(
"name"
),
ctext
))
context
=
{
'id'
:
eid
,
'value'
:
value
,
'state'
:
status
,
'input_type'
:
type
,
'choices'
:
choices
,
'
inline'
:
True
,
'
name_array_suffix'
:
''
}
context
=
{
'id'
:
eid
,
'value'
:
value
,
'state'
:
status
,
'input_type'
:
type
,
'choices'
:
choices
,
'name_array_suffix'
:
''
}
html
=
render_template
(
"choicegroup.html"
,
context
)
return
etree
.
XML
(
html
)
...
...
@@ -226,7 +226,7 @@ def radiogroup(element, value, status, render_template, msg=''):
choices
=
extract_choices
(
element
)
context
=
{
'id'
:
eid
,
'value'
:
value
,
'state'
:
status
,
'input_type'
:
'radio'
,
'choices'
:
choices
,
'
inline'
:
False
,
'
name_array_suffix'
:
'[]'
}
context
=
{
'id'
:
eid
,
'value'
:
value
,
'state'
:
status
,
'input_type'
:
'radio'
,
'choices'
:
choices
,
'name_array_suffix'
:
'[]'
}
html
=
render_template
(
"choicegroup.html"
,
context
)
return
etree
.
XML
(
html
)
...
...
@@ -244,7 +244,7 @@ def checkboxgroup(element, value, status, render_template, msg=''):
choices
=
extract_choices
(
element
)
context
=
{
'id'
:
eid
,
'value'
:
value
,
'state'
:
status
,
'input_type'
:
'checkbox'
,
'choices'
:
choices
,
'
inline'
:
False
,
'
name_array_suffix'
:
'[]'
}
context
=
{
'id'
:
eid
,
'value'
:
value
,
'state'
:
status
,
'input_type'
:
'checkbox'
,
'choices'
:
choices
,
'name_array_suffix'
:
'[]'
}
html
=
render_template
(
"choicegroup.html"
,
context
)
return
etree
.
XML
(
html
)
...
...
common/lib/capa/capa/templates/choicegroup.html
View file @
cf2f5d2a
...
...
@@ -6,9 +6,6 @@
checked=
"true"
%
endif
/>
${choice_description}
</label>
% if not inline:
<br/>
% endif
% endfor
<span
id=
"answer_${id}"
></span>
...
...
lms/djangoapps/django_comment_client/base/views.py
View file @
cf2f5d2a
...
...
@@ -8,6 +8,8 @@ import functools
import
comment_client
as
cc
import
django_comment_client.utils
as
utils
import
django_comment_client.settings
as
cc_settings
from
django.core
import
exceptions
from
django.contrib.auth.decorators
import
login_required
...
...
@@ -15,13 +17,11 @@ from django.views.decorators.http import require_POST, require_GET
from
django.views.decorators
import
csrf
from
django.core.files.storage
import
get_storage_class
from
django.utils.translation
import
ugettext
as
_
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
from
mitxmako.shortcuts
import
render_to_response
,
render_to_string
from
courseware.courses
import
get_course_with_access
from
django_comment_client.utils
import
JsonResponse
,
JsonError
,
extract
from
django_comment_client.permissions
import
check_permissions_by_view
...
...
@@ -115,6 +115,9 @@ def _create_comment(request, course_id, thread_id=None, parent_id=None):
@login_required
@permitted
def
create_comment
(
request
,
course_id
,
thread_id
):
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
)
@require_POST
...
...
@@ -159,6 +162,9 @@ def openclose_thread(request, course_id, thread_id):
@login_required
@permitted
def
create_sub_comment
(
request
,
course_id
,
comment_id
):
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
)
@require_POST
...
...
@@ -282,7 +288,7 @@ def update_moderator_status(request, course_id, user_id):
'course_id'
:
course_id
,
'user'
:
request
.
user
,
'django_user'
:
user
,
'
discussion
_user'
:
discussion_user
.
to_dict
(),
'
profiled
_user'
:
discussion_user
.
to_dict
(),
}
return
JsonResponse
({
'html'
:
render_to_string
(
'discussion/ajax_user_profile.html'
,
context
)
...
...
@@ -298,10 +304,13 @@ def search_similar_threads(request, course_id, commentable_id):
'text'
:
text
,
'commentable_id'
:
commentable_id
,
}
result
=
cc
.
search_similar_threads
(
course_id
,
recursive
=
False
,
query_params
=
query_params
)
return
JsonResponse
(
result
)
threads
=
cc
.
search_similar_threads
(
course_id
,
recursive
=
False
,
query_params
=
query_params
)
else
:
return
JsonResponse
([])
theads
=
[]
context
=
{
'threads'
:
map
(
utils
.
extend_content
,
threads
)
}
return
JsonResponse
({
'html'
:
render_to_string
(
'discussion/_similar_posts.html'
,
context
)
})
@require_GET
def
tags_autocomplete
(
request
,
course_id
):
...
...
@@ -334,8 +343,8 @@ def upload(request, course_id):#ajax upload file to a question or answer
# check file type
f
=
request
.
FILES
[
'file-upload'
]
file_extension
=
os
.
path
.
splitext
(
f
.
name
)[
1
]
.
lower
()
if
not
file_extension
in
settings
.
DISCUSSION_
ALLOWED_UPLOAD_FILE_TYPES
:
file_types
=
"', '"
.
join
(
settings
.
DISCUSSION_
ALLOWED_UPLOAD_FILE_TYPES
)
if
not
file_extension
in
cc_settings
.
ALLOWED_UPLOAD_FILE_TYPES
:
file_types
=
"', '"
.
join
(
cc_settings
.
ALLOWED_UPLOAD_FILE_TYPES
)
msg
=
_
(
"allowed file types are '
%(file_types)
s'"
)
%
\
{
'file_types'
:
file_types
}
raise
exceptions
.
PermissionDenied
(
msg
)
...
...
@@ -354,15 +363,16 @@ def upload(request, course_id):#ajax upload file to a question or answer
# check file size
# byte
size
=
file_storage
.
size
(
new_file_name
)
if
size
>
settings
.
ASKBOT_
MAX_UPLOAD_FILE_SIZE
:
if
size
>
cc_settings
.
MAX_UPLOAD_FILE_SIZE
:
file_storage
.
delete
(
new_file_name
)
msg
=
_
(
"maximum upload file size is
%(file_size)
sK"
)
%
\
{
'file_size'
:
settings
.
ASKBOT_
MAX_UPLOAD_FILE_SIZE
}
{
'file_size'
:
cc_settings
.
MAX_UPLOAD_FILE_SIZE
}
raise
exceptions
.
PermissionDenied
(
msg
)
except
exceptions
.
PermissionDenied
,
e
:
error
=
unicode
(
e
)
except
Exception
,
e
:
print
e
logging
.
critical
(
unicode
(
e
))
error
=
_
(
'Error uploading file. Please contact the site administrator. Thank you.'
)
...
...
lms/djangoapps/django_comment_client/forum/views.py
View file @
cf2f5d2a
...
...
@@ -83,7 +83,7 @@ def render_discussion(request, course_id, threads, *args, **kwargs):
'base_url'
:
base_url
,
'query_params'
:
strip_blank
(
strip_none
(
extract
(
query_params
,
[
'page'
,
'sort_key'
,
'sort_order'
,
'tags'
,
'text'
]))),
'annotated_content_info'
:
json
.
dumps
(
annotated_content_info
),
'discussion_data'
:
json
.
dumps
({
discussion_id
:
threads
}),
'discussion_data'
:
json
.
dumps
({
(
discussion_id
or
user_id
):
threads
})
}
context
=
dict
(
context
.
items
()
+
query_params
.
items
())
return
render_to_string
(
template
,
context
)
...
...
@@ -250,7 +250,10 @@ def user_profile(request, course_id, user_id):
content
=
render_user_discussion
(
request
,
course_id
,
threads
,
user_id
=
user_id
,
query_params
=
query_params
)
if
request
.
is_ajax
():
return
utils
.
HtmlResponse
(
content
)
return
utils
.
JsonResponse
({
'html'
:
content
,
'discussionData'
:
threads
,
})
else
:
context
=
{
'course'
:
course
,
...
...
lms/djangoapps/django_comment_client/helpers.py
View file @
cf2f5d2a
...
...
@@ -6,13 +6,14 @@ from django.core.urlresolvers import reverse
from
functools
import
partial
from
utils
import
*
import
django_comment_client.settings
as
cc_settings
import
pystache_custom
as
pystache
import
urllib
import
os
def
pluralize
(
singular_term
,
count
):
if
int
(
count
)
>=
2
:
if
int
(
count
)
>=
2
or
int
(
count
)
==
0
:
return
singular_term
+
's'
return
singular_term
...
...
@@ -33,26 +34,19 @@ def include_mustache_templates():
file_contents
=
map
(
read_file
,
filter
(
valid_file_name
,
os
.
listdir
(
mustache_dir
)))
return
'
\n
'
.
join
(
map
(
wrap_in_tag
,
map
(
strip_file_name
,
file_contents
)))
def
permalink
(
content
):
if
content
[
'type'
]
==
'thread'
:
return
reverse
(
'django_comment_client.forum.views.single_thread'
,
args
=
[
content
[
'course_id'
],
content
[
'commentable_id'
],
content
[
'id'
]])
else
:
return
reverse
(
'django_comment_client.forum.views.single_thread'
,
args
=
[
content
[
'course_id'
],
content
[
'commentable_id'
],
content
[
'thread_id'
]])
+
'#'
+
content
[
'id'
]
def
render_content
(
content
,
additional_context
=
{}):
content_info
=
{
'displayed_title'
:
content
.
get
(
'highlighted_title'
)
or
content
.
get
(
'title'
,
''
),
'displayed_body'
:
content
.
get
(
'highlighted_body'
)
or
content
.
get
(
'body'
,
''
),
'raw_tags'
:
','
.
join
(
content
.
get
(
'tags'
,
[])),
'permalink'
:
permalink
(
content
),
}
context
=
{
'content'
:
merge_dict
(
content
,
content_info
),
'content'
:
extend_content
(
content
),
content
[
'type'
]:
True
,
}
if
cc_settings
.
MAX_COMMENT_DEPTH
is
not
None
:
if
content
[
'type'
]
==
'thread'
:
if
cc_settings
.
MAX_COMMENT_DEPTH
<
0
:
context
[
'max_depth'
]
=
True
elif
content
[
'type'
]
==
'comment'
:
if
cc_settings
.
MAX_COMMENT_DEPTH
<=
content
[
'depth'
]:
context
[
'max_depth'
]
=
True
context
=
merge_dict
(
context
,
additional_context
)
partial_mustache_helpers
=
{
k
:
partial
(
v
,
content
)
for
k
,
v
in
mustache_helpers
.
items
()}
context
=
merge_dict
(
context
,
partial_mustache_helpers
)
...
...
lms/djangoapps/django_comment_client/mustache_helpers.py
View file @
cf2f5d2a
...
...
@@ -7,7 +7,8 @@ import inspect
def
pluralize
(
content
,
text
):
num
,
word
=
text
.
split
(
' '
)
if
int
(
num
or
'0'
)
>=
2
:
num
=
int
(
num
or
'0'
)
if
num
>=
2
or
num
==
0
:
return
word
+
's'
else
:
return
word
...
...
lms/djangoapps/django_comment_client/settings.py
0 → 100644
View file @
cf2f5d2a
from
django.conf
import
settings
MAX_COMMENT_DEPTH
=
None
MAX_UPLOAD_FILE_SIZE
=
1024
*
1024
#result in bytes
ALLOWED_UPLOAD_FILE_TYPES
=
(
'.jpg'
,
'.jpeg'
,
'.gif'
,
'.bmp'
,
'.png'
,
'.tiff'
)
if
hasattr
(
settings
,
'DISCUSSION_SETTINGS'
):
MAX_COMMENT_DEPTH
=
settings
.
DISCUSSION_SETTINGS
.
get
(
'MAX_COMMENT_DEPTH'
)
MAX_UPLOAD_FILE_SIZE
=
settings
.
DISCUSSION_SETTINGS
.
get
(
'MAX_UPLOAD_FILE_SIZE'
)
or
MAX_UPLOAD_FILE_SIZE
ALLOWED_UPLOAD_FILE_TYPES
=
settings
.
DISCUSSION_SETTINGS
.
get
(
'ALLOWED_UPLOAD_FILE_TYPES'
)
or
ALLOWED_UPLOAD_FILE_TYPES
lms/djangoapps/django_comment_client/utils.py
View file @
cf2f5d2a
...
...
@@ -21,6 +21,8 @@ import pystache_custom as pystache
_FULLMODULES
=
None
_DISCUSSIONINFO
=
None
def
extract
(
dic
,
keys
):
return
{
k
:
dic
.
get
(
k
)
for
k
in
keys
}
...
...
@@ -197,3 +199,20 @@ def url_for_tags(course_id, tags):
def
render_mustache
(
template_name
,
dictionary
,
*
args
,
**
kwargs
):
template
=
middleware
.
lookup
[
'main'
]
.
get_template
(
template_name
)
.
source
return
pystache
.
render
(
template
,
dictionary
)
def
permalink
(
content
):
if
content
[
'type'
]
==
'thread'
:
return
reverse
(
'django_comment_client.forum.views.single_thread'
,
args
=
[
content
[
'course_id'
],
content
[
'commentable_id'
],
content
[
'id'
]])
else
:
return
reverse
(
'django_comment_client.forum.views.single_thread'
,
args
=
[
content
[
'course_id'
],
content
[
'commentable_id'
],
content
[
'thread_id'
]])
+
'#'
+
content
[
'id'
]
def
extend_content
(
content
):
content_info
=
{
'displayed_title'
:
content
.
get
(
'highlighted_title'
)
or
content
.
get
(
'title'
,
''
),
'displayed_body'
:
content
.
get
(
'highlighted_body'
)
or
content
.
get
(
'body'
,
''
),
'raw_tags'
:
','
.
join
(
content
.
get
(
'tags'
,
[])),
'permalink'
:
permalink
(
content
),
}
return
merge_dict
(
content
,
content_info
)
lms/envs/aws.py
View file @
cf2f5d2a
...
...
@@ -68,4 +68,4 @@ if 'COURSE_ID' in ENV_TOKENS:
ASKBOT_URL
=
"courses/{0}/discussions/"
.
format
(
ENV_TOKENS
[
'COURSE_ID'
])
COMMENTS_SERVICE_URL
=
ENV_TOKENS
[
"COMMENTS_SERVICE_URL"
]
COMMENTS_SERVICE_KEY
=
ENV_TOKENS
[
"COMMENTS_SERVICE_KEY"
]
lms/envs/common.py
View file @
cf2f5d2a
...
...
@@ -38,6 +38,10 @@ ASKBOT_ENABLED = False
GENERATE_RANDOM_USER_CREDENTIALS
=
False
PERFSTATS
=
False
DISCUSSION_SETTINGS
=
{
'MAX_COMMENT_DEPTH'
:
2
,
}
# Features
MITX_FEATURES
=
{
'SAMPLE'
:
False
,
...
...
lms/envs/dev.py
View file @
cf2f5d2a
...
...
@@ -92,6 +92,8 @@ SUBDOMAIN_BRANDING = {
'harvard'
:
'HarvardX'
,
}
COMMENTS_SERVICE_KEY
=
"PUT_YOUR_API_KEY_HERE"
################################ LMS Migration #################################
MITX_FEATURES
[
'ENABLE_LMS_MIGRATION'
]
=
True
MITX_FEATURES
[
'ACCESS_REQUIRE_STAFF_FOR_COURSE'
]
=
False
# require that user be in the staff_* group to be able to enroll
...
...
lms/lib/comment_client/settings.py
View file @
cf2f5d2a
...
...
@@ -7,4 +7,7 @@ else:
PREFIX
=
SERVICE_HOST
+
'/api/v1'
API_KEY
=
"PUT_YOUR_API_KEY_HERE"
if
hasattr
(
settings
,
"COMMENTS_SERVICE_KEY"
):
API_KEY
=
settings
.
COMMENTS_SERVICE_KEY
else
:
API_KEY
=
"PUT_YOUR_API_KEY_HERE"
lms/static/coffee/src/discussion/content.coffee
View file @
cf2f5d2a
class
@
Content
extends
Backbone
.
Model
if
Backbone
?
class
@
Content
extends
Backbone
.
Model
template
:
->
DiscussionUtil
.
getTemplate
(
'_content'
)
...
...
@@ -49,7 +50,7 @@ class @Content extends Backbone.Model
@
resetComments
(
@
get
(
'children'
))
class
@
ContentView
extends
Backbone
.
View
class
@
ContentView
extends
Backbone
.
View
$
:
(
selector
)
->
@
$local
.
find
(
selector
)
...
...
@@ -119,7 +120,12 @@ class @ContentView extends Backbone.View
else
$elem
=
$
.
merge
@
$
(
".thread-title"
),
@
$showComments
()
url
=
@
model
.
urlFor
(
'retrieve'
)
DiscussionUtil
.
get
$elem
,
url
,
{},
(
response
,
textStatus
)
=>
DiscussionUtil
.
safeAjax
$elem
:
$elem
$loading
:
$
(
event
.
target
)
if
event
type
:
"GET"
url
:
url
success
:
(
response
,
textStatus
)
=>
@
showed
=
true
@
updateShowComments
()
@
$showComments
().
addClass
(
"retrieved"
)
...
...
@@ -168,6 +174,7 @@ class @ContentView extends Backbone.View
DiscussionUtil
.
safeAjax
$elem
:
$
(
event
.
target
)
$loading
:
$
(
event
.
target
)
if
event
url
:
url
type
:
"POST"
dataType
:
'json'
...
...
@@ -196,14 +203,22 @@ class @ContentView extends Backbone.View
unvote
:
(
event
)
->
url
=
@
model
.
urlFor
(
'unvote'
)
$elem
=
@
$
(
".discussion-vote"
)
DiscussionUtil
.
post
$elem
,
url
,
{},
(
response
,
textStatus
)
=>
DiscussionUtil
.
safeAjax
$elem
:
$elem
url
:
url
type
:
"POST"
success
:
(
response
,
textStatus
)
=>
@
model
.
set
(
'voted'
,
''
)
@
model
.
set
(
'votes_point'
,
response
.
votes
.
point
)
vote
:
(
event
,
value
)
->
url
=
@
model
.
urlFor
(
"
#{
value
}
vote"
)
$elem
=
@
$
(
".discussion-vote"
)
DiscussionUtil
.
post
$elem
,
url
,
{},
(
response
,
textStatus
)
=>
DiscussionUtil
.
safeAjax
$elem
:
$elem
url
:
url
type
:
"POST"
success
:
(
response
,
textStatus
)
=>
@
model
.
set
(
'voted'
,
value
)
@
model
.
set
(
'votes_point'
,
response
.
votes
.
point
)
...
...
@@ -220,7 +235,12 @@ class @ContentView extends Backbone.View
url
=
@
model
.
urlFor
(
'endorse'
)
endorsed
=
@
model
.
get
(
'endorsed'
)
data
=
{
endorsed
:
not
endorsed
}
DiscussionUtil
.
post
$elem
,
url
,
data
,
(
response
,
textStatus
)
=>
DiscussionUtil
.
safeAjax
$elem
:
$elem
url
:
url
data
:
data
type
:
"POST"
success
:
(
response
,
textStatus
)
=>
@
model
.
set
(
'endorsed'
,
not
endorsed
)
toggleFollow
:
(
event
)
->
...
...
@@ -230,7 +250,11 @@ class @ContentView extends Backbone.View
url
=
@
model
.
urlFor
(
'unfollow'
)
else
url
=
@
model
.
urlFor
(
'follow'
)
DiscussionUtil
.
post
$elem
,
url
,
{},
(
response
,
textStatus
)
=>
DiscussionUtil
.
safeAjax
$elem
:
$elem
url
:
url
type
:
"POST"
success
:
(
response
,
textStatus
)
=>
@
model
.
set
(
'subscribed'
,
not
subscribed
)
toggleClosed
:
(
event
)
->
...
...
@@ -238,7 +262,12 @@ class @ContentView extends Backbone.View
url
=
@
model
.
urlFor
(
'close'
)
closed
=
@
model
.
get
(
'closed'
)
data
=
{
closed
:
not
closed
}
DiscussionUtil
.
post
$elem
,
url
,
data
,
(
response
,
textStatus
)
=>
DiscussionUtil
.
safeAjax
$elem
:
$elem
url
:
url
type
:
"POST"
data
:
data
success
:
(
response
,
textStatus
)
=>
@
model
.
set
(
'closed'
,
not
closed
)
edit
:
(
event
)
->
...
...
@@ -256,7 +285,7 @@ class @ContentView extends Backbone.View
else
view
.
body
=
@
$
(
".comment-raw-body"
).
html
()
@
$discussionContent
().
append
Mustache
.
render
DiscussionUtil
.
getTemplate
(
"_edit_
#{
@
model
.
get
(
'type'
)
}
"
),
view
Discussion
.
makeWmdEditor
@
$el
,
$
.
proxy
(
@
$
,
@
),
"
#{
@
model
.
get
(
'type'
)
}
-body-edit"
DiscussionUtil
.
makeWmdEditor
@
$el
,
$
.
proxy
(
@
$
,
@
),
"
#{
@
model
.
get
(
'type'
)
}
-body-edit"
@
$
(
".thread-tags-edit"
).
tagsInput
DiscussionUtil
.
tagsInputOptions
()
@
$
(
".discussion-submit-update"
).
unbind
(
"click"
).
click
$
.
proxy
(
@
submitEdit
,
@
)
@
$
(
".discussion-cancel-update"
).
unbind
(
"click"
).
click
$
.
proxy
(
@
cancelEdit
,
@
)
...
...
@@ -273,6 +302,7 @@ class @ContentView extends Backbone.View
data
.
body
=
DiscussionUtil
.
getWmdContent
@
$el
,
$
.
proxy
(
@
$
,
@
),
"comment-body-edit"
DiscussionUtil
.
safeAjax
$elem
:
$
(
event
.
target
)
$loading
:
$
(
event
.
target
)
if
event
url
:
url
type
:
"POST"
dataType
:
'json'
...
...
@@ -297,7 +327,10 @@ class @ContentView extends Backbone.View
if
not
c
return
$elem
=
$
(
event
.
target
)
DiscussionUtil
.
post
$elem
,
url
,
{},
(
response
,
textStatus
)
=>
DiscussionUtil
.
safeAjax
$elem
:
$elem
url
:
url
success
:
(
response
,
textStatus
)
=>
@
$el
.
remove
()
@
model
.
get
(
'thread'
).
removeComment
(
@
model
)
...
...
@@ -332,9 +365,6 @@ class @ContentView extends Backbone.View
initTimeago
:
->
@
$
(
"span.timeago"
).
timeago
()
initPermalink
:
->
@
$
(
".discussion-permanent-link"
).
attr
"href"
,
@
model
.
permalink
()
renderPartial
:
->
for
attr
,
value
of
@
model
.
changedAttributes
()
if
@
partial
[
attr
]
...
...
@@ -352,7 +382,7 @@ class @ContentView extends Backbone.View
@
initBody
()
@
initCommentViews
()
class
@
Thread
extends
@
Content
class
@
Thread
extends
@
Content
urlMappers
:
'retrieve'
:
->
DiscussionUtil
.
urlFor
(
'retrieve_single_thread'
,
@
discussion
.
id
,
@
id
)
'reply'
:
->
DiscussionUtil
.
urlFor
(
'create_comment'
,
@
id
)
...
...
@@ -369,13 +399,9 @@ class @Thread extends @Content
@
set
(
'thread'
,
@
)
super
()
permalink
:
->
discussion_id
=
@
get
(
'commentable_id'
)
return
Discussion
.
urlFor
(
"permanent_link_thread"
,
discussion_id
,
@
id
)
class
@
ThreadView
extends
@
ContentView
class
@
ThreadView
extends
@
ContentView
class
@
Comment
extends
@
Content
class
@
Comment
extends
@
Content
urlMappers
:
'reply'
:
->
DiscussionUtil
.
urlFor
(
'create_sub_comment'
,
@
id
)
'unvote'
:
->
DiscussionUtil
.
urlFor
(
"undo_vote_for_
#{
@
get
(
'type'
)
}
"
,
@
id
)
...
...
@@ -385,20 +411,15 @@ class @Comment extends @Content
'update'
:
->
DiscussionUtil
.
urlFor
(
'update_comment'
,
@
id
)
'delete'
:
->
DiscussionUtil
.
urlFor
(
'delete_comment'
,
@
id
)
permalink
:
->
thread_id
=
@
get
(
'thread'
).
id
discussion_id
=
@
get
(
'thread'
).
get
(
'commentable_id'
)
return
Discussion
.
urlFor
(
"permanent_link_comment"
,
discussion_id
,
thread_id
,
@
id
)
getCommentsCount
:
->
count
=
0
@
get
(
'comments'
).
each
(
comment
)
->
count
+=
comment
.
getCommentsCount
()
+
1
count
class
@
CommentView
extends
@
ContentView
class
@
CommentView
extends
@
ContentView
class
@
Comments
extends
Backbone
.
Collection
class
@
Comments
extends
Backbone
.
Collection
model
:
Comment
...
...
lms/static/coffee/src/discussion/discussion.coffee
View file @
cf2f5d2a
class
@
Discussion
extends
Backbone
.
Collection
if
Backbone
?
class
@
Discussion
extends
Backbone
.
Collection
model
:
Thread
initialize
:
->
...
...
@@ -15,7 +16,7 @@ class @Discussion extends Backbone.Collection
@
add
model
model
class
@
DiscussionView
extends
Backbone
.
View
class
@
DiscussionView
extends
Backbone
.
View
$
:
(
selector
)
->
@
$local
.
find
(
selector
)
...
...
@@ -38,15 +39,22 @@ class @DiscussionView extends Backbone.View
reload
:
(
$elem
,
url
)
->
if
not
url
then
return
DiscussionUtil
.
get
$elem
,
url
,
{},
(
response
,
textStatus
)
=>
DiscussionUtil
.
safeAjax
$elem
:
$elem
$loading
:
$elem
url
:
url
type
:
"GET"
success
:
(
response
,
textStatus
)
=>
$parent
=
@
$el
.
parent
()
@
$el
.
replaceWith
(
response
.
html
)
$discussion
=
$parent
.
find
(
"section.discussion"
)
@
model
.
reset
(
response
.
discussionData
,
{
silent
:
false
})
view
=
new
DiscussionView
el
:
$discussion
[
0
],
model
:
@
model
DiscussionUtil
.
bulkUpdateContentInfo
(
window
.
$
$annotated_content_info
)
$
(
"html, body"
).
animate
({
scrollTop
:
0
},
0
)
loadSimilarPost
:
(
event
)
->
console
.
log
"loading similar"
$title
=
@
$
(
".new-post-title"
)
$wrapper
=
@
$
(
".new-post-similar-posts-wrapper"
)
$similarPosts
=
@
$
(
".new-post-similar-posts"
)
...
...
@@ -59,16 +67,16 @@ class @DiscussionView extends Backbone.View
$elem
=
$
(
event
.
target
)
url
=
DiscussionUtil
.
urlFor
'search_similar_threads'
,
@
model
.
id
data
=
{
text
:
@
$
(
".new-post-title"
).
val
()
}
DiscussionUtil
.
get
$elem
,
url
,
data
,
(
response
,
textStatus
)
=>
$similarPosts
.
empty
()
if
$
.
type
(
response
)
==
"array"
and
response
.
length
DiscussionUtil
.
safeAjax
$elem
:
$elem
url
:
url
data
:
data
dataType
:
'json'
success
:
(
response
,
textStatus
)
=>
$wrapper
.
html
(
response
.
html
)
if
$wrapper
.
find
(
".similar-post"
).
length
$wrapper
.
show
()
for
thread
in
response
$similarPost
=
$
(
"<a>"
).
addClass
(
"similar-post"
)
.
html
(
thread
[
"title"
])
.
attr
(
"href"
,
"javascript:void(0)"
)
#TODO
.
appendTo
(
$similarPosts
)
else
$wrapper
.
find
(
".hide-similar-posts"
).
click
=>
$wrapper
.
hide
()
else
$wrapper
.
hide
()
...
...
@@ -112,6 +120,7 @@ class @DiscussionView extends Backbone.View
url
=
DiscussionUtil
.
urlFor
(
'create_thread'
,
@
model
.
id
)
DiscussionUtil
.
safeAjax
$elem
:
$
(
event
.
target
)
$loading
:
$
(
event
.
target
)
if
event
url
:
url
type
:
"POST"
dataType
:
'json'
...
...
@@ -127,7 +136,9 @@ class @DiscussionView extends Backbone.View
$thread
=
$
(
response
.
html
)
@
$el
.
children
(
".threads"
).
prepend
(
$thread
)
@
$
(
".new-post-title"
).
val
(
""
)
@
$
(
".new-post-similar-posts"
).
empty
()
@
$
(
".new-post-similar-posts-wrapper"
).
hide
()
@
$
(
".new-post-title"
).
val
(
""
).
attr
(
"prev-text"
,
""
)
DiscussionUtil
.
setWmdContent
@
$el
,
$
.
proxy
(
@
$
,
@
),
"new-post-body"
,
""
@
$
(
".new-post-tags"
).
val
(
""
)
@
$
(
".new-post-tags"
).
importTags
(
""
)
...
...
lms/static/coffee/src/discussion/discussion_module.coffee
View file @
cf2f5d2a
class
@
DiscussionModuleView
extends
Backbone
.
View
if
Backbone
?
class
@
DiscussionModuleView
extends
Backbone
.
View
events
:
"click .discussion-show"
:
"toggleDiscussion"
toggleDiscussion
:
(
event
)
->
...
...
@@ -15,8 +16,9 @@ class @DiscussionModuleView extends Backbone.View
$elem
=
$
(
event
.
target
)
discussion_id
=
$elem
.
attr
(
"discussion_id"
)
url
=
DiscussionUtil
.
urlFor
'retrieve_discussion'
,
discussion_id
Discussion
.
safeAjax
DiscussionUtil
.
safeAjax
$elem
:
$elem
$loading
:
$elem
url
:
url
type
:
"GET"
dataType
:
'json'
...
...
lms/static/coffee/src/discussion/main.coffee
View file @
cf2f5d2a
...
...
@@ -12,4 +12,10 @@ $ ->
discussion
.
reset
(
discussionData
,
{
silent
:
false
})
view
=
new
DiscussionView
(
el
:
elem
,
model
:
discussion
)
if
window
.
$
$annotated_content_info
?
DiscussionUtil
.
bulkUpdateContentInfo
(
window
.
$
$annotated_content_info
)
$userProfile
=
$
(
".discussion-sidebar>.user-profile"
)
if
$userProfile
.
length
console
.
log
"initialize user profile"
view
=
new
DiscussionUserProfileView
(
el
:
$userProfile
[
0
])
lms/static/coffee/src/discussion/user_profile.coffee
View file @
cf2f5d2a
if
not
@
Discussion
?
@
Discussion
=
{}
Discussion
=
@
Discussion
@
Discussion
=
$
.
extend
@
Discussion
,
initializeUserProfile
:
(
$userProfile
)
->
$local
=
Discussion
.
generateLocal
$userProfile
handleUpdateModeratorStatus
=
(
elem
,
isModerator
)
->
class
@
DiscussionUserProfileView
extends
Backbone
.
View
toggleModeratorStatus
:
(
event
)
->
confirmValue
=
confirm
(
"Are you sure?"
)
if
not
confirmValue
then
return
url
=
Discussion
.
urlFor
(
'update_moderator_status'
,
$$profiled_user_id
)
Discussion
.
safeAjax
$elem
:
$
(
elem
)
$elem
=
$
(
event
.
target
)
if
$elem
.
hasClass
(
"sidebar-promote-moderator-button"
)
isModerator
=
true
else
if
$elem
.
hasClass
(
"sidebar-revoke-moderator-button"
)
isModerator
=
false
else
console
.
error
"unrecognized moderator status"
return
url
=
DiscussionUtil
.
urlFor
(
'update_moderator_status'
,
$$profiled_user_id
)
DiscussionUtil
.
safeAjax
$elem
:
$elem
url
:
url
type
:
"POST"
dataType
:
'json'
...
...
@@ -20,15 +20,10 @@ Discussion = @Discussion
is_moderator
:
isModerator
error
:
(
response
,
textStatus
,
e
)
->
console
.
log
e
success
:
(
response
,
textStatus
)
->
parent
=
$userProfile
.
parent
()
$userProfile
.
replaceWith
(
response
.
html
)
Discussion
.
initializeUserProfile
parent
.
children
(
".user-profile"
)
Discussion
.
bindLocalEvents
$local
,
"click .sidebar-revoke-moderator-button"
:
(
event
)
->
handleUpdateModeratorStatus
(
this
,
false
)
"click .sidebar-promote-moderator-button"
:
(
event
)
->
handleUpdateModeratorStatus
(
this
,
true
)
success
:
(
response
,
textStatus
)
=>
parent
=
@
$el
.
parent
()
@
$el
.
replaceWith
(
response
.
html
)
view
=
new
DiscussionUserProfileView
el
:
parent
.
children
(
".user-profile"
)
initializeUserActiveDiscussion
:
(
$discussion
)
->
events
:
"click .sidebar-toggle-moderator-button"
:
"toggleModeratorStatus"
lms/static/coffee/src/discussion/utils.coffee
View file @
cf2f5d2a
$
->
$
.
fn
.
extend
loading
:
->
$
(
this
).
after
(
"<span class='discussion-loading'></span>"
)
loaded
:
->
$
(
this
).
parent
().
children
(
".discussion-loading"
).
remove
()
class
@
DiscussionUtil
@
wmdEditors
:
{}
...
...
@@ -62,9 +69,16 @@ class @DiscussionUtil
$elem
=
params
.
$elem
if
$elem
.
attr
(
"disabled"
)
return
params
[
"beforeSend"
]
=
->
$elem
.
attr
(
"disabled"
,
"disabled"
)
if
params
[
"$loading"
]
console
.
log
"loading"
params
[
"$loading"
].
loading
()
$
.
ajax
(
params
).
always
->
$elem
.
removeAttr
(
"disabled"
)
if
params
[
"$loading"
]
console
.
log
"loaded"
params
[
"$loading"
].
loaded
()
@
get
:
(
$elem
,
url
,
data
,
success
)
->
@
safeAjax
...
...
lms/static/images/discussion/loading.gif
0 → 100644
View file @
cf2f5d2a
809 Bytes
lms/static/sass/_discussion.scss
View file @
cf2f5d2a
...
...
@@ -35,7 +35,13 @@ $tag-text-color: #5b614f;
}
}
.discussion-loading
{
background-image
:
url(../images/discussion/loading.gif)
;
width
:
15px
;
height
:
15px
;
margin-left
:
2px
;
display
:
inline-block
;
}
/*** Discussions ***/
...
...
@@ -49,8 +55,6 @@ $tag-text-color: #5b614f;
margin-top
:
0
;
}
/*** Sidebar ***/
.sidebar-module
{
...
...
lms/static/sass/course.scss
View file @
cf2f5d2a
...
...
@@ -21,6 +21,12 @@
@import
'course/courseware/sidebar'
;
@import
'course/courseware/amplifier'
;
// course-specific courseware (all styles in these files should be gated by a
// course-specific class). This should be replaced with a better way of
// providing course-specific styling.
@import
"course/courseware/courses/_cs188.scss"
;
// wiki
@import
"course/wiki/basic-html"
;
@import
"course/wiki/sidebar"
;
...
...
lms/static/sass/course/courseware/courses/_cs188.scss
0 → 100644
View file @
cf2f5d2a
body
.cs188
{
.course-content
{
.project
{
ul
,
ol
{
margin-top
:
3px
;
list-style
:
disc
;
ul
,
ol
{
margin
:
0px
;
}
}
}
h3
,
h4
{
font-weight
:
bold
;
a
{
color
:
inherit
;
}
}
h4
{
font-size
:
1em
;
}
p
,
.code_snippet
{
margin-bottom
:
1
.416em
;
}
}
}
lms/templates/courseware/courseware.html
View file @
cf2f5d2a
...
...
@@ -22,6 +22,7 @@
##
<script
type=
"text/javascript"
src=
"${static.url('js/vendor/CodeMirror-2.25/mode/python/python.js')}"
></script>
<
%
static:js
group=
'courseware'
/>
<
%
static:js
group=
'discussion'
/>
<
%
include
file=
"../discussion/_js_body_dependencies.html"
/>
...
...
lms/templates/discussion/_content_renderer.html
View file @
cf2f5d2a
<
%!
import
django_comment_client
.
helpers
as
helpers
%
>
<
%
def
name=
"render_content(content)"
>
${helpers.render_content(content)}
<
%
def
name=
"render_content(content
, *args, **kwargs
)"
>
${helpers.render_content(content
, *args, **kwargs
)}
</
%
def>
<
%
def
name=
"render_content_with_comments(content)"
>
<
%
def
name=
"render_content_with_comments(content
, *args, **kwargs
)"
>
<div
class=
"${content['type']}${helpers.show_if(' endorsed', content.get('endorsed'))}"
_id=
"${content['id']}"
_discussion_id=
"${content.get('commentable_id', '')}"
_author_id=
"${helpers.show_if(content['user_id'], not content.get('anonymous'))}"
>
${render_content(content)}
${render_comments(content.get('children', []))}
${render_content(content
, *args, **kwargs
)}
${render_comments(content.get('children', [])
, *args, **kwargs
)}
</div>
</
%
def>
<
%
def
name=
"render_comments(comments)"
>
<
%
def
name=
"render_comments(comments
, *args, **kwargs
)"
>
<div
class=
"comments"
>
% for comment in comments:
${render_content_with_comments(comment)}
${render_content_with_comments(comment
, *args, **kwargs
)}
% endfor
</div>
</
%
def>
lms/templates/discussion/_similar_posts.html
0 → 100644
View file @
cf2f5d2a
% if len(threads) > 0:
Similar Posts:
<a
class=
"hide-similar-posts"
href=
"javascript:void(0)"
>
Hide
</a>
<div
class=
"new-post-similar-posts"
>
% for thread in threads:
<a
class=
"similar-post"
href=
"${thread['permalink']}"
>
${thread['title']}
</a>
% endfor
</div>
% endif
lms/templates/discussion/_user_active_threads.html
View file @
cf2f5d2a
<
%
namespace
name=
"renderer"
file=
"_
thread
.html"
/>
<
%
namespace
name=
"renderer"
file=
"_
content_renderer
.html"
/>
<section
class=
"discussion user-active-discussion"
>
<section
class=
"discussion user-active-discussion"
_id=
"${user_id}"
>
<div
class=
"discussion-non-content
discussion-
local"
></div>
<div
class=
"discussion-non-content local"
></div>
<div
class=
"threads"
>
% for thread in threads:
${renderer.render_
thread(course_id, thread, show_comments=True
)}
${renderer.render_
content_with_comments(thread, {'partial_comments': True}
)}
% endfor
</div>
...
...
lms/templates/discussion/_user_profile.html
View file @
cf2f5d2a
<
%!
from
django_comment_client
.
util
s
import
pluralize
%
>
<
%!
from
django_comment_client
.
helper
s
import
pluralize
%
>
<
%!
from
django_comment_client
.
permissions
import
has_permission
,
check_permissions_by_view
%
>
<
%!
from
operator
import
attrgetter
%
>
...
...
@@ -15,9 +15,9 @@
<div
class=
"sidebar-comments-count"
><span>
${profiled_user['comments_count']}
</span>
${pluralize('comment', profiled_user['comments_count'])}
</div>
% if check_permissions_by_view(user, course.id, content=None, name='update_moderator_status'):
% if "Moderator" in role_names:
<a
href=
"javascript:void(0)"
class=
"sidebar-revoke-moderator-button"
>
Revoke Moderator provileges
</a>
<a
href=
"javascript:void(0)"
class=
"sidebar-
toggle-moderator-button sidebar-
revoke-moderator-button"
>
Revoke Moderator provileges
</a>
% else:
<a
href=
"javascript:void(0)"
class=
"sidebar-promote-moderator-button"
>
Promote to Moderator
</a>
<a
href=
"javascript:void(0)"
class=
"sidebar-
toggle-moderator-button sidebar-
promote-moderator-button"
>
Promote to Moderator
</a>
% endif
% endif
</div>
lms/templates/discussion/index.html
View file @
cf2f5d2a
<
%
inherit
file=
"../main.html"
/>
<
%
namespace
name=
'static'
file=
'../static_content.html'
/>
<
%
block
name=
"bodyclass"
>
discussion
</
%
block>
<
%
block
name=
"title"
><title>
Discussion –
MITx 6.002x
</title></
%
block>
<
%
block
name=
"title"
><title>
Discussion –
${course.number}
</title></
%
block>
<
%
block
name=
"headextra"
>
<
%
static:css
group=
'course'
/>
...
...
@@ -32,7 +32,7 @@
</section>
<section
class=
"course-content"
>
${content}
${content
.decode('utf-8')
}
</section>
</div>
</section>
lms/templates/discussion/mustache/_content.mustache
View file @
cf2f5d2a
...
...
@@ -37,7 +37,7 @@
anonymous
{{/
content
.
anonymous
}}
{{^
content
.
anonymous
}}
{{
content
.
username
}}
<a
href=
"
{{#
#
url_for_user
}}{{
content
.
user_id
}}{{/
url_for_user
}}
"
>
{{
content
.
username
}}
</a>
{{/
content
.
anonymous
}}
</div>
<div
class=
"show-comments-wrapper"
>
...
...
@@ -51,7 +51,9 @@
{{/
thread
}}
</div>
<ul
class=
"discussion-actions"
>
{{^
max_depth
}}
<li><a
class=
"discussion-link discussion-reply discussion-reply-
{{
content
.
type
}}
"
href=
"javascript:void(0)"
>
Reply
</a></li>
{{/
max_depth
}}
{{#
thread
}}
<li><div
class=
"follow-wrapper"
><a
class=
"discussion-link discussion-follow-thread"
href=
"javascript:void(0)"
>
Follow
</a></div></li>
{{/
thread
}}
...
...
lms/templates/discussion/mustache/_new_post.mustache
View file @
cf2f5d2a
<form
class=
"new-post-form collapsed"
id=
"new-post-form"
style=
"display: block; "
>
<ul
class=
"new-post-form-errors discussion-errors"
></ul>
<input
type=
"text"
class=
"new-post-title title-input"
placeholder=
"Title"
/>
<div
class=
"new-post-similar-posts-wrapper"
style=
"display: none"
>
Similar Posts:
<a
class=
"hide-similar-posts"
href=
"javascript:void(0)"
>
Hide
</a>
<div
class=
"new-post-similar-posts"
></div>
</div>
<div
class=
"new-post-similar-posts-wrapper"
style=
"display: none"
></div>
<div
class=
"new-post-body reply-body"
></div>
<input
class=
"new-post-tags"
placeholder=
"Tags"
/>
<div
class=
"post-options"
>
...
...
lms/templates/discussion/single_thread.html
View file @
cf2f5d2a
<
%
inherit
file=
"../main.html"
/>
<
%
namespace
name=
'static'
file=
'../static_content.html'
/>
<
%
block
name=
"bodyclass"
>
discussion
</
%
block>
<
%
block
name=
"title"
><title>
Discussion –
MITx 6.002x
</title></
%
block>
<
%
block
name=
"title"
><title>
Discussion –
${course.number}
</title></
%
block>
<
%
block
name=
"headextra"
>
<
%
static:css
group=
'course'
/>
...
...
@@ -30,7 +30,7 @@
</section>
<section
class=
"course-content"
>
${content}
${content
.decode('utf-8')
}
</section>
</div>
</section>
lms/templates/discussion/user_profile.html
View file @
cf2f5d2a
<
%!
from
django
.
template
.
defaultfilters
import
escapejs
%
>
<
%
namespace
name=
"renderer"
file=
"_thread.html"
/>
<
%
inherit
file=
"../main.html"
/>
<
%
namespace
name=
'static'
file=
'../static_content.html'
/>
<
%
block
name=
"bodyclass"
>
discussion
</
%
block>
<
%
block
name=
"title"
><title>
Discussion –
MITx 6.002x
</title></
%
block>
<
%
block
name=
"title"
><title>
Discussion –
${course.number}
</title></
%
block>
<
%
block
name=
"headextra"
>
<
%
static:css
group=
'course'
/>
<
%
include
file=
"_js_head_dependencies.html"
/>
</
%
block>
<
%
block
name=
"js_extra"
>
<
%
include
file=
"_js_dependencies.html"
/>
<
%
include
file=
"_js_body_dependencies.html"
/>
<
%
static:js
group=
'discussion'
/>
</
%
block>
<
%
include
file=
"/courseware/course_navigation.html"
args=
"active_page='discussion'"
/>
<
%
include
file=
"
..
/courseware/course_navigation.html"
args=
"active_page='discussion'"
/>
<section
class=
"container"
>
<div
class=
"course-wrapper"
>
...
...
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