diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index 77883ae898..51d3a78d03 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -30,7 +30,7 @@ def _general_discussion_id(course_id): def _should_perform_search(request): return bool(request.GET.get('text', False) or \ request.GET.get('tags', False)) - + def render_accordion(request, course, discussion_id): @@ -59,7 +59,7 @@ def render_discussion(request, course_id, threads, *args, **kwargs): }[discussion_type] base_url = { - 'inline': (lambda: reverse('django_comment_client.forum.views.inline_discussion', args=[course_id, discussion_id])), + 'inline': (lambda: reverse('django_comment_client.forum.views.inline_discussion', args=[course_id, discussion_id])), 'forum': (lambda: reverse('django_comment_client.forum.views.forum_form_discussion', args=[course_id])), 'user': (lambda: reverse('django_comment_client.forum.views.user_profile', args=[course_id, user_id])), }[discussion_type]() @@ -123,12 +123,14 @@ def get_threads(request, course_id, discussion_id=None): # discussion per page is fixed for now def inline_discussion(request, course_id, discussion_id): threads, query_params = get_threads(request, course_id, discussion_id) - html = render_inline_discussion(request, course_id, threads, discussion_id=discussion_id, \ - query_params=query_params) - + # TODO: Remove all of this stuff or switch back to server side rendering once templates are mustache again +# html = render_inline_discussion(request, course_id, threads, discussion_id=discussion_id, \ +# query_params=query_params) + user_info = cc.User.from_django_user(request.user).to_dict() return utils.JsonResponse({ - 'html': html, +# 'html': html, 'discussion_data': map(utils.safe_content, threads), + 'user_info': user_info, }) def render_search_bar(request, course_id, discussion_id=None, text=''): @@ -146,7 +148,14 @@ def forum_form_discussion(request, course_id): 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() + + def infogetter(thread): + return utils.get_annotated_content_infos(course_id, thread, request.user, user_info) + + annotated_content_info = reduce(merge_dict, map(infogetter, threads), {}) + if request.is_ajax(): return utils.JsonResponse({ 'html': content, @@ -172,6 +181,7 @@ def forum_form_discussion(request, course_id): 'staff_access' : has_access(request.user, course, 'staff'), '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), 'course_id': course.id, 'category_map': category_map, } @@ -207,6 +217,7 @@ def single_thread(request, course_id, discussion_id, thread_id): 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) context = {'thread': thread.to_dict(), 'course_id': course_id} + # TODO: Remove completely or switch back to server side rendering html = render_to_string('discussion/_ajax_single_thread.html', context) return utils.JsonResponse({ @@ -232,12 +243,18 @@ def single_thread(request, course_id, discussion_id, thread_id): user_info = cc.User.from_django_user(request.user).to_dict() escapedict = {'"': '"'} + + def infogetter(thread): + return utils.get_annotated_content_infos(course_id, thread, request.user, user_info) + + annotated_content_info = reduce(merge_dict, map(infogetter, threads), {}) + context = { 'discussion_id': discussion_id, 'csrf': csrf(request)['csrf_token'], 'init': '', 'user_info': saxutils.escape(json.dumps(user_info),escapedict), - 'content': render_single_thread(request, discussion_id, course_id, thread_id), + 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course': course, 'recent_active_threads': recent_active_threads, 'trending_tags': trending_tags, @@ -273,7 +290,7 @@ def user_profile(request, course_id, user_id): }) else: context = { - 'course': course, + 'course': course, 'user': request.user, 'django_user': User.objects.get(id=user_id), 'profiled_user': profiled_user.to_dict(), diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee index 56e66fa607..93d1414035 100644 --- a/lms/static/coffee/src/discussion/content.coffee +++ b/lms/static/coffee/src/discussion/content.coffee @@ -1,6 +1,9 @@ if Backbone? class @Content extends Backbone.Model + @contents: {} + @contentInfos: {} + template: -> DiscussionUtil.getTemplate('_content') actions: @@ -13,15 +16,18 @@ if Backbone? urlMappers: {} urlFor: (name) -> + console.log @ @urlMappers[name].apply(@) can: (action) -> - DiscussionUtil.getContentInfo @id, action + (@get('ability') || {})[action] updateInfo: (info) -> - @set('ability', info.ability) - @set('voted', info.voted) - @set('subscribed', info.subscribed) + if info + console.log info.ability + @set('ability', info.ability) + @set('voted', info.voted) + @set('subscribed', info.subscribed) addComment: (comment, options) -> options ||= {} @@ -46,10 +52,24 @@ if Backbone? @addComment comment, { silent: true } initialize: -> - DiscussionUtil.addContent @id, @ + Content.addContent @id, @ + if Content.getInfo(@id) + @updateInfo(Content.getInfo(@id)) @set 'user_url', DiscussionUtil.urlFor('user_profile', @get('user_id')) @resetComments(@get('children')) - + + @addContent: (id, content) -> @contents[id] = content + + @getContent: (id) -> @contents[id] + + @getInfo: (id) -> + @contentInfos[id] + + @loadContentInfos: (infos) -> + for id, info of infos + if @getContent(id) + @getContent(id).updateInfo(info) + $.extend @contentInfos, infos class @ContentView extends Backbone.View @@ -423,9 +443,11 @@ if Backbone? super() follow: -> + @set('subscribed', true) @trigger "thread:follow" unfollow: -> + @set('subscribed', false) @trigger "thread:unfollow" display_body: -> diff --git a/lms/static/coffee/src/discussion/discussion_module.coffee b/lms/static/coffee/src/discussion/discussion_module.coffee index 8306d4d2ae..f0f54bada7 100644 --- a/lms/static/coffee/src/discussion/discussion_module.coffee +++ b/lms/static/coffee/src/discussion/discussion_module.coffee @@ -23,12 +23,17 @@ if Backbone? type: "GET" dataType: 'json' success: (response, textStatus) => - @$el.append(response.html) - $discussion = @$el.find("section.discussion") + #@$el.append(response.html) + window.user = new DiscussionUser(response.user_info) $(event.target).html("Hide Discussion") discussion = new Discussion() discussion.reset(response.discussion_data, {silent: false}) - view = new DiscussionView(el: $discussion[0], model: discussion) + $discussion = $(Mustache.render $("script#_inline_discussion").html(), {'threads':response.discussion_data}) + $(".discussion-module").append($discussion) + discussion.each (thread) -> + element = $("article#thread_#{thread.id}") + dtv = new DiscussionThreadView el: element, model: thread + dtv.render() DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info) @retrieved = true @showed = true diff --git a/lms/static/coffee/src/discussion/main.coffee b/lms/static/coffee/src/discussion/main.coffee index e1f651ddb2..4ec7dc9b8d 100644 --- a/lms/static/coffee/src/discussion/main.coffee +++ b/lms/static/coffee/src/discussion/main.coffee @@ -6,7 +6,9 @@ DiscussionApp = window.$$course_id = element.data("course-id") user_info = element.data("user-info") threads = element.data("threads") + content_info = element.data("content-info") window.user = new DiscussionUser(user_info) + Content.loadContentInfos(content_info) discussion = new Discussion(threads) new DiscussionRouter({discussion: discussion}) Backbone.history.start({pushState: true, root: "/courses/#{$$course_id}/discussion/forum/"}) diff --git a/lms/static/coffee/src/discussion/utils.coffee b/lms/static/coffee/src/discussion/utils.coffee index fa0bbda5e8..4beec4734d 100644 --- a/lms/static/coffee/src/discussion/utils.coffee +++ b/lms/static/coffee/src/discussion/utils.coffee @@ -1,4 +1,6 @@ $ -> + if !window.$$contents + window.$$contents = {} $.fn.extend loading: -> @$_loading = $("") diff --git a/lms/static/coffee/src/discussion/views/discussion_content_view.coffee b/lms/static/coffee/src/discussion/views/discussion_content_view.coffee new file mode 100644 index 0000000000..5d7aeb2a37 --- /dev/null +++ b/lms/static/coffee/src/discussion/views/discussion_content_view.coffee @@ -0,0 +1,51 @@ +class @DiscussionContentView extends Backbone.View + + partialRenderer: + endorsed: (endorsed) -> + + closed: (closed) -> # we should just re-render the whole thread, or update according to new abilities + + voted: (voted) -> + if voted + @$(".discussion-vote").addClass("is-cast") + else + @$(".discussion-vote").removeClass("is-cast") + + votes_point: (votes_point) -> + @$(".discussion-vote .votes-count-number").html(votes_point) + + comments_count: (comments_count) -> + + subscribed: (subscribed) -> + if subscribed + @$(".dogear").addClass("is-followed") + else + @$(".dogear").removeClass("is-followed") + + ability: (ability) -> + console.log "ability changed" + for action, selector of @abilityRenderer + if not ability[action] + selector.disable.apply(@) + else + selector.enable.apply(@) + + abilityRenderer: + editable: + enable: -> @$(".action-edit").closest("li").show() + disable: -> @$(".action-edit").closest("li").hide() + can_delete: + enable: -> @$(".action-delete").closest("li").show() + disable: -> @$(".action-delete").closest("li").hide() + can_endorse: + enable: -> @$(".action-endorse").css("cursor", "auto") + disable: -> @$(".action-endorse").css("cursor", "default") + + renderPartial: -> + console.log "changed" + for attr, value of @model.changedAttributes() + if @partialRenderer[attr] + @partialRenderer[attr].apply(@, [value]) + + initialize: -> + @model.bind('change', @renderPartial, @) diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee index 441929a05f..b97fbc30cf 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee @@ -1,8 +1,25 @@ -class @DiscussionThreadView extends Backbone.View +class @DiscussionThreadView extends DiscussionContentView + + abilityRenderer: + editable: + enable: -> @$(".action-edit").closest("li").show() + disable: -> @$(".action-edit").closest("li").hide() + can_delete: + enable: -> @$(".action-delete").closest("li").show() + disable: -> @$(".action-delete").closest("li").hide() + can_endorse: + enable: -> + @$(".action-endorse").css("cursor", "auto") + disable: -> + @$(".action-endorse").css("cursor", "default") + events: "click .discussion-vote": "toggleVote" - "click .dogear": "toggleFollowing" + "click .action-follow": "toggleFollowing" "click .discussion-submit-post": "submitComment" + "click .action-edit": "edit" + "click .action-delete": "delete" + template: _.template($("#thread-template").html()) render: -> @@ -29,9 +46,10 @@ class @DiscussionThreadView extends Backbone.View renderResponses: -> DiscussionUtil.safeAjax - url: @model.id + url: "/courses/#{$$course_id}/discussion/forum/#{@model.get('commentable_id')}/threads/#{@model.id}" success: (data, textStatus, xhr) => @$(".loading").remove() + Content.loadContentInfos(data['annotated_content_info']) comments = new Comments(data['content']['children']) comments.each @renderResponse @@ -44,19 +62,17 @@ class @DiscussionThreadView extends Backbone.View addComment: => @model.trigger "comment:add" - toggleVote: -> - @$(".discussion-vote").toggleClass("is-cast") - if @$(".discussion-vote").hasClass("is-cast") + toggleVote: (event) -> + event.preventDefault() + if not @model.get('voted')#@$(".discussion-vote").hasClass("is-cast") @vote() else @unvote() - false toggleFollowing: (event) -> $elem = $(event.target) - @$(".dogear").toggleClass("is-followed") url = null - if @$(".dogear").hasClass("is-followed") + if not @model.get('subscribed') @model.follow() url = @model.urlFor("follow") else @@ -69,7 +85,8 @@ class @DiscussionThreadView extends Backbone.View vote: -> url = @model.urlFor("upvote") - @$(".discussion-vote .votes-count-number").html(parseInt(@$(".discussion-vote .votes-count-number").html()) + 1) + @model.set('votes_point', parseInt(@model.get('votes_point')) + 1) + #@$(".discussion-vote .votes-count-number").html(parseInt(@$(".discussion-vote .votes-count-number").html()) + 1) DiscussionUtil.safeAjax $elem: @$(".discussion-vote") url: url @@ -80,7 +97,8 @@ class @DiscussionThreadView extends Backbone.View unvote: -> url = @model.urlFor("unvote") - @$(".discussion-vote .votes-count-number").html(parseInt(@$(".discussion-vote .votes-count-number").html()) - 1) + @model.set('votes_point', parseInt(@model.get('votes_point')) - 1) + #@$(".discussion-vote .votes-count-number").html(parseInt(@$(".discussion-vote .votes-count-number").html()) - 1) DiscussionUtil.safeAjax $elem: @$(".discussion-vote") url: url @@ -89,7 +107,8 @@ class @DiscussionThreadView extends Backbone.View if textStatus == 'success' @model.set(response) - submitComment: -> + submitComment: (event) -> + event.preventDefault() url = @model.urlFor('reply') body = @$("#wmd-input").val() response = new Comment(body: body, created_at: (new Date()).toISOString(), username: window.user.get("username"), votes: { up_count: 0 }) @@ -103,4 +122,20 @@ class @DiscussionThreadView extends Backbone.View dataType: 'json' data: body: body - false + + edit: -> + + delete: -> + + toggleEndorse: -> + $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) diff --git a/lms/static/coffee/src/discussion/views/response_comment_view.coffee b/lms/static/coffee/src/discussion/views/response_comment_view.coffee index d43832f537..634fad6556 100644 --- a/lms/static/coffee/src/discussion/views/response_comment_view.coffee +++ b/lms/static/coffee/src/discussion/views/response_comment_view.coffee @@ -1,4 +1,4 @@ -class @ResponseCommentView extends Backbone.View +class @ResponseCommentView extends DiscussionContentView tagName: "li" template: _.template($("#response-comment-template").html()) render: -> diff --git a/lms/static/coffee/src/discussion/views/thread_list_item_view.coffee b/lms/static/coffee/src/discussion/views/thread_list_item_view.coffee index de8ae74f05..5cf2954357 100644 --- a/lms/static/coffee/src/discussion/views/thread_list_item_view.coffee +++ b/lms/static/coffee/src/discussion/views/thread_list_item_view.coffee @@ -13,9 +13,9 @@ class @ThreadListItemView extends Backbone.View if window.user.following(@model) @follow() @ - threadSelected: -> + threadSelected: (event) -> + event.preventDefault() @trigger("thread:selected", @model.id) - false follow: => @$("a").addClass("followed") diff --git a/lms/static/coffee/src/discussion/views/thread_response_view.coffee b/lms/static/coffee/src/discussion/views/thread_response_view.coffee index a66e781b27..8e0509f0f4 100644 --- a/lms/static/coffee/src/discussion/views/thread_response_view.coffee +++ b/lms/static/coffee/src/discussion/views/thread_response_view.coffee @@ -1,9 +1,11 @@ -class @ThreadResponseView extends Backbone.View +class @ThreadResponseView extends DiscussionContentView tagName: "li" template: _.template($("#thread-response-template").html()) + events: "click .vote-btn": "toggleVote" "submit form": "submitComment" + "click .action-endorse": "toggleEndorse" render: -> @$el.html(@template(@model.toJSON())) @@ -27,13 +29,13 @@ class @ThreadResponseView extends Backbone.View view.render() @$(".comments li:last").before(view.el) - toggleVote: -> + toggleVote: (event) -> + event.preventDefault() @$(".vote-btn").toggleClass("is-cast") if @$(".vote-btn").hasClass("is-cast") @vote() else @unvote() - false vote: -> url = @model.urlFor("upvote") @@ -58,10 +60,11 @@ class @ThreadResponseView extends Backbone.View @model.set(response) submitComment: (event) -> + event.preventDefault() url = @model.urlFor('reply') body = @$(".comment-form-input").val() if not body.trim().length - return false + return comment = new Comment(body: body, created_at: (new Date()).toISOString(), username: window.user.get("username")) @renderComment(comment) @trigger "comment:add" @@ -74,5 +77,18 @@ class @ThreadResponseView extends Backbone.View dataType: 'json' data: body: body - - false + + toggleEndorse: (event) -> + event.preventDefault() + if not @model.can('can_endorse') + return + $elem = $(event.target) + url = @model.urlFor('endorse') + endorsed = @model.get('endorsed') + data = { endorsed: not endorsed } + @model.set('endorsed', not endorsed) + DiscussionUtil.safeAjax + $elem: $elem + url: url + data: data + type: "POST" diff --git a/lms/static/sass/_discussion.scss b/lms/static/sass/_discussion.scss index 0c8e7972ca..0df6eceb85 100644 --- a/lms/static/sass/_discussion.scss +++ b/lms/static/sass/_discussion.scss @@ -1261,12 +1261,16 @@ body.discussion { } .moderator-actions { + margin: 0; margin-top: 20px; + padding: 0; @include clearfix; + li { float: left; margin-right: 8px; + list-style: none; } a { @@ -1401,13 +1405,6 @@ body.discussion { - - - - - - - .global-discussion-actions { height: 60px; @include linear-gradient(top, #ebebeb, #d9d9d9); @@ -1415,21 +1412,13 @@ body.discussion { border-bottom: 1px solid #bcbcbc; } +.discussion-module { + @extend .discussion-body +} - - - - - - - - - - - - - - - - - +/* For some reason I have to do this to get the SCSS to compile, can't stick it under the above .discussion-module */ +.discussion-module { + .discussion-reply-new { + display:none + } +} diff --git a/lms/templates/discussion/_discussion_module.html b/lms/templates/discussion/_discussion_module.html index b9e69cc0ad..99c0602b41 100644 --- a/lms/templates/discussion/_discussion_module.html +++ b/lms/templates/discussion/_discussion_module.html @@ -1,3 +1,5 @@ +<%include file="_underscore_templates.html" /> +
Show Discussion
diff --git a/lms/templates/discussion/_underscore_templates.html b/lms/templates/discussion/_underscore_templates.html index 1a28843271..64fca600e4 100644 --- a/lms/templates/discussion/_underscore_templates.html +++ b/lms/templates/discussion/_underscore_templates.html @@ -1,23 +1,32 @@