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
00c7e60e
Commit
00c7e60e
authored
Sep 15, 2014
by
polesye
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
TNL-171: Change topic of a previously posted post.
parent
3cdfdae8
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
662 additions
and
398 deletions
+662
-398
common/static/coffee/spec/discussion/.gitignore
+2
-0
common/static/coffee/spec/discussion/discussion_spec_helper.coffee
+28
-31
common/static/coffee/spec/discussion/view/discussion_thread_edit_view_spec.js
+65
-0
common/static/coffee/spec/discussion/view/discussion_thread_view_spec.coffee
+2
-0
common/static/coffee/spec/discussion/view/discussion_topic_menu_view_spec.js
+125
-0
common/static/coffee/spec/discussion/view/new_post_view_spec.coffee
+0
-109
common/static/coffee/src/discussion/.gitignore
+2
-0
common/static/coffee/src/discussion/discussion_module_view.coffee
+15
-2
common/static/coffee/src/discussion/discussion_router.coffee
+6
-1
common/static/coffee/src/discussion/views/discussion_thread_edit_view.coffee
+0
-25
common/static/coffee/src/discussion/views/discussion_thread_edit_view.js
+103
-0
common/static/coffee/src/discussion/views/discussion_thread_list_view.coffee
+14
-12
common/static/coffee/src/discussion/views/discussion_thread_view.coffee
+17
-46
common/static/coffee/src/discussion/views/discussion_topic_menu_view.js
+193
-0
common/static/coffee/src/discussion/views/new_post_view.coffee
+22
-125
lms/djangoapps/django_comment_client/base/tests.py
+15
-5
lms/djangoapps/django_comment_client/base/views.py
+14
-5
lms/djangoapps/django_comment_client/utils.py
+2
-2
lms/static/sass/application-extend2.scss.mako
+1
-1
lms/static/sass/discussion/utilities/_shame.scss
+3
-2
lms/static/sass/discussion/views/_create-edit-post.scss
+8
-4
lms/templates/discussion/_underscore_templates.html
+25
-28
No files found.
common/static/coffee/spec/discussion/.gitignore
0 → 100644
View file @
00c7e60e
!view/discussion_thread_edit_view_spec.js
!view/discussion_topic_menu_view_spec.js
common/static/coffee/spec/discussion/discussion_spec_helper.coffee
View file @
00c7e60e
...
@@ -71,9 +71,9 @@ browser and pasting the output. When that file changes, this one should be rege
...
@@ -71,9 +71,9 @@ browser and pasting the output. When that file changes, this one should be rege
</script>
</script>
<script aria-hidden="true" type="text/template" id="thread-edit-template">
<script aria-hidden="true" type="text/template" id="thread-edit-template">
<div class="discussion-post edit-post-form">
<h1>Editing post</h1>
<h1>Editing post</h1>
<ul class="edit-post-form-errors"></ul>
<ul class="edit-post-form-errors"></ul>
<div class="forum-edit-post-form-wrapper"></div>
<div class="form-row">
<div class="form-row">
<label class="sr" for="edit-post-title">Edit post title</label>
<label class="sr" for="edit-post-title">Edit post title</label>
<input type="text" id="edit-post-title" class="edit-post-title" name="title" value="<%-title %>" placeholder="Title">
<input type="text" id="edit-post-title" class="edit-post-title" name="title" value="<%-title %>" placeholder="Title">
...
@@ -83,7 +83,6 @@ browser and pasting the output. When that file changes, this one should be rege
...
@@ -83,7 +83,6 @@ browser and pasting the output. When that file changes, this one should be rege
</div>
</div>
<input type="submit" id="edit-post-submit" class="post-update" value="Update post">
<input type="submit" id="edit-post-submit" class="post-update" value="Update post">
<a href="#" class="post-cancel">Cancel</a>
<a href="#" class="post-cancel">Cancel</a>
</div>
</script>
</script>
<script aria-hidden="true" type="text/template" id="thread-response-template">
<script aria-hidden="true" type="text/template" id="thread-response-template">
...
@@ -113,7 +112,7 @@ browser and pasting the output. When that file changes, this one should be rege
...
@@ -113,7 +112,7 @@ browser and pasting the output. When that file changes, this one should be rege
<%= author_display %>
<%= author_display %>
<p class="posted-details">
<p class="posted-details">
<span class="timeago" title="<%= created_at %>"><%= created_at %></span>
<span class="timeago" title="<%= created_at %>"><%= created_at %></span>
<% if (obj.endorsement) { %> - <%=
<% if (obj.endorsement) { %> - <%=
interpolate(
interpolate(
thread.get("thread_type") == "question" ?
thread.get("thread_type") == "question" ?
...
@@ -174,7 +173,7 @@ browser and pasting the output. When that file changes, this one should be rege
...
@@ -174,7 +173,7 @@ browser and pasting the output. When that file changes, this one should be rege
}
}
)
)
%>
%>
<p class="posted-details">
<p class="posted-details">
<%=
<%=
interpolate(
interpolate(
...
@@ -222,7 +221,7 @@ browser and pasting the output. When that file changes, this one should be rege
...
@@ -222,7 +221,7 @@ browser and pasting the output. When that file changes, this one should be rege
<i class="icon <%= icon_class %>"></i>
<i class="icon <%= icon_class %>"></i>
</div><div class="forum-nav-thread-wrapper-1">
</div><div class="forum-nav-thread-wrapper-1">
<span class="forum-nav-thread-title"><%- title %></span>
<span class="forum-nav-thread-title"><%- title %></span>
<%
<%
var labels = "";
var labels = "";
if (pinned) {
if (pinned) {
...
@@ -242,7 +241,7 @@ browser and pasting the output. When that file changes, this one should be rege
...
@@ -242,7 +241,7 @@ browser and pasting the output. When that file changes, this one should be rege
}
}
%>
%>
</div><div class="forum-nav-thread-wrapper-2">
</div><div class="forum-nav-thread-wrapper-2">
<span class="forum-nav-thread-votes-count">+<%=
<span class="forum-nav-thread-votes-count">+<%=
interpolate(
interpolate(
'%(votes_up_count)s%(span_sr_open)s votes %(span_close)s',
'%(votes_up_count)s%(span_sr_open)s votes %(span_close)s',
...
@@ -250,7 +249,7 @@ browser and pasting the output. When that file changes, this one should be rege
...
@@ -250,7 +249,7 @@ browser and pasting the output. When that file changes, this one should be rege
true
true
)
)
%></span>
%></span>
<span class="forum-nav-thread-comments-count <% if (unread_comments_count > 0) { %>is-unread<% } %>">
<span class="forum-nav-thread-comments-count <% if (unread_comments_count > 0) { %>is-unread<% } %>">
<%
<%
var fmt;
var fmt;
...
@@ -319,30 +318,7 @@ browser and pasting the output. When that file changes, this one should be rege
...
@@ -319,30 +318,7 @@ browser and pasting the output. When that file changes, this one should be rege
Questions raise issues that need answers. Discussions share ideas and start conversations.
Questions raise issues that need answers. Discussions share ideas and start conversations.
</span>
</span>
</div>
</div>
<% if (mode=="tab") { %>
<div class="forum-new-post-form-wrapper"></div>
<div class="post-field">
<div class="field-label">
<span class="field-label-text">
Topic Area:
</span><div class="field-input post-topic">
<a href="#" class="post-topic-button">
<span class="sr">Discussion topics; current selection is: </span>
<span class="js-selected-topic"></span>
<span class="drop-arrow" aria-hidden="true">▾</span>
</a>
<div class="topic-menu-wrapper">
<label class="topic-filter-label">
<span class="sr">Filter topics</span>
<input type="text" class="topic-filter-input" placeholder="Filter topics">
</label>
<ul class="topic-menu" role="menu"><%= topics_html %></ul>
</div>
</div>
</div><span class="field-help">
Add your post to a relevant topic to help others find it.
</span>
</div>
<% } %>
<% if (cohort_options) { %>
<% if (cohort_options) { %>
<div class="post-field">
<div class="post-field">
<label class="field-label">
<label class="field-label">
...
@@ -406,6 +382,27 @@ browser and pasting the output. When that file changes, this one should be rege
...
@@ -406,6 +382,27 @@ browser and pasting the output. When that file changes, this one should be rege
</li>
</li>
</script>
</script>
<script aria-hidden="true" type="text/template" id="topic-template">
<div class="field-label">
<span class="field-label-text">Topic Area:</span><div class="field-input post-topic">
<a href="#" class="post-topic-button">
<span class="sr">Discussion topics; current selection is: </span>
<span class="js-selected-topic"></span>
<span class="drop-arrow" aria-hidden="true">▾</span>
</a>
<div class="topic-menu-wrapper">
<label class="topic-filter-label">
<span class="sr">Filter topics</span>
<input type="text" class="topic-filter-input" placeholder="Filter topics">
</label>
<ul class="topic-menu" role="menu"><%= topics_html %></ul>
</div>
</div>
</div><span class="field-help">
Add your post to a relevant topic to help others find it.
</span>
</script>
...
...
common/static/coffee/spec/discussion/view/discussion_thread_edit_view_spec.js
0 → 100644
View file @
00c7e60e
(
function
()
{
'use strict'
;
describe
(
'DiscussionThreadEditView'
,
function
()
{
beforeEach
(
function
()
{
DiscussionSpecHelper
.
setUpGlobals
();
DiscussionSpecHelper
.
setUnderscoreFixtures
();
spyOn
(
DiscussionUtil
,
'makeWmdEditor'
);
this
.
threadData
=
DiscussionViewSpecHelper
.
makeThreadWithProps
();
this
.
thread
=
new
Thread
(
this
.
threadData
);
this
.
course_settings
=
new
DiscussionCourseSettings
({
'category_map'
:
{
'children'
:
[
'Topic'
],
'entries'
:
{
'Topic'
:
{
'is_cohorted'
:
true
,
'id'
:
'topic'
}
}
},
'is_cohorted'
:
true
});
this
.
createEditView
=
function
(
options
)
{
options
=
_
.
extend
({
container
:
$
(
'#fixture-element'
),
model
:
this
.
thread
,
mode
:
'tab'
,
topicId
:
'dummy_id'
,
course_settings
:
this
.
course_settings
},
options
);
this
.
view
=
new
DiscussionThreadEditView
(
options
);
this
.
view
.
render
();
};
});
it
(
'can save new data correctly'
,
function
()
{
var
view
;
spyOn
(
$
,
'ajax'
).
andCallFake
(
function
(
params
)
{
expect
(
params
.
url
.
path
()).
toEqual
(
DiscussionUtil
.
urlFor
(
'update_thread'
,
'dummy_id'
));
expect
(
params
.
data
.
commentable_id
).
toBe
(
'topic'
);
expect
(
params
.
data
.
title
).
toBe
(
'new_title'
);
params
.
success
();
return
{
always
:
function
()
{}};
});
this
.
createEditView
();
this
.
view
.
$el
.
find
(
'a.topic-title'
).
first
().
click
();
// set new topic
this
.
view
.
$
(
'.edit-post-title'
).
val
(
'new_title'
);
// set new title
this
.
view
.
$
(
'.post-update'
).
click
();
expect
(
$
.
ajax
).
toHaveBeenCalled
();
expect
(
this
.
thread
.
get
(
'title'
)).
toBe
(
'new_title'
);
expect
(
this
.
thread
.
get
(
'commentable_id'
)).
toBe
(
'topic'
);
expect
(
this
.
thread
.
get
(
'courseware_title'
)).
toBe
(
'Topic'
);
expect
(
this
.
view
.
$
(
'.edit-post-title'
)).
toHaveValue
(
''
);
expect
(
this
.
view
.
$
(
'.wmd-preview p'
)).
toHaveText
(
''
);
});
it
(
'can close the view'
,
function
()
{
this
.
createEditView
();
this
.
view
.
$
(
'.post-cancel'
).
click
();
expect
(
$
(
'.edit-post-form'
)).
not
.
toExist
();
});
});
}).
call
(
this
);
common/static/coffee/spec/discussion/view/discussion_thread_view_spec.coffee
View file @
00c7e60e
...
@@ -6,6 +6,7 @@ describe "DiscussionThreadView", ->
...
@@ -6,6 +6,7 @@ describe "DiscussionThreadView", ->
jasmine
.
Clock
.
useMock
()
jasmine
.
Clock
.
useMock
()
@
threadData
=
DiscussionViewSpecHelper
.
makeThreadWithProps
({})
@
threadData
=
DiscussionViewSpecHelper
.
makeThreadWithProps
({})
@
thread
=
new
Thread
(
@
threadData
)
@
thread
=
new
Thread
(
@
threadData
)
@
discussion
=
new
Discussion
(
@
thread
)
spyOn
(
$
,
"ajax"
)
spyOn
(
$
,
"ajax"
)
# Avoid unnecessary boilerplate
# Avoid unnecessary boilerplate
spyOn
(
DiscussionThreadShowView
.
prototype
,
"convertMath"
)
spyOn
(
DiscussionThreadShowView
.
prototype
,
"convertMath"
)
...
@@ -44,6 +45,7 @@ describe "DiscussionThreadView", ->
...
@@ -44,6 +45,7 @@ describe "DiscussionThreadView", ->
checkCommentForm
=
(
originallyClosed
,
mode
)
->
checkCommentForm
=
(
originallyClosed
,
mode
)
->
threadData
=
DiscussionViewSpecHelper
.
makeThreadWithProps
({
closed
:
originallyClosed
})
threadData
=
DiscussionViewSpecHelper
.
makeThreadWithProps
({
closed
:
originallyClosed
})
thread
=
new
Thread
(
threadData
)
thread
=
new
Thread
(
threadData
)
discussion
=
new
Discussion
(
thread
)
view
=
new
DiscussionThreadView
({
model
:
thread
,
el
:
$
(
"#fixture-element"
),
mode
:
mode
})
view
=
new
DiscussionThreadView
({
model
:
thread
,
el
:
$
(
"#fixture-element"
),
mode
:
mode
})
renderWithContent
(
view
,
{
resp_total
:
1
,
children
:
[{}]})
renderWithContent
(
view
,
{
resp_total
:
1
,
children
:
[{}]})
if
mode
==
"inline"
if
mode
==
"inline"
...
...
common/static/coffee/spec/discussion/view/discussion_topic_menu_view_spec.js
0 → 100644
View file @
00c7e60e
(
function
()
{
'use strict'
;
describe
(
'DiscussionTopicMenuView'
,
function
()
{
beforeEach
(
function
()
{
this
.
createTopicView
=
function
(
options
)
{
options
=
_
.
extend
({
course_settings
:
this
.
course_settings
,
topicId
:
void
0
},
options
);
this
.
view
=
new
DiscussionTopicMenuView
(
options
);
this
.
view
.
render
().
appendTo
(
'#fixture-element'
);
this
.
defaultTextWidth
=
this
.
view
.
getNameWidth
(
this
.
completeText
);
};
this
.
openMenu
=
function
()
{
var
menuWrapper
=
this
.
view
.
$
(
'.topic-menu-wrapper'
);
expect
(
menuWrapper
).
toBeHidden
();
this
.
view
.
$el
.
find
(
'.post-topic-button'
).
first
().
click
();
expect
(
menuWrapper
).
toBeVisible
();
};
this
.
closeMenu
=
function
()
{
var
menuWrapper
=
this
.
view
.
$
(
'.topic-menu-wrapper'
);
expect
(
menuWrapper
).
toBeVisible
();
this
.
view
.
$el
.
find
(
'.post-topic-button'
).
first
().
click
();
expect
(
menuWrapper
).
toBeHidden
();
};
DiscussionSpecHelper
.
setUpGlobals
();
DiscussionSpecHelper
.
setUnderscoreFixtures
();
this
.
course_settings
=
new
DiscussionCourseSettings
({
'category_map'
:
{
'subcategories'
:
{
'Basic Question Types'
:
{
'subcategories'
:
{},
'children'
:
[
'Selection From Options'
,
'Numerical Input'
],
'entries'
:
{
'Selection From Options'
:
{
'sort_key'
:
null
,
'is_cohorted'
:
true
,
'id'
:
'cba3e4cd91d0466b9ac50926e495b76f'
},
'Numerical Input'
:
{
'sort_key'
:
null
,
'is_cohorted'
:
false
,
'id'
:
'c49f0dfb8fc94c9c8d9999cc95190c56'
}
}
}
},
'children'
:
[
'Basic Question Types'
],
'entries'
:
{}
},
'is_cohorted'
:
true
});
this
.
parentCategoryText
=
'Basic Question Types'
;
this
.
selectedOptionText
=
'Selection From Options'
;
this
.
completeText
=
this
.
parentCategoryText
+
' / '
+
this
.
selectedOptionText
;
});
it
(
'completely show parent category and sub-category'
,
function
()
{
var
dropdownText
;
this
.
createTopicView
();
this
.
view
.
maxNameWidth
=
this
.
defaultTextWidth
+
1
;
this
.
view
.
$el
.
find
(
'a.topic-title'
).
first
().
click
();
dropdownText
=
this
.
view
.
$el
.
find
(
'.js-selected-topic'
).
text
();
expect
(
this
.
completeText
).
toEqual
(
dropdownText
);
});
it
(
'completely show just sub-category'
,
function
()
{
var
dropdownText
;
this
.
createTopicView
();
this
.
view
.
maxNameWidth
=
this
.
defaultTextWidth
-
10
;
this
.
view
.
$el
.
find
(
'a.topic-title'
).
first
().
click
();
dropdownText
=
this
.
view
.
$el
.
find
(
'.js-selected-topic'
).
text
();
expect
(
dropdownText
.
indexOf
(
'…'
)).
toEqual
(
0
);
expect
(
dropdownText
).
toContain
(
this
.
selectedOptionText
);
});
it
(
'partially show sub-category'
,
function
()
{
this
.
createTopicView
();
var
parentWidth
=
this
.
view
.
getNameWidth
(
this
.
parentCategoryText
),
dropdownText
;
this
.
view
.
maxNameWidth
=
this
.
defaultTextWidth
-
parentWidth
;
this
.
view
.
$el
.
find
(
'a.topic-title'
).
first
().
click
();
dropdownText
=
this
.
view
.
$el
.
find
(
'.js-selected-topic'
).
text
();
expect
(
dropdownText
.
indexOf
(
'…'
)).
toEqual
(
0
);
expect
(
dropdownText
.
lastIndexOf
(
'…'
)).
toBeGreaterThan
(
0
);
});
it
(
'broken span doesn
\'
t occur'
,
function
()
{
var
dropdownText
;
this
.
createTopicView
();
this
.
view
.
maxNameWidth
=
this
.
view
.
getNameWidth
(
this
.
selectedOptionText
)
+
100
;
this
.
view
.
$el
.
find
(
'a.topic-title'
).
first
().
click
();
dropdownText
=
this
.
view
.
$el
.
find
(
'.js-selected-topic'
).
text
();
expect
(
dropdownText
.
indexOf
(
'/ span>'
)).
toEqual
(
-
1
);
});
it
(
'appropriate topic is selected if `topicId` is passed'
,
function
()
{
var
completeText
=
this
.
parentCategoryText
+
' / Numerical Input'
,
dropdownText
;
this
.
createTopicView
({
topicId
:
'c49f0dfb8fc94c9c8d9999cc95190c56'
});
this
.
view
.
maxNameWidth
=
this
.
defaultTextWidth
+
1
;
this
.
view
.
render
();
dropdownText
=
this
.
view
.
$el
.
find
(
'.js-selected-topic'
).
text
();
expect
(
completeText
).
toEqual
(
dropdownText
);
});
it
(
'click outside of the dropdown close it'
,
function
()
{
this
.
createTopicView
();
this
.
openMenu
();
$
(
document
.
body
).
click
();
expect
(
this
.
view
.
$
(
'.topic-menu-wrapper'
)).
toBeHidden
();
});
it
(
'can toggle the menu'
,
function
()
{
this
.
createTopicView
();
this
.
openMenu
();
this
.
closeMenu
();
});
});
}).
call
(
this
);
common/static/coffee/spec/discussion/view/new_post_view_spec.coffee
View file @
00c7e60e
...
@@ -10,115 +10,6 @@ describe "NewPostView", ->
...
@@ -10,115 +10,6 @@ describe "NewPostView", ->
)
)
@
discussion
=
new
Discussion
([],
{
pages
:
1
})
@
discussion
=
new
Discussion
([],
{
pages
:
1
})
describe
"Drop down works correct"
,
->
beforeEach
->
@
course_settings
=
new
DiscussionCourseSettings
({
"category_map"
:
{
"subcategories"
:
{
"Basic Question Types"
:
{
"subcategories"
:
{},
"children"
:
[
"Selection From Options"
],
"entries"
:
{
"Selection From Options"
:
{
"sort_key"
:
null
,
"is_cohorted"
:
true
,
"id"
:
"cba3e4cd91d0466b9ac50926e495b76f"
}
},
},
},
"children"
:
[
"Basic Question Types"
],
"entries"
:
{}
},
"allow_anonymous"
:
true
,
"allow_anonymous_to_peers"
:
true
,
"is_cohorted"
:
true
})
@
view
=
new
NewPostView
(
el
:
$
(
"#fixture-element"
),
collection
:
@
discussion
,
course_settings
:
@
course_settings
,
mode
:
"tab"
)
@
view
.
render
()
@
parent_category_text
=
"Basic Question Types"
@
selected_option_text
=
"Selection From Options"
it
"completely show parent category and sub-category"
,
->
complete_text
=
@
parent_category_text
+
" / "
+
@
selected_option_text
selected_text_width
=
@
view
.
getNameWidth
(
complete_text
)
@
view
.
maxNameWidth
=
selected_text_width
+
1
@
view
.
$el
.
find
(
"a.topic-title"
).
first
().
click
()
dropdown_text
=
@
view
.
$el
.
find
(
".js-selected-topic"
).
text
()
expect
(
complete_text
).
toEqual
(
dropdown_text
)
it
"completely show just sub-category"
,
->
complete_text
=
@
parent_category_text
+
" / "
+
@
selected_option_text
selected_text_width
=
@
view
.
getNameWidth
(
complete_text
)
@
view
.
maxNameWidth
=
selected_text_width
-
10
@
view
.
$el
.
find
(
"a.topic-title"
).
first
().
click
()
dropdown_text
=
@
view
.
$el
.
find
(
".js-selected-topic"
).
text
()
expect
(
dropdown_text
.
indexOf
(
"…"
)).
toEqual
(
0
)
expect
(
dropdown_text
).
toContain
(
@
selected_option_text
)
it
"partially show sub-category"
,
->
parent_width
=
@
view
.
getNameWidth
(
@
parent_category_text
)
complete_text
=
@
parent_category_text
+
" / "
+
@
selected_option_text
selected_text_width
=
@
view
.
getNameWidth
(
complete_text
)
@
view
.
maxNameWidth
=
selected_text_width
-
parent_width
@
view
.
$el
.
find
(
"a.topic-title"
).
first
().
click
()
dropdown_text
=
@
view
.
$el
.
find
(
".js-selected-topic"
).
text
()
expect
(
dropdown_text
.
indexOf
(
"…"
)).
toEqual
(
0
)
expect
(
dropdown_text
.
lastIndexOf
(
"…"
)).
toBeGreaterThan
(
0
)
it
"broken span doesn't occur"
,
->
complete_text
=
@
parent_category_text
+
" / "
+
@
selected_option_text
selected_text_width
=
@
view
.
getNameWidth
(
complete_text
)
@
view
.
maxNameWidth
=
@
view
.
getNameWidth
(
@
selected_option_text
)
+
100
@
view
.
$el
.
find
(
"a.topic-title"
).
first
().
click
()
dropdown_text
=
@
view
.
$el
.
find
(
".js-selected-topic"
).
text
()
expect
(
dropdown_text
.
indexOf
(
"/ span>"
)).
toEqual
(
-
1
)
describe
"cohort selector"
,
->
renderWithCohortedTopics
=
(
course_settings
,
view
,
isCohortedFirst
)
->
course_settings
.
set
(
"category_map"
,
{
"children"
:
if
isCohortedFirst
then
[
"Cohorted"
,
"Non-Cohorted"
]
else
[
"Non-Cohorted"
,
"Cohorted"
],
"entries"
:
{
"Non-Cohorted"
:
{
"sort_key"
:
null
,
"is_cohorted"
:
false
,
"id"
:
"non-cohorted"
},
"Cohorted"
:
{
"sort_key"
:
null
,
"is_cohorted"
:
true
,
"id"
:
"cohorted"
}
}
}
)
DiscussionSpecHelper
.
makeModerator
()
view
.
render
()
expectCohortSelectorEnabled
=
(
view
,
enabled
)
->
expect
(
view
.
$
(
".js-group-select"
).
prop
(
"disabled"
)).
toEqual
(
not
enabled
)
if
not
enabled
expect
(
view
.
$
(
".js-group-select option:selected"
).
attr
(
"value"
)).
toEqual
(
""
)
it
"is disabled with non-cohorted default topic and enabled by selecting cohorted topic"
,
->
renderWithCohortedTopics
(
@
course_settings
,
@
view
,
false
)
expectCohortSelectorEnabled
(
@
view
,
false
)
@
view
.
$
(
"a.topic-title[data-discussion-id=cohorted]"
).
click
()
expectCohortSelectorEnabled
(
@
view
,
true
)
it
"is enabled with cohorted default topic and disabled by selecting non-cohorted topic"
,
->
renderWithCohortedTopics
(
@
course_settings
,
@
view
,
true
)
expectCohortSelectorEnabled
(
@
view
,
true
)
@
view
.
$
(
"a.topic-title[data-discussion-id=non-cohorted]"
).
click
()
expectCohortSelectorEnabled
(
@
view
,
false
)
describe
"cohort selector"
,
->
describe
"cohort selector"
,
->
beforeEach
->
beforeEach
->
@
course_settings
=
new
DiscussionCourseSettings
({
@
course_settings
=
new
DiscussionCourseSettings
({
...
...
common/static/coffee/src/discussion/.gitignore
0 → 100644
View file @
00c7e60e
!views/discussion_thread_edit_view.js
!views/discussion_topic_menu_view.js
common/static/coffee/src/discussion/discussion_module_view.coffee
View file @
00c7e60e
...
@@ -99,7 +99,13 @@ if Backbone?
...
@@ -99,7 +99,13 @@ if Backbone?
@
newPostForm
=
$
(
'.new-post-article'
)
@
newPostForm
=
$
(
'.new-post-article'
)
@
threadviews
=
@
discussion
.
map
(
thread
)
->
@
threadviews
=
@
discussion
.
map
(
thread
)
->
new
DiscussionThreadView
el
:
@
$
(
"article#thread_
#{
thread
.
id
}
"
),
model
:
thread
,
mode
:
"inline"
new
DiscussionThreadView
(
el
:
@
$
(
"article#thread_
#{
thread
.
id
}
"
),
model
:
thread
,
mode
:
"inline"
,
course_settings
:
@
course_settings
,
topicId
:
discussionId
)
_
.
each
@
threadviews
,
(
dtv
)
->
dtv
.
render
()
_
.
each
@
threadviews
,
(
dtv
)
->
dtv
.
render
()
DiscussionUtil
.
bulkUpdateContentInfo
(
window
.
$
$annotated_content_info
)
DiscussionUtil
.
bulkUpdateContentInfo
(
window
.
$
$annotated_content_info
)
@
newPostView
=
new
NewPostView
(
@
newPostView
=
new
NewPostView
(
...
@@ -123,7 +129,14 @@ if Backbone?
...
@@ -123,7 +129,14 @@ if Backbone?
# TODO: When doing pagination, this will need to repaginate. Perhaps just reload page 1?
# TODO: When doing pagination, this will need to repaginate. Perhaps just reload page 1?
article
=
$
(
"<article class='discussion-thread' id='thread_
#{
thread
.
id
}
'></article>"
)
article
=
$
(
"<article class='discussion-thread' id='thread_
#{
thread
.
id
}
'></article>"
)
@
$
(
'section.discussion > .threads'
).
prepend
(
article
)
@
$
(
'section.discussion > .threads'
).
prepend
(
article
)
threadView
=
new
DiscussionThreadView
el
:
article
,
model
:
thread
,
mode
:
"inline"
threadView
=
new
DiscussionThreadView
(
el
:
article
,
model
:
thread
,
mode
:
"inline"
,
course_settings
:
@
course_settings
,
topicId
:
@
$el
.
data
(
"discussion-id"
)
)
threadView
.
render
()
threadView
.
render
()
@
threadviews
.
unshift
threadView
@
threadviews
.
unshift
threadView
...
...
common/static/coffee/src/discussion/discussion_router.coffee
View file @
00c7e60e
...
@@ -54,7 +54,12 @@ if Backbone?
...
@@ -54,7 +54,12 @@ if Backbone?
if
(
@
newPost
.
is
(
":visible"
))
if
(
@
newPost
.
is
(
":visible"
))
@
newPost
.
fadeOut
()
@
newPost
.
fadeOut
()
@
main
=
new
DiscussionThreadView
(
el
:
$
(
".forum-content"
),
model
:
@
thread
,
mode
:
"tab"
)
@
main
=
new
DiscussionThreadView
(
el
:
$
(
".forum-content"
),
model
:
@
thread
,
mode
:
"tab"
,
course_settings
:
@
course_settings
,
)
@
main
.
render
()
@
main
.
render
()
@
main
.
on
"thread:responses:rendered"
,
=>
@
main
.
on
"thread:responses:rendered"
,
=>
@
nav
.
updateSidebar
()
@
nav
.
updateSidebar
()
...
...
common/static/coffee/src/discussion/views/discussion_thread_edit_view.coffee
deleted
100644 → 0
View file @
3cdfdae8
if
Backbone
?
class
@
DiscussionThreadEditView
extends
Backbone
.
View
events
:
"click .post-update"
:
"update"
"click .post-cancel"
:
"cancel_edit"
$
:
(
selector
)
->
@
$el
.
find
(
selector
)
initialize
:
->
super
()
render
:
->
@
template
=
_
.
template
(
$
(
"#thread-edit-template"
).
html
())
@
$el
.
html
(
@
template
(
@
model
.
toJSON
()))
@
delegateEvents
()
DiscussionUtil
.
makeWmdEditor
@
$el
,
$
.
proxy
(
@
$
,
@
),
"edit-post-body"
@
update
:
(
event
)
->
@
trigger
"thread:update"
,
event
cancel_edit
:
(
event
)
->
@
trigger
"thread:cancel_edit"
,
event
common/static/coffee/src/discussion/views/discussion_thread_edit_view.js
0 → 100644
View file @
00c7e60e
(
function
(
Backbone
)
{
'use strict'
;
if
(
Backbone
)
{
this
.
DiscussionThreadEditView
=
Backbone
.
View
.
extend
({
tagName
:
'form'
,
events
:
{
'submit'
:
'updateHandler'
,
'click .post-cancel'
:
'cancelHandler'
},
attributes
:
{
'class'
:
'discussion-post edit-post-form'
},
initialize
:
function
(
options
)
{
this
.
container
=
options
.
container
||
$
(
'.thread-content-wrapper'
);
this
.
mode
=
options
.
mode
||
'inline'
;
this
.
course_settings
=
options
.
course_settings
;
this
.
topicId
=
options
.
topicId
;
_
.
bindAll
(
this
);
return
this
;
},
render
:
function
()
{
this
.
template
=
_
.
template
(
$
(
'#thread-edit-template'
).
html
());
this
.
$el
.
html
(
this
.
template
(
this
.
model
.
toJSON
())).
appendTo
(
this
.
container
);
this
.
submitBtn
=
this
.
$
(
'.post-update'
);
if
(
this
.
isTabMode
())
{
this
.
topicView
=
new
DiscussionTopicMenuView
({
topicId
:
this
.
topicId
,
course_settings
:
this
.
course_settings
});
this
.
addField
(
this
.
topicView
.
render
());
}
DiscussionUtil
.
makeWmdEditor
(
this
.
$el
,
$
.
proxy
(
this
.
$
,
this
),
'edit-post-body'
);
return
this
;
},
addField
:
function
(
fieldView
)
{
this
.
$
(
'.forum-edit-post-form-wrapper'
).
append
(
fieldView
);
return
this
;
},
isTabMode
:
function
()
{
return
this
.
mode
===
'tab'
;
},
save
:
function
()
{
var
title
=
this
.
$
(
'.edit-post-title'
).
val
(),
body
=
this
.
$
(
'.edit-post-body textarea'
).
val
(),
commentableId
=
this
.
isTabMode
()
?
this
.
topicView
.
getCurrentTopicId
()
:
null
;
return
DiscussionUtil
.
safeAjax
({
$elem
:
this
.
submitBtn
,
$loading
:
this
.
submitBtn
,
url
:
DiscussionUtil
.
urlFor
(
'update_thread'
,
this
.
model
.
id
),
type
:
'POST'
,
dataType
:
'json'
,
async
:
false
,
// @TODO when the rest of the stuff below is made to work properly..
data
:
{
title
:
title
,
body
:
body
,
commentable_id
:
commentableId
},
error
:
DiscussionUtil
.
formErrorHandler
(
this
.
$
(
'.post-errors'
)),
success
:
function
()
{
var
newAttrs
=
{
title
:
title
,
body
:
body
};
// @TODO: Move this out of the callback, this makes it feel sluggish
this
.
$
(
'.edit-post-title'
).
val
(
''
).
attr
(
'prev-text'
,
''
);
this
.
$
(
'.edit-post-body textarea'
).
val
(
''
).
attr
(
'prev-text'
,
''
);
this
.
$
(
'.wmd-preview p'
).
html
(
''
);
if
(
this
.
isTabMode
())
{
_
.
extend
(
newAttrs
,
{
commentable_id
:
commentableId
,
courseware_title
:
this
.
topicView
.
getFullTopicName
()
});
}
this
.
model
.
set
(
newAttrs
).
unset
(
'abbreviatedBody'
);
this
.
trigger
(
'thread:updated'
);
}.
bind
(
this
)
});
},
updateHandler
:
function
(
event
)
{
event
.
preventDefault
();
// this event is for the moment triggered and used nowhere.
this
.
trigger
(
'thread:update'
,
event
);
this
.
save
();
return
this
;
},
cancelHandler
:
function
(
event
)
{
event
.
preventDefault
();
this
.
trigger
(
"thread:cancel_edit"
,
event
);
this
.
remove
();
return
this
;
}
});
}
}).
call
(
this
,
Backbone
);
common/static/coffee/src/discussion/views/discussion_thread_list_view.coffee
View file @
00c7e60e
...
@@ -5,7 +5,7 @@ if Backbone?
...
@@ -5,7 +5,7 @@ if Backbone?
"keypress .forum-nav-browse-filter-input"
:
(
event
)
=>
DiscussionUtil
.
ignoreEnterKey
(
event
)
"keypress .forum-nav-browse-filter-input"
:
(
event
)
=>
DiscussionUtil
.
ignoreEnterKey
(
event
)
"keyup .forum-nav-browse-filter-input"
:
"filterTopics"
"keyup .forum-nav-browse-filter-input"
:
"filterTopics"
"click .forum-nav-browse-menu-wrapper"
:
"ignoreClick"
"click .forum-nav-browse-menu-wrapper"
:
"ignoreClick"
"click .forum-nav-browse-title"
:
"selectTopic"
"click .forum-nav-browse-title"
:
"selectTopic
Handler
"
"keydown .forum-nav-search-input"
:
"performSearch"
"keydown .forum-nav-search-input"
:
"performSearch"
"change .forum-nav-sort-control"
:
"sortThreads"
"change .forum-nav-sort-control"
:
"sortThreads"
"click .forum-nav-thread-link"
:
"threadSelected"
"click .forum-nav-thread-link"
:
"threadSelected"
...
@@ -130,12 +130,12 @@ if Backbone?
...
@@ -130,12 +130,12 @@ if Backbone?
)
)
@
$
(
".forum-nav-sort-control"
).
val
(
@
collection
.
sort_preference
)
@
$
(
".forum-nav-sort-control"
).
val
(
@
collection
.
sort_preference
)
$
(
window
).
bind
"load"
,
@
updateSidebar
$
(
window
).
bind
"load scroll resize"
,
@
updateSidebar
$
(
window
).
bind
"scroll"
,
@
updateSidebar
$
(
window
).
bind
"resize"
,
@
updateSidebar
@
displayedCollection
.
on
"reset"
,
@
renderThreads
@
displayedCollection
.
on
"reset"
,
@
renderThreads
@
displayedCollection
.
on
"thread:remove"
,
@
renderThreads
@
displayedCollection
.
on
"thread:remove"
,
@
renderThreads
@
displayedCollection
.
on
"change:commentable_id"
,
(
model
,
commentable_id
)
=>
@
retrieveDiscussions
@
discussionIds
.
split
(
","
)
if
@
mode
is
"commentables"
@
renderThreads
()
@
renderThreads
()
@
@
...
@@ -185,7 +185,7 @@ if Backbone?
...
@@ -185,7 +185,7 @@ if Backbone?
when
'search'
when
'search'
options
.
search_text
=
@
current_search
options
.
search_text
=
@
current_search
if
@
group_id
if
@
group_id
options
.
group_id
=
@
group_id
options
.
group_id
=
@
group_id
when
'followed'
when
'followed'
options
.
user_id
=
window
.
user
.
id
options
.
user_id
=
window
.
user
.
id
options
.
group_id
=
"all"
options
.
group_id
=
"all"
...
@@ -196,8 +196,7 @@ if Backbone?
...
@@ -196,8 +196,7 @@ if Backbone?
when
'all'
when
'all'
if
@
group_id
if
@
group_id
options
.
group_id
=
@
group_id
options
.
group_id
=
@
group_id
lastThread
=
@
collection
.
last
()
?
.
get
(
'id'
)
lastThread
=
@
collection
.
last
()
?
.
get
(
'id'
)
if
lastThread
if
lastThread
# Pagination; focus the first thread after what was previously the last thread
# Pagination; focus the first thread after what was previously the last thread
...
@@ -262,7 +261,7 @@ if Backbone?
...
@@ -262,7 +261,7 @@ if Backbone?
else
else
$
(
'input.email-setting'
).
removeAttr
(
'checked'
)
$
(
'input.email-setting'
).
removeAttr
(
'checked'
)
thread_id
=
null
thread_id
=
null
@
trigger
(
"thread:removed"
)
@
trigger
(
"thread:removed"
)
#select all threads
#select all threads
isBrowseMenuVisible
:
=>
isBrowseMenuVisible
:
=>
...
@@ -359,12 +358,15 @@ if Backbone?
...
@@ -359,12 +358,15 @@ if Backbone?
name
=
prefix
+
rawName
+
gettext
(
"…"
)
name
=
prefix
+
rawName
+
gettext
(
"…"
)
return
name
return
name
selectTopic
:
(
event
)
->
selectTopic
Handler
:
(
event
)
->
event
.
preventDefault
()
event
.
preventDefault
()
@
selectTopic
$
(
event
.
target
)
selectTopic
:
(
$target
)
->
@
hideBrowseMenu
()
@
hideBrowseMenu
()
@
clearSearch
()
@
clearSearch
()
item
=
$
(
event
.
target
)
.
closest
(
'.forum-nav-browse-menu-item'
)
item
=
$
target
.
closest
(
'.forum-nav-browse-menu-item'
)
@
setCurrentTopicDisplay
(
@
getPathText
(
item
))
@
setCurrentTopicDisplay
(
@
getPathText
(
item
))
if
item
.
hasClass
(
"forum-nav-browse-menu-all"
)
if
item
.
hasClass
(
"forum-nav-browse-menu-all"
)
@
discussionIds
=
""
@
discussionIds
=
""
...
@@ -388,7 +390,7 @@ if Backbone?
...
@@ -388,7 +390,7 @@ if Backbone?
chooseCohort
:
(
event
)
=>
chooseCohort
:
(
event
)
=>
@
group_id
=
@
$
(
'.forum-nav-filter-cohort-control :selected'
).
val
()
@
group_id
=
@
$
(
'.forum-nav-filter-cohort-control :selected'
).
val
()
@
retrieveFirstPage
()
@
retrieveFirstPage
()
retrieveDiscussion
:
(
discussion_id
,
callback
=
null
)
->
retrieveDiscussion
:
(
discussion_id
,
callback
=
null
)
->
url
=
DiscussionUtil
.
urlFor
(
"retrieve_discussion"
,
discussion_id
)
url
=
DiscussionUtil
.
urlFor
(
"retrieve_discussion"
,
discussion_id
)
DiscussionUtil
.
safeAjax
DiscussionUtil
.
safeAjax
...
@@ -403,7 +405,7 @@ if Backbone?
...
@@ -403,7 +405,7 @@ if Backbone?
if
callback
?
if
callback
?
callback
()
callback
()
retrieveDiscussions
:
(
discussion_ids
)
->
retrieveDiscussions
:
(
discussion_ids
)
->
@
discussionIds
=
discussion_ids
.
join
(
','
)
@
discussionIds
=
discussion_ids
.
join
(
','
)
@
mode
=
'commentables'
@
mode
=
'commentables'
...
...
common/static/coffee/src/discussion/views/discussion_thread_view.coffee
View file @
00c7e60e
...
@@ -21,6 +21,13 @@ if Backbone?
...
@@ -21,6 +21,13 @@ if Backbone?
@
mode
=
options
.
mode
or
"inline"
# allowed values are "tab" or "inline"
@
mode
=
options
.
mode
or
"inline"
# allowed values are "tab" or "inline"
if
@
mode
not
in
[
"tab"
,
"inline"
]
if
@
mode
not
in
[
"tab"
,
"inline"
]
throw
new
Error
(
"invalid mode: "
+
@
mode
)
throw
new
Error
(
"invalid mode: "
+
@
mode
)
# Quick fix to have an actual model when we're receiving new models from
# the server.
@
model
.
collection
.
on
"reset"
,
(
collection
)
=>
id
=
@
model
.
get
(
"id"
)
@
model
=
collection
.
get
(
id
)
if
collection
.
get
(
id
)
@
createShowView
()
@
createShowView
()
@
responses
=
new
Comments
()
@
responses
=
new
Comments
()
@
loadedResponses
=
false
@
loadedResponses
=
false
...
@@ -254,49 +261,20 @@ if Backbone?
...
@@ -254,49 +261,20 @@ if Backbone?
@
createEditView
()
@
createEditView
()
@
renderEditView
()
@
renderEditView
()
update
:
(
event
)
=>
newTitle
=
@
editView
.
$
(
".edit-post-title"
).
val
()
newBody
=
@
editView
.
$
(
".edit-post-body textarea"
).
val
()
url
=
DiscussionUtil
.
urlFor
(
'update_thread'
,
@
model
.
id
)
DiscussionUtil
.
safeAjax
$elem
:
$
(
event
.
target
)
$loading
:
$
(
event
.
target
)
if
event
url
:
url
type
:
"POST"
dataType
:
'json'
async
:
false
# TODO when the rest of the stuff below is made to work properly..
data
:
title
:
newTitle
body
:
newBody
error
:
DiscussionUtil
.
formErrorHandler
(
@
$
(
".edit-post-form-errors"
))
success
:
(
response
,
textStatus
)
=>
# TODO: Move this out of the callback, this makes it feel sluggish
@
editView
.
$
(
".edit-post-title"
).
val
(
""
).
attr
(
"prev-text"
,
""
)
@
editView
.
$
(
".edit-post-body textarea"
).
val
(
""
).
attr
(
"prev-text"
,
""
)
@
editView
.
$
(
".wmd-preview p"
).
html
(
""
)
@
model
.
set
title
:
newTitle
body
:
newBody
@
model
.
unset
(
"abbreviatedBody"
)
@
createShowView
()
@
renderShowView
()
createEditView
:
()
->
createEditView
:
()
->
if
@
showView
?
if
@
showView
?
@
showView
.
undelegateEvents
()
@
showView
.
undelegateEvents
()
@
showView
.
$el
.
empty
()
@
showView
.
$el
.
empty
()
@
showView
=
null
@
showView
=
null
@
editView
=
new
DiscussionThreadEditView
(
model
:
@
model
)
@
editView
=
new
DiscussionThreadEditView
(
@
editView
.
bind
"thread:update"
,
@
update
container
:
@
$
(
'.thread-content-wrapper'
)
@
editView
.
bind
"thread:cancel_edit"
,
@
cancelEdit
model
:
@
model
mode
:
@
mode
course_settings
:
@
options
.
course_settings
topicId
:
@
model
.
get
(
'commentable_id'
)
)
@
editView
.
bind
"thread:updated thread:cancel_edit"
,
@
closeEditView
renderSubView
:
(
view
)
->
renderSubView
:
(
view
)
->
view
.
setElement
(
@
$
(
'.thread-content-wrapper'
))
view
.
setElement
(
@
$
(
'.thread-content-wrapper'
))
...
@@ -304,15 +282,9 @@ if Backbone?
...
@@ -304,15 +282,9 @@ if Backbone?
view
.
delegateEvents
()
view
.
delegateEvents
()
renderEditView
:
()
->
renderEditView
:
()
->
@
renderSubView
(
@
editView
)
@
editView
.
render
(
)
createShowView
:
()
->
createShowView
:
()
->
if
@
editView
?
@
editView
.
undelegateEvents
()
@
editView
.
$el
.
empty
()
@
editView
=
null
@
showView
=
new
DiscussionThreadShowView
({
model
:
@
model
,
mode
:
@
mode
})
@
showView
=
new
DiscussionThreadShowView
({
model
:
@
model
,
mode
:
@
mode
})
@
showView
.
bind
"thread:_delete"
,
@
_delete
@
showView
.
bind
"thread:_delete"
,
@
_delete
@
showView
.
bind
"thread:edit"
,
@
edit
@
showView
.
bind
"thread:edit"
,
@
edit
...
@@ -320,8 +292,7 @@ if Backbone?
...
@@ -320,8 +292,7 @@ if Backbone?
renderShowView
:
()
->
renderShowView
:
()
->
@
renderSubView
(
@
showView
)
@
renderSubView
(
@
showView
)
cancelEdit
:
(
event
)
=>
closeEditView
:
(
event
)
=>
event
.
preventDefault
()
@
createShowView
()
@
createShowView
()
@
renderShowView
()
@
renderShowView
()
...
...
common/static/coffee/src/discussion/views/discussion_topic_menu_view.js
0 → 100644
View file @
00c7e60e
(
function
(
Backbone
)
{
'use strict'
;
if
(
Backbone
)
{
this
.
DiscussionTopicMenuView
=
Backbone
.
View
.
extend
({
events
:
{
'click .post-topic-button'
:
'toggleTopicDropdown'
,
'click .topic-menu-wrapper'
:
'handleTopicEvent'
,
'click .topic-filter-label'
:
'ignoreClick'
,
'keyup .topic-filter-input'
:
this
.
DiscussionFilter
.
filterDrop
},
attributes
:
{
'class'
:
'post-field'
},
initialize
:
function
(
options
)
{
this
.
course_settings
=
options
.
course_settings
;
this
.
currentTopicId
=
options
.
topicId
;
this
.
maxNameWidth
=
100
;
_
.
bindAll
(
this
);
return
this
;
},
/**
* When the menu is expanded, a click on the body element (outside of the menu) or on a menu element
* should close the menu except when the target is the search field. To accomplish this, we have to ignore
* clicks on the search field by stopping the propagation of the event.
*/
ignoreClick
:
function
(
event
)
{
event
.
stopPropagation
();
return
this
;
},
render
:
function
()
{
var
context
=
_
.
clone
(
this
.
course_settings
.
attributes
);
context
.
topics_html
=
this
.
renderCategoryMap
(
this
.
course_settings
.
get
(
'category_map'
));
this
.
$el
.
html
(
_
.
template
(
$
(
'#topic-template'
).
html
(),
context
));
this
.
dropdownButton
=
this
.
$
(
'.post-topic-button'
);
this
.
topicMenu
=
this
.
$
(
'.topic-menu-wrapper'
);
this
.
selectedTopic
=
this
.
$
(
'.js-selected-topic'
);
this
.
hideTopicDropdown
();
if
(
this
.
getCurrentTopicId
())
{
this
.
setTopic
(
this
.
$
(
'a.topic-title'
).
filter
(
'[data-discussion-id='
+
this
.
getCurrentTopicId
()
+
']'
));
}
else
{
this
.
setTopic
(
this
.
$
(
'a.topic-title'
).
first
());
}
return
this
.
$el
;
},
renderCategoryMap
:
function
(
map
)
{
var
category_template
=
_
.
template
(
$
(
'#new-post-menu-category-template'
).
html
()),
entry_template
=
_
.
template
(
$
(
'#new-post-menu-entry-template'
).
html
());
return
_
.
map
(
map
.
children
,
function
(
name
)
{
var
html
=
''
,
entry
;
if
(
_
.
has
(
map
.
entries
,
name
))
{
entry
=
map
.
entries
[
name
];
html
=
entry_template
({
text
:
name
,
id
:
entry
.
id
,
is_cohorted
:
entry
.
is_cohorted
});
}
else
{
// subcategory
html
=
category_template
({
text
:
name
,
entries
:
this
.
renderCategoryMap
(
map
.
subcategories
[
name
])
});
}
return
html
;
},
this
).
join
(
''
);
},
toggleTopicDropdown
:
function
(
event
)
{
event
.
preventDefault
();
event
.
stopPropagation
();
if
(
this
.
menuOpen
)
{
this
.
hideTopicDropdown
();
}
else
{
this
.
showTopicDropdown
();
}
return
this
;
},
showTopicDropdown
:
function
()
{
this
.
menuOpen
=
true
;
this
.
dropdownButton
.
addClass
(
'dropped'
);
this
.
topicMenu
.
show
();
$
(
document
.
body
).
on
(
'click.topicMenu'
,
this
.
hideTopicDropdown
);
// Set here because 1) the window might get resized and things could
// change and 2) can't set in initialize because the button is hidden
this
.
maxNameWidth
=
this
.
dropdownButton
.
width
()
-
40
;
return
this
;
},
hideTopicDropdown
:
function
()
{
this
.
menuOpen
=
false
;
this
.
dropdownButton
.
removeClass
(
'dropped'
);
this
.
topicMenu
.
hide
();
$
(
document
.
body
).
off
(
'click.topicMenu'
);
return
this
;
},
handleTopicEvent
:
function
(
event
)
{
event
.
preventDefault
();
event
.
stopPropagation
();
this
.
setTopic
(
$
(
event
.
target
));
return
this
;
},
setTopic
:
function
(
$target
)
{
if
(
$target
.
data
(
'discussion-id'
))
{
this
.
topicText
=
this
.
getFullTopicName
(
$target
);
this
.
currentTopicId
=
$target
.
data
(
'discussion-id'
);
this
.
setSelectedTopicName
(
this
.
topicText
);
this
.
trigger
(
'thread:topic_change'
,
$target
);
this
.
hideTopicDropdown
();
}
return
this
;
},
getCurrentTopicId
:
function
()
{
return
this
.
currentTopicId
;
},
setSelectedTopicName
:
function
(
text
)
{
return
this
.
selectedTopic
.
html
(
this
.
fitName
(
text
));
},
/**
* Return full name for the `topicElement` if it is passed.
* Otherwise, full name for the current topic will be returned.
* @param {jQuery Element} [topicElement]
* @return {String}
*/
getFullTopicName
:
function
(
topicElement
)
{
var
name
;
if
(
topicElement
)
{
name
=
topicElement
.
html
();
_
.
each
(
topicElement
.
parents
(
'.topic-submenu'
),
function
(
item
)
{
name
=
$
(
item
).
siblings
(
'.topic-title'
).
text
()
+
' / '
+
name
;
});
return
name
;
}
else
{
return
this
.
topicText
;
}
},
// @TODO move into utils.coffee
getNameWidth
:
function
(
name
)
{
var
test
=
$
(
'<div>'
),
width
;
test
.
css
({
'font-size'
:
this
.
dropdownButton
.
css
(
'font-size'
),
'opacity'
:
0
,
'position'
:
'absolute'
,
'left'
:
-
1000
,
'top'
:
-
1000
}).
html
(
name
).
appendTo
(
document
.
body
);
width
=
test
.
width
();
test
.
remove
();
return
width
;
},
// @TODO move into utils.coffee
fitName
:
function
(
name
)
{
var
ellipsisText
=
gettext
(
'…'
),
partialName
,
path
,
rawName
;
if
(
this
.
getNameWidth
(
name
)
<
this
.
maxNameWidth
)
{
return
name
;
}
else
{
path
=
_
.
map
(
name
.
split
(
'/'
),
function
(
item
){
return
item
.
replace
(
/^
\s
+|
\s
+$/g
,
''
);
});
while
(
path
.
length
>
1
)
{
path
.
shift
();
partialName
=
ellipsisText
+
' / '
+
path
.
join
(
' / '
);
if
(
this
.
getNameWidth
(
partialName
)
<
this
.
maxNameWidth
)
{
return
partialName
;
}
}
rawName
=
path
[
0
];
name
=
ellipsisText
+
' / '
+
rawName
;
while
(
this
.
getNameWidth
(
name
)
>
this
.
maxNameWidth
)
{
rawName
=
rawName
.
slice
(
0
,
-
1
);
name
=
ellipsisText
+
' / '
+
rawName
+
' '
+
ellipsisText
;
}
}
return
name
;
}
});
}
}).
call
(
this
,
Backbone
);
common/static/coffee/src/discussion/views/new_post_view.coffee
View file @
00c7e60e
...
@@ -6,7 +6,6 @@ if Backbone?
...
@@ -6,7 +6,6 @@ if Backbone?
if
@
mode
not
in
[
"tab"
,
"inline"
]
if
@
mode
not
in
[
"tab"
,
"inline"
]
throw
new
Error
(
"invalid mode: "
+
@
mode
)
throw
new
Error
(
"invalid mode: "
+
@
mode
)
@
course_settings
=
options
.
course_settings
@
course_settings
=
options
.
course_settings
@
maxNameWidth
=
100
@
topicId
=
options
.
topicId
@
topicId
=
options
.
topicId
render
:
()
->
render
:
()
->
...
@@ -16,29 +15,21 @@ if Backbone?
...
@@ -16,29 +15,21 @@ if Backbone?
mode
:
@
mode
,
mode
:
@
mode
,
form_id
:
@
mode
+
(
if
@
topicId
then
"-"
+
@
topicId
else
""
)
form_id
:
@
mode
+
(
if
@
topicId
then
"-"
+
@
topicId
else
""
)
})
})
context
.
topics_html
=
@
renderCategoryMap
(
@
course_settings
.
get
(
"category_map"
))
if
@
mode
is
"tab"
@
$el
.
html
(
_
.
template
(
$
(
"#new-post-template"
).
html
(),
context
))
@
$el
.
html
(
_
.
template
(
$
(
"#new-post-template"
).
html
(),
context
))
if
@
isTabMode
()
if
@
mode
is
"tab"
@
topicView
=
new
DiscussionTopicMenuView
{
# set up the topic dropdown in tab mode
topicId
:
@
topicId
@
dropdownButton
=
@
$
(
".post-topic-button"
)
course_settings
:
@
course_settings
@
topicMenu
=
@
$
(
".topic-menu-wrapper"
)
}
@
hideTopicDropdown
()
@
topicView
.
on
(
'thread:topic_change'
,
@
toggleGroupDropdown
)
@
setTopic
(
@
$
(
"a.topic-title"
).
first
())
@
addField
(
@
topicView
.
render
())
DiscussionUtil
.
makeWmdEditor
@
$el
,
$
.
proxy
(
@
$
,
@
),
"js-post-body"
DiscussionUtil
.
makeWmdEditor
@
$el
,
$
.
proxy
(
@
$
,
@
),
"js-post-body"
renderCategoryMap
:
(
map
)
->
addField
:
(
fieldView
)
->
category_template
=
_
.
template
(
$
(
"#new-post-menu-category-template"
).
html
())
@
$
(
'.forum-new-post-form-wrapper'
).
append
fieldView
entry_template
=
_
.
template
(
$
(
"#new-post-menu-entry-template"
).
html
())
html
=
""
isTabMode
:
()
->
for
name
in
map
.
children
@
mode
is
"tab"
if
name
of
map
.
entries
entry
=
map
.
entries
[
name
]
html
+=
entry_template
({
text
:
name
,
id
:
entry
.
id
,
is_cohorted
:
entry
.
is_cohorted
})
else
# subcategory
html
+=
category_template
({
text
:
name
,
entries
:
@
renderCategoryMap
(
map
.
subcategories
[
name
])})
html
getCohortOptions
:
()
->
getCohortOptions
:
()
->
if
@
course_settings
.
get
(
"is_cohorted"
)
and
DiscussionUtil
.
isPrivilegedUser
()
if
@
course_settings
.
get
(
"is_cohorted"
)
and
DiscussionUtil
.
isPrivilegedUser
()
...
@@ -50,19 +41,15 @@ if Backbone?
...
@@ -50,19 +41,15 @@ if Backbone?
events
:
events
:
"submit .forum-new-post-form"
:
"createPost"
"submit .forum-new-post-form"
:
"createPost"
"click .post-topic-button"
:
"toggleTopicDropdown"
"click .topic-menu-wrapper"
:
"handleTopicEvent"
"click .topic-filter-label"
:
"ignoreClick"
"keyup .topic-filter-input"
:
DiscussionFilter
.
filterDrop
"change .post-option-input"
:
"postOptionChange"
"change .post-option-input"
:
"postOptionChange"
"click .cancel"
:
"cancel"
"click .cancel"
:
"cancel"
"reset .forum-new-post-form"
:
"updateStyles"
"reset .forum-new-post-form"
:
"updateStyles"
# Because we want the behavior that when the body is clicked the menu is
toggleGroupDropdown
:
(
$target
)
->
# closed, we need to ignore clicks in the search field and stop propagation.
if
$target
.
data
(
'cohorted'
)
# Without this, clicking the search field would also close the menu.
$
(
'.js-group-select'
).
prop
(
'disabled'
,
false
);
ignoreClick
:
(
event
)
->
else
event
.
stopPropagation
()
$
(
'.js-group-select'
).
val
(
''
).
prop
(
'disabled'
,
true
);
postOptionChange
:
(
event
)
->
postOptionChange
:
(
event
)
->
$target
=
$
(
event
.
target
)
$target
=
$
(
event
.
target
)
...
@@ -77,13 +64,14 @@ if Backbone?
...
@@ -77,13 +64,14 @@ if Backbone?
thread_type
=
@
$
(
".post-type-input:checked"
).
val
()
thread_type
=
@
$
(
".post-type-input:checked"
).
val
()
title
=
@
$
(
".js-post-title"
).
val
()
title
=
@
$
(
".js-post-title"
).
val
()
body
=
@
$
(
".js-post-body"
).
find
(
".wmd-input"
).
val
()
body
=
@
$
(
".js-post-body"
).
find
(
".wmd-input"
).
val
()
group
=
@
$
(
".js-group-select option:selected"
).
attr
(
"value"
)
group
=
@
$
(
".js-group-select option:selected"
).
attr
(
"value"
)
anonymous
=
false
||
@
$
(
".js-anon"
).
is
(
":checked"
)
anonymous
=
false
||
@
$
(
".js-anon"
).
is
(
":checked"
)
anonymous_to_peers
=
false
||
@
$
(
".js-anon-peers"
).
is
(
":checked"
)
anonymous_to_peers
=
false
||
@
$
(
".js-anon-peers"
).
is
(
":checked"
)
follow
=
false
||
@
$
(
".js-follow"
).
is
(
":checked"
)
follow
=
false
||
@
$
(
".js-follow"
).
is
(
":checked"
)
url
=
DiscussionUtil
.
urlFor
(
'create_thread'
,
@
topicId
)
topicId
=
if
@
isTabMode
()
then
@
topicView
.
getCurrentTopicId
()
else
@
topicId
url
=
DiscussionUtil
.
urlFor
(
'create_thread'
,
topicId
)
DiscussionUtil
.
safeAjax
DiscussionUtil
.
safeAjax
$elem
:
$
(
event
.
target
)
$elem
:
$
(
event
.
target
)
...
@@ -108,97 +96,6 @@ if Backbone?
...
@@ -108,97 +96,6 @@ if Backbone?
@
resetForm
()
@
resetForm
()
@
collection
.
add
thread
@
collection
.
add
thread
toggleTopicDropdown
:
(
event
)
->
event
.
preventDefault
()
event
.
stopPropagation
()
if
@
menuOpen
@
hideTopicDropdown
()
else
@
showTopicDropdown
()
showTopicDropdown
:
()
->
@
menuOpen
=
true
@
dropdownButton
.
addClass
(
'dropped'
)
@
topicMenu
.
show
()
$
(
".form-topic-drop-search-input"
).
focus
()
$
(
"body"
).
bind
"click"
,
@
hideTopicDropdown
# Set here because 1) the window might get resized and things could
# change and 2) can't set in initialize because the button is hidden
@
maxNameWidth
=
@
dropdownButton
.
width
()
-
40
# Need a fat arrow because hideTopicDropdown is passed as a callback to bind
hideTopicDropdown
:
()
=>
@
menuOpen
=
false
@
dropdownButton
.
removeClass
(
'dropped'
)
@
topicMenu
.
hide
()
$
(
"body"
).
unbind
"click"
,
@
hideTopicDropdown
handleTopicEvent
:
(
event
)
->
event
.
preventDefault
()
event
.
stopPropagation
()
@
setTopic
(
$
(
event
.
target
))
setTopic
:
(
$target
)
->
if
$target
.
data
(
'discussion-id'
)
@
topicText
=
$target
.
html
()
@
topicText
=
@
getFullTopicName
(
$target
)
@
topicId
=
$target
.
data
(
'discussion-id'
)
@
setSelectedTopic
()
if
$target
.
data
(
"cohorted"
)
$
(
".js-group-select"
).
prop
(
"disabled"
,
false
)
else
$
(
".js-group-select"
).
val
(
""
)
$
(
".js-group-select"
).
prop
(
"disabled"
,
true
)
@
hideTopicDropdown
()
setSelectedTopic
:
->
@
$
(
".js-selected-topic"
).
html
(
@
fitName
(
@
topicText
))
getFullTopicName
:
(
topicElement
)
->
name
=
topicElement
.
html
()
topicElement
.
parents
(
'.topic-submenu'
).
each
->
name
=
$
(
this
).
siblings
(
'.topic-title'
).
text
()
+
' / '
+
name
return
name
getNameWidth
:
(
name
)
->
test
=
$
(
"<div>"
)
test
.
css
"font-size"
:
@
dropdownButton
.
css
(
'font-size'
)
opacity
:
0
position
:
'absolute'
left
:
-
1000
top
:
-
1000
$
(
"body"
).
append
(
test
)
test
.
html
(
name
)
width
=
test
.
width
()
test
.
remove
()
return
width
fitName
:
(
name
)
->
width
=
@
getNameWidth
(
name
)
if
width
<
@
maxNameWidth
return
name
path
=
(
x
.
replace
/^\s+|\s+$/g
,
""
for
x
in
name
.
split
(
"/"
))
while
path
.
length
>
1
path
.
shift
()
partialName
=
gettext
(
"…"
)
+
" / "
+
path
.
join
(
" / "
)
if
@
getNameWidth
(
partialName
)
<
@
maxNameWidth
return
partialName
rawName
=
path
[
0
]
name
=
gettext
(
"…"
)
+
" / "
+
rawName
while
@
getNameWidth
(
name
)
>
@
maxNameWidth
rawName
=
rawName
[
0
...
rawName
.
length
-
1
]
name
=
gettext
(
"…"
)
+
" / "
+
rawName
+
" "
+
gettext
(
"…"
)
return
name
cancel
:
(
event
)
->
cancel
:
(
event
)
->
event
.
preventDefault
()
event
.
preventDefault
()
if
not
confirm
gettext
(
"Your post will be discarded."
)
if
not
confirm
gettext
(
"Your post will be discarded."
)
...
@@ -210,8 +107,8 @@ if Backbone?
...
@@ -210,8 +107,8 @@ if Backbone?
@
$
(
".forum-new-post-form"
)[
0
].
reset
()
@
$
(
".forum-new-post-form"
)[
0
].
reset
()
DiscussionUtil
.
clearFormErrors
(
@
$
(
".post-errors"
))
DiscussionUtil
.
clearFormErrors
(
@
$
(
".post-errors"
))
@
$
(
".wmd-preview p"
).
html
(
""
)
@
$
(
".wmd-preview p"
).
html
(
""
)
if
@
mode
is
"tab"
if
@
isTabMode
()
@
setTopic
(
@
$
(
"a.topic-title"
).
first
())
@
topicView
.
setTopic
(
@
$
(
"a.topic-title"
).
first
())
updateStyles
:
=>
updateStyles
:
=>
# form reset doesn't change the style of checkboxes so this event is to do that job
# form reset doesn't change the style of checkboxes so this event is to do that job
...
...
lms/djangoapps/django_comment_client/base/tests.py
View file @
00c7e60e
...
@@ -15,7 +15,7 @@ from django_comment_client.base import views
...
@@ -15,7 +15,7 @@ from django_comment_client.base import views
from
django_comment_client.tests.group_id
import
CohortedTopicGroupIdTestMixin
,
NonCohortedTopicGroupIdTestMixin
,
GroupIdAssertionMixin
from
django_comment_client.tests.group_id
import
CohortedTopicGroupIdTestMixin
,
NonCohortedTopicGroupIdTestMixin
,
GroupIdAssertionMixin
from
django_comment_client.tests.utils
import
CohortedContentTestCase
from
django_comment_client.tests.utils
import
CohortedContentTestCase
from
django_comment_client.tests.unicode
import
UnicodeTestMixin
from
django_comment_client.tests.unicode
import
UnicodeTestMixin
from
django_comment_common.models
import
Role
,
FORUM_ROLE_STUDENT
from
django_comment_common.models
import
Role
from
django_comment_common.utils
import
seed_permissions_roles
from
django_comment_common.utils
import
seed_permissions_roles
from
student.tests.factories
import
CourseEnrollmentFactory
,
UserFactory
from
student.tests.factories
import
CourseEnrollmentFactory
,
UserFactory
from
util.testing
import
UrlResetMixin
from
util.testing
import
UrlResetMixin
...
@@ -160,7 +160,6 @@ class ThreadActionGroupIdTestCase(
...
@@ -160,7 +160,6 @@ class ThreadActionGroupIdTestCase(
)
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
@patch
(
'lms.lib.comment_client.utils.requests.request'
)
@patch
(
'lms.lib.comment_client.utils.requests.request'
)
class
ViewsTestCase
(
UrlResetMixin
,
ModuleStoreTestCase
,
MockRequestSetupMixin
):
class
ViewsTestCase
(
UrlResetMixin
,
ModuleStoreTestCase
,
MockRequestSetupMixin
):
...
@@ -369,6 +368,15 @@ class ViewsTestCase(UrlResetMixin, ModuleStoreTestCase, MockRequestSetupMixin):
...
@@ -369,6 +368,15 @@ class ViewsTestCase(UrlResetMixin, ModuleStoreTestCase, MockRequestSetupMixin):
mock_request
mock_request
)
)
@patch
(
'django_comment_client.base.views.get_discussion_id_map'
,
return_value
=
{
"test_commentable"
:
{}})
def
test_update_thread_wrong_commentable_id
(
self
,
mock_get_discussion_id_map
,
mock_request
):
self
.
_test_request_error
(
"update_thread"
,
{
"thread_id"
:
"dummy"
,
"course_id"
:
self
.
course_id
.
to_deprecated_string
()},
{
"body"
:
"foo"
,
"title"
:
"foo"
,
"commentable_id"
:
"wrong_commentable"
},
mock_request
)
def
test_create_comment_no_body
(
self
,
mock_request
):
def
test_create_comment_no_body
(
self
,
mock_request
):
self
.
_test_request_error
(
self
.
_test_request_error
(
"create_comment"
,
"create_comment"
,
...
@@ -460,7 +468,7 @@ class ViewsTestCase(UrlResetMixin, ModuleStoreTestCase, MockRequestSetupMixin):
...
@@ -460,7 +468,7 @@ class ViewsTestCase(UrlResetMixin, ModuleStoreTestCase, MockRequestSetupMixin):
"at_position_list"
:
[],
"at_position_list"
:
[],
"closed"
:
is_closed
,
"closed"
:
is_closed
,
"id"
:
"518d4237b023791dca00000d"
,
"id"
:
"518d4237b023791dca00000d"
,
"user_id"
:
"1"
,
"username"
:
"robot"
,
"user_id"
:
"1"
,
"username"
:
"robot"
,
"votes"
:
{
"votes"
:
{
"count"
:
0
,
"count"
:
0
,
"up_count"
:
0
,
"up_count"
:
0
,
...
@@ -853,13 +861,14 @@ class UpdateThreadUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin, MockReq
...
@@ -853,13 +861,14 @@ class UpdateThreadUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin, MockReq
self
.
student
=
UserFactory
.
create
()
self
.
student
=
UserFactory
.
create
()
CourseEnrollmentFactory
(
user
=
self
.
student
,
course_id
=
self
.
course
.
id
)
CourseEnrollmentFactory
(
user
=
self
.
student
,
course_id
=
self
.
course
.
id
)
@patch
(
'django_comment_client.base.views.get_discussion_id_map'
,
return_value
=
{
"test_commentable"
:
{}})
@patch
(
'lms.lib.comment_client.utils.requests.request'
)
@patch
(
'lms.lib.comment_client.utils.requests.request'
)
def
_test_unicode_data
(
self
,
text
,
mock_request
):
def
_test_unicode_data
(
self
,
text
,
mock_request
,
mock_get_discussion_id_map
):
self
.
_set_mock_request_data
(
mock_request
,
{
self
.
_set_mock_request_data
(
mock_request
,
{
"user_id"
:
str
(
self
.
student
.
id
),
"user_id"
:
str
(
self
.
student
.
id
),
"closed"
:
False
,
"closed"
:
False
,
})
})
request
=
RequestFactory
()
.
post
(
"dummy_url"
,
{
"body"
:
text
,
"title"
:
text
})
request
=
RequestFactory
()
.
post
(
"dummy_url"
,
{
"body"
:
text
,
"title"
:
text
,
"commentable_id"
:
"test_commentable"
})
request
.
user
=
self
.
student
request
.
user
=
self
.
student
request
.
view_name
=
"update_thread"
request
.
view_name
=
"update_thread"
response
=
views
.
update_thread
(
request
,
course_id
=
self
.
course
.
id
.
to_deprecated_string
(),
thread_id
=
"dummy_thread_id"
)
response
=
views
.
update_thread
(
request
,
course_id
=
self
.
course
.
id
.
to_deprecated_string
(),
thread_id
=
"dummy_thread_id"
)
...
@@ -868,6 +877,7 @@ class UpdateThreadUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin, MockReq
...
@@ -868,6 +877,7 @@ class UpdateThreadUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin, MockReq
self
.
assertTrue
(
mock_request
.
called
)
self
.
assertTrue
(
mock_request
.
called
)
self
.
assertEqual
(
mock_request
.
call_args
[
1
][
"data"
][
"body"
],
text
)
self
.
assertEqual
(
mock_request
.
call_args
[
1
][
"data"
][
"body"
],
text
)
self
.
assertEqual
(
mock_request
.
call_args
[
1
][
"data"
][
"title"
],
text
)
self
.
assertEqual
(
mock_request
.
call_args
[
1
][
"data"
][
"title"
],
text
)
self
.
assertEqual
(
mock_request
.
call_args
[
1
][
"data"
][
"commentable_id"
],
"test_commentable"
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
...
...
lms/djangoapps/django_comment_client/base/views.py
View file @
00c7e60e
...
@@ -18,8 +18,6 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
...
@@ -18,8 +18,6 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from
courseware.access
import
has_access
from
courseware.access
import
has_access
from
courseware.courses
import
get_course_with_access
,
get_course_by_id
from
courseware.courses
import
get_course_with_access
,
get_course_by_id
from
course_groups.models
import
CourseUserGroup
from
course_groups.cohorts
import
get_cohort_by_id
,
get_cohort_id
,
is_commentable_cohorted
import
django_comment_client.settings
as
cc_settings
import
django_comment_client.settings
as
cc_settings
from
django_comment_client.utils
import
(
from
django_comment_client.utils
import
(
add_courseware_context
,
add_courseware_context
,
...
@@ -28,7 +26,8 @@ from django_comment_client.utils import (
...
@@ -28,7 +26,8 @@ from django_comment_client.utils import (
JsonError
,
JsonError
,
JsonResponse
,
JsonResponse
,
prepare_content
,
prepare_content
,
get_group_id_for_comments_service
get_group_id_for_comments_service
,
get_discussion_id_map
,
)
)
from
django_comment_client.permissions
import
check_permissions_by_view
,
cached_has_permission
from
django_comment_client.permissions
import
check_permissions_by_view
,
cached_has_permission
import
lms.lib.comment_client
as
cc
import
lms.lib.comment_client
as
cc
...
@@ -139,12 +138,21 @@ def update_thread(request, course_id, thread_id):
...
@@ -139,12 +138,21 @@ def update_thread(request, course_id, thread_id):
return
JsonError
(
_
(
"Title can't be empty"
))
return
JsonError
(
_
(
"Title can't be empty"
))
if
'body'
not
in
request
.
POST
or
not
request
.
POST
[
'body'
]
.
strip
():
if
'body'
not
in
request
.
POST
or
not
request
.
POST
[
'body'
]
.
strip
():
return
JsonError
(
_
(
"Body can't be empty"
))
return
JsonError
(
_
(
"Body can't be empty"
))
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
thread
=
cc
.
Thread
.
find
(
thread_id
)
thread
=
cc
.
Thread
.
find
(
thread_id
)
thread
.
body
=
request
.
POST
[
"body"
]
thread
.
body
=
request
.
POST
[
"body"
]
thread
.
title
=
request
.
POST
[
"title"
]
thread
.
title
=
request
.
POST
[
"title"
]
thread
.
save
()
if
"commentable_id"
in
request
.
POST
:
course
=
get_course_with_access
(
request
.
user
,
'load'
,
course_key
)
id_map
=
get_discussion_id_map
(
course
)
if
request
.
POST
.
get
(
"commentable_id"
)
in
id_map
:
thread
.
commentable_id
=
request
.
POST
[
"commentable_id"
]
else
:
return
JsonError
(
_
(
"Topic doesn't exist"
))
thread
.
save
()
if
request
.
is_ajax
():
if
request
.
is_ajax
():
return
ajax_content_response
(
request
,
course_key
,
thread
.
to_dict
())
return
ajax_content_response
(
request
,
course_key
,
thread
.
to_dict
())
else
:
else
:
...
@@ -614,6 +622,7 @@ def upload(request, course_id): # ajax upload file to a question or answer
...
@@ -614,6 +622,7 @@ def upload(request, course_id): # ajax upload file to a question or answer
}
}
})
})
@require_GET
@require_GET
@login_required
@login_required
def
users
(
request
,
course_id
):
def
users
(
request
,
course_id
):
...
@@ -640,7 +649,7 @@ def users(request, course_id):
...
@@ -640,7 +649,7 @@ def users(request, course_id):
try
:
try
:
matched_user
=
User
.
objects
.
get
(
username
=
username
)
matched_user
=
User
.
objects
.
get
(
username
=
username
)
cc_user
=
cc
.
User
.
from_django_user
(
matched_user
)
cc_user
=
cc
.
User
.
from_django_user
(
matched_user
)
cc_user
.
course_id
=
course_key
cc_user
.
course_id
=
course_key
cc_user
.
retrieve
(
complete
=
False
)
cc_user
.
retrieve
(
complete
=
False
)
if
(
cc_user
[
'threads_count'
]
+
cc_user
[
'comments_count'
])
>
0
:
if
(
cc_user
[
'threads_count'
]
+
cc_user
[
'comments_count'
])
>
0
:
user_objs
.
append
({
user_objs
.
append
({
...
...
lms/djangoapps/django_comment_client/utils.py
View file @
00c7e60e
...
@@ -71,7 +71,7 @@ def _get_discussion_modules(course):
...
@@ -71,7 +71,7 @@ def _get_discussion_modules(course):
return
filter
(
has_required_keys
,
all_modules
)
return
filter
(
has_required_keys
,
all_modules
)
def
_
get_discussion_id_map
(
course
):
def
get_discussion_id_map
(
course
):
def
get_entry
(
module
):
def
get_entry
(
module
):
discussion_id
=
module
.
discussion_id
discussion_id
=
module
.
discussion_id
title
=
module
.
discussion_target
title
=
module
.
discussion_target
...
@@ -352,7 +352,7 @@ def extend_content(content):
...
@@ -352,7 +352,7 @@ def extend_content(content):
def
add_courseware_context
(
content_list
,
course
):
def
add_courseware_context
(
content_list
,
course
):
id_map
=
_
get_discussion_id_map
(
course
)
id_map
=
get_discussion_id_map
(
course
)
for
content
in
content_list
:
for
content
in
content_list
:
commentable_id
=
content
[
'commentable_id'
]
commentable_id
=
content
[
'commentable_id'
]
...
...
lms/static/sass/application-extend2.scss.mako
View file @
00c7e60e
...
@@ -56,7 +56,7 @@
...
@@ -56,7 +56,7 @@
@import "discussion/elements/labels";
@import "discussion/elements/labels";
@import "discussion/elements/navigation";
@import "discussion/elements/navigation";
@import "discussion/views/thread";
@import "discussion/views/thread";
@import "discussion/views/
new
-post";
@import "discussion/views/
create-edit
-post";
@import "discussion/views/response";
@import "discussion/views/response";
@import 'discussion/utilities/developer';
@import 'discussion/utilities/developer';
@import 'discussion/utilities/shame';
@import 'discussion/utilities/shame';
...
...
lms/static/sass/discussion/utilities/_shame.scss
View file @
00c7e60e
...
@@ -107,7 +107,8 @@ li[class*=forum-nav-thread-label-] {
...
@@ -107,7 +107,8 @@ li[class*=forum-nav-thread-label-] {
// new post form
// new post form
// -------------
// -------------
.forum-new-post-form
{
.forum-new-post-form
,
.edit-post-form
{
// Override global label rules
// Override global label rules
.post-type
{
.post-type
{
text-shadow
:
none
;
text-shadow
:
none
;
...
@@ -127,7 +128,7 @@ li[class*=forum-nav-thread-label-] {
...
@@ -127,7 +128,7 @@ li[class*=forum-nav-thread-label-] {
margin-bottom
:
0
;
margin-bottom
:
0
;
}
}
// Override global span rules
// Override global span rules
.post-topic-button
.drop-arrow
{
.post-topic-button
.drop-arrow
{
line-height
:
36px
;
line-height
:
36px
;
}
}
...
...
lms/static/sass/discussion/views/_
new
-post.scss
→
lms/static/sass/discussion/views/_
create-edit
-post.scss
View file @
00c7e60e
...
@@ -2,7 +2,8 @@
...
@@ -2,7 +2,8 @@
// ====================
// ====================
// UI: form structure
// UI: form structure
.forum-new-post-form
{
.forum-new-post-form
,
.edit-post-form
{
@include
clearfix
;
@include
clearfix
;
box-sizing
:
border-box
;
box-sizing
:
border-box
;
margin
:
0
;
margin
:
0
;
...
@@ -64,7 +65,8 @@
...
@@ -64,7 +65,8 @@
// ====================
// ====================
// UI: inputs
// UI: inputs
.forum-new-post-form
{
.forum-new-post-form
,
.edit-post-form
{
.post-topic-button
{
.post-topic-button
{
@include
white-button
;
@include
white-button
;
@extend
%cont-truncated
;
@extend
%cont-truncated
;
...
@@ -172,7 +174,8 @@
...
@@ -172,7 +174,8 @@
// ====================
// ====================
// UI: errors - new post creation
// UI: errors - new post creation
.forum-new-post-form
{
.forum-new-post-form
,
.edit-post-form
{
.post-errors
{
.post-errors
{
margin-bottom
:
$baseline
;
margin-bottom
:
$baseline
;
border-radius
:
3px
;
border-radius
:
3px
;
...
@@ -199,7 +202,8 @@
...
@@ -199,7 +202,8 @@
// UI: topic menu
// UI: topic menu
// TO-DO: refactor to use _navigation.scss as general topic selector
// TO-DO: refactor to use _navigation.scss as general topic selector
.forum-new-post-form
.post-topic
{
.forum-new-post-form
.post-topic
,
.edit-post-form
.post-topic
{
position
:
relative
;
position
:
relative
;
.topic-menu-wrapper
{
.topic-menu-wrapper
{
...
...
lms/templates/discussion/_underscore_templates.html
View file @
00c7e60e
...
@@ -54,9 +54,9 @@
...
@@ -54,9 +54,9 @@
% endfor
% endfor
<script
aria-hidden=
"true"
type=
"text/template"
id=
"thread-edit-template"
>
<script
aria-hidden=
"true"
type=
"text/template"
id=
"thread-edit-template"
>
<
div
class
=
"discussion-post edit-post-form"
>
<
h1
>
$
{
_
(
"Editing post"
)}
<
/h1
>
<
h1
>
$
{
_
(
"Editing post"
)}
<
/h1
>
<
ul
class
=
"edit-post-form-errors"
><
/ul
>
<
ul
class
=
"post-errors"
><
/ul
>
<
div
class
=
"forum-edit-post-form-wrapper"
><
/div
>
<
div
class
=
"form-row"
>
<
div
class
=
"form-row"
>
<
label
class
=
"sr"
for
=
"edit-post-title"
>
$
{
_
(
"Edit post title"
)}
<
/label
>
<
label
class
=
"sr"
for
=
"edit-post-title"
>
$
{
_
(
"Edit post title"
)}
<
/label
>
<
input
type
=
"text"
id
=
"edit-post-title"
class
=
"edit-post-title"
name
=
"title"
value
=
"${"
<%-
title
%>
"}"
placeholder
=
"${_('Title') | h}"
>
<
input
type
=
"text"
id
=
"edit-post-title"
class
=
"edit-post-title"
name
=
"title"
value
=
"${"
<%-
title
%>
"}"
placeholder
=
"${_('Title') | h}"
>
...
@@ -66,7 +66,6 @@
...
@@ -66,7 +66,6 @@
<
/div
>
<
/div
>
<
input
type
=
"submit"
id
=
"edit-post-submit"
class
=
"post-update"
value
=
"${_("
Update
post
") | h}"
>
<
input
type
=
"submit"
id
=
"edit-post-submit"
class
=
"post-update"
value
=
"${_("
Update
post
") | h}"
>
<
a
href
=
"#"
class
=
"post-cancel"
>
$
{
_
(
"Cancel"
)}
<
/a
>
<
a
href
=
"#"
class
=
"post-cancel"
>
$
{
_
(
"Cancel"
)}
<
/a
>
<
/div
>
</script>
</script>
<script
aria-hidden=
"true"
type=
"text/template"
id=
"thread-response-template"
>
<script
aria-hidden=
"true"
type=
"text/template"
id=
"thread-response-template"
>
...
@@ -408,31 +407,7 @@
...
@@ -408,31 +407,7 @@
$
{
_
(
"Questions raise issues that need answers. Discussions share ideas and start conversations."
)}
$
{
_
(
"Questions raise issues that need answers. Discussions share ideas and start conversations."
)}
<
/span
>
<
/span
>
<
/div
>
<
/div
>
$
{
'<% if (mode=="tab") { %>'
}
<
div
class
=
"forum-new-post-form-wrapper"
><
/div
>
<
div
class
=
"post-field"
>
##
Using
div
here
instead
of
label
because
we
are
using
a
non
-
native
control
<
div
class
=
"field-label"
>
<
span
class
=
"field-label-text"
>
$
{
_
(
"Topic Area:"
)}
<
/span><div class="field-input post-topic"
>
<
a
href
=
"#"
class
=
"post-topic-button"
>
<
span
class
=
"sr"
>
$
{
_
(
"Discussion topics; current selection is: "
)}
<
/span
>
<
span
class
=
"js-selected-topic"
><
/span
>
<
span
class
=
"drop-arrow"
aria
-
hidden
=
"true"
>
▾
<
/span
>
<
/a
>
<
div
class
=
"topic-menu-wrapper"
>
<
label
class
=
"topic-filter-label"
>
<
span
class
=
"sr"
>
$
{
_
(
"Filter topics"
)}
<
/span
>
<
input
type
=
"text"
class
=
"topic-filter-input"
placeholder
=
"${_('Filter topics')}"
>
<
/label
>
<
ul
class
=
"topic-menu"
role
=
"menu"
>
$
{
'<%= topics_html %>'
}
<
/ul
>
<
/div
>
<
/div
>
<
/div><span class="field-help"
>
$
{
_
(
"Add your post to a relevant topic to help others find it."
)}
<
/span
>
<
/div
>
$
{
'<% } %>'
}
$
{
'<% if (cohort_options) { %>'
}
$
{
'<% if (cohort_options) { %>'
}
<
div
class
=
"post-field"
>
<
div
class
=
"post-field"
>
<
label
class
=
"field-label"
>
<
label
class
=
"field-label"
>
...
@@ -497,6 +472,28 @@
...
@@ -497,6 +472,28 @@
<
/li
>
<
/li
>
</script>
</script>
<script
aria-hidden=
"true"
type=
"text/template"
id=
"topic-template"
>
##
Using
div
here
instead
of
label
because
we
are
using
a
non
-
native
control
<
div
class
=
"field-label"
>
<
span
class
=
"field-label-text"
>
$
{
_
(
"Topic Area:"
)}
<
/span><div class="field-input post-topic"
>
<
a
href
=
"#"
class
=
"post-topic-button"
>
<
span
class
=
"sr"
>
$
{
_
(
"Discussion topics; current selection is: "
)}
<
/span
>
<
span
class
=
"js-selected-topic"
><
/span
>
<
span
class
=
"drop-arrow"
aria
-
hidden
=
"true"
>
▾
<
/span
>
<
/a
>
<
div
class
=
"topic-menu-wrapper"
>
<
label
class
=
"topic-filter-label"
>
<
span
class
=
"sr"
>
$
{
_
(
"Filter topics"
)}
<
/span
>
<
input
type
=
"text"
class
=
"topic-filter-input"
placeholder
=
"${_('Filter topics')}"
>
<
/label
>
<
ul
class
=
"topic-menu"
role
=
"menu"
>
$
{
'<%= topics_html %>'
}
<
/ul
>
<
/div
>
<
/div
>
<
/div><span class="field-help"
>
$
{
_
(
"Add your post to a relevant topic to help others find it."
)}
<
/span
>
</script>
<
%
def
name=
"primaryAction(action_class, icon, sr_label, unchecked_label, checked_label)"
>
<
%
def
name=
"primaryAction(action_class, icon, sr_label, unchecked_label, checked_label)"
>
<script
type=
"text/template"
id=
"forum-action-${action_class}"
>
<script
type=
"text/template"
id=
"forum-action-${action_class}"
>
<
li
class
=
"actions-item"
>
<
li
class
=
"actions-item"
>
...
...
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