consolidate new post form variants into single client-side template/view
This commit is contained in:
@@ -83,40 +83,39 @@ if Backbone?
|
||||
|
||||
renderDiscussion: ($elem, response, textStatus, discussionId) =>
|
||||
$elem.focus()
|
||||
window.user = new DiscussionUser(response.user_info)
|
||||
user = new DiscussionUser(response.user_info)
|
||||
window.user = user
|
||||
DiscussionUtil.setUser(user)
|
||||
Content.loadContentInfos(response.annotated_content_info)
|
||||
DiscussionUtil.loadRoles(response.roles)
|
||||
allow_anonymous = response.allow_anonymous
|
||||
allow_anonymous_to_peers = response.allow_anonymous_to_peers
|
||||
cohorts = response.cohorts
|
||||
# $elem.html("Hide Discussion")
|
||||
|
||||
@course_settings = new DiscussionCourseSettings(response.course_settings)
|
||||
@discussion = new Discussion()
|
||||
@discussion.reset(response.discussion_data, {silent: false})
|
||||
|
||||
#use same discussion template but different thread templated
|
||||
#determined in the coffeescript based on whether or not there's a
|
||||
#group id
|
||||
|
||||
if response.is_cohorted and response.is_moderator
|
||||
source = "script#_inline_discussion_cohorted"
|
||||
else
|
||||
source = "script#_inline_discussion"
|
||||
|
||||
$discussion = $(Mustache.render $(source).html(), {'threads':response.discussion_data, 'discussionId': discussionId, 'allow_anonymous_to_peers': allow_anonymous_to_peers, 'allow_anonymous': allow_anonymous, 'cohorts':cohorts})
|
||||
$discussion = $(Mustache.render $("script#_inline_discussion").html(), {'threads':response.discussion_data, 'discussionId': discussionId})
|
||||
if @$('section.discussion').length
|
||||
@$('section.discussion').replaceWith($discussion)
|
||||
else
|
||||
@$el.append($discussion)
|
||||
|
||||
@newPostForm = $('.new-post-article')
|
||||
@threadviews = @discussion.map (thread) ->
|
||||
new DiscussionThreadInlineView el: @$("article#thread_#{thread.id}"), model: thread
|
||||
_.each @threadviews, (dtv) -> dtv.render()
|
||||
DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
|
||||
@newPostView = new NewPostInlineView el: @$('.new-post-article'), collection: @discussion
|
||||
@newPostView = new NewPostView(
|
||||
el: @newPostForm,
|
||||
collection: @discussion,
|
||||
course_settings: @course_settings
|
||||
)
|
||||
@newPostView.render()
|
||||
@discussion.on "add", @addThread
|
||||
|
||||
@retrieved = true
|
||||
@showed = true
|
||||
@renderPagination(response.num_pages)
|
||||
|
||||
if @isWaitingOnNewPost
|
||||
@newPostForm.show()
|
||||
|
||||
|
||||
@@ -6,15 +6,23 @@ if Backbone?
|
||||
|
||||
initialize: (options) ->
|
||||
@discussion = options['discussion']
|
||||
@course_settings = options['course_settings']
|
||||
|
||||
@nav = new DiscussionThreadListView(collection: @discussion, el: $(".sidebar"))
|
||||
@nav.on "thread:selected", @navigateToThread
|
||||
@nav.on "thread:removed", @navigateToAllThreads
|
||||
@nav.on "threads:rendered", @setActiveThread
|
||||
@nav.on "thread:created", @navigateToThread
|
||||
@nav.render()
|
||||
|
||||
@newPostView = new NewPostView(el: $(".new-post-article"), collection: @discussion)
|
||||
@nav.on "thread:created", @navigateToThread
|
||||
@newPost = $('.new-post-article')
|
||||
@newPostView = new NewPostView(
|
||||
el: @newPost,
|
||||
collection: @discussion,
|
||||
course_settings: @course_settings,
|
||||
mode: "tab"
|
||||
)
|
||||
@newPostView.render()
|
||||
$('.new-post-btn').bind "click", @showNewPost
|
||||
$('.new-post-btn').bind "keydown", (event) => DiscussionUtil.activateOnSpace(event, @showNewPost)
|
||||
$('.new-post-cancel').bind "click", @hideNewPost
|
||||
|
||||
@@ -10,10 +10,13 @@ if Backbone?
|
||||
threads = element.data("threads")
|
||||
thread_pages = element.data("thread-pages")
|
||||
content_info = element.data("content-info")
|
||||
window.user = new DiscussionUser(user_info)
|
||||
user = new DiscussionUser(user_info)
|
||||
DiscussionUtil.setUser(user)
|
||||
window.user = user
|
||||
Content.loadContentInfos(content_info)
|
||||
discussion = new Discussion(threads, {pages: thread_pages, sort: sort_preference})
|
||||
new DiscussionRouter({discussion: discussion})
|
||||
course_settings = new DiscussionCourseSettings(element.data("course-settings"))
|
||||
new DiscussionRouter({discussion: discussion, course_settings: course_settings})
|
||||
Backbone.history.start({pushState: true, root: "/courses/#{$$course_id}/discussion/forum/"})
|
||||
DiscussionProfileApp =
|
||||
start: (elem) ->
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
if Backbone?
|
||||
class @DiscussionCourseSettings extends Backbone.Model
|
||||
@@ -18,6 +18,9 @@ class @DiscussionUtil
|
||||
@getTemplate: (id) ->
|
||||
$("script##{id}").html()
|
||||
|
||||
@setUser: (user) ->
|
||||
@user = user
|
||||
|
||||
@loadRoles: (roles)->
|
||||
@roleIds = roles
|
||||
|
||||
@@ -29,10 +32,12 @@ class @DiscussionUtil
|
||||
@loadFlagModerator($("#discussion-container").data("flag-moderator"))
|
||||
|
||||
@isStaff: (user_id) ->
|
||||
user_id ?= @user.id
|
||||
staff = _.union(@roleIds['Moderator'], @roleIds['Administrator'])
|
||||
_.include(staff, parseInt(user_id))
|
||||
|
||||
@isTA: (user_id) ->
|
||||
user_id ?= @user.id
|
||||
ta = _.union(@roleIds['Community TA'])
|
||||
_.include(ta, parseInt(user_id))
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
if Backbone?
|
||||
class @NewPostInlineView extends Backbone.View
|
||||
|
||||
initialize: () ->
|
||||
|
||||
@topicId = @$(".topic").first().data("discussion-id")
|
||||
|
||||
@maxNameWidth = 100
|
||||
|
||||
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "new-post-body"
|
||||
|
||||
events:
|
||||
"submit .new-post-form": "createPost"
|
||||
|
||||
# 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()
|
||||
|
||||
createPost: (event) ->
|
||||
event.preventDefault()
|
||||
title = @$(".new-post-title").val()
|
||||
body = @$(".new-post-body").find(".wmd-input").val()
|
||||
group = @$(".new-post-group option:selected").attr("value")
|
||||
|
||||
anonymous = false || @$("input.discussion-anonymous").is(":checked")
|
||||
anonymous_to_peers = false || @$("input.discussion-anonymous-to-peers").is(":checked")
|
||||
follow = false || @$("input.discussion-follow").is(":checked")
|
||||
|
||||
url = DiscussionUtil.urlFor('create_thread', @topicId)
|
||||
|
||||
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: title
|
||||
body: body
|
||||
group_id: group
|
||||
|
||||
anonymous: anonymous
|
||||
anonymous_to_peers: anonymous_to_peers
|
||||
auto_subscribe: follow
|
||||
error: DiscussionUtil.formErrorHandler(@$(".new-post-form-errors"))
|
||||
success: (response, textStatus) =>
|
||||
# TODO: Move this out of the callback, this makes it feel sluggish
|
||||
thread = new Thread response['content']
|
||||
DiscussionUtil.clearFormErrors(@$(".new-post-form-errors"))
|
||||
@$el.hide()
|
||||
@$(".new-post-title").val("").attr("prev-text", "")
|
||||
@$(".new-post-body textarea").val("").attr("prev-text", "")
|
||||
|
||||
@collection.add thread
|
||||
@@ -1,25 +1,74 @@
|
||||
if Backbone?
|
||||
class @NewPostView extends Backbone.View
|
||||
|
||||
initialize: () ->
|
||||
@dropdownButton = @$(".topic_dropdown_button")
|
||||
@topicMenu = @$(".topic_menu_wrapper")
|
||||
|
||||
@menuOpen = @dropdownButton.hasClass('dropped')
|
||||
|
||||
@topicId = @$(".topic").first().data("discussion_id")
|
||||
@topicText = @getFullTopicName(@$(".topic").first())
|
||||
|
||||
initialize: (options) ->
|
||||
@mode = options.mode or "inline" # allowed values are "tab" or "inline"
|
||||
if @mode not in ["tab", "inline"]
|
||||
throw new Error("invalid mode: " + @mode)
|
||||
@course_settings = options.course_settings
|
||||
@maxNameWidth = 100
|
||||
@setSelectedTopic()
|
||||
|
||||
render: () ->
|
||||
if @mode is "tab"
|
||||
@$el.html(
|
||||
_.template(
|
||||
$("#new-post-tab-template").html(), {
|
||||
topic_dropdown_html: @getTopicDropdownHTML(),
|
||||
options_html: @getOptionsHTML(),
|
||||
editor_html: @getEditorHTML()
|
||||
}
|
||||
)
|
||||
)
|
||||
# set up the topic dropdown in tab mode
|
||||
@dropdownButton = @$(".topic_dropdown_button")
|
||||
@topicMenu = @$(".topic_menu_wrapper")
|
||||
@menuOpen = @dropdownButton.hasClass('dropped')
|
||||
@topicId = @$(".topic").first().data("discussion_id")
|
||||
@topicText = @getFullTopicName(@$(".topic").first())
|
||||
$('.choose-cohort').hide() unless @$(".topic_menu li a").first().is("[cohorted=true]")
|
||||
@setSelectedTopic()
|
||||
else # inline
|
||||
@$el.html(
|
||||
_.template(
|
||||
$("#new-post-inline-template").html(), {
|
||||
options_html: @getOptionsHTML(),
|
||||
editor_html: @getEditorHTML()
|
||||
}
|
||||
)
|
||||
)
|
||||
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "new-post-body"
|
||||
|
||||
if @$($(".topic_menu li a")[0]).attr('cohorted') != "True"
|
||||
$('.choose-cohort').hide();
|
||||
|
||||
|
||||
|
||||
|
||||
getTopicDropdownHTML: () ->
|
||||
# populate the category menu (topic dropdown)
|
||||
_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
|
||||
topics_html = _renderCategoryMap(@course_settings.get("category_map"))
|
||||
_.template($("#new-post-topic-dropdown-template").html(), {topics_html: topics_html})
|
||||
|
||||
getEditorHTML: () ->
|
||||
_.template($("#new-post-editor-template").html(), {})
|
||||
|
||||
getOptionsHTML: () ->
|
||||
# cohort options?
|
||||
if @course_settings.get("is_cohorted") and DiscussionUtil.isStaff()
|
||||
user_cohort_id = $("#discussion-container").data("user-cohort-id")
|
||||
cohort_options = _.map @course_settings.get("cohorts"), (cohort) ->
|
||||
{value: cohort.id, text: cohort.name, selected: cohort.id==user_cohort_id}
|
||||
else
|
||||
cohort_options = null
|
||||
context = _.clone(@course_settings.attributes)
|
||||
context.cohort_options = cohort_options
|
||||
_.template($("#new-post-options-template").html(), context)
|
||||
|
||||
events:
|
||||
"submit .new-post-form": "createPost"
|
||||
"click .topic_dropdown_button": "toggleTopicDropdown"
|
||||
@@ -33,6 +82,43 @@ if Backbone?
|
||||
ignoreClick: (event) ->
|
||||
event.stopPropagation()
|
||||
|
||||
createPost: (event) ->
|
||||
event.preventDefault()
|
||||
title = @$(".new-post-title").val()
|
||||
body = @$(".new-post-body").find(".wmd-input").val()
|
||||
group = @$(".new-post-group option:selected").attr("value")
|
||||
|
||||
anonymous = false || @$("input.discussion-anonymous").is(":checked")
|
||||
anonymous_to_peers = false || @$("input.discussion-anonymous-to-peers").is(":checked")
|
||||
follow = false || @$("input.discussion-follow").is(":checked")
|
||||
|
||||
url = DiscussionUtil.urlFor('create_thread', @topicId)
|
||||
|
||||
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: title
|
||||
body: body
|
||||
anonymous: anonymous
|
||||
anonymous_to_peers: anonymous_to_peers
|
||||
auto_subscribe: follow
|
||||
group_id: group
|
||||
error: DiscussionUtil.formErrorHandler(@$(".new-post-form-errors"))
|
||||
success: (response, textStatus) =>
|
||||
# TODO: Move this out of the callback, this makes it feel sluggish
|
||||
thread = new Thread response['content']
|
||||
DiscussionUtil.clearFormErrors(@$(".new-post-form-errors"))
|
||||
@$el.hide()
|
||||
@$(".new-post-title").val("").attr("prev-text", "")
|
||||
@$(".new-post-body textarea").val("").attr("prev-text", "")
|
||||
@$(".wmd-preview p").html("") # only line not duplicated in new post inline view
|
||||
@collection.add thread
|
||||
|
||||
toggleTopicDropdown: (event) ->
|
||||
event.stopPropagation()
|
||||
if @menuOpen
|
||||
@@ -69,11 +155,10 @@ if Backbone?
|
||||
@topicText = @getFullTopicName($target)
|
||||
@topicId = $target.data('discussion_id')
|
||||
@setSelectedTopic()
|
||||
if $target.attr('cohorted') == "True"
|
||||
if $target.is('[cohorted=true]')
|
||||
$('.choose-cohort').show();
|
||||
else
|
||||
$('.choose-cohort').hide();
|
||||
|
||||
|
||||
setSelectedTopic: ->
|
||||
@dropdownButton.html(@fitName(@topicText) + ' <span class="drop-arrow">▾</span>')
|
||||
@@ -119,44 +204,6 @@ if Backbone?
|
||||
|
||||
return name
|
||||
|
||||
|
||||
createPost: (event) ->
|
||||
event.preventDefault()
|
||||
title = @$(".new-post-title").val()
|
||||
body = @$(".new-post-body").find(".wmd-input").val()
|
||||
group = @$(".new-post-group option:selected").attr("value")
|
||||
|
||||
anonymous = false || @$("input.discussion-anonymous").is(":checked")
|
||||
anonymous_to_peers = false || @$("input.discussion-anonymous-to-peers").is(":checked")
|
||||
follow = false || @$("input.discussion-follow").is(":checked")
|
||||
|
||||
url = DiscussionUtil.urlFor('create_thread', @topicId)
|
||||
|
||||
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: title
|
||||
body: body
|
||||
anonymous: anonymous
|
||||
anonymous_to_peers: anonymous_to_peers
|
||||
auto_subscribe: follow
|
||||
group_id: group
|
||||
error: DiscussionUtil.formErrorHandler(@$(".new-post-form-errors"))
|
||||
success: (response, textStatus) =>
|
||||
# TODO: Move this out of the callback, this makes it feel sluggish
|
||||
thread = new Thread response['content']
|
||||
DiscussionUtil.clearFormErrors(@$(".new-post-form-errors"))
|
||||
@$el.hide()
|
||||
@$(".new-post-title").val("").attr("prev-text", "")
|
||||
@$(".new-post-body textarea").val("").attr("prev-text", "")
|
||||
@$(".wmd-preview p").html("")
|
||||
@collection.add thread
|
||||
|
||||
setActiveItem: (event) ->
|
||||
if event.which == 13
|
||||
$(".topic_menu_wrapper .focused").click()
|
||||
|
||||
@@ -26,10 +26,34 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
THREADS_PER_PAGE = 20
|
||||
INLINE_THREADS_PER_PAGE = 20
|
||||
PAGES_NEARBY_DELTA = 2
|
||||
escapedict = {'"': '"'}
|
||||
log = logging.getLogger("edx.discussions")
|
||||
|
||||
|
||||
def _attr_safe_json(obj):
|
||||
"""
|
||||
return a JSON string for obj which is safe to embed as the value of an attribute in a DOM node
|
||||
"""
|
||||
return saxutils.escape(json.dumps(obj), {'"': '"'})
|
||||
|
||||
@newrelic.agent.function_trace()
|
||||
def make_course_settings(course, include_category_map=False):
|
||||
"""
|
||||
Generate a JSON-serializable model for course settings, which will be used to initialize a
|
||||
DiscussionCourseSettings object on the client.
|
||||
"""
|
||||
|
||||
obj = {
|
||||
'is_cohorted': is_course_cohorted(course.id),
|
||||
'allow_anonymous': course.allow_anonymous,
|
||||
'allow_anonymous_to_peers': course.allow_anonymous_to_peers,
|
||||
'cohorts': [{"id": str(g.id), "name": g.name} for g in get_course_cohorts(course.id)],
|
||||
}
|
||||
|
||||
if include_category_map:
|
||||
obj['category_map'] = utils.get_discussion_category_map(course)
|
||||
|
||||
return obj
|
||||
|
||||
@newrelic.agent.function_trace()
|
||||
def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAGE):
|
||||
"""
|
||||
@@ -125,31 +149,6 @@ def inline_discussion(request, course_id, discussion_id):
|
||||
with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
|
||||
annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info)
|
||||
|
||||
allow_anonymous = course.allow_anonymous
|
||||
allow_anonymous_to_peers = course.allow_anonymous_to_peers
|
||||
|
||||
#since inline is all one commentable, only show or allow the choice of cohorts
|
||||
#if the commentable is cohorted, otherwise everything is not cohorted
|
||||
#and no one has the option of choosing a cohort
|
||||
is_cohorted = is_course_cohorted(course_id) and is_commentable_cohorted(course_id, discussion_id)
|
||||
is_moderator = cached_has_permission(request.user, "see_all_cohorts", course_id)
|
||||
|
||||
cohorts_list = list()
|
||||
|
||||
if is_cohorted:
|
||||
cohorts_list.append({'name': _('All Groups'), 'id': None})
|
||||
|
||||
#if you're a mod, send all cohorts and let you pick
|
||||
|
||||
if is_moderator:
|
||||
cohorts = get_course_cohorts(course_id)
|
||||
for cohort in cohorts:
|
||||
cohorts_list.append({'name': cohort.name, 'id': cohort.id})
|
||||
|
||||
else:
|
||||
#students don't get to choose
|
||||
cohorts_list = None
|
||||
|
||||
return utils.JsonResponse({
|
||||
'discussion_data': map(utils.safe_content, threads),
|
||||
'user_info': user_info,
|
||||
@@ -157,14 +156,9 @@ def inline_discussion(request, course_id, discussion_id):
|
||||
'page': query_params['page'],
|
||||
'num_pages': query_params['num_pages'],
|
||||
'roles': utils.get_role_ids(course_id),
|
||||
'allow_anonymous_to_peers': allow_anonymous_to_peers,
|
||||
'allow_anonymous': allow_anonymous,
|
||||
'cohorts': cohorts_list,
|
||||
'is_moderator': is_moderator,
|
||||
'is_cohorted': is_cohorted
|
||||
'course_settings': make_course_settings(course)
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def forum_form_discussion(request, course_id):
|
||||
"""
|
||||
@@ -174,8 +168,7 @@ def forum_form_discussion(request, course_id):
|
||||
nr_transaction = newrelic.agent.current_transaction()
|
||||
|
||||
course = get_course_with_access(request.user, 'load_forum', course_id)
|
||||
with newrelic.agent.FunctionTrace(nr_transaction, "get_discussion_category_map"):
|
||||
category_map = utils.get_discussion_category_map(course)
|
||||
course_settings = make_course_settings(course, include_category_map=True)
|
||||
|
||||
try:
|
||||
unsafethreads, query_params = get_threads(request, course_id) # This might process a search query
|
||||
@@ -203,9 +196,6 @@ def forum_form_discussion(request, course_id):
|
||||
})
|
||||
else:
|
||||
with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"):
|
||||
cohorts = get_course_cohorts(course_id)
|
||||
cohorted_commentables = get_cohorted_commentables(course_id)
|
||||
|
||||
user_cohort_id = get_cohort_id(request.user, course_id)
|
||||
|
||||
context = {
|
||||
@@ -213,20 +203,20 @@ def forum_form_discussion(request, course_id):
|
||||
'course': course,
|
||||
#'recent_active_threads': recent_active_threads,
|
||||
'staff_access': has_access(request.user, 'staff', course),
|
||||
'threads': saxutils.escape(json.dumps(threads), escapedict),
|
||||
'threads': _attr_safe_json(threads),
|
||||
'thread_pages': query_params['num_pages'],
|
||||
'user_info': saxutils.escape(json.dumps(user_info), escapedict),
|
||||
'user_info': _attr_safe_json(user_info),
|
||||
'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, 'staff', course),
|
||||
'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict),
|
||||
'annotated_content_info': _attr_safe_json(annotated_content_info),
|
||||
'course_id': course.id.to_deprecated_string(),
|
||||
'category_map': category_map,
|
||||
'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict),
|
||||
'roles': _attr_safe_json(utils.get_role_ids(course_id)),
|
||||
'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id),
|
||||
'cohorts': cohorts,
|
||||
'user_cohort': user_cohort_id,
|
||||
'cohorted_commentables': cohorted_commentables,
|
||||
'is_course_cohorted': is_course_cohorted(course_id),
|
||||
'cohorts': course_settings["cohorts"], # still needed to render _thread_list_template
|
||||
'user_cohort': user_cohort_id, # read from container in NewPostView
|
||||
'is_course_cohorted': is_course_cohorted(course_id), # still needed to render _thread_list_template
|
||||
'sort_preference': user.default_sort_key,
|
||||
'category_map': course_settings["category_map"],
|
||||
'course_settings': _attr_safe_json(course_settings)
|
||||
}
|
||||
# print "start rendering.."
|
||||
return render_to_response('discussion/index.html', context)
|
||||
@@ -239,6 +229,7 @@ def single_thread(request, course_id, discussion_id, thread_id):
|
||||
nr_transaction = newrelic.agent.current_transaction()
|
||||
|
||||
course = get_course_with_access(request.user, 'load_forum', course_id)
|
||||
course_settings = make_course_settings(course, include_category_map=True)
|
||||
cc_user = cc.User.from_django_user(request.user)
|
||||
user_info = cc_user.to_dict()
|
||||
|
||||
@@ -269,14 +260,9 @@ def single_thread(request, course_id, discussion_id, thread_id):
|
||||
})
|
||||
|
||||
else:
|
||||
with newrelic.agent.FunctionTrace(nr_transaction, "get_discussion_category_map"):
|
||||
category_map = utils.get_discussion_category_map(course)
|
||||
|
||||
threads, query_params = get_threads(request, course_id)
|
||||
threads.append(thread.to_dict())
|
||||
|
||||
course = get_course_with_access(request.user, 'load_forum', course_id)
|
||||
|
||||
with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
|
||||
add_courseware_context(threads, course)
|
||||
|
||||
@@ -294,30 +280,29 @@ def single_thread(request, course_id, discussion_id, thread_id):
|
||||
annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info)
|
||||
|
||||
with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"):
|
||||
cohorts = get_course_cohorts(course_id)
|
||||
cohorted_commentables = get_cohorted_commentables(course_id)
|
||||
user_cohort = get_cohort_id(request.user, course_id)
|
||||
|
||||
context = {
|
||||
'discussion_id': discussion_id,
|
||||
'csrf': csrf(request)['csrf_token'],
|
||||
'init': '', # TODO: What is this?
|
||||
'user_info': saxutils.escape(json.dumps(user_info), escapedict),
|
||||
'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict),
|
||||
'user_info': _attr_safe_json(user_info),
|
||||
'annotated_content_info': _attr_safe_json(annotated_content_info),
|
||||
'course': course,
|
||||
#'recent_active_threads': recent_active_threads,
|
||||
'course_id': course.id.to_deprecated_string(), # TODO: Why pass both course and course.id to template?
|
||||
'thread_id': thread_id,
|
||||
'threads': saxutils.escape(json.dumps(threads), escapedict),
|
||||
'category_map': category_map,
|
||||
'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict),
|
||||
'threads': _attr_safe_json(threads),
|
||||
'roles': _attr_safe_json(utils.get_role_ids(course_id)),
|
||||
'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id),
|
||||
'thread_pages': query_params['num_pages'],
|
||||
'is_course_cohorted': is_course_cohorted(course_id),
|
||||
'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id),
|
||||
'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, 'staff', course),
|
||||
'cohorts': cohorts,
|
||||
'user_cohort': get_cohort_id(request.user, course_id),
|
||||
'cohorted_commentables': cohorted_commentables,
|
||||
'cohorts': course_settings["cohorts"],
|
||||
'user_cohort': user_cohort,
|
||||
'sort_preference': cc_user.default_sort_key,
|
||||
'category_map': course_settings["category_map"],
|
||||
'course_settings': _attr_safe_json(course_settings)
|
||||
}
|
||||
return render_to_response('discussion/index.html', context)
|
||||
|
||||
@@ -350,7 +335,7 @@ def user_profile(request, course_id, user_id):
|
||||
'discussion_data': map(utils.safe_content, threads),
|
||||
'page': query_params['page'],
|
||||
'num_pages': query_params['num_pages'],
|
||||
'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict),
|
||||
'annotated_content_info': _attr_safe_json(annotated_content_info),
|
||||
})
|
||||
else:
|
||||
context = {
|
||||
@@ -358,9 +343,9 @@ def user_profile(request, course_id, user_id):
|
||||
'user': request.user,
|
||||
'django_user': User.objects.get(id=user_id),
|
||||
'profiled_user': profiled_user.to_dict(),
|
||||
'threads': saxutils.escape(json.dumps(threads), escapedict),
|
||||
'user_info': saxutils.escape(json.dumps(user_info), escapedict),
|
||||
'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict),
|
||||
'threads': _attr_safe_json(threads),
|
||||
'user_info': _attr_safe_json(user_info),
|
||||
'annotated_content_info': _attr_safe_json(annotated_content_info),
|
||||
'page': query_params['page'],
|
||||
'num_pages': query_params['num_pages'],
|
||||
# 'content': content,
|
||||
@@ -408,9 +393,9 @@ def followed_threads(request, course_id, user_id):
|
||||
'user': request.user,
|
||||
'django_user': User.objects.get(id=user_id),
|
||||
'profiled_user': profiled_user.to_dict(),
|
||||
'threads': saxutils.escape(json.dumps(threads), escapedict),
|
||||
'user_info': saxutils.escape(json.dumps(user_info), escapedict),
|
||||
'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict),
|
||||
'threads': _attr_safe_json(threads),
|
||||
'user_info': _attr_safe_json(user_info),
|
||||
'annotated_content_info': _attr_safe_json(annotated_content_info),
|
||||
# 'content': content,
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
|
||||
def create_discussion(self, discussion_category, discussion_target, **kwargs):
|
||||
self.discussion_num += 1
|
||||
ItemFactory.create(
|
||||
return ItemFactory.create(
|
||||
parent_location=self.course.location,
|
||||
category="discussion",
|
||||
discussion_id="discussion{}".format(self.discussion_num),
|
||||
@@ -179,17 +179,38 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
"Topic B": {"id": "Topic_B"},
|
||||
"Topic C": {"id": "Topic_C"}
|
||||
}
|
||||
self.assertCategoryMapEquals(
|
||||
{
|
||||
"entries": {
|
||||
"Topic A": {"id": "Topic_A", "sort_key": "Topic A"},
|
||||
"Topic B": {"id": "Topic_B", "sort_key": "Topic B"},
|
||||
"Topic C": {"id": "Topic_C", "sort_key": "Topic C"},
|
||||
},
|
||||
"subcategories": {},
|
||||
"children": ["Topic A", "Topic B", "Topic C"]
|
||||
}
|
||||
)
|
||||
|
||||
def check_cohorted_topics(expected_ids):
|
||||
self.assertCategoryMapEquals(
|
||||
{
|
||||
"entries": {
|
||||
"Topic A": {"id": "Topic_A", "sort_key": "Topic A", "is_cohorted": "Topic_A" in expected_ids},
|
||||
"Topic B": {"id": "Topic_B", "sort_key": "Topic B", "is_cohorted": "Topic_B" in expected_ids},
|
||||
"Topic C": {"id": "Topic_C", "sort_key": "Topic C", "is_cohorted": "Topic_C" in expected_ids},
|
||||
},
|
||||
"subcategories": {},
|
||||
"children": ["Topic A", "Topic B", "Topic C"]
|
||||
}
|
||||
)
|
||||
|
||||
check_cohorted_topics([]) # default (empty) cohort config
|
||||
|
||||
self.course.cohort_config = {"cohorted": False, "cohorted_discussions": []}
|
||||
check_cohorted_topics([])
|
||||
|
||||
self.course.cohort_config = {"cohorted": True, "cohorted_discussions": []}
|
||||
check_cohorted_topics([])
|
||||
|
||||
self.course.cohort_config = {"cohorted": True, "cohorted_discussions": ["Topic_B", "Topic_C"]}
|
||||
check_cohorted_topics(["Topic_B", "Topic_C"])
|
||||
|
||||
self.course.cohort_config = {"cohorted": True, "cohorted_discussions": ["Topic_A", "Some_Other_Topic"]}
|
||||
check_cohorted_topics(["Topic_A"])
|
||||
|
||||
# unlikely case, but make sure it works.
|
||||
self.course.cohort_config = {"cohorted": False, "cohorted_discussions": ["Topic_A"]}
|
||||
check_cohorted_topics([])
|
||||
|
||||
|
||||
def test_single_inline(self):
|
||||
self.create_discussion("Chapter", "Discussion")
|
||||
@@ -201,7 +222,8 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
"entries": {
|
||||
"Discussion": {
|
||||
"id": "discussion1",
|
||||
"sort_key": None
|
||||
"sort_key": None,
|
||||
"is_cohorted": False,
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
@@ -220,81 +242,101 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
self.create_discussion("Chapter 2 / Section 1 / Subsection 2", "Discussion")
|
||||
self.create_discussion("Chapter 3 / Section 1", "Discussion")
|
||||
|
||||
self.assertCategoryMapEquals(
|
||||
{
|
||||
"entries": {},
|
||||
"subcategories": {
|
||||
"Chapter 1": {
|
||||
"entries": {
|
||||
"Discussion 1": {
|
||||
"id": "discussion1",
|
||||
"sort_key": None
|
||||
def check_cohorted(is_cohorted):
|
||||
|
||||
self.assertCategoryMapEquals(
|
||||
{
|
||||
"entries": {},
|
||||
"subcategories": {
|
||||
"Chapter 1": {
|
||||
"entries": {
|
||||
"Discussion 1": {
|
||||
"id": "discussion1",
|
||||
"sort_key": None,
|
||||
"is_cohorted": is_cohorted,
|
||||
},
|
||||
"Discussion 2": {
|
||||
"id": "discussion2",
|
||||
"sort_key": None,
|
||||
"is_cohorted": is_cohorted,
|
||||
}
|
||||
},
|
||||
"Discussion 2": {
|
||||
"id": "discussion2",
|
||||
"sort_key": None
|
||||
}
|
||||
"subcategories": {},
|
||||
"children": ["Discussion 1", "Discussion 2"]
|
||||
},
|
||||
"subcategories": {},
|
||||
"children": ["Discussion 1", "Discussion 2"]
|
||||
},
|
||||
"Chapter 2": {
|
||||
"entries": {
|
||||
"Discussion": {
|
||||
"id": "discussion3",
|
||||
"sort_key": None
|
||||
}
|
||||
},
|
||||
"subcategories": {
|
||||
"Section 1": {
|
||||
"entries": {},
|
||||
"subcategories": {
|
||||
"Subsection 1": {
|
||||
"entries": {
|
||||
"Discussion": {
|
||||
"id": "discussion4",
|
||||
"sort_key": None
|
||||
}
|
||||
"Chapter 2": {
|
||||
"entries": {
|
||||
"Discussion": {
|
||||
"id": "discussion3",
|
||||
"sort_key": None,
|
||||
"is_cohorted": is_cohorted,
|
||||
}
|
||||
},
|
||||
"subcategories": {
|
||||
"Section 1": {
|
||||
"entries": {},
|
||||
"subcategories": {
|
||||
"Subsection 1": {
|
||||
"entries": {
|
||||
"Discussion": {
|
||||
"id": "discussion4",
|
||||
"sort_key": None,
|
||||
"is_cohorted": is_cohorted,
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
"children": ["Discussion"]
|
||||
},
|
||||
"subcategories": {},
|
||||
"children": ["Discussion"]
|
||||
"Subsection 2": {
|
||||
"entries": {
|
||||
"Discussion": {
|
||||
"id": "discussion5",
|
||||
"sort_key": None,
|
||||
"is_cohorted": is_cohorted,
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
"children": ["Discussion"]
|
||||
}
|
||||
},
|
||||
"Subsection 2": {
|
||||
"entries": {
|
||||
"Discussion": {
|
||||
"id": "discussion5",
|
||||
"sort_key": None
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
"children": ["Discussion"]
|
||||
}
|
||||
},
|
||||
"children": ["Subsection 1", "Subsection 2"]
|
||||
}
|
||||
"children": ["Subsection 1", "Subsection 2"]
|
||||
}
|
||||
},
|
||||
"children": ["Discussion", "Section 1"]
|
||||
},
|
||||
"children": ["Discussion", "Section 1"]
|
||||
"Chapter 3": {
|
||||
"entries": {},
|
||||
"subcategories": {
|
||||
"Section 1": {
|
||||
"entries": {
|
||||
"Discussion": {
|
||||
"id": "discussion6",
|
||||
"sort_key": None,
|
||||
"is_cohorted": is_cohorted,
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
"children": ["Discussion"]
|
||||
}
|
||||
},
|
||||
"children": ["Section 1"]
|
||||
}
|
||||
},
|
||||
"Chapter 3": {
|
||||
"entries": {},
|
||||
"subcategories": {
|
||||
"Section 1": {
|
||||
"entries": {
|
||||
"Discussion": {
|
||||
"id": "discussion6",
|
||||
"sort_key": None
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
"children": ["Discussion"]
|
||||
}
|
||||
},
|
||||
"children": ["Section 1"]
|
||||
}
|
||||
},
|
||||
"children": ["Chapter 1", "Chapter 2", "Chapter 3"]
|
||||
}
|
||||
)
|
||||
"children": ["Chapter 1", "Chapter 2", "Chapter 3"]
|
||||
}
|
||||
)
|
||||
|
||||
# empty / default config
|
||||
check_cohorted(False)
|
||||
|
||||
# explicitly disabled cohorting
|
||||
self.course.cohort_config = {"cohorted": False}
|
||||
check_cohorted(False)
|
||||
|
||||
# explicitly enabled cohorting
|
||||
self.course.cohort_config = {"cohorted": True}
|
||||
check_cohorted(True)
|
||||
|
||||
|
||||
def test_start_date_filter(self):
|
||||
now = datetime.now()
|
||||
@@ -314,7 +356,8 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
"entries": {
|
||||
"Discussion 1": {
|
||||
"id": "discussion1",
|
||||
"sort_key": None
|
||||
"sort_key": None,
|
||||
"is_cohorted": False,
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
@@ -324,7 +367,8 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
"entries": {
|
||||
"Discussion": {
|
||||
"id": "discussion3",
|
||||
"sort_key": None
|
||||
"sort_key": None,
|
||||
"is_cohorted": False,
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
@@ -350,23 +394,28 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
"entries": {
|
||||
"Discussion 1": {
|
||||
"id": "discussion1",
|
||||
"sort_key": "D"
|
||||
"sort_key": "D",
|
||||
"is_cohorted": False,
|
||||
},
|
||||
"Discussion 2": {
|
||||
"id": "discussion2",
|
||||
"sort_key": "A"
|
||||
"sort_key": "A",
|
||||
"is_cohorted": False,
|
||||
},
|
||||
"Discussion 3": {
|
||||
"id": "discussion3",
|
||||
"sort_key": "E"
|
||||
"sort_key": "E",
|
||||
"is_cohorted": False,
|
||||
},
|
||||
"Discussion 4": {
|
||||
"id": "discussion4",
|
||||
"sort_key": "C"
|
||||
"sort_key": "C",
|
||||
"is_cohorted": False,
|
||||
},
|
||||
"Discussion 5": {
|
||||
"id": "discussion5",
|
||||
"sort_key": "B"
|
||||
"sort_key": "B",
|
||||
"is_cohorted": False,
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
@@ -392,9 +441,9 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
self.assertCategoryMapEquals(
|
||||
{
|
||||
"entries": {
|
||||
"Topic A": {"id": "Topic_A", "sort_key": "B"},
|
||||
"Topic B": {"id": "Topic_B", "sort_key": "C"},
|
||||
"Topic C": {"id": "Topic_C", "sort_key": "A"},
|
||||
"Topic A": {"id": "Topic_A", "sort_key": "B", "is_cohorted": False},
|
||||
"Topic B": {"id": "Topic_B", "sort_key": "C", "is_cohorted": False},
|
||||
"Topic C": {"id": "Topic_C", "sort_key": "A", "is_cohorted": False},
|
||||
},
|
||||
"subcategories": {},
|
||||
"children": ["Topic C", "Topic A", "Topic B"]
|
||||
@@ -418,23 +467,28 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
"entries": {
|
||||
"Discussion D": {
|
||||
"id": "discussion1",
|
||||
"sort_key": "Discussion D"
|
||||
"sort_key": "Discussion D",
|
||||
"is_cohorted": False,
|
||||
},
|
||||
"Discussion A": {
|
||||
"id": "discussion2",
|
||||
"sort_key": "Discussion A"
|
||||
"sort_key": "Discussion A",
|
||||
"is_cohorted": False,
|
||||
},
|
||||
"Discussion E": {
|
||||
"id": "discussion3",
|
||||
"sort_key": "Discussion E"
|
||||
"sort_key": "Discussion E",
|
||||
"is_cohorted": False,
|
||||
},
|
||||
"Discussion C": {
|
||||
"id": "discussion4",
|
||||
"sort_key": "Discussion C"
|
||||
"sort_key": "Discussion C",
|
||||
"is_cohorted": False,
|
||||
},
|
||||
"Discussion B": {
|
||||
"id": "discussion5",
|
||||
"sort_key": "Discussion B"
|
||||
"sort_key": "Discussion B",
|
||||
"is_cohorted": False,
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
@@ -466,11 +520,13 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
"entries": {
|
||||
"Discussion 1": {
|
||||
"id": "discussion3",
|
||||
"sort_key": None
|
||||
"sort_key": None,
|
||||
"is_cohorted": False,
|
||||
},
|
||||
"Discussion 2": {
|
||||
"id": "discussion5",
|
||||
"sort_key": None
|
||||
"sort_key": None,
|
||||
"is_cohorted": False,
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
@@ -480,11 +536,13 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
"entries": {
|
||||
"Discussion 1": {
|
||||
"id": "discussion4",
|
||||
"sort_key": None
|
||||
"sort_key": None,
|
||||
"is_cohorted": False,
|
||||
},
|
||||
"Discussion 2": {
|
||||
"id": "discussion1",
|
||||
"sort_key": None
|
||||
"sort_key": None,
|
||||
"is_cohorted": False,
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
@@ -494,7 +552,8 @@ class CategoryMapTestCase(ModuleStoreTestCase):
|
||||
"entries": {
|
||||
"Discussion": {
|
||||
"id": "discussion2",
|
||||
"sort_key": None
|
||||
"sort_key": None,
|
||||
"is_cohorted": False,
|
||||
}
|
||||
},
|
||||
"subcategories": {},
|
||||
|
||||
@@ -135,6 +135,9 @@ def get_discussion_category_map(course):
|
||||
|
||||
modules = _get_discussion_modules(course)
|
||||
|
||||
is_course_cohorted = course.is_cohorted
|
||||
cohorted_discussion_ids = course.cohorted_discussions
|
||||
|
||||
for module in modules:
|
||||
id = module.discussion_id
|
||||
title = module.discussion_target
|
||||
@@ -179,7 +182,8 @@ def get_discussion_category_map(course):
|
||||
for entry in entries:
|
||||
node[level]["entries"][entry["title"]] = {"id": entry["id"],
|
||||
"sort_key": entry["sort_key"],
|
||||
"start_date": entry["start_date"]}
|
||||
"start_date": entry["start_date"],
|
||||
"is_cohorted": is_course_cohorted}
|
||||
|
||||
# TODO. BUG! : course location is not unique across multiple course runs!
|
||||
# (I think Kevin already noticed this) Need to send course_id with requests, store it
|
||||
@@ -187,7 +191,8 @@ def get_discussion_category_map(course):
|
||||
for topic, entry in course.discussion_topics.items():
|
||||
category_map['entries'][topic] = {"id": entry["id"],
|
||||
"sort_key": entry.get("sort_key", topic),
|
||||
"start_date": datetime.now(UTC())}
|
||||
"start_date": datetime.now(UTC()),
|
||||
"is_cohorted": is_course_cohorted and entry["id"] in cohorted_discussion_ids}
|
||||
|
||||
_sort_map_entries(category_map, course.discussion_sort_alpha)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</%def>
|
||||
|
||||
<%def name="render_entry(entries, entry)">
|
||||
<li><a href="#" class="drop-menu-entry"><span class="board-name" data-discussion_id='${json.dumps(entries[entry])}' cohorted = "${entries[entry]['id'] in cohorted_commentables}">${entry}</span></a></li>
|
||||
<li><a href="#" class="drop-menu-entry"><span class="board-name" data-discussion_id='${json.dumps(entries[entry])}' cohorted = "${str(entries[entry]['is_cohorted']).lower()}">${entry}</span></a></li>
|
||||
</%def>
|
||||
|
||||
<%def name="render_category(categories, category)">
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<article class="new-post-article">
|
||||
<div class="inner-wrapper">
|
||||
<div class="new-post-form-errors">
|
||||
</div>
|
||||
<form class="new-post-form">
|
||||
<div class="left-column">
|
||||
<div class="options">
|
||||
<input type="checkbox" name="follow" class="discussion-follow" class="discussion-follow" id="new-post-follow" checked><label for="new-post-follow">${_("follow this post")}</label>
|
||||
<br>
|
||||
% if course.allow_anonymous:
|
||||
<input type="checkbox" name="anonymous" class="discussion-anonymous" id="new-post-anonymous"><label for="new-post-anonymous">${_("post anonymously")}</label>
|
||||
%elif course.allow_anonymous_to_peers:
|
||||
<input type="checkbox" name="anonymous_to_peers" class="discussion-anonymous-to-peers" id="new-post-anonymous-to-peers"><label for="new-post-anonymous-to-peers">${_("post anonymously to classmates")}</label>
|
||||
%endif
|
||||
%if is_course_cohorted:
|
||||
<div class="form-group-label choose-cohort">
|
||||
## Translators: This labels the selector for which group of students can view a post
|
||||
${_("Make visible to:")}
|
||||
<select class="group-filter-select new-post-group" name = "group_id">
|
||||
<option value="">${_("All Groups")}</option>
|
||||
%if is_moderator:
|
||||
%for c in cohorts:
|
||||
<option value="${c.id}">${c.name}</option>
|
||||
%endfor
|
||||
%else:
|
||||
%if user_cohort:
|
||||
<option value="${user_cohort}">${_("My Cohort")}</option>
|
||||
%endif
|
||||
%endif
|
||||
|
||||
</select>
|
||||
</div>
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-column">
|
||||
<div class="form-row">
|
||||
<label class="sr" for="new-inline-post-title">${_("new post title")}</label>
|
||||
<input type="text" id="new-inline-post-title" class="new-post-title" name="title" placeholder="${_('Title')}">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="new-post-body" name="body" placeholder="${_(u'Enter your question or comment…')}"></div>
|
||||
<!---<div class="new-post-preview"><span class="new-post-preview-label">Preview</span></div>-->
|
||||
</div>
|
||||
<input type="submit" id="new-inline-post-submit" class="submit" value="${_("Add post")}">
|
||||
<a href="#" class="new-post-cancel">${_("Cancel")}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
@@ -1,82 +0,0 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%def name="render_form_filter_dropdown(map)">
|
||||
% for child in map["children"]:
|
||||
% if child in map["entries"]:
|
||||
${render_entry(map["entries"], child)}
|
||||
%else:
|
||||
${render_category(map["subcategories"], child)}
|
||||
%endif
|
||||
%endfor
|
||||
</%def>
|
||||
|
||||
<%def name="render_entry(entries, entry)">
|
||||
<li role="menuitem"><a href="#" class="topic" data-discussion_id="${entries[entry]['id']}" aria-describedby="topic-name-span-${entries[entry]['id']}" cohorted = "${entries[entry]['id'] in cohorted_commentables}">${entry}</a></li>
|
||||
</%def>
|
||||
|
||||
<%def name="render_category(categories, category)">
|
||||
<li role="menuitem">
|
||||
<a href="#"><span class="category-menu-span">${category}</span></a>
|
||||
<ul role="menu">
|
||||
${render_form_filter_dropdown(categories[category])}
|
||||
</ul>
|
||||
</li>
|
||||
</%def>
|
||||
<article class="new-post-article">
|
||||
<div class="inner-wrapper">
|
||||
<form class="new-post-form">
|
||||
<div class="left-column">
|
||||
<span class="topic-dropdown-label" id="topic-dropdown-label">${_("Create new post about:")}</span>
|
||||
<div class="form-topic-drop">
|
||||
<a href="#" aria-labelledby="topic-dropdown-label" class="topic_dropdown_button">${_("Show All Discussions")}<span class="drop-arrow" aria-hidden="true">▾</span></a>
|
||||
<div class="topic_menu_wrapper">
|
||||
<div class="topic_menu_search" role="menu">
|
||||
<label class="sr" for="browse-topic-newpost">${_("Filter List")}</label>
|
||||
<input type="text" id="browse-topic-newpost" class="form-topic-drop-search-input" placeholder="${_("Filter discussion areas")}">
|
||||
</div>
|
||||
<ul class="topic_menu" role="menu">
|
||||
${render_form_filter_dropdown(category_map)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="options">
|
||||
<input type="checkbox" name="follow" class="discussion-follow" class="discussion-follow" id="new-post-follow" checked><label for="new-post-follow">${_("follow this post")}</label>
|
||||
<br>
|
||||
% if course.allow_anonymous:
|
||||
<input type="checkbox" name="anonymous" class="discussion-anonymous" id="new-post-anonymous"><label for="new-post-anonymous">${_("post anonymously")}</label>
|
||||
%elif course.allow_anonymous_to_peers:
|
||||
<input type="checkbox" name="anonymous_to_peers" class="discussion-anonymous-to-peers" id="new-post-anonymous-to-peers"><label for="new-post-anonymous-to-peers">${_("post anonymously to classmates")}</label>
|
||||
%endif
|
||||
%if is_course_cohorted and is_moderator:
|
||||
<div class="form-group-label choose-cohort">
|
||||
## Translators: This labels the selector for which group of students can view a post
|
||||
${_("Make visible to:")}
|
||||
<select class="group-filter-select new-post-group" name = "group_id">
|
||||
<option value="">${_("All Groups")}</option>
|
||||
%for c in cohorts:
|
||||
<option value="${c.id}"
|
||||
%if user_cohort and str(user_cohort) == str(c.id):
|
||||
selected
|
||||
%endif
|
||||
>${c.name}</option>
|
||||
%endfor
|
||||
</select>
|
||||
</div>
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-column">
|
||||
<ul class="new-post-form-errors"></ul>
|
||||
<div class="form-row">
|
||||
<label class="sr" for="new-post-title">${_("new post title")}</label>
|
||||
<input type="text" id="new-post-title" class="new-post-title" name="title" placeholder="${_("Title")}">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="new-post-body" name="body" placeholder="${_(u"Enter your question or comment…")}"></div>
|
||||
<!---<div class="new-post-preview"><span class="new-post-preview-label">Preview</span></div>-->
|
||||
</div>
|
||||
<input type="submit" id="new-post-submit" class="submit" value="${_("Add post")}">
|
||||
<a href="#" class="new-post-cancel">${_("Cancel")}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
@@ -34,13 +34,12 @@
|
||||
<li><a href="#" role="radio" aria-checked="false" data-sort="comments">${_("comments")}</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
%if is_course_cohorted and is_moderator:
|
||||
<span class="group-filter-label cohort">${_("Show:")}</span>
|
||||
<select class="group-filter-select cohort-options cohort">
|
||||
<option value="all">${_("View All")}</option>
|
||||
%for c in cohorts:
|
||||
<option value="${c.id}">${_("View as {name}").format(name=c.name)}</option>
|
||||
<option value="${c['id']}">${_("View as {name}").format(name=c['name'])}</option>
|
||||
%endfor
|
||||
</select>
|
||||
%endif
|
||||
|
||||
@@ -319,3 +319,108 @@
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script aria-hidden="true" type="text/template" id="new-post-tab-template">
|
||||
<div class="inner-wrapper">
|
||||
<form class="new-post-form">
|
||||
<div class="left-column">
|
||||
<!-- BEGIN topic dropdown -->
|
||||
${'<%= topic_dropdown_html %>'}
|
||||
<!-- END topic dropdown -->
|
||||
<!-- BEGIN options -->
|
||||
${'<%= options_html %>'}
|
||||
<!-- END options -->
|
||||
</div>
|
||||
<div class="right-column">
|
||||
<!-- BEGIN errors display -->
|
||||
<ul class="new-post-form-errors"></ul>
|
||||
<!-- END errors display -->
|
||||
<!-- BEGIN content editor -->
|
||||
${'<%= editor_html %>'}
|
||||
<!-- END content editor -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script aria-hidden="true" type="text/template" id="new-post-inline-template">
|
||||
<div class="inner-wrapper">
|
||||
<!-- BEGIN errors display -->
|
||||
<div class="new-post-form-errors">
|
||||
</div>
|
||||
<!-- END errors display -->
|
||||
<form class="new-post-form">
|
||||
<!-- BEGIN content editor -->
|
||||
${'<%= editor_html %>'}
|
||||
<!-- END content editor -->
|
||||
<!-- BEGIN options -->
|
||||
${'<%= options_html %>'}
|
||||
<!-- END options -->
|
||||
</form>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script aria-hidden="true" type="text/template" id="new-post-menu-entry-template">
|
||||
<li role="menuitem"><a href="#" class="topic" data-discussion_id="${'<%- id %>'}" aria-describedby="topic-name-span-${'<%- id %>'}" cohorted="${'<%- is_cohorted %>'}">${'<%- text %>'}</a></li>
|
||||
</script>
|
||||
|
||||
<script aria-hidden="true" type="text/template" id="new-post-menu-category-template">
|
||||
<li role="menuitem">
|
||||
<a href="#"><span class="category-menu-span">${'<%- text %>'}</span></a>
|
||||
<ul role="menu">${'<%= entries %>'}</ul>
|
||||
</li>
|
||||
</script>
|
||||
|
||||
<script aria-hidden="true" type="text/template" id="new-post-topic-dropdown-template">
|
||||
<span class="topic-dropdown-label" id="topic-dropdown-label">${_("Create new post about:")}</span>
|
||||
<div class="form-topic-drop">
|
||||
<a href="#" aria-labelledby="topic-dropdown-label" class="topic_dropdown_button">${_("Show All Discussions")}<span class="drop-arrow" aria-hidden="true">▾</span></a>
|
||||
<div class="topic_menu_wrapper">
|
||||
<div class="topic_menu_search" role="menu">
|
||||
<label class="sr" for="browse-topic-newpost">${_("Filter List")}</label>
|
||||
<input type="text" id="browse-topic-newpost" class="form-topic-drop-search-input" placeholder="${_('Filter discussion areas')}">
|
||||
</div>
|
||||
<ul class="topic_menu" role="menu">${'<%= topics_html %>'}</ul>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script aria-hidden="true" type="text/template" id="new-post-options-template">
|
||||
<div class="options">
|
||||
<input type="checkbox" name="follow" class="discussion-follow" id="new-post-follow" checked><label for="new-post-follow">${_("follow this post")}</label>
|
||||
${'<% if (allow_anonymous) { %>'}
|
||||
<br>
|
||||
<input type="checkbox" name="anonymous" class="discussion-anonymous" id="new-post-anonymous">
|
||||
<label for="new-post-anonymous">${_("post anonymously")}</label>
|
||||
${'<% } %>'}
|
||||
${'<% if (allow_anonymous_to_peers) { %>'}
|
||||
<br>
|
||||
<input type="checkbox" name="anonymous_to_peers" class="discussion-anonymous-to-peers" id="new-post-anonymous-to-peers">
|
||||
<label for="new-post-anonymous-to-peers">${_("post anonymously to classmates")}</label>
|
||||
${'<% } %>'}
|
||||
${'<% if (cohort_options) { %>'}
|
||||
<div class="form-group-label choose-cohort">
|
||||
## Translators: This labels the selector for which group of students can view a post
|
||||
${_("Make visible to:")}
|
||||
<select class="group-filter-select new-post-group" name="group_id">
|
||||
<option value="">${_("All Groups")}</option>
|
||||
${'<% _.each(cohort_options, function(opt) { %>'}
|
||||
<option value="${'<%= opt.value %>'}" ${'<% if (opt.selected) { %>selected<% } %>'}>${'<%- opt.text %>'}</option>
|
||||
${'<% }); %>'}
|
||||
</select>
|
||||
</div>
|
||||
${'<% } %>'}
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script aria-hidden="true" type="text/template" id="new-post-editor-template">
|
||||
<div class="form-row">
|
||||
<label class="sr" for="new-post-title">${_("new post title")}</label>
|
||||
<input type="text" id="new-post-title" class="new-post-title" name="title" placeholder="${_('Title')}">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="new-post-body" name="body" placeholder="${_(u'Enter your question or comment…')}"></div>
|
||||
</div>
|
||||
<input type="submit" id="new-post-submit" class="submit" value="${_('Add post')}">
|
||||
<a href="#" class="new-post-cancel">${_('Cancel')}</a>
|
||||
</script>
|
||||
|
||||
@@ -23,9 +23,19 @@
|
||||
|
||||
<%include file="_discussion_course_navigation.html" args="active_page='discussion'" />
|
||||
|
||||
<%include file="_new_post.html" />
|
||||
<article class="new-post-article"></article>
|
||||
|
||||
<section class="discussion container" id="discussion-container" data-roles="${roles}" data-course-id="${course_id}" data-user-info="${user_info}" data-threads="${threads}" data-thread-pages="${thread_pages}" data-content-info="${annotated_content_info}" data-sort-preference="${sort_preference}" data-flag-moderator="${flag_moderator}">
|
||||
<section class="discussion container" id="discussion-container"
|
||||
data-roles="${roles}"
|
||||
data-course-id="${course_id}"
|
||||
data-user-info="${user_info}"
|
||||
data-threads="${threads}"
|
||||
data-thread-pages="${thread_pages}"
|
||||
data-content-info="${annotated_content_info}"
|
||||
data-sort-preference="${sort_preference}"
|
||||
data-flag-moderator="${flag_moderator}"
|
||||
data-user-cohort-id="${user_cohort}"
|
||||
data-course-settings="${course_settings}">
|
||||
<div class="discussion-body">
|
||||
<div class="sidebar"></div>
|
||||
<div class="discussion-column">
|
||||
|
||||
@@ -1,33 +1,7 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
|
||||
<section class="discussion" data-discussion-id="{{discussionId}}">
|
||||
<article class="new-post-article">
|
||||
<span class="topic" data-discussion-id="{{discussionId}}" />
|
||||
<div class="inner-wrapper">
|
||||
<div class="new-post-form-errors">
|
||||
</div>
|
||||
<form class="new-post-form">
|
||||
<div class="form-row">
|
||||
<input type="text" class="new-post-title" name="title" placeholder="${_("Title")}">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="new-post-body" name="body" placeholder="${_(u"Enter your question or comment…")}"></div>
|
||||
</div>
|
||||
<input type="submit" class="submit" value="${_("Add post")}">
|
||||
<a href="#" class="new-post-cancel">${_("Cancel")}</a>
|
||||
<div class="options">
|
||||
<input type="checkbox" name="follow" class="discussion-follow" class="discussion-follow" id="new-post-follow" checked><label for="new-post-follow">${_("follow this post")}</label>
|
||||
<br>
|
||||
{{#allow_anonymous}}
|
||||
<input type="checkbox" name="anonymous" class="discussion-anonymous" id="new-post-anonymous"><label for="new-post-anonymous">${_("post anonymously")}</label>
|
||||
{{/allow_anonymous}}
|
||||
{{#allow_anonymous_to_peers}}
|
||||
<input type="checkbox" name="anonymous" class="discussion-anonymous-to-peers" id="new-post-anonymous-to-peers"><label for="new-post-anonymous-to-peers">${_("post anonymously to classmates")}</label>
|
||||
{{/allow_anonymous_to_peers}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
<article class="new-post-article"></article>
|
||||
|
||||
<section class="threads">
|
||||
{{#threads}}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
|
||||
<section class="discussion" data-discussion-id="{{discussionId}}">
|
||||
<article class="new-post-article">
|
||||
<span class="topic" data-discussion-id="{{discussionId}}" />
|
||||
<div class="inner-wrapper">
|
||||
<div class="new-post-form-errors">
|
||||
</div>
|
||||
<form class="new-post-form">
|
||||
<div class="form-row">
|
||||
<input type="text" class="new-post-title" name="title" placeholder="${_("Title")}">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="new-post-body" name="body" placeholder="${_(u"Enter your question or comment…")}"></div>
|
||||
</div>
|
||||
<input type="submit" class="submit" value="${_("Add post")}">
|
||||
<a href="#" class="new-post-cancel">${_("Cancel")}</a>
|
||||
<div class="options">
|
||||
<input type="checkbox" name="follow" class="discussion-follow" class="discussion-follow" id="new-post-follow" checked><label for="new-post-follow">${_("follow this post")}</label>
|
||||
<br>
|
||||
{{#allow_anonymous}}
|
||||
<input type="checkbox" name="anonymous" class="discussion-anonymous" id="new-post-anonymous"><label for="new-post-anonymous">${_("post anonymously")}</label>
|
||||
{{/allow_anonymous}}
|
||||
{{#allow_anonymous_to_peers}}
|
||||
<input type="checkbox" name="anonymous" class="discussion-anonymous-to-peers" id="new-post-anonymous-to-peers"><label for="new-post-anonymous-to-peers">${_("post anonymously to classmates")}</label>
|
||||
{{/allow_anonymous_to_peers}}
|
||||
|
||||
<div class="form-group-label choose-cohort">
|
||||
## Translators: This labels the selector for which group of students can view a post
|
||||
${_("Make visible to:")}
|
||||
<select class="group-filter-select new-post-group" name = "group_id">
|
||||
{{#cohorts}}
|
||||
<option value="{{id}}">{{name}}</option>
|
||||
{{/cohorts}}
|
||||
|
||||
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<section class="threads">
|
||||
{{#threads}}
|
||||
<article class="discussion-thread" id="thread_{{id}}">
|
||||
</article>
|
||||
{{/threads}}
|
||||
</section>
|
||||
|
||||
<section class="pagination">
|
||||
</section>
|
||||
</section>
|
||||
Reference in New Issue
Block a user