Merge branch 'feature/tomg/new-discussions' of github.com:MITx/mitx into feature/tomg/new-discussions
This commit is contained in:
453
:w
453
:w
@@ -1,453 +0,0 @@
|
||||
if Backbone?
|
||||
class @Content extends Backbone.Model
|
||||
|
||||
template: -> DiscussionUtil.getTemplate('_content')
|
||||
|
||||
actions:
|
||||
editable: '.admin-edit'
|
||||
can_reply: '.discussion-reply'
|
||||
can_endorse: '.admin-endorse'
|
||||
can_delete: '.admin-delete'
|
||||
can_openclose: '.admin-openclose'
|
||||
|
||||
urlMappers: {}
|
||||
|
||||
urlFor: (name) ->
|
||||
@urlMappers[name].apply(@)
|
||||
|
||||
can: (action) ->
|
||||
DiscussionUtil.getContentInfo @id, action
|
||||
|
||||
updateInfo: (info) ->
|
||||
@set('ability', info.ability)
|
||||
@set('voted', info.voted)
|
||||
@set('subscribed', info.subscribed)
|
||||
|
||||
addComment: (comment, options) ->
|
||||
options ||= {}
|
||||
if not options.silent
|
||||
thread = @get('thread')
|
||||
comments_count = parseInt(thread.get('comments_count'))
|
||||
thread.set('comments_count', comments_count + 1)
|
||||
@get('children').push comment
|
||||
model = new Comment $.extend {}, comment, { thread: @get('thread') }
|
||||
@get('comments').add model
|
||||
model
|
||||
|
||||
removeComment: (comment) ->
|
||||
thread = @get('thread')
|
||||
comments_count = parseInt(thread.get('comments_count'))
|
||||
thread.set('comments_count', comments_count - 1 - comment.getCommentsCount())
|
||||
|
||||
resetComments: (children) ->
|
||||
@set 'children', []
|
||||
@set 'comments', new Comments()
|
||||
for comment in (children || [])
|
||||
@addComment comment, { silent: true }
|
||||
|
||||
initialize: ->
|
||||
DiscussionUtil.addContent @id, @
|
||||
@resetComments(@get('children'))
|
||||
|
||||
|
||||
class @ContentView extends Backbone.View
|
||||
|
||||
$: (selector) ->
|
||||
@$local.find(selector)
|
||||
|
||||
partial:
|
||||
endorsed: (endorsed) ->
|
||||
if endorsed
|
||||
@$el.addClass("endorsed")
|
||||
else
|
||||
@$el.removeClass("endorsed")
|
||||
|
||||
closed: (closed) -> # we should just re-render the whole thread, or update according to new abilities
|
||||
if closed
|
||||
@$el.addClass("closed")
|
||||
@$(".admin-openclose").text "Re-open Thread"
|
||||
else
|
||||
@$el.removeClass("closed")
|
||||
@$(".admin-openclose").text "Close Thread"
|
||||
|
||||
voted: (voted) ->
|
||||
@$(".discussion-vote-up").removeClass("voted") if voted != "up"
|
||||
@$(".discussion-vote-down").removeClass("voted") if voted != "down"
|
||||
@$(".discussion-vote-#{voted}").addClass("voted") if voted in ["up", "down"]
|
||||
|
||||
votes_point: (votes_point) ->
|
||||
@$(".discussion-votes-point").html(votes_point)
|
||||
|
||||
comments_count: (comments_count) ->
|
||||
@$(".comments-count").html(comments_count)
|
||||
|
||||
subscribed: (subscribed) ->
|
||||
if subscribed
|
||||
@$(".discussion-follow-thread").addClass("discussion-unfollow-thread").html("Unfollow")
|
||||
else
|
||||
@$(".discussion-follow-thread").removeClass("discussion-unfollow-thread").html("Follow")
|
||||
|
||||
ability: (ability) ->
|
||||
for action, elemSelector of @model.actions
|
||||
if not ability[action]
|
||||
@$(elemSelector).parent().hide()
|
||||
else
|
||||
@$(elemSelector).parent().show()
|
||||
|
||||
$discussionContent: ->
|
||||
@_discussionContent ||= @$el.children(".discussion-content")
|
||||
|
||||
$showComments: ->
|
||||
@_showComments ||= @$(".discussion-show-comments")
|
||||
|
||||
updateShowComments: ->
|
||||
if @showed
|
||||
@$showComments().html @$showComments().html().replace "Show", "Hide"
|
||||
else
|
||||
@$showComments().html @$showComments().html().replace "Hide", "Show"
|
||||
|
||||
retrieved: ->
|
||||
@$showComments().hasClass("retrieved")
|
||||
|
||||
hideSingleThread: (event) ->
|
||||
@$el.children(".comments").hide()
|
||||
@showed = false
|
||||
@updateShowComments()
|
||||
|
||||
showSingleThread: (event) ->
|
||||
if @retrieved()
|
||||
@$el.children(".comments").show()
|
||||
@showed = true
|
||||
@updateShowComments()
|
||||
else
|
||||
$elem = $.merge @$(".thread-title"), @$showComments()
|
||||
url = @model.urlFor('retrieve')
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
$loading: @$(".discussion-show-comments")
|
||||
type: "GET"
|
||||
url: url
|
||||
success: (response, textStatus) =>
|
||||
@showed = true
|
||||
@updateShowComments()
|
||||
@$showComments().addClass("retrieved")
|
||||
@$el.children(".comments").replaceWith response.html
|
||||
@model.resetComments response.content.children
|
||||
@initCommentViews()
|
||||
DiscussionUtil.bulkUpdateContentInfo response.annotated_content_info
|
||||
|
||||
toggleSingleThread: (event) ->
|
||||
if @showed
|
||||
@hideSingleThread(event)
|
||||
else
|
||||
@showSingleThread(event)
|
||||
|
||||
initCommentViews: ->
|
||||
@$el.children(".comments").children(".comment").each (index, elem) =>
|
||||
model = @model.get('comments').find $(elem).attr("_id")
|
||||
if not model.view
|
||||
commentView = new CommentView el: elem, model: model
|
||||
|
||||
reply: ->
|
||||
if @model.get('type') == 'thread'
|
||||
@showSingleThread()
|
||||
$replyView = @$(".discussion-reply-new")
|
||||
if $replyView.length
|
||||
$replyView.show()
|
||||
else
|
||||
view = {}
|
||||
view.id = @model.id
|
||||
view.showWatchCheckbox = not @model.get('thread').get('subscribed')
|
||||
html = Mustache.render DiscussionUtil.getTemplate('_reply'), view
|
||||
@$discussionContent().append html
|
||||
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "reply-body"
|
||||
@$(".discussion-submit-post").click $.proxy(@submitReply, @)
|
||||
@$(".discussion-cancel-post").click $.proxy(@cancelReply, @)
|
||||
@$(".discussion-reply").hide()
|
||||
@$(".discussion-edit").hide()
|
||||
|
||||
submitReply: (event) ->
|
||||
url = @model.urlFor('reply')
|
||||
|
||||
body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "reply-body"
|
||||
|
||||
anonymous = false || @$(".discussion-post-anonymously").is(":checked")
|
||||
autowatch = false || @$(".discussion-auto-watch").is(":checked")
|
||||
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $(event.target)
|
||||
$loading: $(event.target) if event
|
||||
url: url
|
||||
type: "POST"
|
||||
dataType: 'json'
|
||||
data:
|
||||
body: body
|
||||
anonymous: anonymous
|
||||
auto_subscribe: autowatch
|
||||
error: DiscussionUtil.formErrorHandler @$(".discussion-errors")
|
||||
success: (response, textStatus) =>
|
||||
DiscussionUtil.clearFormErrors @$(".discussion-errors")
|
||||
$comment = $(response.html)
|
||||
@$el.children(".comments").prepend $comment
|
||||
DiscussionUtil.setWmdContent @$el, $.proxy(@$, @), "reply-body", ""
|
||||
comment = @model.addComment response.content
|
||||
commentView = new CommentView el: $comment[0], model: comment
|
||||
comment.updateInfo response.annotated_content_info
|
||||
if autowatch
|
||||
@model.get('thread').set('subscribed', true)
|
||||
@cancelReply()
|
||||
|
||||
cancelReply: ->
|
||||
$replyView = @$(".discussion-reply-new")
|
||||
if $replyView.length
|
||||
$replyView.hide()
|
||||
@$(".discussion-reply").show()
|
||||
@$(".discussion-edit").show()
|
||||
|
||||
unvote: (event) ->
|
||||
url = @model.urlFor('unvote')
|
||||
$elem = @$(".discussion-vote")
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
type: "POST"
|
||||
success: (response, textStatus) =>
|
||||
@model.set('voted', '')
|
||||
@model.set('votes_point', response.votes.point)
|
||||
|
||||
vote: (event, value) ->
|
||||
url = @model.urlFor("#{value}vote")
|
||||
$elem = @$(".discussion-vote")
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
type: "POST"
|
||||
success: (response, textStatus) =>
|
||||
@model.set('voted', value)
|
||||
@model.set('votes_point', response.votes.point)
|
||||
|
||||
toggleVote: (event) ->
|
||||
console.log("HERE")
|
||||
$elem = $(event.target)
|
||||
value = $elem.attr("value")
|
||||
$elem.toggleClass("is-cast")
|
||||
return false
|
||||
# if @model.get("voted") == value
|
||||
# @unvote(event)
|
||||
# else
|
||||
# @vote(event, value)
|
||||
|
||||
toggleEndorse: (event) ->
|
||||
$elem = $(event.target)
|
||||
url = @model.urlFor('endorse')
|
||||
endorsed = @model.get('endorsed')
|
||||
data = { endorsed: not endorsed }
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
data: data
|
||||
type: "POST"
|
||||
success: (response, textStatus) =>
|
||||
@model.set('endorsed', not endorsed)
|
||||
|
||||
toggleFollow: (event) ->
|
||||
$elem = $(event.target)
|
||||
subscribed = @model.get('subscribed')
|
||||
if subscribed
|
||||
url = @model.urlFor('unfollow')
|
||||
else
|
||||
url = @model.urlFor('follow')
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
type: "POST"
|
||||
success: (response, textStatus) =>
|
||||
@model.set('subscribed', not subscribed)
|
||||
|
||||
toggleClosed: (event) ->
|
||||
$elem = $(event.target)
|
||||
url = @model.urlFor('close')
|
||||
closed = @model.get('closed')
|
||||
data = { closed: not closed }
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
type: "POST"
|
||||
data: data
|
||||
success: (response, textStatus) =>
|
||||
@model.set('closed', not closed)
|
||||
@model.set('ability', response.ability)
|
||||
|
||||
edit: (event) ->
|
||||
@$(".discussion-content-wrapper").hide()
|
||||
$editView = @$(".discussion-content-edit")
|
||||
if $editView.length
|
||||
$editView.show()
|
||||
else
|
||||
view = {}
|
||||
view.id = @model.id
|
||||
if @model.get('type') == 'thread'
|
||||
view.title = @model.get('title')
|
||||
view.body = @model.get('body')
|
||||
view.tags = @model.get('tags')
|
||||
else
|
||||
view.body = @model.get('body')
|
||||
@$discussionContent().append Mustache.render DiscussionUtil.getTemplate("_edit_#{@model.get('type')}"), view
|
||||
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "#{@model.get('type')}-body-edit"
|
||||
@$(".thread-tags-edit").tagsInput DiscussionUtil.tagsInputOptions()
|
||||
@$(".discussion-submit-update").unbind("click").click $.proxy(@submitEdit, @)
|
||||
@$(".discussion-cancel-update").unbind("click").click $.proxy(@cancelEdit, @)
|
||||
|
||||
submitEdit: (event) ->
|
||||
|
||||
url = @model.urlFor('update')
|
||||
data = {}
|
||||
if @model.get('type') == 'thread'
|
||||
data.title = @$(".thread-title-edit").val()
|
||||
data.body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "thread-body-edit"
|
||||
data.tags = @$(".thread-tags-edit").val()
|
||||
else
|
||||
data.body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "comment-body-edit"
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $(event.target)
|
||||
$loading: $(event.target) if event
|
||||
url: url
|
||||
type: "POST"
|
||||
dataType: 'json'
|
||||
data: data
|
||||
error: DiscussionUtil.formErrorHandler @$(".discussion-update-errors")
|
||||
success: (response, textStatus) =>
|
||||
DiscussionUtil.clearFormErrors @$(".discussion-update-errors")
|
||||
@$discussionContent().replaceWith(response.html)
|
||||
if @model.get('type') == 'thread'
|
||||
@model = new Thread response.content
|
||||
else
|
||||
@model = new Comment $.extend {}, response.content, { thread: @model.get('thread') }
|
||||
@reconstruct()
|
||||
@model.updateInfo response.annotated_content_info, { forceUpdate: true }
|
||||
|
||||
cancelEdit: (event) ->
|
||||
@$(".discussion-content-edit").hide()
|
||||
@$(".discussion-content-wrapper").show()
|
||||
|
||||
delete: (event) ->
|
||||
url = @model.urlFor('delete')
|
||||
if @model.get('type') == 'thread'
|
||||
c = confirm "Are you sure to delete thread \"#{@model.get('title')}\"?"
|
||||
else
|
||||
c = confirm "Are you sure to delete this comment? "
|
||||
if not c
|
||||
return
|
||||
$elem = $(event.target)
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
type: "POST"
|
||||
success: (response, textStatus) =>
|
||||
@$el.remove()
|
||||
if @model.get('type') == 'comment'
|
||||
@model.get('thread').removeComment(@model)
|
||||
|
||||
events:
|
||||
"click .discussion-follow-thread": "toggleFollow"
|
||||
"click .thread-title": "toggleSingleThread"
|
||||
"click .discussion-show-comments": "toggleSingleThread"
|
||||
"click .discussion-reply-thread": "reply"
|
||||
"click .discussion-reply-comment": "reply"
|
||||
"click .discussion-cancel-reply": "cancelReply"
|
||||
"click .discussion-vote-up": "toggleVote"
|
||||
"click .discussion-vote-down": "toggleVote"
|
||||
"click .admin-endorse": "toggleEndorse"
|
||||
"click .admin-openclose": "toggleClosed"
|
||||
"click .admin-edit": "edit"
|
||||
"click .admin-delete": "delete"
|
||||
|
||||
initLocal: ->
|
||||
@$local = @$el.children(".local")
|
||||
@$delegateElement = @$local
|
||||
|
||||
initTitle: ->
|
||||
$contentTitle = @$(".thread-title")
|
||||
if $contentTitle.length
|
||||
$contentTitle.html DiscussionUtil.unescapeHighlightTag DiscussionUtil.stripLatexHighlight $contentTitle.html()
|
||||
|
||||
initBody: ->
|
||||
$contentBody = @$(".content-body")
|
||||
$contentBody.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight $contentBody.html()
|
||||
MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")]
|
||||
|
||||
initTimeago: ->
|
||||
@$("span.timeago").timeago()
|
||||
|
||||
renderPartial: ->
|
||||
for attr, value of @model.changedAttributes()
|
||||
if @partial[attr]
|
||||
@partial[attr].apply(@, [value])
|
||||
|
||||
initBindings: ->
|
||||
@model.view = @
|
||||
@model.bind('change', @renderPartial, @)
|
||||
|
||||
initialize: ->
|
||||
@initBindings()
|
||||
@initLocal()
|
||||
@initTimeago()
|
||||
@initTitle()
|
||||
@initBody()
|
||||
@initCommentViews()
|
||||
|
||||
reconstruct: ->
|
||||
@initBindings()
|
||||
@initLocal()
|
||||
@initTimeago()
|
||||
@initTitle()
|
||||
@initBody()
|
||||
@delegateEvents()
|
||||
|
||||
class @Thread extends @Content
|
||||
urlMappers:
|
||||
'retrieve' : -> DiscussionUtil.urlFor('retrieve_single_thread', @discussion.id, @id)
|
||||
'reply' : -> DiscussionUtil.urlFor('create_comment', @id)
|
||||
'unvote' : -> DiscussionUtil.urlFor("undo_vote_for_#{@get('type')}", @id)
|
||||
'upvote' : -> DiscussionUtil.urlFor("upvote_#{@get('type')}", @id)
|
||||
'downvote' : -> DiscussionUtil.urlFor("downvote_#{@get('type')}", @id)
|
||||
'close' : -> DiscussionUtil.urlFor('openclose_thread', @id)
|
||||
'update' : -> DiscussionUtil.urlFor('update_thread', @id)
|
||||
'delete' : -> DiscussionUtil.urlFor('delete_thread', @id)
|
||||
'follow' : -> DiscussionUtil.urlFor('follow_thread', @id)
|
||||
'unfollow' : -> DiscussionUtil.urlFor('unfollow_thread', @id)
|
||||
|
||||
initialize: ->
|
||||
@set('thread', @)
|
||||
super()
|
||||
|
||||
class @ThreadView extends @ContentView
|
||||
|
||||
class @Comment extends @Content
|
||||
urlMappers:
|
||||
'reply': -> DiscussionUtil.urlFor('create_sub_comment', @id)
|
||||
'unvote': -> DiscussionUtil.urlFor("undo_vote_for_#{@get('type')}", @id)
|
||||
'upvote': -> DiscussionUtil.urlFor("upvote_#{@get('type')}", @id)
|
||||
'downvote': -> DiscussionUtil.urlFor("downvote_#{@get('type')}", @id)
|
||||
'endorse': -> DiscussionUtil.urlFor('endorse_comment', @id)
|
||||
'update': -> DiscussionUtil.urlFor('update_comment', @id)
|
||||
'delete': -> DiscussionUtil.urlFor('delete_comment', @id)
|
||||
|
||||
getCommentsCount: ->
|
||||
count = 0
|
||||
@get('comments').each (comment) ->
|
||||
count += comment.getCommentsCount() + 1
|
||||
count
|
||||
|
||||
class @CommentView extends @ContentView
|
||||
|
||||
class @Comments extends Backbone.Collection
|
||||
|
||||
model: Comment
|
||||
|
||||
initialize: ->
|
||||
@bind "add", (item) =>
|
||||
item.collection = @
|
||||
|
||||
find: (id) ->
|
||||
_.first @where(id: id)
|
||||
@@ -18,7 +18,7 @@ from django_comment_client.utils import merge_dict, extract, strip_none, strip_b
|
||||
import json
|
||||
import django_comment_client.utils as utils
|
||||
import comment_client as cc
|
||||
|
||||
import xml.sax.saxutils as saxutils
|
||||
|
||||
THREADS_PER_PAGE = 50000
|
||||
PAGES_NEARBY_DELTA = 2
|
||||
@@ -143,9 +143,10 @@ def render_search_bar(request, course_id, discussion_id=None, text=''):
|
||||
|
||||
def forum_form_discussion(request, course_id):
|
||||
course = get_course_with_access(request.user, course_id, 'load')
|
||||
category_map = utils.get_discussion_category_map(course)
|
||||
threads, query_params = get_threads(request, course_id)
|
||||
content = render_forum_discussion(request, course_id, threads, discussion_id=_general_discussion_id(course_id), query_params=query_params)
|
||||
|
||||
user_info = cc.User.from_django_user(request.user).to_dict()
|
||||
if request.is_ajax():
|
||||
return utils.JsonResponse({
|
||||
'html': content,
|
||||
@@ -161,6 +162,7 @@ def forum_form_discussion(request, course_id):
|
||||
trending_tags = cc.search_trending_tags(
|
||||
course_id,
|
||||
)
|
||||
escapedict = {'"': '"'}
|
||||
context = {
|
||||
'csrf': csrf(request)['csrf_token'],
|
||||
'course': course,
|
||||
@@ -168,7 +170,10 @@ def forum_form_discussion(request, course_id):
|
||||
'recent_active_threads': recent_active_threads,
|
||||
'trending_tags': trending_tags,
|
||||
'staff_access' : has_access(request.user, course, 'staff'),
|
||||
'threads': threads,
|
||||
'threads': saxutils.escape(json.dumps(threads),escapedict),
|
||||
'user_info': saxutils.escape(json.dumps(user_info),escapedict),
|
||||
'course_id': course.id,
|
||||
'category_map': category_map,
|
||||
}
|
||||
# print "start rendering.."
|
||||
return render_to_response('discussion/index.html', context)
|
||||
@@ -197,7 +202,7 @@ def render_single_thread(request, discussion_id, course_id, thread_id):
|
||||
def single_thread(request, course_id, discussion_id, thread_id):
|
||||
|
||||
if request.is_ajax():
|
||||
|
||||
|
||||
user_info = cc.User.from_django_user(request.user).to_dict()
|
||||
thread = cc.Thread.find(thread_id).retrieve(recursive=True)
|
||||
annotated_content_info = utils.get_annotated_content_infos(course_id, thread, request.user, user_info=user_info)
|
||||
@@ -226,19 +231,19 @@ def single_thread(request, course_id, discussion_id, thread_id):
|
||||
)
|
||||
|
||||
user_info = cc.User.from_django_user(request.user).to_dict()
|
||||
|
||||
escapedict = {'"': '"'}
|
||||
context = {
|
||||
'discussion_id': discussion_id,
|
||||
'csrf': csrf(request)['csrf_token'],
|
||||
'init': '',
|
||||
'user_info': json.dumps(user_info),
|
||||
'user_info': saxutils.escape(json.dumps(user_info),escapedict),
|
||||
'content': render_single_thread(request, discussion_id, course_id, thread_id),
|
||||
'course': course,
|
||||
'recent_active_threads': recent_active_threads,
|
||||
'trending_tags': trending_tags,
|
||||
'course_id': course.id,
|
||||
'thread_id': thread_id,
|
||||
'threads': json.dumps(threads),
|
||||
'threads': saxutils.escape(json.dumps(threads), escapedict),
|
||||
'category_map': category_map,
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ class User(models.Model):
|
||||
accessible_fields = ['username', 'email', 'follower_ids', 'upvoted_ids', 'downvoted_ids',
|
||||
'id', 'external_id', 'subscribed_user_ids', 'children', 'course_id',
|
||||
'subscribed_thread_ids', 'subscribed_commentable_ids',
|
||||
'default_sort_key',
|
||||
'threads_count', 'comments_count',
|
||||
]
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ if Backbone?
|
||||
|
||||
initialize: ->
|
||||
DiscussionUtil.addContent @id, @
|
||||
@set 'user_url', DiscussionUtil.urlFor('user_profile', @get('user_id'))
|
||||
@resetComments(@get('children'))
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ if Backbone?
|
||||
model: Thread
|
||||
|
||||
initialize: ->
|
||||
DiscussionUtil.addDiscussion @id, @
|
||||
@bind "add", (item) =>
|
||||
item.discussion = @
|
||||
@comparator = @sortByDate
|
||||
|
||||
@@ -13,7 +13,8 @@ class @DiscussionRouter extends Backbone.Router
|
||||
@newPostView = new NewPostView(el: $(".new-post-article"))
|
||||
|
||||
allThreads: ->
|
||||
true
|
||||
# TODO: Do something reasonable here
|
||||
$(".discussion-column").html("No thread selected.")
|
||||
|
||||
setActiveThread: =>
|
||||
if @thread
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
DiscussionApp =
|
||||
start: (elem)->
|
||||
# TODO: Perhaps eliminate usage of global variables when possible
|
||||
element = $(elem)
|
||||
window.$$contents = {}
|
||||
window.$$course_id = element.data("course-id")
|
||||
user_info = element.data("user-info")
|
||||
threads = element.data("threads")
|
||||
window.user = new DiscussionUser(user_info)
|
||||
discussion = new Discussion(threads)
|
||||
new DiscussionRouter({discussion: discussion})
|
||||
Backbone.history.start({pushState: true, root: "/courses/#{$$course_id}/discussion/forum/"})
|
||||
|
||||
$ ->
|
||||
$("section.discussion").each (index, elem) ->
|
||||
DiscussionApp.start(elem)
|
||||
|
||||
@@ -13,16 +13,9 @@ class @DiscussionUtil
|
||||
@getTemplate: (id) ->
|
||||
$("script##{id}").html()
|
||||
|
||||
@getDiscussionData: (id) ->
|
||||
return $$discussion_data[id]
|
||||
|
||||
@addContent: (id, content) -> window.$$contents[id] = content
|
||||
|
||||
@getContent: (id) -> window.$$contents[id]
|
||||
|
||||
@addDiscussion: (id, discussion) -> window.$$discussions[id] = discussion
|
||||
|
||||
@getDiscussion: (id) -> window.$$discussions[id]
|
||||
|
||||
@bulkUpdateContentInfo: (infos) ->
|
||||
for id, info of infos
|
||||
@@ -64,21 +57,25 @@ class @DiscussionUtil
|
||||
openclose_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/close"
|
||||
permanent_link_thread : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}"
|
||||
permanent_link_comment : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}##{param2}"
|
||||
user_profile : "/courses/#{$$course_id}/discussion/forum/users/#{param}"
|
||||
}[name]
|
||||
|
||||
@safeAjax: (params) ->
|
||||
$elem = params.$elem
|
||||
if $elem.attr("disabled")
|
||||
if $elem and $elem.attr("disabled")
|
||||
return
|
||||
params["url"] = URI(params["url"]).addSearch ajax: 1
|
||||
params["beforeSend"] = ->
|
||||
$elem.attr("disabled", "disabled")
|
||||
if $elem
|
||||
$elem.attr("disabled", "disabled")
|
||||
if params["$loading"]
|
||||
if params["loadingCallback"]?
|
||||
params["loadingCallback"].apply(params["$loading"])
|
||||
else
|
||||
params["$loading"].loading()
|
||||
$.ajax(params).always ->
|
||||
$elem.removeAttr("disabled")
|
||||
if $elem
|
||||
$elem.removeAttr("disabled")
|
||||
if params["$loading"]
|
||||
if params["loadedCallback"]?
|
||||
params["loadedCallback"].apply(params["$loading"])
|
||||
|
||||
@@ -29,10 +29,12 @@ class @DiscussionThreadView extends Backbone.View
|
||||
MathJax.Hub.Queue ["Typeset", MathJax.Hub, element.attr("id")]
|
||||
|
||||
renderResponses: ->
|
||||
$.ajax @model.id, success: (data, textStatus, xhr) =>
|
||||
@$(".loading").remove()
|
||||
comments = new Comments(data['content']['children'])
|
||||
comments.each @renderResponse
|
||||
DiscussionUtil.safeAjax
|
||||
url: @model.id
|
||||
success: (data, textStatus, xhr) =>
|
||||
@$(".loading").remove()
|
||||
comments = new Comments(data['content']['children'])
|
||||
comments.each @renderResponse
|
||||
|
||||
renderResponse: (response) =>
|
||||
view = new ThreadResponseView(model: response)
|
||||
|
||||
@@ -57,12 +57,15 @@ class @ThreadResponseView extends Backbone.View
|
||||
if textStatus == 'success'
|
||||
@model.set(response)
|
||||
|
||||
submitComment: ->
|
||||
submitComment: (event) ->
|
||||
url = @model.urlFor('reply')
|
||||
body = @$(".comment-form-input").val()
|
||||
if not body.trim().length
|
||||
return false
|
||||
comment = new Comment(body: body, created_at: (new Date()).toISOString(), username: window.user.get("username"))
|
||||
@renderComment(comment)
|
||||
@trigger "comment:add"
|
||||
@$(".comment-form-input").val("")
|
||||
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $(event.target)
|
||||
@@ -71,4 +74,5 @@ class @ThreadResponseView extends Backbone.View
|
||||
dataType: 'json'
|
||||
data:
|
||||
body: body
|
||||
|
||||
false
|
||||
|
||||
@@ -844,6 +844,7 @@ body.discussion {
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
width: 68%;
|
||||
min-height: 500px
|
||||
}
|
||||
|
||||
.discussion-article {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<%namespace name="renderer" file="_content_renderer.html"/>
|
||||
<%! from django_comment_client.mustache_helpers import url_for_user %>
|
||||
|
||||
<article class="discussion-article" data-id="${discussion_id| h}">
|
||||
<a href="#" class="dogear"></a>
|
||||
@@ -8,7 +9,7 @@
|
||||
<h1>${thread['title']}</h1>
|
||||
<p class="posted-details">
|
||||
<span class="timeago" title="${thread['created_at'] | h}">sometime</span> by
|
||||
<a href="${thread['user_id']}">${thread['username']}</a>
|
||||
<a href="${url_for_user(thread, thread['user_id'])}">${thread['username']}</a>
|
||||
</p>
|
||||
</header>
|
||||
<div class="post-body">
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<h1>${'<%= title %>'}</h1>
|
||||
<p class="posted-details">
|
||||
<span class="timeago" title="${'<%= created_at %>'}">sometime</span> by
|
||||
<a href="${'<%= user_id %>'}">${'<%= username %>'}</a>
|
||||
<a href="${'<%= user_url %>'}">${'<%= username %>'}</a>
|
||||
</p>
|
||||
</header>
|
||||
<div class="post-body">
|
||||
@@ -58,7 +58,7 @@
|
||||
<script type="text/template" id="thread-response-template">
|
||||
<header>
|
||||
<a href="#" class="vote-btn" data-tooltip="vote"><span class="plus-icon"></span><span class="votes-count-number">${"<%= votes['up_count'] %>"}</span></a>
|
||||
<a href="#" class="posted-by">${"<%= username %>"}</a>
|
||||
<a href="${'<%= user_url %>'}" class="posted-by">${"<%= username %>"}</a>
|
||||
<p class="posted-details" title="${'<%= created_at %>'}">Sometime</p>
|
||||
</header>
|
||||
<div class="response-body">${"<%= body %>"}</div>
|
||||
@@ -72,7 +72,7 @@
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="response-comment-template">
|
||||
<p>${'<%= body %>'}<span class="posted-details">–posted <span class="timeago" title="${'<%= created_at %>'}">sometime</span> by <a href="#">${'<%= username %>'}</a></span></p>
|
||||
<p>${'<%= body %>'}<span class="posted-details">–posted <span class="timeago" title="${'<%= created_at %>'}">sometime</span> by <a href="${'<%= user_url %>'}">${'<%= username %>'}</a></span></p>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="thread-list-item-template">
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<%! import django_comment_client.helpers as helpers %>
|
||||
<%! from django.template.defaultfilters import escapejs %>
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
|
||||
<%inherit file="../main.html" />
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
<%block name="bodyclass">discussion</%block>
|
||||
@@ -16,51 +19,15 @@
|
||||
|
||||
<%include file="../courseware/course_navigation.html" args="active_page='discussion'" />
|
||||
|
||||
<%include file="_new_post.html" />
|
||||
|
||||
<section class="container">
|
||||
<div class="discussion-wrapper">
|
||||
<article class="discussion-sidebar">
|
||||
<div class="board-selector">
|
||||
<a href="#" class="board-drop-btn">Homework / Week 1 <span class="drop-arrow">▾</span></a>
|
||||
<div class="board-drop-menu">
|
||||
</div>
|
||||
</div>
|
||||
<div class="sort-bar">
|
||||
<a href="#"><span class="sort-label">Sort by:</span> most recent <span class="drop-arrow">▾</span></a>
|
||||
</div>
|
||||
<div class="post-list-wrapper">
|
||||
<ul class="post-list">
|
||||
% for thread in threads:
|
||||
<li><a href="${helpers.permalink(thread) | h}"><span class="title">${thread['title'] | h}</span> <span class="comments">${thread['comments_count'] | h}</span><span class="votes">+${thread['votes']['up_count'] | h}</span></a></li>
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
<section class="discussion-content">
|
||||
This page intentionally left blank
|
||||
</section>
|
||||
</div>
|
||||
<script type="text/javascript" src="${static.url('js/discussions-temp.js')}"></script>
|
||||
|
||||
<section class="discussion container" id="discussion-container" data-course-id="${course_id}" data-user-info="${user_info}" data-threads="${threads}">
|
||||
<div class="discussion-body">
|
||||
<div class="sidebar"></div>
|
||||
<div class="discussion-column"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="course-wrapper">
|
||||
<section aria-label="Course Navigation" class="course-index">
|
||||
<nav>
|
||||
|
||||
<article class="sidebar-module discussion-sidebar">
|
||||
<a href="#" class="sidebar-new-post-button">New Post</a>
|
||||
</article>
|
||||
|
||||
<%include file="_recent_active_posts.html" />
|
||||
|
||||
<%include file="_trending_tags.html" />
|
||||
|
||||
</nav>
|
||||
</section>
|
||||
|
||||
<section class="course-content">
|
||||
${content.decode('utf-8')}
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
<%include file="_underscore_templates.html" />
|
||||
@@ -17,23 +17,7 @@
|
||||
<%include file="_js_body_dependencies.html" />
|
||||
<%static:js group='discussion'/>
|
||||
<script type="text/javascript" src="${static.url('js/discussions-temp.js')}"></script>
|
||||
<script>
|
||||
$$contents = {};
|
||||
$$discussions = {};
|
||||
$$course_id = "${course_id}";
|
||||
DiscussionApp = {
|
||||
start: function() {
|
||||
window.user = new DiscussionUser(JSON.parse("${user_info | escapejs}"));
|
||||
var discussion = new Discussion(JSON.parse("${threads | escapejs}"));
|
||||
|
||||
new DiscussionRouter({discussion: discussion});
|
||||
Backbone.history.start({pushState: true, root: "/courses/${course_id}/discussion/forum/"});
|
||||
}
|
||||
}
|
||||
$(document).ready(function() {
|
||||
DiscussionApp.start();
|
||||
});
|
||||
</script>
|
||||
</%block>
|
||||
|
||||
<nav class="course-material">
|
||||
@@ -71,11 +55,11 @@
|
||||
|
||||
<%include file="_new_post.html" />
|
||||
|
||||
<div class="discussion container">
|
||||
<section class="discussion container" id="discussion-container" data-course-id="${course_id}" data-user-info="${user_info}" data-threads="${threads}">
|
||||
<div class="discussion-body">
|
||||
<div class="sidebar"></div>
|
||||
<div class="discussion-column"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%include file="_underscore_templates.html" />
|
||||
|
||||
Reference in New Issue
Block a user