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
89f93df4
Commit
89f93df4
authored
Jul 01, 2016
by
Andy Armstrong
Committed by
Brian Jacobel
Aug 17, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor out a new discussions Django app
parent
c4e5b201
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
477 additions
and
328 deletions
+477
-328
common/test/acceptance/tests/discussion/test_discussion.py
+1
-1
lms/djangoapps/courseware/tests/test_tabs.py
+1
-1
lms/djangoapps/discussion/__init__.py
+0
-0
lms/djangoapps/discussion/plugins.py
+28
-0
lms/djangoapps/discussion/static/discussion/js/discussion_board_factory.js
+36
-0
lms/djangoapps/discussion/static/discussion/js/discussion_profile_page_factory.js
+29
-0
lms/djangoapps/discussion/static/discussion/js/discussion_router.js
+0
-0
lms/djangoapps/discussion/static/discussion/js/spec/views/discussion_user_profile_view_spec.js
+0
-0
lms/djangoapps/discussion/static/discussion/js/views/discussion_user_profile_view.js
+0
-0
lms/djangoapps/discussion/templates/discussion/discussion_board.html
+17
-4
lms/djangoapps/discussion/templates/discussion/discussion_profile_page.html
+0
-0
lms/djangoapps/discussion/templates/discussion/maintenance.html
+0
-0
lms/djangoapps/discussion/templates/discussion/user_profile.html
+62
-0
lms/djangoapps/discussion/tests/test_views.py
+7
-7
lms/djangoapps/discussion/urls.py
+1
-1
lms/djangoapps/discussion/views.py
+14
-30
lms/djangoapps/django_comment_client/urls.py
+0
-1
lms/djangoapps/django_comment_client/utils.py
+2
-2
lms/envs/common.py
+3
-1
lms/static/lms/js/build.js
+1
-0
lms/static/sass/discussion/_build.scss
+1
-0
lms/static/sass/discussion/_discussion.scss
+0
-157
lms/static/sass/discussion/_layouts.scss
+40
-0
lms/static/sass/discussion/views/_thread.scss
+225
-121
lms/static/sass/shared-v2/_layouts.scss
+1
-0
lms/urls.py
+6
-0
setup.py
+2
-2
No files found.
common/test/acceptance/tests/discussion/test_discussion.py
View file @
89f93df4
...
...
@@ -1037,7 +1037,7 @@ class DiscussionUserProfileTest(UniqueCourseTest):
Tests for user profile page in discussion tab.
"""
PAGE_SIZE
=
20
# d
jango_comment_client.forum
.views.THREADS_PER_PAGE
PAGE_SIZE
=
20
# d
iscussion
.views.THREADS_PER_PAGE
PROFILED_USERNAME
=
"profiled-user"
def
setUp
(
self
):
...
...
lms/djangoapps/courseware/tests/test_tabs.py
View file @
89f93df4
...
...
@@ -754,7 +754,7 @@ class DiscussionLinkTestCase(TabTestCase):
"""Custom reverse function"""
def
reverse_discussion_link
(
viewname
,
args
):
"""reverse lookup for discussion link"""
if
viewname
==
"d
jango_comment_client.forum
.views.forum_form_discussion"
and
args
==
[
unicode
(
course
.
id
)]:
if
viewname
==
"d
iscussion
.views.forum_form_discussion"
and
args
==
[
unicode
(
course
.
id
)]:
return
"default_discussion_link"
return
reverse_discussion_link
...
...
lms/djangoapps/d
jango_comment_client/forum
/__init__.py
→
lms/djangoapps/d
iscussion
/__init__.py
View file @
89f93df4
File moved
lms/djangoapps/discussion/plugins.py
0 → 100644
View file @
89f93df4
"""
Views handling read (GET) requests for the Discussion tab and inline discussions.
"""
from
django.conf
import
settings
from
django.utils.translation
import
ugettext_noop
from
courseware.tabs
import
EnrolledTab
import
django_comment_client.utils
as
utils
class
DiscussionTab
(
EnrolledTab
):
"""
A tab for the cs_comments_service forums.
"""
type
=
'discussion'
title
=
ugettext_noop
(
'Discussion'
)
priority
=
None
view_name
=
'discussion.views.forum_form_discussion'
is_hideable
=
settings
.
FEATURES
.
get
(
'ALLOW_HIDING_DISCUSSION_TAB'
,
False
)
is_default
=
False
@classmethod
def
is_enabled
(
cls
,
course
,
user
=
None
):
if
not
super
(
DiscussionTab
,
cls
)
.
is_enabled
(
course
,
user
):
return
False
return
utils
.
is_discussion_enabled
(
course
.
id
)
lms/djangoapps/discussion/static/discussion/js/discussion_board_factory.js
0 → 100644
View file @
89f93df4
;(
function
(
define
)
{
'use strict'
;
define
([
'jquery'
,
'backbone'
],
function
(
$
,
Backbone
)
{
return
function
(
options
)
{
var
element
=
options
.
el
,
userInfo
=
element
.
data
(
'user-info'
),
sortPreference
=
element
.
data
(
'sort-preference'
),
threads
=
element
.
data
(
'threads'
),
threadPages
=
element
.
data
(
'thread-pages'
),
contentInfo
=
element
.
data
(
'content-info'
),
user
=
new
window
.
DiscussionUser
(
userInfo
),
discussion
,
courseSettings
;
// TODO: Perhaps eliminate usage of global variables when possible
window
.
DiscussionUtil
.
loadRolesFromContainer
();
window
.
$$course_id
=
options
.
courseId
;
window
.
courseName
=
element
.
data
(
'course-name'
);
window
.
DiscussionUtil
.
setUser
(
user
);
window
.
user
=
user
;
window
.
Content
.
loadContentInfos
(
contentInfo
);
discussion
=
new
window
.
Discussion
(
threads
,
{
pages
:
threadPages
,
sort
:
sortPreference
});
courseSettings
=
new
window
.
DiscussionCourseSettings
(
element
.
data
(
'course-settings'
));
// jshint nonew:false
new
window
.
DiscussionRouter
({
discussion
:
discussion
,
course_settings
:
courseSettings
});
Backbone
.
history
.
start
({
pushState
:
true
,
root
:
'/courses/'
+
options
.
courseId
+
'/discussion/forum/'
});
};
});
}).
call
(
this
,
define
||
RequireJS
.
define
);
lms/djangoapps/discussion/static/discussion/js/discussion_profile_page_factory.js
0 → 100644
View file @
89f93df4
;(
function
(
define
)
{
'use strict'
;
define
([
'jquery'
,
'DiscussionUserProfileView'
],
function
(
$
,
DiscussionUserProfileView
)
{
return
function
(
options
)
{
var
element
=
options
.
el
,
threads
=
element
.
data
(
'threads'
),
userInfo
=
element
.
data
(
'user-info'
),
page
=
element
.
data
(
'page'
),
numPages
=
element
.
data
(
'num-pages'
);
// Roles are not included in user profile page, but they are not used for anything
window
.
DiscussionUtil
.
loadRoles
({
'Moderator'
:
[],
'Administrator'
:
[],
'Community TA'
:
[]
});
window
.
$$course_id
=
element
.
data
(
'course-id'
);
window
.
user
=
new
window
.
DiscussionUser
(
userInfo
);
// jshint nonew:false
new
DiscussionUserProfileView
({
el
:
element
,
collection
:
threads
,
page
:
page
,
numPages
:
numPages
});
};
});
}).
call
(
this
,
define
||
RequireJS
.
define
);
common/static/common/js/discussion
/discussion_router.js
→
lms/djangoapps/discussion/static/discussion/js
/discussion_router.js
View file @
89f93df4
File moved
common/static/common/js/spec/discussion/view
/discussion_user_profile_view_spec.js
→
lms/djangoapps/discussion/static/discussion/js/spec/views
/discussion_user_profile_view_spec.js
View file @
89f93df4
File moved
common/static/common/js/discussion
/views/discussion_user_profile_view.js
→
lms/djangoapps/discussion/static/discussion/js
/views/discussion_user_profile_view.js
View file @
89f93df4
File moved
lms/
templates/discussion/index
.html
→
lms/
djangoapps/discussion/templates/discussion/discussion_board
.html
View file @
89f93df4
...
...
@@ -9,25 +9,38 @@
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
template
.
defaultfilters
import
escapejs
from
django
.
core
.
urlresolvers
import
reverse
from
openedx
.
core
.
djangolib
.
js_utils
import
(
dump_js_escaped_json
,
js_escaped_string
)
%
>
<
%
block
name=
"bodyclass"
>
discussion
</
%
block>
<
%
block
name=
"pagetitle"
>
${_("Discussion - {course_number}").format(course_number=course.display_number_with_default)}
</
%
block>
<
%
block
name=
"headextra"
>
<
%
include
file=
"_js_head_dependencies.html"
/>
<
%
include
file=
"
../discussion/
_js_head_dependencies.html"
/>
</
%
block>
<
%
block
name=
"js_extra"
>
## Enable fast preview to fix discussion MathJax rendering bug when page first loads.
<
%
include
file=
"_js_body_dependencies.html"
args=
"disable_fast_preview=False"
/>
<
%
include
file=
"
/discussion/
_js_body_dependencies.html"
args=
"disable_fast_preview=False"
/>
<
%
static:js
group=
'discussion'
/>
<script
type=
"text/javascript"
>
RequireJS
.
define
(
'DiscussionBoardFactory'
,
[],
function
()
{
return
window
[
'DiscussionBoardFactory'
];});
</script>
<
%
static:require_module
module_name=
"discussion/js/discussion_board_factory"
class_name=
"DiscussionBoardFactory"
>
DiscussionBoardFactory({
courseId: '${unicode(course.id) | n, js_escaped_string}',
el: $(".discussion-board")
});
</
%
static:require
_module
>
</
%
block>
<
%
include
file=
"_discussion_course_navigation.html"
args=
"active_page='discussion'"
/>
<
%
include
file=
"
/discussion/
_discussion_course_navigation.html"
args=
"active_page='discussion'"
/>
<
%
block
name=
"content"
>
<section
class=
"discussion container"
id=
"discussion-container"
<section
class=
"discussion
discussion-board
container"
id=
"discussion-container"
data-roles=
"${roles}"
data-course-id=
"${course_id}"
data-course-name=
"${course.display_name_with_default}"
...
...
lms/
templates/discussion/user_profil
e.html
→
lms/
djangoapps/discussion/templates/discussion/discussion_profile_pag
e.html
View file @
89f93df4
File moved
lms/templates/discussion/maintenance.html
→
lms/
djangoapps/discussion/
templates/discussion/maintenance.html
View file @
89f93df4
File moved
lms/djangoapps/discussion/templates/discussion/user_profile.html
0 → 100644
View file @
89f93df4
## mako
<
%!
main_css =
"style-discussion-main"
%
>
<
%
page
expression_filter=
"h"
/>
<
%
inherit
file=
"../main.html"
/>
<
%
namespace
name=
'static'
file=
'../static_content.html'
/>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
template
.
defaultfilters
import
escapejs
from
openedx
.
core
.
djangolib
.
js_utils
import
(
dump_js_escaped_json
,
js_escaped_string
)
%
>
<
%
block
name=
"bodyclass"
>
discussion-user-profile
</
%
block>
<
%
block
name=
"pagetitle"
>
${_("Discussion - {course_number}").format(course_number=course.display_number_with_default)}
</
%
block>
<
%
block
name=
"headextra"
>
<
%
include
file=
"_js_head_dependencies.html"
/>
</
%
block>
<
%
block
name=
"js_extra"
>
<
%
include
file=
"_js_body_dependencies.html"
/>
<
%
static:js
group=
'discussion'
/>
<script
type=
"text/javascript"
>
RequireJS
.
define
(
'DiscussionUserProfileView'
,
[],
function
()
{
return
window
[
'DiscussionUserProfileView'
];});
</script>
<
%
static:require_module
module_name=
"discussion/js/discussion_profile_page_factory"
class_name=
"DiscussionProfilePageFactory"
>
DiscussionProfilePageFactory({
courseId: '${unicode(course.id) | n, js_escaped_string}',
el: $(".discussion-user-threads")
});
</
%
static:require
_module
>
</
%
block>
<
%
include
file=
"../courseware/course_navigation.html"
args=
"active_page='discussion'"
/>
<section
class=
"container"
>
<div
class=
"forum-nav"
>
<section
class=
"user-profile"
>
<nav
aria-label=
"${_('User Profile')}"
>
<article
class=
"sidebar-module discussion-sidebar"
>
<
%
include
file=
"_user_profile.html"
/>
</article>
</nav>
</section>
</div>
<div
class=
"discussion-column"
id=
"discussion-column"
>
<main
id=
"main"
aria-label=
"Content"
tabindex=
"-1"
>
<section
class=
"course-content container discussion-user-threads"
data-course-id=
"${course.id}"
data-course-name=
"${course.display_name_with_default}"
data-threads=
"${threads}"
data-user-info=
"${user_info}"
data-page=
"${page}"
data-num-pages=
"${num_pages}"
/>
</main>
</div>
</section>
<
%
include
file=
"_underscore_templates.html"
/>
lms/djangoapps/d
jango_comment_client/forum/test
s.py
→
lms/djangoapps/d
iscussion/tests/test_view
s.py
View file @
89f93df4
...
...
@@ -10,7 +10,6 @@ from django.utils import translation
from
lms.lib.comment_client.utils
import
CommentClientPaginatedResult
from
django_comment_common.utils
import
ThreadContext
from
django_comment_client.forum
import
views
from
django_comment_client.permissions
import
get_team
from
django_comment_client.tests.group_id
import
(
CohortedTopicGroupIdTestMixin
,
...
...
@@ -19,6 +18,7 @@ from django_comment_client.tests.group_id import (
from
django_comment_client.tests.unicode
import
UnicodeTestMixin
from
django_comment_client.tests.utils
import
CohortedTestCase
from
django_comment_client.utils
import
strip_none
from
lms.djangoapps.discussion
import
views
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
from
util.testing
import
UrlResetMixin
from
openedx.core.djangoapps.util.testing
import
ContentGroupTestCase
...
...
@@ -87,7 +87,7 @@ class ViewsExceptionTestCase(UrlResetMixin, ModuleStoreTestCase):
# that gets the current user's info
mock_from_django_user
.
return_value
=
Mock
()
url
=
reverse
(
'd
jango_comment_client.forum
.views.user_profile'
,
url
=
reverse
(
'd
iscussion
.views.user_profile'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
(),
'user_id'
:
'12345'
})
# There is no user 12345
self
.
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
self
.
response
.
status_code
,
404
)
...
...
@@ -104,7 +104,7 @@ class ViewsExceptionTestCase(UrlResetMixin, ModuleStoreTestCase):
# that gets the current user's info
mock_from_django_user
.
return_value
=
Mock
()
url
=
reverse
(
'd
jango_comment_client.forum
.views.followed_threads'
,
url
=
reverse
(
'd
iscussion
.views.followed_threads'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
(),
'user_id'
:
'12345'
})
# There is no user 12345
self
.
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
self
.
response
.
status_code
,
404
)
...
...
@@ -1257,7 +1257,7 @@ class CommentsServiceRequestHeadersTestCase(UrlResetMixin, ModuleStoreTestCase):
self
.
client
.
get
(
reverse
(
"d
jango_comment_client.forum
.views.single_thread"
,
"d
iscussion
.views.single_thread"
,
kwargs
=
{
"course_id"
:
self
.
course
.
id
.
to_deprecated_string
(),
"discussion_id"
:
"dummy_discussion_id"
,
...
...
@@ -1274,7 +1274,7 @@ class CommentsServiceRequestHeadersTestCase(UrlResetMixin, ModuleStoreTestCase):
self
.
client
.
get
(
reverse
(
"d
jango_comment_client.forum
.views.forum_form_discussion"
,
"d
iscussion
.views.forum_form_discussion"
,
kwargs
=
{
"course_id"
:
self
.
course
.
id
.
to_deprecated_string
()}
),
)
...
...
@@ -1361,7 +1361,7 @@ class ForumDiscussionXSSTestCase(UrlResetMixin, ModuleStoreTestCase):
Test that XSS attack is prevented
"""
reverse_url
=
"
%
s
%
s"
%
(
reverse
(
"d
jango_comment_client.forum
.views.forum_form_discussion"
,
"d
iscussion
.views.forum_form_discussion"
,
kwargs
=
{
"course_id"
:
unicode
(
self
.
course
.
id
)}),
'/forum_form_discussion'
)
# Test that malicious code does not appear in html
url
=
"
%
s?
%
s=
%
s"
%
(
reverse_url
,
'sort_key'
,
malicious_code
)
...
...
@@ -1380,7 +1380,7 @@ class ForumDiscussionXSSTestCase(UrlResetMixin, ModuleStoreTestCase):
mock_from_django_user
.
return_value
=
Mock
()
mock_request
.
side_effect
=
make_mock_request_impl
(
course
=
self
.
course
,
text
=
'dummy'
)
url
=
reverse
(
'd
jango_comment_client.forum
.views.user_profile'
,
url
=
reverse
(
'd
iscussion
.views.user_profile'
,
kwargs
=
{
'course_id'
:
unicode
(
self
.
course
.
id
),
'user_id'
:
str
(
self
.
student
.
id
)})
# Test that malicious code does not appear in html
url_string
=
"
%
s?
%
s=
%
s"
%
(
url
,
'page'
,
malicious_code
)
...
...
lms/djangoapps/d
jango_comment_client/forum
/urls.py
→
lms/djangoapps/d
iscussion
/urls.py
View file @
89f93df4
...
...
@@ -4,7 +4,7 @@ Forum urls for the django_comment_client.
from
django.conf.urls
import
url
,
patterns
urlpatterns
=
patterns
(
'd
jango_comment_client.forum
.views'
,
'd
iscussion
.views'
,
url
(
r'users/(?P<user_id>\w+)/followed$'
,
'followed_threads'
,
name
=
'followed_threads'
),
url
(
r'users/(?P<user_id>\w+)$'
,
'user_profile'
,
name
=
'user_profile'
),
...
...
lms/djangoapps/d
jango_comment_client/forum
/views.py
→
lms/djangoapps/d
iscussion
/views.py
View file @
89f93df4
...
...
@@ -7,23 +7,20 @@ import json
import
logging
from
django.contrib.auth.decorators
import
login_required
from
django.conf
import
settings
from
django.core.context_processors
import
csrf
from
django.core.urlresolvers
import
reverse
from
django.contrib.auth.models
import
User
from
django.http
import
Http404
,
HttpResponseBadRequest
from
django.
utils.translation
import
ugettext_noop
from
django.
shortcuts
import
render_to_response
from
django.views.decorators.http
import
require_GET
import
newrelic.agent
from
edxmako.shortcuts
import
render_to_response
from
courseware.courses
import
get_course_with_access
from
openedx.core.djangoapps.course_groups.cohorts
import
(
is_course_cohorted
,
get_cohort_id
,
get_course_cohorts
,
)
from
courseware.tabs
import
EnrolledTab
from
courseware.access
import
has_access
from
xmodule.modulestore.django
import
modulestore
...
...
@@ -48,25 +45,6 @@ PAGES_NEARBY_DELTA = 2
log
=
logging
.
getLogger
(
"edx.discussions"
)
class
DiscussionTab
(
EnrolledTab
):
"""
A tab for the cs_comments_service forums.
"""
type
=
'discussion'
title
=
ugettext_noop
(
'Discussion'
)
priority
=
None
view_name
=
'django_comment_client.forum.views.forum_form_discussion'
is_hideable
=
settings
.
FEATURES
.
get
(
'ALLOW_HIDING_DISCUSSION_TAB'
,
False
)
is_default
=
False
@classmethod
def
is_enabled
(
cls
,
course
,
user
=
None
):
if
not
super
(
DiscussionTab
,
cls
)
.
is_enabled
(
course
,
user
):
return
False
return
utils
.
is_discussion_enabled
(
course
.
id
)
@newrelic.agent.function_trace
()
def
make_course_settings
(
course
,
user
):
"""
...
...
@@ -115,7 +93,8 @@ def get_threads(request, course, discussion_id=None, per_page=THREADS_PER_PAGE):
# If the user did not select a sort key, use their last used sort key
cc_user
=
cc
.
User
.
from_django_user
(
request
.
user
)
cc_user
.
retrieve
()
# TODO: After the comment service is updated this can just be user.default_sort_key because the service returns the default value
# TODO: After the comment service is updated this can just be
# user.default_sort_key because the service returns the default value
default_query_params
[
'sort_key'
]
=
cc_user
.
get
(
'default_sort_key'
)
or
default_query_params
[
'sort_key'
]
else
:
# If the user clicked a sort key, update their default sort key
...
...
@@ -239,7 +218,10 @@ def forum_form_discussion(request, course_key):
threads
=
[
utils
.
prepare_content
(
thread
,
course_key
,
is_staff
)
for
thread
in
unsafethreads
]
except
cc
.
utils
.
CommentClientMaintenanceError
:
log
.
warning
(
"Forum is in maintenance mode"
)
return
render_to_response
(
'discussion/maintenance.html'
,
{})
return
render_to_response
(
'discussion/maintenance.html'
,
{
'disable_courseware_js'
:
True
,
'uses_pattern_library'
:
True
,
})
except
ValueError
:
return
HttpResponseBadRequest
(
"Invalid group_id"
)
...
...
@@ -290,7 +272,7 @@ def forum_form_discussion(request, course_key):
'uses_pattern_library'
:
True
,
}
# print "start rendering.."
return
render_to_response
(
'discussion/
index
.html'
,
context
)
return
render_to_response
(
'discussion/
discussion_board
.html'
,
context
)
@require_GET
...
...
@@ -318,8 +300,8 @@ def single_thread(request, course_key, discussion_id, thread_id):
response_skip
=
request
.
GET
.
get
(
"resp_skip"
),
response_limit
=
request
.
GET
.
get
(
"resp_limit"
)
)
except
cc
.
utils
.
CommentClientRequestError
as
e
:
if
e
.
status_code
==
404
:
except
cc
.
utils
.
CommentClientRequestError
as
e
rror
:
if
e
rror
.
status_code
==
404
:
raise
Http404
raise
...
...
@@ -404,7 +386,7 @@ def single_thread(request, course_key, discussion_id, thread_id):
'disable_courseware_js'
:
True
,
'uses_pattern_library'
:
True
,
}
return
render_to_response
(
'discussion/
index
.html'
,
context
)
return
render_to_response
(
'discussion/
discussion_board
.html'
,
context
)
@require_GET
...
...
@@ -465,7 +447,9 @@ def user_profile(request, course_key, user_id):
'annotated_content_info'
:
json
.
dumps
(
annotated_content_info
),
'page'
:
query_params
[
'page'
],
'num_pages'
:
query_params
[
'num_pages'
],
'learner_profile_page_url'
:
reverse
(
'learner_profile'
,
kwargs
=
{
'username'
:
django_user
.
username
})
'learner_profile_page_url'
:
reverse
(
'learner_profile'
,
kwargs
=
{
'username'
:
django_user
.
username
}),
'disable_courseware_js'
:
True
,
'uses_pattern_library'
:
True
,
}
return
render_to_response
(
'discussion/user_profile.html'
,
context
)
...
...
lms/djangoapps/django_comment_client/urls.py
View file @
89f93df4
...
...
@@ -6,6 +6,5 @@ from django.conf.urls import url, patterns, include
urlpatterns
=
patterns
(
''
,
url
(
r'forum/?'
,
include
(
'django_comment_client.forum.urls'
)),
url
(
r''
,
include
(
'django_comment_client.base.urls'
)),
)
lms/djangoapps/django_comment_client/utils.py
View file @
89f93df4
...
...
@@ -591,10 +591,10 @@ def permalink(content):
else
:
course_id
=
content
[
'course_id'
]
if
content
[
'type'
]
==
'thread'
:
return
reverse
(
'd
jango_comment_client.forum
.views.single_thread'
,
return
reverse
(
'd
iscussion
.views.single_thread'
,
args
=
[
course_id
,
content
[
'commentable_id'
],
content
[
'id'
]])
else
:
return
reverse
(
'd
jango_comment_client.forum
.views.single_thread'
,
return
reverse
(
'd
iscussion
.views.single_thread'
,
args
=
[
course_id
,
content
[
'commentable_id'
],
content
[
'thread_id'
]])
+
'#'
+
content
[
'id'
]
...
...
lms/envs/common.py
View file @
89f93df4
...
...
@@ -1941,8 +1941,10 @@ INSTALLED_APPS = (
'django_comment_client'
,
'django_comment_common'
,
'discussion_api'
,
'
notes
'
,
'
lms.djangoapps.discussion
'
,
# Notes
'notes'
,
'edxnotes'
,
# Splash screen
...
...
lms/static/lms/js/build.js
View file @
89f93df4
...
...
@@ -18,6 +18,7 @@
* done.
*/
modules
:
getModulesList
([
'discussion/js/discussion_board_factory'
,
'js/api_admin/catalog_preview_factory'
,
'js/courseware/courseware_factory'
,
'js/discovery/discovery_factory'
,
...
...
lms/static/sass/discussion/_build.scss
View file @
89f93df4
...
...
@@ -27,6 +27,7 @@ $static-path: '../..' !default;
// Discussion styling
@import
'mixins'
;
@import
'discussion'
;
// Process old file after definitions but before everything else, partial is deprecated.
@import
'layouts'
;
@import
'elements/actions'
;
@import
'elements/editor'
;
@import
'elements/labels'
;
...
...
lms/static/sass/discussion/_discussion.scss
View file @
89f93df4
...
...
@@ -4,10 +4,6 @@
body
.discussion
{
.course-tabs
.right
{
@include
float
(
right
);
}
.edit-post-form
{
@include
clearfix
();
box-sizing
:
border-box
;
...
...
@@ -65,39 +61,6 @@ body.discussion {
font-size
:
21px
;
}
section
.user-profile
{
// TODO: don't use table-cell for layout purposes as it switches the screenreader mode
display
:
table-cell
;
border-right
:
1px
solid
#ddd
;
border-radius
:
3px
0
0
3px
;
background-color
:
$sidebar-color
;
box-shadow
:
none
;
.user-profile
{
padding
:
32px
36px
;
}
.sidebar-username
{
font-weight
:
700
;
font-size
:
18px
;
}
.sidebar-user-roles
{
margin-top
:
6px
;
font-style
:
italic
;
font-size
:
13px
;
}
.sidebar-threads-count
{
margin-top
:
14px
;
}
.sidebar-threads-count
span
,
.sidebar-comments-count
span
{
font-weight
:
700
;
}
}
.wmd-panel
{
width
:
100%
;
}
...
...
@@ -227,16 +190,6 @@ body.discussion {
text-align
:
center
;
}
.discussion-column
{
@include
float
(
right
);
box-sizing
:
border-box
;
padding-left
:
(
$baseline
/
2
);
width
:
68%
;
max-width
:
800px
;
min-height
:
500px
;
border-radius
:
3px
;
}
.discussion-article
{
position
:
relative
;
min-height
:
500px
;
...
...
@@ -406,108 +359,6 @@ body.discussion {
.threads
{
}
.discussion-thread
{
padding
:
0
;
margin-bottom
:
$baseline
;
@include
transition
(
all
.25s
linear
0s
);
p
{
margin-bottom
:
0
;
}
.discussion-article
{
@include
transition
(
all
.2s
linear
0s
);
border
:
1px
solid
$forum-color-border
;
border-radius
:
3px
;
min-height
:
0
;
background
:
$forum-color-background
;
box-shadow
:
0
1px
0
$shadow
;
@include
transition
(
all
.2s
linear
0s
);
.thread-wrapper
{
@include
border-radius
(
3px
,
3px
,
0
,
0
);
position
:
relative
;
overflow-x
:
hidden
;
overflow-y
:
auto
;
max-height
:
600px
;
background-color
:
$forum-color-background
;
.discussion-post
{
.inline-comment-count
{
@extend
%ui-depth2
;
position
:
relative
;
float
:
right
;
display
:
block
;
height
:
27px
;
margin-top
:
6px
;
margin-right
:
8px
;
padding
:
0
8px
;
border-radius
:
(
$baseline
/
4
);
font-size
:
12px
;
font-weight
:
400
;
line-height
:
25px
;
color
:
#888
;
}
}
.responses
{
header
{
padding-bottom
:
0
;
margin-bottom
:
(
$baseline
*
0
.75
);
.posted-by
{
float
:
left
;
margin-right
:
(
$baseline
/
4
);
font-size
:
16px
;
}
}
.response-body
{
margin-bottom
:
0
.2em
;
font-size
:
14px
;
}
}
.discussion-reply-new
{
.wmd-input
{
height
:
120px
;
}
}
// Content that is hidden by default in the inline view
.post-extended-content
{
display
:
none
;
}
}
.post-tools
{
box-shadow
:
0
1px
1px
$shadow
inset
;
background
:
$gray-l6
;
&
:hover
{
background
:
#fcfcfc
;
.icon
{
color
:
$link-hover
;
}
}
a
{
display
:
block
;
padding
:
(
$baseline
*
0
.25
)
$baseline
;
font-size
:
12px
;
line-height
:
30px
;
.icon
{
color
:
$link-color
;
margin-right
:
(
$baseline
*
0
.25
);
}
}
}
}
}
}
.new-post-article
{
...
...
@@ -603,14 +454,6 @@ body.discussion {
}
}
.discussion-user-threads
{
@extend
.discussion-module
;
.discussion-post
{
padding-bottom
:
$baseline
!
important
;
}
}
.xblock-student_view-discussion
{
@extend
%ui-print-excluded
;
}
...
...
lms/static/sass/discussion/_layouts.scss
0 → 100644
View file @
89f93df4
// Layouts for discussion pages
section
.user-profile
{
background-color
:
$sidebar-color
;
.user-profile
{
padding
:
32px
36px
;
min-height
:
500px
;
}
.sidebar-username
{
font-weight
:
700
;
font-size
:
18px
;
}
.sidebar-user-roles
{
margin-top
:
6px
;
font-style
:
italic
;
font-size
:
13px
;
}
.sidebar-threads-count
{
margin-top
:
14px
;
}
.sidebar-threads-count
span
,
.sidebar-comments-count
span
{
font-weight
:
700
;
}
}
.discussion-column
{
@include
float
(
right
);
box-sizing
:
border-box
;
padding-left
:
(
$baseline
/
2
);
width
:
68%
;
max-width
:
800px
;
min-height
:
500px
;
border-radius
:
3px
;
}
lms/static/sass/discussion/views/_thread.scss
View file @
89f93df4
...
...
@@ -9,136 +9,132 @@
// * +post - individual element styling
// * +post - answered question - collapsed comment area
// +general thread layout
body
.discussion
,
.discussion-module
{
// post layout
.discussion-post
{
// post layout
.discussion-post
{
padding
:
0
(
$baseline
/
2
);
.wrapper-post-header
{
padding-bottom
:
0
;
margin-bottom
:
(
$baseline
*
0
.75
);
padding-bottom
:
0
;
margin-bottom
:
(
$baseline
*
0
.75
);
}
.post-header-content
{
display
:
inline-block
;
width
:
flex-grid
(
9
,
12
);
display
:
inline-block
;
width
:
flex-grid
(
9
,
12
);
}
.post-header-actions
{
@include
float
(
right
);
@include
float
(
right
);
}
.post-body
{
width
:
flex-grid
(
10
,
12
);
width
:
flex-grid
(
10
,
12
);
}
}
}
.posted-details
{
.posted-details
{
@extend
%t-copy-sub2
;
margin-top
:
(
$baseline
/
5
);
color
:
$gray-d1
;
.username
{
@extend
%t-strong
;
display
:
inline
;
@extend
%t-strong
;
display
:
inline
;
}
.timeago
,
.top-post-status
{
color
:
inherit
;
color
:
inherit
;
}
}
}
// response layout
.discussion-response
{
// response layout
.discussion-response
{
min-height
:
(
$baseline
*
5
);
.response-header-content
{
display
:
inline-block
;
vertical-align
:
top
;
width
:
flex-grid
(
11
,
12
);
display
:
inline-block
;
vertical-align
:
top
;
width
:
flex-grid
(
11
,
12
);
}
.response-header-actions
{
@include
float
(
right
);
@include
right
(
$baseline
);
position
:
absolute
;
top
:
$baseline
;
@include
float
(
right
);
@include
right
(
$baseline
);
position
:
absolute
;
top
:
$baseline
;
}
}
}
// comments layout
.discussion-comment
{
// comments layout
.discussion-comment
{
.response-body
{
@extend
%t-copy-sub2
;
display
:
inline-block
;
margin-bottom
:
(
$baseline
/
2
);
width
:
flex-grid
(
10
,
12
);
p
+
p
{
margin-top
:
12px
;
}
@extend
%t-copy-sub2
;
display
:
inline-block
;
margin-bottom
:
(
$baseline
/
2
);
width
:
flex-grid
(
10
,
12
);
p
+
p
{
margin-top
:
12px
;
}
}
.comment-actions-list
{
@include
float
(
right
);
@include
float
(
right
);
}
}
}
// +thread - wrapper styling
.thread-wrapper
{
.thread-main-wrapper
{
padding-bottom
:
$baseline
;
}
.thread-main-wrapper
{
padding-bottom
:
$baseline
;
}
}
// +thread - elements - shared styles
body
.discussion
{
.discussion-post
,
.discussion-response
,
.discussion-comment
{
@include
clearfix
();
// thread - images
.author-image
{
@include
margin-right
(
$baseline
/
2
);
display
:
inline-block
;
vertical-align
:
top
;
// STATE: No profile image
&
:empty
{
display
:
none
;
}
// CASE: post image
&
.level-post
{
height
:
$post-image-dimension
;
width
:
$post-image-dimension
;
}
// CASE: response image
&
.level-response
{
height
:
$response-image-dimension
;
width
:
$response-image-dimension
;
}
// CASE: comment image
&
.level-comment
{
height
:
$comment-image-dimension
;
width
:
$comment-image-dimension
;
}
img
{
border-radius
:
3px
;
}
.discussion-post
,
.discussion-response
,
.discussion-comment
{
@include
clearfix
();
// thread - images
.author-image
{
@include
margin-right
(
$baseline
/
2
);
display
:
inline-block
;
vertical-align
:
top
;
// STATE: No profile image
&
:empty
{
display
:
none
;
}
// CASE: post image
&
.level-post
{
height
:
$post-image-dimension
;
width
:
$post-image-dimension
;
}
// CASE: response image
&
.level-response
{
height
:
$response-image-dimension
;
width
:
$response-image-dimension
;
}
// CASE: comment image
&
.level-comment
{
height
:
$comment-image-dimension
;
width
:
$comment-image-dimension
;
}
img
{
border-radius
:
3px
;
}
}
}
}
.discussion-response
.response-body
{
@include
padding-right
(
$baseline
);
//ensures content doesn't overlap on post or response actions.
}
.discussion-response
.response-body
{
@include
padding-right
(
$baseline
);
//ensures content doesn't overlap on post or response actions.
}
}
// +post - individual element styling
...
...
@@ -146,62 +142,170 @@ body.discussion .discussion-post,
body
.discussion
.discussion-article
,
body
.view-in-course
.discussion-post
,
body
.view-in-course
.discussion-article
{
// NOTE: discussion-article is used for inline discussion modules.
@include
clearfix
();
// NOTE: discussion-article is used for inline discussion modules.
@include
clearfix
();
.post-header-content
{
.post-header-content
{
// post title
.post-title
{
@extend
%t-title4
;
@extend
%t-ultrastrong
;
margin-bottom
:
(
$baseline
/
4
);
letter-spacing
:
0
;
// post title
.post-title
{
@extend
%t-title4
;
@extend
%t-ultrastrong
;
margin-bottom
:
(
$baseline
/
4
);
letter-spacing
:
0
;
}
}
}
// post body
.post-body
{
@extend
%t-copy-sub1
;
// clear: both; //TO-DO: confirm that removing this is ok for all cases of discussion posts.
}
// post context
.post-context
{
@extend
%t-copy-sub2
;
margin-top
:
$baseline
;
color
:
$gray-d1
;
// CASE: no courseware context or cohort visibility rules
&
:empty
{
display
:
none
;
// post body
.post-body
{
@extend
%t-copy-sub1
;
// clear: both; //TO-DO: confirm that removing this is ok for all cases of discussion posts.
}
// post visibility - cohorts
.group-visibility-label
{
margin-top
:
(
$baseline
/
4
);
// post context
.post-context
{
@extend
%t-copy-sub2
;
margin-top
:
$baseline
;
color
:
$gray-d1
;
// CASE: no courseware context or cohort visibility rules
&
:empty
{
display
:
none
;
}
// post visibility - cohorts
.group-visibility-label
{
margin-top
:
(
$baseline
/
4
);
}
}
}
}
// Layout control for discussion modules that does not apply to the discussion board
.discussion-module
{
.discussion-thread
{
.btn-brand
{
@include
blue-button
;
display
:
inline-block
;
padding-bottom
:
(
$baseline
/
10
);
height
:
37px
;
&
:hover
,
&
:focus
{
border-color
:
#222
;
}
}
}
// Styling for discussion threads
.discussion-thread
{
padding
:
0
;
margin-bottom
:
$baseline
;
@include
transition
(
all
.25s
linear
0s
);
p
{
margin-bottom
:
0
;
}
.thread-main-wrapper
,
.thread-responses-wrapper
{
padding
:
$baseline
;
padding
:
$baseline
;
}
}
.btn-brand
{
@include
blue-button
;
display
:
inline-block
;
padding-bottom
:
(
$baseline
/
10
);
height
:
37px
;
.discussion-article
{
@include
transition
(
all
.2s
linear
0s
);
border
:
1px
solid
$forum-color-border
;
border-radius
:
3px
;
min-height
:
0
;
background
:
$forum-color-background
;
box-shadow
:
0
1px
0
$shadow
;
@include
transition
(
all
.2s
linear
0s
);
.thread-wrapper
{
@include
border-radius
(
3px
,
3px
,
0
,
0
);
position
:
relative
;
overflow-x
:
hidden
;
overflow-y
:
auto
;
max-height
:
600px
;
background-color
:
$forum-color-background
;
.discussion-post
{
.inline-comment-count
{
@extend
%ui-depth2
;
position
:
relative
;
float
:
right
;
display
:
block
;
height
:
27px
;
margin-top
:
6px
;
margin-right
:
8px
;
padding
:
0
8px
;
border-radius
:
(
$baseline
/
4
);
font-size
:
12px
;
font-weight
:
400
;
line-height
:
25px
;
color
:
#888
;
}
}
.responses
{
header
{
padding-bottom
:
0
;
margin-bottom
:
(
$baseline
*
0
.75
);
.posted-by
{
float
:
left
;
margin-right
:
(
$baseline
/
4
);
font-size
:
16px
;
}
}
.response-body
{
margin-bottom
:
0
.2em
;
font-size
:
14px
;
}
}
.discussion-reply-new
{
.wmd-input
{
height
:
120px
;
}
}
// Content that is hidden by default in the inline view
.post-extended-content
{
display
:
none
;
}
}
.post-tools
{
box-shadow
:
0
1px
1px
$shadow
inset
;
background
:
$gray-l6
;
&
:hover
{
background
:
#fcfcfc
;
.icon
{
color
:
$link-hover
;
}
}
a
{
display
:
block
;
padding
:
(
$baseline
*
0
.25
)
$baseline
;
font-size
:
12px
;
line-height
:
30px
;
.icon
{
color
:
$link-color
;
margin-right
:
(
$baseline
*
0
.25
);
}
}
}
}
}
&
:hover
,
&
:focus
{
border-color
:
#222
;
// Custom styling for the list of user threads
.discussion-user-threads
{
.discussion-post
{
padding
:
$baseline
/
2
;
}
}
}
.thread-wrapper
,
...
...
lms/static/sass/shared-v2/_layouts.scss
View file @
89f93df4
...
...
@@ -8,6 +8,7 @@
}
.container
{
@include
clearfix
();
border
:
1px
solid
$lms-border-color
;
background-color
:
$lms-container-background-color
;
padding
:
$baseline
;
...
...
lms/urls.py
View file @
89f93df4
...
...
@@ -726,6 +726,12 @@ if settings.FEATURES.get('ENABLE_DISCUSSION_SERVICE'):
include
(
'django_comment_client.urls'
)
),
url
(
r'^courses/{}/discussion/forum/'
.
format
(
settings
.
COURSE_ID_PATTERN
,
),
include
(
'discussion.urls'
)
),
url
(
r'^notification_prefs/enable/'
,
'notification_prefs.views.ajax_enable'
),
...
...
setup.py
View file @
89f93df4
...
...
@@ -6,7 +6,7 @@ from setuptools import setup
setup
(
name
=
"Open edX"
,
version
=
"0.
5
"
,
version
=
"0.
6
"
,
install_requires
=
[
"setuptools"
],
requires
=
[],
# NOTE: These are not the names we should be installing. This tree should
...
...
@@ -23,7 +23,7 @@ setup(
"ccx = lms.djangoapps.ccx.plugins:CcxCourseTab"
,
"courseware = lms.djangoapps.courseware.tabs:CoursewareTab"
,
"course_info = lms.djangoapps.courseware.tabs:CourseInfoTab"
,
"discussion = lms.djangoapps.d
jango_comment_client.forum.view
s:DiscussionTab"
,
"discussion = lms.djangoapps.d
iscussion.plugin
s:DiscussionTab"
,
"edxnotes = lms.djangoapps.edxnotes.plugins:EdxNotesTab"
,
"external_discussion = lms.djangoapps.courseware.tabs:ExternalDiscussionCourseTab"
,
"external_link = lms.djangoapps.courseware.tabs:ExternalLinkCourseTab"
,
...
...
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