diff --git a/lms/djangoapps/django_comment_client/base/urls.py b/lms/djangoapps/django_comment_client/base/urls.py index 49c11324b8..29e2dc310d 100644 --- a/lms/djangoapps/django_comment_client/base/urls.py +++ b/lms/djangoapps/django_comment_client/base/urls.py @@ -10,8 +10,8 @@ urlpatterns = patterns('django_comment_client.base.views', url(r'threads/(?P[\w\-]+)/delete', 'delete_thread', name='delete_thread'), url(r'threads/(?P[\w\-]+)/upvote$', 'vote_for_thread', {'value': 'up'}, name='upvote_thread'), url(r'threads/(?P[\w\-]+)/downvote$', 'vote_for_thread', {'value': 'down'}, name='downvote_thread'), - url(r'threads/(?P[\w\-]+)/watch$', 'watch_thread', name='watch_thread'), - url(r'threads/(?P[\w\-]+)/unwatch$', 'unwatch_thread', name='unwatch_thread'), + url(r'threads/(?P[\w\-]+)/follow$', 'follow_thread', name='follow_thread'), + url(r'threads/(?P[\w\-]+)/unfollow$', 'unfollow_thread', name='unfollow_thread'), url(r'comments/(?P[\w\-]+)/update$', 'update_comment', name='update_comment'), url(r'comments/(?P[\w\-]+)/endorse$', 'endorse_comment', name='endorse_comment'), @@ -21,8 +21,8 @@ urlpatterns = patterns('django_comment_client.base.views', url(r'comments/(?P[\w\-]+)/downvote$', 'vote_for_comment', {'value': 'down'}, name='downvote_comment'), url(r'(?P[\w\-]+)/threads/create$', 'create_thread', name='create_thread'), - url(r'(?P[\w\-]+)/watch$', 'watch_commentable', name='watch_commentable'), - url(r'(?P[\w\-]+)/unwatch$', 'unwatch_commentable', name='unwatch_commentable'), + url(r'(?P[\w\-]+)/follow$', 'follow_commentable', name='follow_commentable'), + url(r'(?P[\w\-]+)/unfollow$', 'unfollow_commentable', name='unfollow_commentable'), url(r'search$', 'search', name='search'), ) diff --git a/lms/djangoapps/django_comment_client/base/views.py b/lms/djangoapps/django_comment_client/base/views.py index bc191df158..49964b91d5 100644 --- a/lms/djangoapps/django_comment_client/base/views.py +++ b/lms/djangoapps/django_comment_client/base/views.py @@ -140,42 +140,42 @@ def vote_for_thread(request, course_id, thread_id, value): @login_required @require_POST -def watch_thread(request, course_id, thread_id): +def follow_thread(request, course_id, thread_id): user_id = request.user.id response = comment_client.subscribe_thread(user_id, thread_id) return JsonResponse(response) @login_required @require_POST -def watch_commentable(request, course_id, commentable_id): +def follow_commentable(request, course_id, commentable_id): user_id = request.user.id response = comment_client.subscribe_commentable(user_id, commentable_id) return JsonResponse(response) @login_required @require_POST -def follow(request, course_id, followed_user_id): +def follow_user(request, course_id, followed_user_id): user_id = request.user.id response = comment_client.follow(user_id, followed_user_id) return JsonResponse(response) @login_required @require_POST -def unwatch_thread(request, course_id, thread_id): +def unfollow_thread(request, course_id, thread_id): user_id = request.user.id response = comment_client.unsubscribe_thread(user_id, thread_id) return JsonResponse(response) @login_required @require_POST -def unwatch_commentable(request, course_id, commentable_id): +def unfollow_commentable(request, course_id, commentable_id): user_id = request.user.id response = comment_client.unsubscribe_commentable(user_id, commentable_id) return JsonResponse(response) @login_required @require_POST -def unfollow(request, course_id, followed_user_id): +def unfollow_user(request, course_id, followed_user_id): user_id = request.user.id response = comment_client.unfollow(user_id, followed_user_id) return JsonResponse(response) diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee index 82b5e2b177..db20caac3a 100644 --- a/lms/static/coffee/src/discussion/content.coffee +++ b/lms/static/coffee/src/discussion/content.coffee @@ -28,9 +28,10 @@ Discussion = @Discussion if $replyView.length $replyView.show() else + thread_id = $discussionContent.parents(".thread").attr("_id") view = { id: id - showWatchCheckbox: $discussionContent.parents(".thread").attr("_id") not in $$user_info.subscribed_thread_ids + showWatchCheckbox: not Discussion.isSubscribed(thread_id, "thread") } $discussionContent.append Mustache.render Discussion.replyTemplate, view Markdown.makeWmdEditor $local(".reply-body"), "-reply-body-#{id}", Discussion.urlFor('upload') @@ -61,19 +62,15 @@ Discussion = @Discussion autowatch = false || $local(".discussion-auto-watch").is(":checked") Discussion.safeAjax + $elem: $(elem) url: url type: "POST" data: body: body anonymous: anonymous autowatch: autowatch - success: (response, textStatus) -> - if response.errors? and response.errors.length > 0 - errorsField = $local(".discussion-errors").empty() - for error in response.errors - errorsField.append($("
  • ").addClass("new-post-form-error").html(error)) - else - Discussion.handleAnchorAndReload(response) + success: Discussion.formErrorHandler $local(".discussion-errors"), (response, textStatus) -> + Discussion.handleAnchorAndReload(response) dataType: 'json' handleVote = (elem, value) -> @@ -119,14 +116,13 @@ Discussion = @Discussion title = $local(".thread-title-edit").val() body = $local("#wmd-input-thread-body-edit-#{id}").val() tags = $local(".thread-tags-edit").val() - $.post url, {title: title, body: body, tags: tags}, (response, textStatus) -> - if response.errors - errorsField = $local(".discussion-update-errors").empty() - for error in response.errors - errorsField.append($("
  • ").addClass("new-post-form-error").html(error)) - else + $.ajax + url: url + type: "POST" + data: {title: title, body: body, tags: tags}, + success: Discussion.formErrorHandler $local(".discussion-update-errors"), (response, textStatus) -> Discussion.handleAnchorAndReload(response) - , 'json' + dataType: 'json' handleEditComment = (elem) -> $local(".discussion-content-wrapper").hide() @@ -134,10 +130,7 @@ Discussion = @Discussion if $editView.length $editView.show() else - view = { - id: id - body: $local(".comment-raw-body").html() - } + view = { id: id, body: $local(".comment-raw-body").html() } $discussionContent.append Mustache.render Discussion.editCommentTemplate, view Markdown.makeWmdEditor $local(".comment-body-edit"), "-comment-body-edit-#{id}", Discussion.urlFor('update_comment', id) $local(".discussion-submit-update").unbind("click").click -> handleSubmitEditComment(this) @@ -146,14 +139,13 @@ Discussion = @Discussion handleSubmitEditComment= (elem) -> url = Discussion.urlFor('update_comment', id) body = $local("#wmd-input-comment-body-edit-#{id}").val() - $.post url, {body: body}, (response, textStatus) -> - if response.errors - errorsField = $local(".discussion-update-errors").empty() - for error in response.errors - errorsField.append($("
  • ").addClass("new-post-form-error").html(error)) - else + $.ajax + url: url + data: {body: body} + type: "POST" + success: Discussion.formErrorHandler $local(".discussion-update-errors"), (response, textStatus) -> Discussion.handleAnchorAndReload(response) - , 'json' + dataType: "json" handleEndorse = (elem) -> url = Discussion.urlFor('endorse_comment', id) @@ -203,35 +195,38 @@ Discussion = @Discussion rebindHideEvents() dataType: 'json' - - $local(".thread-title").click handleShowSingleThread + Discussion.bindLocalEvents $local, - $local(".discussion-show-comments").click handleShowSingleThread + "click .thread-title": -> + handleShowSingleThread(this) - $local(".discussion-reply-thread").click -> - handleShowSingleThread($local(".thread-title")) - handleReply(this) + "click .discussion-show-comments": -> + handleShowSingleThread(this) - $local(".discussion-reply-comment").click -> - handleReply(this) + "click .discussion-reply-thread": -> + handleShowSingleThread($local(".thread-title")) + handleReply(this) - $local(".discussion-cancel-reply").click -> - handleCancelReply(this) + "click .discussion-reply-comment": -> + handleReply(this) - $local(".discussion-vote-up").click -> - handleVote(this, "up") + "click .discussion-cancel-reply": -> + handleCancelReply(this) - $local(".discussion-vote-down").click -> - handleVote(this, "down") + "click .discussion-vote-up": -> + handleVote(this, "up") - $local(".discussion-endorse").click -> - handleEndorse(this) + "click .discussion-vote-down": -> + handleVote(this, "down") - $local(".discussion-edit").click -> - if $content.hasClass("thread") - handleEditThread(this) - else - handleEditComment(this) + "click .discussion-endorse": -> + handleEndorse(this) + + "click .discussion-edit": -> + if $content.hasClass("thread") + handleEditThread(this) + else + handleEditComment(this) initializeContent: (content) -> $content = $(content) diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index d5f3535130..9654c1ce20 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -3,107 +3,79 @@ if not @Discussion? Discussion = @Discussion +initializeVote = (index, content) -> + $content = $(content) + $local = Discussion.generateLocal($content.children(".discussion-content")) + id = $content.attr("_id") + if id in $$user_info.upvoted_ids + $local(".discussion-vote-up").addClass("voted") + else if id in $$user_info.downvoted_ids + $local(".discussion-vote-down").addClass("voted") + +subscriptionLink = (type, id) -> + + followLink = -> + Discussion.generateDiscussionLink("discussion-follow-#{type}", "Follow", handleFollow) + + unfollowLink = -> + Discussion.generateDiscussionLink("discussion-unfollow-#{type}", "Unfollow", handleUnfollow) + + handleFollow = (elem) -> + Discussion.safeAjax + $elem: $(elem) + url: Discussion.urlFor("follow_#{type}", id) + type: "POST" + success: (response, textStatus) -> + if textStatus == "success" + $(elem).replaceWith unfollowLink() + dataType: 'json' + + handleUnfollow = (elem) -> + Discussion.safeAjax + $elem: $(elem) + url: Discussion.urlFor("unfollow_#{type}", id) + type: "POST" + success: (response, textStatus) -> + if textStatus == "success" + $(elem).replaceWith followLink() + dataType: 'json' + + if type == 'discussion' and id in $$user_info.subscribed_commentable_ids \ + or type == 'thread' and id in $$user_info.subscribed_thread_ids + unfollowLink() + else + followLink() + +initializeFollowDiscussion = (discussion) -> + $discussion = $(discussion) + id = $following.attr("_id") + $local = Discussion.generateLocal() + $discussion.children(".discussion-non-content") + .find(".discussion-title-wrapper") + .append(subscriptionLink('discussion', id)) + +initializeFollowThread = (index, thread) -> + $thread = $(thread) + id = $thread.attr("_id") + $thread.children(".discussion-content") + .find(".info") + .append(subscriptionLink('thread', id)) + @Discussion = $.extend @Discussion, initializeDiscussion: (discussion) -> - initializeVote = (index, content) -> - $content = $(content) - $local = Discussion.generateLocal($content.children(".discussion-content")) - id = $content.attr("_id") - if id in $$user_info.upvoted_ids - $local(".discussion-vote-up").addClass("voted") - else if id in $$user_info.downvoted_ids - $local(".discussion-vote-down").addClass("voted") - - initializeWatchDiscussion = (discussion) -> - $discussion = $(discussion) - id = $discussion.attr("_id") - $local = Discussion.generateLocal($discussion.children(".discussion-non-content")) - - handleWatchDiscussion = (elem) -> - url = Discussion.urlFor('watch_commentable', id) - $.post url, {}, (response, textStatus) -> - if textStatus == "success" - Discussion.handleAnchorAndReload(response) - , 'json' - - handleUnwatchDiscussion = (elem) -> - url = Discussion.urlFor('unwatch_commentable', id) - $.post url, {}, (response, textStatus) -> - if textStatus == "success" - Discussion.handleAnchorAndReload(response) - , 'json' - - if id in $$user_info.subscribed_commentable_ids - unwatchDiscussion = Discussion.generateDiscussionLink("discussion-unwatch-discussion", "Unwatch", handleUnwatchDiscussion) - $local(".discussion-title-wrapper").append(unwatchDiscussion) - else - watchDiscussion = Discussion.generateDiscussionLink("discussion-watch-discussion", "Watch", handleWatchDiscussion) - $local(".discussion-title-wrapper").append(watchDiscussion) - - initializeWatchThreads = (index, thread) -> - $thread = $(thread) - id = $thread.attr("_id") - $local = Discussion.generateLocal($thread.children(".discussion-content")) - - handleWatchThread = (elem) -> - $elem = $(elem) - url = Discussion.urlFor('watch_thread', id) - Discussion.safeAjax - $elem: $elem - url: url - type: "POST" - success: (response, textStatus) -> - if textStatus == "success" - $elem.removeClass("discussion-watch-thread") - .addClass("discussion-unwatch-thread") - .html("Unfollow") - .unbind('click').click -> - handleUnwatchThread(this) - dataType: 'json' - - handleUnwatchThread = (elem) -> - $elem = $(elem) - url = Discussion.urlFor('unwatch_thread', id) - Discussion.safeAjax - $elem: $elem - url: url - type: "POST" - success: (response, textStatus) -> - if textStatus == "success" - $elem.removeClass("discussion-unwatch-thread") - .addClass("discussion-watch-thread") - .html("Follow") - .unbind('click').click -> - handleWatchThread(this) - dataType: 'json' - - if id in $$user_info.subscribed_thread_ids - unwatchThread = Discussion.generateDiscussionLink("discussion-unwatch-thread", "Unfollow", handleUnwatchThread) - $local(".info").append(unwatchThread) - else - watchThread = Discussion.generateDiscussionLink("discussion-watch-thread", "Follow", handleWatchThread) - $local(".info").append(watchThread) - $local = Discussion.generateLocal(discussion) if $$user_info? $local(".comment").each(initializeVote) - $local(".thread").each(initializeVote).each(initializeWatchThreads) - #initializeWatchDiscussion(discussion) TODO move this somewhere else + $local(".thread").each(initializeVote).each(initializeFollowThread) + #initializeFollowDiscussion(discussion) TODO move this somewhere else - $local(".new-post-tags").tagsInput - autocomplete_url: Discussion.urlFor('tags_autocomplete') - autocomplete: - remoteDataType: 'json' - interactive: true - defaultText: "Tag your post" - height: "30px" - width: "90%" - removeWithBackspace: true + $local(".new-post-tags").tagsInput Discussion.tagsInputOptions() bindDiscussionEvents: (discussion) -> + $discussion = $(discussion) $discussionNonContent = $discussion.children(".discussion-non-content") $local = Discussion.generateLocal($discussionNonContent)#(selector) -> $discussionNonContent.find(selector) @@ -141,32 +113,25 @@ Discussion = @Discussion newPostForm.show() $(elem).hide() else - view = { - discussion_id: id - } + view = { discussion_id: id } $discussionNonContent.append Mustache.render Discussion.newPostTemplate, view newPostBody = $(discussion).find(".new-post-body") if newPostBody.length Markdown.makeWmdEditor newPostBody, "-new-post-body-#{$(discussion).attr('_id')}", Discussion.urlFor('upload') - $local(".new-post-tags").tagsInput - autocomplete_url: Discussion.urlFor('tags_autocomplete') - autocomplete: - remoteDataType: 'json' - interactive: true - defaultText: "Tag your post: press enter after each tag" - height: "30px" - width: "100%" - removeWithBackspace: true + + $local(".new-post-tags").tagsInput Discussion.tagsInputOptions() + $local(".discussion-submit-post").click -> handleSubmitNewPost(this) $local(".discussion-cancel-post").click -> handleCancelNewPost(this) + $(elem).hide() handleAjaxSearch = (elem) -> + handle $elem = $(elem) - $discussionModule = $elem.parents(".discussion-module") - $discussion = $discussionModule.find(".discussion") + $discussion = $elem.parents(".discussion") Discussion.safeAjax $elem: $elem url: $elem.attr("action") @@ -174,10 +139,10 @@ Discussion = @Discussion text: $local(".search-input").val() type: "GET" success: (data, textStatus) -> - $discussion.replaceWith(data) - $discussion = $discussionModule.find(".discussion") - Discussion.initializeDiscussion($discussion) - Discussion.bindDiscussionEvents($discussion) + $data = $(data) + $discussion.replaceWith($data) + Discussion.initializeDiscussion($data) + Discussion.bindDiscussionEvents($data) dataType: 'html' handleAjaxSort = (elem) -> @@ -195,25 +160,26 @@ Discussion = @Discussion Discussion.bindDiscussionEvents($discussion) dataType: 'html' - $local(".search-wrapper-forum > .discussion-search-form").submit (event) -> - event.preventDefault() - text = $local(".search-input").val() - isSearchWithinBoard = $local(".discussion-search-within-board").is(":checked") - handleSearch(text, isSearchWithinBoard) + Discussion.bindLocalEvents $local, - $local(".discussion-new-post").click -> - handleNewPost(this) + "submit .search-wrapper-forum>.discussion-search-form": (event) -> + event.preventDefault() + text = $local(".search-input").val() + isSearchWithinBoard = $local(".discussion-search-within-board").is(":checked") + handleSearch(text, isSearchWithinBoard) - $local(".discussion-search-link").click -> - handleAjaxSearch(this) + "submit .search-wrapper-inline>.discussion-search-form": (event) -> + event.preventDefault() + handleAjaxSearch(this) - $local(".search-wrapper-inline > .discussion-search-form").submit (e)-> - e.preventDefault() - handleAjaxSearch(this) + "click .discussion-new-post": -> + handleNewPost(this) - $local(".discussion-inline-sort-link").click -> - handleAjaxSort(this) + "click .discussion-search-link": -> + handleAjaxSearch(this) + "click .discussion-inline-sort-link": -> + handleAjaxSort(this) $discussion.find(".thread").each (index, thread) -> Discussion.initializeContent(thread) diff --git a/lms/static/coffee/src/discussion/main.coffee b/lms/static/coffee/src/discussion/main.coffee index 274afe20ea..6187763532 100644 --- a/lms/static/coffee/src/discussion/main.coffee +++ b/lms/static/coffee/src/discussion/main.coffee @@ -1,6 +1,7 @@ $ -> Discussion = window.Discussion + console.log "here" if $('#accordion').length active = $('#accordion ul:has(li.active)').index('#accordion ul') $('#accordion').bind('accordionchange', @log).accordion diff --git a/lms/static/coffee/src/discussion/utils.coffee b/lms/static/coffee/src/discussion/utils.coffee index 9988839f12..e7b95941d5 100644 --- a/lms/static/coffee/src/discussion/utils.coffee +++ b/lms/static/coffee/src/discussion/utils.coffee @@ -9,23 +9,23 @@ Discussion = @Discussion (selector) -> $(elem).find(selector) generateDiscussionLink: (cls, txt, handler) -> - $("").addClass("discussion-link"). - attr("href", "javascript:void(0)"). - addClass(cls).html(txt). - click(-> handler(this)) - + $("").addClass("discussion-link") + .attr("href", "javascript:void(0)") + .addClass(cls).html(txt) + .click -> handler(this) + urlFor: (name, param, param1) -> { - watch_commentable : "/courses/#{$$course_id}/discussion/#{param}/watch" - unwatch_commentable : "/courses/#{$$course_id}/discussion/#{param}/unwatch" + follow_discussion : "/courses/#{$$course_id}/discussion/#{param}/follow" + unfollow_discussion : "/courses/#{$$course_id}/discussion/#{param}/unfollow" create_thread : "/courses/#{$$course_id}/discussion/#{param}/threads/create" update_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/update" create_comment : "/courses/#{$$course_id}/discussion/threads/#{param}/reply" delete_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/delete" upvote_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/upvote" downvote_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/downvote" - watch_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/watch" - unwatch_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/unwatch" + follow_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/follow" + unfollow_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/unfollow" update_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/update" endorse_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/endorse" create_sub_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/reply" @@ -50,3 +50,35 @@ Discussion = @Discussion handleAnchorAndReload: (response) -> #window.location = window.location.pathname + "#" + response['id'] window.location.reload() + + bindLocalEvents: ($local, eventsHandler) -> + for eventSelector, handler of eventsHandler + [event, selector] = eventSelector.split(' ') + $local(selector)[event] handler + + tagsInputOptions: -> + autocomplete_url: Discussion.urlFor('tags_autocomplete') + autocomplete: + remoteDataType: 'json' + interactive: true + defaultText: "Tag your post: press enter after each tag" + height: "30px" + width: "100%" + removeWithBackspace: true + + isSubscribed: (id, type) -> + if type == "thread" + id in $$user_info.subscribed_thread_ids + else if type == "commentable" or type == "discussion" + id in $$user_info.subscribed_commentable_ids + else + id in $$user_info.subscribed_user_ids + + formErrorHandler: (errorsField, success) -> + (response, textStatus, xhr) -> + if response.errors? and response.errors.length > 0 + errorsField.empty() + for error in response.errors + errorsField.append($("
  • ").addClass("new-post-form-error").html(error)) + else + success(response, textStatus, xhr) diff --git a/lms/templates/discussion/_sort.html b/lms/templates/discussion/_sort.html index 3ddb97074c..8400a53f24 100644 --- a/lms/templates/discussion/_sort.html +++ b/lms/templates/discussion/_sort.html @@ -3,16 +3,16 @@ <%def name="link_to_sort(key, title)"> % if key == sort_key: % if sort_order.lower() == 'desc': - ${_link_to_sort(key, 'asc', title + ' [v]')} + ${_link_to_sort(key, 'asc', title + ' [v]', 'sorted')} % else: - ${_link_to_sort(key, 'desc', title + ' [^]')} + ${_link_to_sort(key, 'desc', title + ' [^]', 'sorted')} % endif % else: ${_link_to_sort(key, 'desc', title)} % endif -<%def name="_link_to_sort(key, order, title)"> +<%def name="_link_to_sort(key, order, title, cls='')"> <% def merge(dic1, dic2): return dict(dic1.items() + dic2.items()) @@ -21,9 +21,9 @@ return base_url + '?' + urlencode(merge(query_params, {'page': 1, 'sort_key': key, 'sort_order': order})) %> % if discussion_type == 'inline': - ${title} + ${title} % else: - ${title} + ${title} % endif