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
85af48a3
Commit
85af48a3
authored
Sep 25, 2014
by
Anton Stupak
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #5276 from edx/anton/forums-topic
Change the topic of a previously posted post.
parents
fcf57a8e
00c7e60e
Show whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
650 additions
and
386 deletions
+650
-386
common/static/coffee/spec/discussion/.gitignore
+2
-0
common/static/coffee/spec/discussion/discussion_spec_helper.coffee
+23
-26
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
+9
-7
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
+21
-124
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
+2
-1
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 @
85af48a3
!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 @
85af48a3
...
...
@@ -71,9 +71,9 @@ browser and pasting the output. When that file changes, this one should be rege
</script>
<script aria-hidden="true" type="text/template" id="thread-edit-template">
<div class="discussion-post edit-post-form">
<h1>Editing post</h1>
<ul class="edit-post-form-errors"></ul>
<div class="forum-edit-post-form-wrapper"></div>
<div class="form-row">
<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">
...
...
@@ -83,7 +83,6 @@ browser and pasting the output. When that file changes, this one should be rege
</div>
<input type="submit" id="edit-post-submit" class="post-update" value="Update post">
<a href="#" class="post-cancel">Cancel</a>
</div>
</script>
<script aria-hidden="true" type="text/template" id="thread-response-template">
...
...
@@ -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.
</span>
</div>
<% if (mode=="tab") { %>
<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>
<% } %>
<div class="forum-new-post-form-wrapper"></div>
<% if (cohort_options) { %>
<div class="post-field">
<label class="field-label">
...
...
@@ -406,6 +382,27 @@ browser and pasting the output. When that file changes, this one should be rege
</li>
</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 @
85af48a3
(
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 @
85af48a3
...
...
@@ -6,6 +6,7 @@ describe "DiscussionThreadView", ->
jasmine
.
Clock
.
useMock
()
@
threadData
=
DiscussionViewSpecHelper
.
makeThreadWithProps
({})
@
thread
=
new
Thread
(
@
threadData
)
@
discussion
=
new
Discussion
(
@
thread
)
spyOn
(
$
,
"ajax"
)
# Avoid unnecessary boilerplate
spyOn
(
DiscussionThreadShowView
.
prototype
,
"convertMath"
)
...
...
@@ -44,6 +45,7 @@ describe "DiscussionThreadView", ->
checkCommentForm
=
(
originallyClosed
,
mode
)
->
threadData
=
DiscussionViewSpecHelper
.
makeThreadWithProps
({
closed
:
originallyClosed
})
thread
=
new
Thread
(
threadData
)
discussion
=
new
Discussion
(
thread
)
view
=
new
DiscussionThreadView
({
model
:
thread
,
el
:
$
(
"#fixture-element"
),
mode
:
mode
})
renderWithContent
(
view
,
{
resp_total
:
1
,
children
:
[{}]})
if
mode
==
"inline"
...
...
common/static/coffee/spec/discussion/view/discussion_topic_menu_view_spec.js
0 → 100644
View file @
85af48a3
(
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 @
85af48a3
...
...
@@ -10,115 +10,6 @@ describe "NewPostView", ->
)
@
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"
,
->
beforeEach
->
@
course_settings
=
new
DiscussionCourseSettings
({
...
...
common/static/coffee/src/discussion/.gitignore
0 → 100644
View file @
85af48a3
!views/discussion_thread_edit_view.js
!views/discussion_topic_menu_view.js
common/static/coffee/src/discussion/discussion_module_view.coffee
View file @
85af48a3
...
...
@@ -99,7 +99,13 @@ if Backbone?
@
newPostForm
=
$
(
'.new-post-article'
)
@
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
()
DiscussionUtil
.
bulkUpdateContentInfo
(
window
.
$
$annotated_content_info
)
@
newPostView
=
new
NewPostView
(
...
...
@@ -123,7 +129,14 @@ if Backbone?
# TODO: When doing pagination, this will need to repaginate. Perhaps just reload page 1?
article
=
$
(
"<article class='discussion-thread' id='thread_
#{
thread
.
id
}
'></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
()
@
threadviews
.
unshift
threadView
...
...
common/static/coffee/src/discussion/discussion_router.coffee
View file @
85af48a3
...
...
@@ -54,7 +54,12 @@ if Backbone?
if
(
@
newPost
.
is
(
":visible"
))
@
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
.
on
"thread:responses:rendered"
,
=>
@
nav
.
updateSidebar
()
...
...
common/static/coffee/src/discussion/views/discussion_thread_edit_view.coffee
deleted
100644 → 0
View file @
fcf57a8e
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 @
85af48a3
(
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 @
85af48a3
...
...
@@ -5,7 +5,7 @@ if Backbone?
"keypress .forum-nav-browse-filter-input"
:
(
event
)
=>
DiscussionUtil
.
ignoreEnterKey
(
event
)
"keyup .forum-nav-browse-filter-input"
:
"filterTopics"
"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"
"change .forum-nav-sort-control"
:
"sortThreads"
"click .forum-nav-thread-link"
:
"threadSelected"
...
...
@@ -130,12 +130,12 @@ if Backbone?
)
@
$
(
".forum-nav-sort-control"
).
val
(
@
collection
.
sort_preference
)
$
(
window
).
bind
"load"
,
@
updateSidebar
$
(
window
).
bind
"scroll"
,
@
updateSidebar
$
(
window
).
bind
"resize"
,
@
updateSidebar
$
(
window
).
bind
"load scroll resize"
,
@
updateSidebar
@
displayedCollection
.
on
"reset"
,
@
renderThreads
@
displayedCollection
.
on
"thread:remove"
,
@
renderThreads
@
displayedCollection
.
on
"change:commentable_id"
,
(
model
,
commentable_id
)
=>
@
retrieveDiscussions
@
discussionIds
.
split
(
","
)
if
@
mode
is
"commentables"
@
renderThreads
()
@
...
...
@@ -197,7 +197,6 @@ if Backbone?
if
@
group_id
options
.
group_id
=
@
group_id
lastThread
=
@
collection
.
last
()
?
.
get
(
'id'
)
if
lastThread
# Pagination; focus the first thread after what was previously the last thread
...
...
@@ -359,12 +358,15 @@ if Backbone?
name
=
prefix
+
rawName
+
gettext
(
"…"
)
return
name
selectTopic
:
(
event
)
->
selectTopic
Handler
:
(
event
)
->
event
.
preventDefault
()
@
selectTopic
$
(
event
.
target
)
selectTopic
:
(
$target
)
->
@
hideBrowseMenu
()
@
clearSearch
()
item
=
$
(
event
.
target
)
.
closest
(
'.forum-nav-browse-menu-item'
)
item
=
$
target
.
closest
(
'.forum-nav-browse-menu-item'
)
@
setCurrentTopicDisplay
(
@
getPathText
(
item
))
if
item
.
hasClass
(
"forum-nav-browse-menu-all"
)
@
discussionIds
=
""
...
...
common/static/coffee/src/discussion/views/discussion_thread_view.coffee
View file @
85af48a3
...
...
@@ -21,6 +21,13 @@ if Backbone?
@
mode
=
options
.
mode
or
"inline"
# allowed values are "tab" or "inline"
if
@
mode
not
in
[
"tab"
,
"inline"
]
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
()
@
responses
=
new
Comments
()
@
loadedResponses
=
false
...
...
@@ -254,49 +261,20 @@ if Backbone?
@
createEditView
()
@
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
:
()
->
if
@
showView
?
@
showView
.
undelegateEvents
()
@
showView
.
$el
.
empty
()
@
showView
=
null
@
editView
=
new
DiscussionThreadEditView
(
model
:
@
model
)
@
editView
.
bind
"thread:update"
,
@
update
@
editView
.
bind
"thread:cancel_edit"
,
@
cancelEdit
@
editView
=
new
DiscussionThreadEditView
(
container
:
@
$
(
'.thread-content-wrapper'
)
model
:
@
model
mode
:
@
mode
course_settings
:
@
options
.
course_settings
topicId
:
@
model
.
get
(
'commentable_id'
)
)
@
editView
.
bind
"thread:updated thread:cancel_edit"
,
@
closeEditView
renderSubView
:
(
view
)
->
view
.
setElement
(
@
$
(
'.thread-content-wrapper'
))
...
...
@@ -304,15 +282,9 @@ if Backbone?
view
.
delegateEvents
()
renderEditView
:
()
->
@
renderSubView
(
@
editView
)
@
editView
.
render
(
)
createShowView
:
()
->
if
@
editView
?
@
editView
.
undelegateEvents
()
@
editView
.
$el
.
empty
()
@
editView
=
null
@
showView
=
new
DiscussionThreadShowView
({
model
:
@
model
,
mode
:
@
mode
})
@
showView
.
bind
"thread:_delete"
,
@
_delete
@
showView
.
bind
"thread:edit"
,
@
edit
...
...
@@ -320,8 +292,7 @@ if Backbone?
renderShowView
:
()
->
@
renderSubView
(
@
showView
)
cancelEdit
:
(
event
)
=>
event
.
preventDefault
()
closeEditView
:
(
event
)
=>
@
createShowView
()
@
renderShowView
()
...
...
common/static/coffee/src/discussion/views/discussion_topic_menu_view.js
0 → 100644
View file @
85af48a3
(
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 @
85af48a3
...
...
@@ -6,7 +6,6 @@ if Backbone?
if
@
mode
not
in
[
"tab"
,
"inline"
]
throw
new
Error
(
"invalid mode: "
+
@
mode
)
@
course_settings
=
options
.
course_settings
@
maxNameWidth
=
100
@
topicId
=
options
.
topicId
render
:
()
->
...
...
@@ -16,29 +15,21 @@ if Backbone?
mode
:
@
mode
,
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
))
if
@
mode
is
"tab"
# set up the topic dropdown in tab mode
@
dropdownButton
=
@
$
(
".post-topic-button"
)
@
topicMenu
=
@
$
(
".topic-menu-wrapper"
)
@
hideTopicDropdown
()
@
setTopic
(
@
$
(
"a.topic-title"
).
first
())
if
@
isTabMode
()
@
topicView
=
new
DiscussionTopicMenuView
{
topicId
:
@
topicId
course_settings
:
@
course_settings
}
@
topicView
.
on
(
'thread:topic_change'
,
@
toggleGroupDropdown
)
@
addField
(
@
topicView
.
render
())
DiscussionUtil
.
makeWmdEditor
@
$el
,
$
.
proxy
(
@
$
,
@
),
"js-post-body"
renderCategoryMap
:
(
map
)
->
category_template
=
_
.
template
(
$
(
"#new-post-menu-category-template"
).
html
())
entry_template
=
_
.
template
(
$
(
"#new-post-menu-entry-template"
).
html
())
html
=
""
for
name
in
map
.
children
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
addField
:
(
fieldView
)
->
@
$
(
'.forum-new-post-form-wrapper'
).
append
fieldView
isTabMode
:
()
->
@
mode
is
"tab"
getCohortOptions
:
()
->
if
@
course_settings
.
get
(
"is_cohorted"
)
and
DiscussionUtil
.
isPrivilegedUser
()
...
...
@@ -50,19 +41,15 @@ if Backbone?
events
:
"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"
"click .cancel"
:
"cancel"
"reset .forum-new-post-form"
:
"updateStyles"
# Because we want the behavior that when the body is clicked the menu is
# closed, we need to ignore clicks in the search field and stop propagation.
# Without this, clicking the search field would also close the menu.
ignoreClick
:
(
event
)
->
event
.
stopPropagation
()
toggleGroupDropdown
:
(
$target
)
->
if
$target
.
data
(
'cohorted'
)
$
(
'.js-group-select'
).
prop
(
'disabled'
,
false
);
else
$
(
'.js-group-select'
).
val
(
''
).
prop
(
'disabled'
,
true
);
postOptionChange
:
(
event
)
->
$target
=
$
(
event
.
target
)
...
...
@@ -83,7 +70,8 @@ if Backbone?
anonymous_to_peers
=
false
||
@
$
(
".js-anon-peers"
).
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
$elem
:
$
(
event
.
target
)
...
...
@@ -108,97 +96,6 @@ if Backbone?
@
resetForm
()
@
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
)
->
event
.
preventDefault
()
if
not
confirm
gettext
(
"Your post will be discarded."
)
...
...
@@ -210,8 +107,8 @@ if Backbone?
@
$
(
".forum-new-post-form"
)[
0
].
reset
()
DiscussionUtil
.
clearFormErrors
(
@
$
(
".post-errors"
))
@
$
(
".wmd-preview p"
).
html
(
""
)
if
@
mode
is
"tab"
@
setTopic
(
@
$
(
"a.topic-title"
).
first
())
if
@
isTabMode
()
@
topicView
.
setTopic
(
@
$
(
"a.topic-title"
).
first
())
updateStyles
:
=>
# 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 @
85af48a3
...
...
@@ -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.utils
import
CohortedContentTestCase
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
student.tests.factories
import
CourseEnrollmentFactory
,
UserFactory
from
util.testing
import
UrlResetMixin
...
...
@@ -160,7 +160,6 @@ class ThreadActionGroupIdTestCase(
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
@patch
(
'lms.lib.comment_client.utils.requests.request'
)
class
ViewsTestCase
(
UrlResetMixin
,
ModuleStoreTestCase
,
MockRequestSetupMixin
):
...
...
@@ -369,6 +368,15 @@ class ViewsTestCase(UrlResetMixin, ModuleStoreTestCase, MockRequestSetupMixin):
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
):
self
.
_test_request_error
(
"create_comment"
,
...
...
@@ -460,7 +468,7 @@ class ViewsTestCase(UrlResetMixin, ModuleStoreTestCase, MockRequestSetupMixin):
"at_position_list"
:
[],
"closed"
:
is_closed
,
"id"
:
"518d4237b023791dca00000d"
,
"user_id"
:
"1"
,
"username"
:
"robot"
,
"user_id"
:
"1"
,
"username"
:
"robot"
,
"votes"
:
{
"count"
:
0
,
"up_count"
:
0
,
...
...
@@ -853,13 +861,14 @@ class UpdateThreadUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin, MockReq
self
.
student
=
UserFactory
.
create
()
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'
)
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
,
{
"user_id"
:
str
(
self
.
student
.
id
),
"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
.
view_name
=
"update_thread"
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
self
.
assertTrue
(
mock_request
.
called
)
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"
][
"commentable_id"
],
"test_commentable"
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
...
...
lms/djangoapps/django_comment_client/base/views.py
View file @
85af48a3
...
...
@@ -18,8 +18,6 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from
courseware.access
import
has_access
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
from
django_comment_client.utils
import
(
add_courseware_context
,
...
...
@@ -28,7 +26,8 @@ from django_comment_client.utils import (
JsonError
,
JsonResponse
,
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
import
lms.lib.comment_client
as
cc
...
...
@@ -139,12 +138,21 @@ def update_thread(request, course_id, thread_id):
return
JsonError
(
_
(
"Title can't be empty"
))
if
'body'
not
in
request
.
POST
or
not
request
.
POST
[
'body'
]
.
strip
():
return
JsonError
(
_
(
"Body can't be empty"
))
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
thread
=
cc
.
Thread
.
find
(
thread_id
)
thread
.
body
=
request
.
POST
[
"body"
]
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
():
return
ajax_content_response
(
request
,
course_key
,
thread
.
to_dict
())
else
:
...
...
@@ -614,6 +622,7 @@ def upload(request, course_id): # ajax upload file to a question or answer
}
})
@require_GET
@login_required
def
users
(
request
,
course_id
):
...
...
@@ -640,7 +649,7 @@ def users(request, course_id):
try
:
matched_user
=
User
.
objects
.
get
(
username
=
username
)
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
)
if
(
cc_user
[
'threads_count'
]
+
cc_user
[
'comments_count'
])
>
0
:
user_objs
.
append
({
...
...
lms/djangoapps/django_comment_client/utils.py
View file @
85af48a3
...
...
@@ -71,7 +71,7 @@ def _get_discussion_modules(course):
return
filter
(
has_required_keys
,
all_modules
)
def
_
get_discussion_id_map
(
course
):
def
get_discussion_id_map
(
course
):
def
get_entry
(
module
):
discussion_id
=
module
.
discussion_id
title
=
module
.
discussion_target
...
...
@@ -352,7 +352,7 @@ def extend_content(content):
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
:
commentable_id
=
content
[
'commentable_id'
]
...
...
lms/static/sass/application-extend2.scss.mako
View file @
85af48a3
...
...
@@ -56,7 +56,7 @@
@import "discussion/elements/labels";
@import "discussion/elements/navigation";
@import "discussion/views/thread";
@import "discussion/views/
new
-post";
@import "discussion/views/
create-edit
-post";
@import "discussion/views/response";
@import 'discussion/utilities/developer';
@import 'discussion/utilities/shame';
...
...
lms/static/sass/discussion/utilities/_shame.scss
View file @
85af48a3
...
...
@@ -107,7 +107,8 @@ li[class*=forum-nav-thread-label-] {
// new post form
// -------------
.forum-new-post-form
{
.forum-new-post-form
,
.edit-post-form
{
// Override global label rules
.post-type
{
text-shadow
:
none
;
...
...
lms/static/sass/discussion/views/_
new
-post.scss
→
lms/static/sass/discussion/views/_
create-edit
-post.scss
View file @
85af48a3
...
...
@@ -2,7 +2,8 @@
// ====================
// UI: form structure
.forum-new-post-form
{
.forum-new-post-form
,
.edit-post-form
{
@include
clearfix
;
box-sizing
:
border-box
;
margin
:
0
;
...
...
@@ -64,7 +65,8 @@
// ====================
// UI: inputs
.forum-new-post-form
{
.forum-new-post-form
,
.edit-post-form
{
.post-topic-button
{
@include
white-button
;
@extend
%cont-truncated
;
...
...
@@ -172,7 +174,8 @@
// ====================
// UI: errors - new post creation
.forum-new-post-form
{
.forum-new-post-form
,
.edit-post-form
{
.post-errors
{
margin-bottom
:
$baseline
;
border-radius
:
3px
;
...
...
@@ -199,7 +202,8 @@
// UI: topic menu
// 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
;
.topic-menu-wrapper
{
...
...
lms/templates/discussion/_underscore_templates.html
View file @
85af48a3
...
...
@@ -54,9 +54,9 @@
% endfor
<script
aria-hidden=
"true"
type=
"text/template"
id=
"thread-edit-template"
>
<
div
class
=
"discussion-post edit-post-form"
>
<
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"
>
<
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}"
>
...
...
@@ -66,7 +66,6 @@
<
/div
>
<
input
type
=
"submit"
id
=
"edit-post-submit"
class
=
"post-update"
value
=
"${_("
Update
post
") | h}"
>
<
a
href
=
"#"
class
=
"post-cancel"
>
$
{
_
(
"Cancel"
)}
<
/a
>
<
/div
>
</script>
<script
aria-hidden=
"true"
type=
"text/template"
id=
"thread-response-template"
>
...
...
@@ -408,31 +407,7 @@
$
{
_
(
"Questions raise issues that need answers. Discussions share ideas and start conversations."
)}
<
/span
>
<
/div
>
$
{
'<% if (mode=="tab") { %>'
}
<
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
>
$
{
'<% } %>'
}
<
div
class
=
"forum-new-post-form-wrapper"
><
/div
>
$
{
'<% if (cohort_options) { %>'
}
<
div
class
=
"post-field"
>
<
label
class
=
"field-label"
>
...
...
@@ -497,6 +472,28 @@
<
/li
>
</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)"
>
<script
type=
"text/template"
id=
"forum-action-${action_class}"
>
<
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