From 151dbda2421f0628efbd48fae0bd95a84aff9721 Mon Sep 17 00:00:00 2001 From: Ibrahim Awwal Date: Tue, 18 Sep 2012 04:06:17 -0700 Subject: [PATCH 01/10] Fix followed state not showing up on initial forum load in sidebar. --- lms/templates/discussion/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/templates/discussion/index.html b/lms/templates/discussion/index.html index 8f04470506..e30ee4a2db 100644 --- a/lms/templates/discussion/index.html +++ b/lms/templates/discussion/index.html @@ -21,7 +21,7 @@ <%include file="_new_post.html" /> -
+
From a444166d98d367fe8b2705fbcc2b583adcb92fdf Mon Sep 17 00:00:00 2001 From: Ibrahim Awwal Date: Tue, 18 Sep 2012 04:08:47 -0700 Subject: [PATCH 02/10] Add a followed threads entry to the topic dropdown in the discussion sidebar. Also fixed up some random bugs with content_info, and changed pagination code a little. Needs comments service update for the subscribed threads API call. --- .../django_comment_client/forum/urls.py | 1 + .../django_comment_client/forum/views.py | 62 ++++++++++++++----- lms/djangoapps/django_comment_client/utils.py | 23 +++++-- lms/lib/comment_client/user.py | 14 ++++- .../coffee/src/discussion/discussion.coffee | 22 ++++--- lms/static/coffee/src/discussion/utils.coffee | 1 + .../views/discussion_thread_list_view.coffee | 33 +++++++--- .../discussion_thread_profile_view.coffee | 1 - .../views/discussion_user_profile_view.coffee | 3 - .../discussion/_filter_dropdown.html | 7 ++- 10 files changed, 126 insertions(+), 41 deletions(-) diff --git a/lms/djangoapps/django_comment_client/forum/urls.py b/lms/djangoapps/django_comment_client/forum/urls.py index 974a0b2c7b..696e981328 100644 --- a/lms/djangoapps/django_comment_client/forum/urls.py +++ b/lms/djangoapps/django_comment_client/forum/urls.py @@ -2,6 +2,7 @@ from django.conf.urls.defaults import url, patterns import django_comment_client.forum.views urlpatterns = patterns('django_comment_client.forum.views', + url(r'users/(?P\w+)/following$', 'following_threads', name='following_threads'), url(r'users/(?P\w+)$', 'user_profile', name='user_profile'), url(r'(?P[\w\-]+)/threads/(?P\w+)$', 'single_thread', name='single_thread'), url(r'(?P[\w\-]+)/inline$', 'inline_discussion', name='inline_discussion'), diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index 0abcbf5fbb..9fe812ec3a 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -83,10 +83,7 @@ def inline_discussion(request, course_id, discussion_id): # checking for errors on request. Check and fix as needed. raise Http404 - 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), {}) + annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) allow_anonymous = course.metadata.get("allow_anonymous", True) allow_anonymous_to_peers = course.metadata.get("allow_anonymous_to_peers", False) @@ -118,10 +115,8 @@ def forum_form_discussion(request, course_id): 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 = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) - annotated_content_info = reduce(merge_dict, map(infogetter, threads), {}) for thread in threads: courseware_context = get_courseware_context(thread, course) if courseware_context: @@ -218,10 +213,7 @@ def single_thread(request, course_id, discussion_id, thread_id): 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), {}) + annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) context = { 'discussion_id': discussion_id, @@ -244,7 +236,7 @@ def single_thread(request, course_id, discussion_id, thread_id): @login_required def user_profile(request, course_id, user_id): - + #TODO: Allow sorting? course = get_course_with_access(request.user, course_id, 'load') try: profiled_user = cc.User(id=user_id, course_id=course_id) @@ -260,16 +252,13 @@ def user_profile(request, course_id, user_id): if request.is_ajax(): return utils.JsonResponse({ - 'html': content, 'discussion_data': map(utils.safe_content, threads), }) else: 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 = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) - annotated_content_info = reduce(merge_dict, map(infogetter, threads), {}) context = { 'course': course, 'user': request.user, @@ -284,3 +273,44 @@ def user_profile(request, course_id, user_id): return render_to_response('discussion/user_profile.html', context) except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError) as err: raise Http404 + + +def following_threads(request, course_id, user_id): + course = get_course_with_access(request.user, course_id, 'load') + try: + profiled_user = cc.User(id=user_id, course_id=course_id) + + query_params = { + 'page': request.GET.get('page', 1), + 'per_page': THREADS_PER_PAGE, # more than threads_per_page to show more activities + 'sort_key': 'date',#TODO: Allow custom sorting? + 'sort_order': 'desc', + } + print user_id + threads, page, num_pages = profiled_user.subscribed_threads(query_params) + query_params['page'] = page + query_params['num_pages'] = num_pages + user_info = cc.User.from_django_user(request.user).to_dict() + + annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) + if request.is_ajax(): + return utils.JsonResponse({ + 'annotated_content_info': annotated_content_info, + 'discussion_data': map(utils.safe_content, threads), + }) + else: + + context = { + 'course': course, + '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), + # 'content': content, + } + + return render_to_response('discussion/user_profile.html', context) + except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError) as err: + raise Http404 diff --git a/lms/djangoapps/django_comment_client/utils.py b/lms/djangoapps/django_comment_client/utils.py index 305f3d0929..1dcb5522f7 100644 --- a/lms/djangoapps/django_comment_client/utils.py +++ b/lms/djangoapps/django_comment_client/utils.py @@ -259,7 +259,11 @@ def get_ability(course_id, content, user): 'can_vote': check_permissions_by_view(user, course_id, content, "vote_for_thread" if content['type'] == 'thread' else "vote_for_comment"), } +#TODO: RENAME def get_annotated_content_info(course_id, content, user, user_info): + """ + Get metadata for an individual content (thread or comment) + """ voted = '' if content['id'] in user_info['upvoted_ids']: voted = 'up' @@ -271,7 +275,11 @@ def get_annotated_content_info(course_id, content, user, user_info): 'ability': get_ability(course_id, content, user), } +#TODO: RENAME def get_annotated_content_infos(course_id, thread, user, user_info): + """ + Get metadata for a thread and its children + """ infos = {} def annotate(content): infos[str(content['id'])] = get_annotated_content_info(course_id, content, user, user_info) @@ -280,6 +288,13 @@ def get_annotated_content_infos(course_id, thread, user, user_info): annotate(thread) return infos +def get_metadata_for_threads(course_id, threads, user, user_info): + def infogetter(thread): + return get_annotated_content_infos(course_id, thread, user, user_info) + + metadata = reduce(merge_dict, map(infogetter, threads), {}) + return metadata + # put this method in utils.py to avoid circular import dependency between helpers and mustache_helpers def url_for_tags(course_id, tags): return reverse('django_comment_client.forum.views.forum_form_discussion', args=[course_id]) + '?' + urllib.urlencode({'tags': tags}) @@ -304,7 +319,7 @@ def extend_content(content): roles = dict(('name', role.name.lower()) for role in user.roles.filter(course_id=content['course_id'])) except user.DoesNotExist: logging.error('User ID {0} in comment content {1} but not in our DB.'.format(content.get('user_id'), content.get('id'))) - + content_info = { 'displayed_title': content.get('highlighted_title') or content.get('title', ''), 'displayed_body': content.get('highlighted_body') or content.get('body', ''), @@ -323,9 +338,9 @@ def get_courseware_context(content, course): location = id_map[id]["location"].url() title = id_map[id]["title"] (course_id, chapter, section, position) = path_to_location(modulestore(), course.id, location) - url = reverse('courseware_position', kwargs={"course_id":course_id, - "chapter":chapter, - "section":section, + url = reverse('courseware_position', kwargs={"course_id":course_id, + "chapter":chapter, + "section":section, "position":position}) content_info = {"courseware_url": url, "courseware_title": title} return content_info diff --git a/lms/lib/comment_client/user.py b/lms/lib/comment_client/user.py index 9813e9a199..546b27556c 100644 --- a/lms/lib/comment_client/user.py +++ b/lms/lib/comment_client/user.py @@ -8,7 +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', - 'subscribed_course_ids', 'threads_count', 'comments_count', + 'subscribed_course_ids', 'threads_count', 'comments_count', 'default_sort_key' ] @@ -65,6 +65,15 @@ class User(models.Model): response = perform_request('get', url, params) return response.get('collection', []), response.get('page', 1), response.get('num_pages', 1) + def subscribed_threads(self, query_params={}): + if not self.course_id: + raise CommentClientError("Must provide course_id when retrieving subscribed threads for the user") + url = _url_for_user_subscribed_threads(self.id) + params = {'course_id': self.course_id} + params = merge_dict(params, query_params) + response = perform_request('get', url, params) + return response.get('collection', []), response.get('page', 1), response.get('num_pages', 1) + def _retrieve(self, *args, **kwargs): url = self.url(action='get', params=self.attributes) retrieve_params = self.default_retrieve_params @@ -84,3 +93,6 @@ def _url_for_subscription(user_id): def _url_for_user_active_threads(user_id): return "{prefix}/users/{user_id}/active_threads".format(prefix=settings.PREFIX, user_id=user_id) + +def _url_for_user_subscribed_threads(user_id): + return "{prefix}/users/{user_id}/subscribed_threads".format(prefix=settings.PREFIX, user_id=user_id) diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index eadf65c6ae..52e8d466e2 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -25,17 +25,22 @@ if Backbone? @add model model - retrieveAnotherPage: (search_text="", commentable_ids="", sort_key="")-> + retrieveAnotherPage: (mode, options={}, sort_options={})-> # TODO: I really feel that this belongs in DiscussionThreadListView @current_page += 1 - url = DiscussionUtil.urlFor 'threads' data = { page: @current_page } - if search_text - data['text'] = search_text - if sort_key - data['sort_key'] = sort_key - if commentable_ids - data['commentable_ids'] = commentable_ids + switch mode + when 'search' + url = DiscussionUtil.urlFor 'search' + data['text'] = options.search_text + if options.commentable_ids + data['commentable_ids'] = options.commentable_ids + when 'all' + url = DiscussionUtil.urlFor 'threads' + when 'following' + url = DiscussionUtil.urlFor 'following_threads', options.user_id + data['sort_key'] = sort_options.sort_key || 'date' + data['sort_order'] = sort_options.sort_order || 'desc' DiscussionUtil.safeAjax $elem: @$el url: url @@ -45,6 +50,7 @@ if Backbone? models = @models new_threads = [new Thread(data) for data in response.discussion_data][0] new_collection = _.union(models, new_threads) + Content.loadContentInfos(response.annotated_content_info) @reset new_collection sortByDate: (thread) -> diff --git a/lms/static/coffee/src/discussion/utils.coffee b/lms/static/coffee/src/discussion/utils.coffee index e76ea346ff..e1e5649edc 100644 --- a/lms/static/coffee/src/discussion/utils.coffee +++ b/lms/static/coffee/src/discussion/utils.coffee @@ -66,6 +66,7 @@ class @DiscussionUtil 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}" + following_threads : "/courses/#{$$course_id}/discussion/forum/users/#{param}/following" threads : "/courses/#{$$course_id}/discussion/forum" }[name] diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee index d998763f50..98579b7a74 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee @@ -31,6 +31,7 @@ if Backbone? @boardName @template = _.template($("#thread-list-template").html()) @current_search = "" + @mode = 'all' reloadDisplayedCollection: (thread) => thread_id = thread.get('id') @@ -122,7 +123,14 @@ if Backbone? event.preventDefault() @$(".more-pages").html('
') @$(".more-pages").addClass("loading") - @collection.retrieveAnotherPage(@current_search, @discussionIds, @sortBy) + options = {} + switch @mode + when 'search' + options.search_text = @current_search + options.commentable_ids = @discussionIds + when 'following' + options.user_id = window.user.id + @collection.retrieveAnotherPage(@mode, options, {sort_key: @sortBy}) renderThread: (thread) => content = $(_.template($("#thread-list-item-template").html())(thread.toJSON())) @@ -146,7 +154,7 @@ if Backbone? threadSelected: (e) => thread_id = $(e.target).closest("a").data("id") @setActiveThread(thread_id) - @trigger("thread:selected", thread_id) + @trigger("thread:selected", thread_id) # This triggers a callback in the DiscussionRouter which calls the line above... false threadRemoved: (thread_id) => @@ -243,10 +251,14 @@ if Backbone? else @setTopic(event) # just sets the title for the dropdown item = $(event.target).closest('li') - if item.find("span.board-name").data("discussion_id") == "#all" + discussionId = item.find("span.board-name").data("discussion_id") + if discussionId == "#all" @discussionIds = "" @$(".post-search-field").val("") @retrieveAllThreads() + else if discussionId == "#following" + @retrieveFollowing(event) + # Retrieve following else discussionIds = _.map item.find(".board-name[data-discussion_id]"), (board) -> $(board).data("discussion_id").id @retrieveDiscussions(discussionIds) @@ -260,7 +272,7 @@ if Backbone? @collection.current_page = response.page @collection.pages = response.num_pages @collection.reset(response.discussion_data) - Content.loadContentInfos(response.content_info) + Content.loadContentInfos(response.annotated_content_info) @displayedCollection.reset(@collection.models) if callback? callback() @@ -276,7 +288,7 @@ if Backbone? @collection.current_page = response.page @collection.pages = response.num_pages @collection.reset(response.discussion_data) - Content.loadContentInfos(response.content_info) + Content.loadContentInfos(response.annotated_content_info) @displayedCollection.reset(@collection.models) retrieveAllThreads: () -> @@ -288,7 +300,7 @@ if Backbone? @collection.current_page = response.page @collection.pages = response.num_pages @collection.reset(response.discussion_data) - Content.loadContentInfos(response.content_info) + Content.loadContentInfos(response.annotated_content_info) @displayedCollection.reset(@collection.models) sortThreads: (event) -> @@ -315,6 +327,7 @@ if Backbone? @searchFor(text) searchFor: (text, callback, value) -> + @mode = 'search' @current_search = text url = DiscussionUtil.urlFor("search") DiscussionUtil.safeAjax @@ -332,7 +345,7 @@ if Backbone? if textStatus == 'success' # TODO: Augment existing collection? @collection.reset(response.discussion_data) - Content.loadContentInfos(response.content_info) + Content.loadContentInfos(response.annotated_content_info) @collection.current_page = response.page @collection.pages = response.num_pages # TODO: Perhaps reload user info so that votes can be updated. @@ -370,3 +383,9 @@ if Backbone? scrollTarget = Math.min(scrollTop - itemFromTop, scrollTop) scrollTarget = Math.max(scrollTop - itemFromTop - $(".browse-topic-drop-menu").height() + $(items[index]).height(), scrollTarget) $(".browse-topic-drop-menu").scrollTop(scrollTarget) + + retrieveFollowing: (event)=> + @mode = 'following' + @collection.reset() + @collection.current_page = 0 + @loadMorePages(event) diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee index fa7a8a86b7..d31a402a99 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee @@ -84,7 +84,6 @@ if Backbone? toggleFollowing: (event) -> $elem = $(event.target) url = null - console.log "follow" if not @model.get('subscribed') @model.follow() url = @model.urlFor("follow") diff --git a/lms/static/coffee/src/discussion/views/discussion_user_profile_view.coffee b/lms/static/coffee/src/discussion/views/discussion_user_profile_view.coffee index ed5645e5e5..c3415fecf9 100644 --- a/lms/static/coffee/src/discussion/views/discussion_user_profile_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_user_profile_view.coffee @@ -6,15 +6,12 @@ if Backbone? @renderThreads @$el, @collection renderThreads: ($elem, threads) => #Content.loadContentInfos(response.annotated_content_info) - console.log threads @discussion = new Discussion() @discussion.reset(threads, {silent: false}) $discussion = $(Mustache.render $("script#_user_profile").html(), {'threads':threads}) - console.log $discussion $elem.append($discussion) @threadviews = @discussion.map (thread) -> new DiscussionThreadProfileView el: @$("article#thread_#{thread.id}"), model: thread - console.log @threadviews _.each @threadviews, (dtv) -> dtv.render() addThread: (thread, collection, options) => diff --git a/lms/templates/discussion/_filter_dropdown.html b/lms/templates/discussion/_filter_dropdown.html index d24c89a734..484ee05101 100644 --- a/lms/templates/discussion/_filter_dropdown.html +++ b/lms/templates/discussion/_filter_dropdown.html @@ -27,12 +27,17 @@ -
From ef8ca23deeae70041af36c16eb8bc85fb9bb89cb Mon Sep 17 00:00:00 2001 From: Ibrahim Awwal Date: Tue, 18 Sep 2012 04:35:32 -0700 Subject: [PATCH 03/10] Fix pagination and sorting for followed threads. Also fixed sorting so that it always retrieves a new set of threads from the server for the initial set. --- .../django_comment_client/forum/views.py | 12 +++++++++--- .../coffee/src/discussion/discussion.coffee | 1 + .../views/discussion_thread_list_view.coffee | 16 ++++++++++------ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index 9fe812ec3a..48bf97b8c7 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -249,15 +249,19 @@ def user_profile(request, course_id, user_id): threads, page, num_pages = profiled_user.active_threads(query_params) query_params['page'] = page query_params['num_pages'] = num_pages + user_info = cc.User.from_django_user(request.user).to_dict() + + annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) if request.is_ajax(): return utils.JsonResponse({ '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), }) else: - user_info = cc.User.from_django_user(request.user).to_dict() - annotated_content_info = utils.get_metadata_for_threads(course_id, threads, request.user, user_info) context = { 'course': course, @@ -286,7 +290,7 @@ def following_threads(request, course_id, user_id): 'sort_key': 'date',#TODO: Allow custom sorting? 'sort_order': 'desc', } - print user_id + threads, page, num_pages = profiled_user.subscribed_threads(query_params) query_params['page'] = page query_params['num_pages'] = num_pages @@ -297,6 +301,8 @@ def following_threads(request, course_id, user_id): return utils.JsonResponse({ 'annotated_content_info': annotated_content_info, 'discussion_data': map(utils.safe_content, threads), + 'page': query_params['page'], + 'num_pages': query_params['num_pages'], }) else: diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index 52e8d466e2..fd3cd6a0b6 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -52,6 +52,7 @@ if Backbone? new_collection = _.union(models, new_threads) Content.loadContentInfos(response.annotated_content_info) @reset new_collection + @pages = response.num_pages sortByDate: (thread) -> thread.get("created_at") diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee index 98579b7a74..03ed3a595c 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee @@ -307,12 +307,13 @@ if Backbone? @$(".sort-bar a").removeClass("active") $(event.target).addClass("active") @sortBy = $(event.target).data("sort") - if @sortBy == "date" - @displayedCollection.comparator = @displayedCollection.sortByDateRecentFirst - else if @sortBy == "votes" - @displayedCollection.comparator = @displayedCollection.sortByVotes - else if @sortBy == "comments" - @displayedCollection.comparator = @displayedCollection.sortByComments + @collection.reset() + @collection.current_page = 0 + @loadMorePages(event) + @displayedCollection.comparator = switch @sortBy + when 'date' then @displayedCollection.sortByDateRecentFirst + when 'votes' then @displayedCollection.sortByVotes + when 'comments' then @displayedCollection.sortByComments @displayedCollection.sort() performSearch: (event) -> @@ -330,6 +331,9 @@ if Backbone? @mode = 'search' @current_search = text url = DiscussionUtil.urlFor("search") + #TODO: This might be better done by setting discussion.current_page=0 and calling discussion.loadMorePages + # Mainly because this currently does not reset any pagination variables which could cause problems. + # This doesn't use pagination either. DiscussionUtil.safeAjax $elem: @$(".post-search-field") data: { text: text } From 53a1611eee9b1d1a9c5592c26ac74d15ce8be252 Mon Sep 17 00:00:00 2001 From: Ibrahim Awwal Date: Tue, 18 Sep 2012 14:50:14 -0700 Subject: [PATCH 04/10] Change following=>followed. --- lms/djangoapps/django_comment_client/forum/urls.py | 2 +- lms/djangoapps/django_comment_client/forum/views.py | 2 +- lms/static/coffee/src/discussion/discussion.coffee | 5 ++--- lms/static/coffee/src/discussion/utils.coffee | 2 +- .../discussion/views/discussion_thread_list_view.coffee | 8 ++++---- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lms/djangoapps/django_comment_client/forum/urls.py b/lms/djangoapps/django_comment_client/forum/urls.py index 696e981328..526ae3e582 100644 --- a/lms/djangoapps/django_comment_client/forum/urls.py +++ b/lms/djangoapps/django_comment_client/forum/urls.py @@ -2,7 +2,7 @@ from django.conf.urls.defaults import url, patterns import django_comment_client.forum.views urlpatterns = patterns('django_comment_client.forum.views', - url(r'users/(?P\w+)/following$', 'following_threads', name='following_threads'), + url(r'users/(?P\w+)/followed$', 'followed_threads', name='followed_threads'), url(r'users/(?P\w+)$', 'user_profile', name='user_profile'), url(r'(?P[\w\-]+)/threads/(?P\w+)$', 'single_thread', name='single_thread'), url(r'(?P[\w\-]+)/inline$', 'inline_discussion', name='inline_discussion'), diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index 48bf97b8c7..21c58ad398 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -279,7 +279,7 @@ def user_profile(request, course_id, user_id): raise Http404 -def following_threads(request, course_id, user_id): +def followed_threads(request, course_id, user_id): course = get_course_with_access(request.user, course_id, 'load') try: profiled_user = cc.User(id=user_id, course_id=course_id) diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index fd3cd6a0b6..2b4339015f 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -26,7 +26,6 @@ if Backbone? model retrieveAnotherPage: (mode, options={}, sort_options={})-> - # TODO: I really feel that this belongs in DiscussionThreadListView @current_page += 1 data = { page: @current_page } switch mode @@ -37,8 +36,8 @@ if Backbone? data['commentable_ids'] = options.commentable_ids when 'all' url = DiscussionUtil.urlFor 'threads' - when 'following' - url = DiscussionUtil.urlFor 'following_threads', options.user_id + when 'followed' + url = DiscussionUtil.urlFor 'followed_threads', options.user_id data['sort_key'] = sort_options.sort_key || 'date' data['sort_order'] = sort_options.sort_order || 'desc' DiscussionUtil.safeAjax diff --git a/lms/static/coffee/src/discussion/utils.coffee b/lms/static/coffee/src/discussion/utils.coffee index e1e5649edc..769aaa0bb1 100644 --- a/lms/static/coffee/src/discussion/utils.coffee +++ b/lms/static/coffee/src/discussion/utils.coffee @@ -66,7 +66,7 @@ class @DiscussionUtil 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}" - following_threads : "/courses/#{$$course_id}/discussion/forum/users/#{param}/following" + followed_threads : "/courses/#{$$course_id}/discussion/forum/users/#{param}/followed" threads : "/courses/#{$$course_id}/discussion/forum" }[name] diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee index 03ed3a595c..7a6bac1d91 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee @@ -128,7 +128,7 @@ if Backbone? when 'search' options.search_text = @current_search options.commentable_ids = @discussionIds - when 'following' + when 'followed' options.user_id = window.user.id @collection.retrieveAnotherPage(@mode, options, {sort_key: @sortBy}) @@ -257,7 +257,7 @@ if Backbone? @$(".post-search-field").val("") @retrieveAllThreads() else if discussionId == "#following" - @retrieveFollowing(event) + @retrieveFollowed(event) # Retrieve following else discussionIds = _.map item.find(".board-name[data-discussion_id]"), (board) -> $(board).data("discussion_id").id @@ -388,8 +388,8 @@ if Backbone? scrollTarget = Math.max(scrollTop - itemFromTop - $(".browse-topic-drop-menu").height() + $(items[index]).height(), scrollTarget) $(".browse-topic-drop-menu").scrollTop(scrollTarget) - retrieveFollowing: (event)=> - @mode = 'following' + retrieveFollowed: (event)=> + @mode = 'followed' @collection.reset() @collection.current_page = 0 @loadMorePages(event) From 6315fe3fe6aa86a251874e05a017766225886bcd Mon Sep 17 00:00:00 2001 From: Ibrahim Awwal Date: Tue, 18 Sep 2012 14:50:38 -0700 Subject: [PATCH 05/10] Allow sorting followed threads (works same as sorting anything else). --- lms/djangoapps/django_comment_client/forum/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index 21c58ad398..a883ab92e0 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -287,8 +287,8 @@ def followed_threads(request, course_id, user_id): query_params = { 'page': request.GET.get('page', 1), 'per_page': THREADS_PER_PAGE, # more than threads_per_page to show more activities - 'sort_key': 'date',#TODO: Allow custom sorting? - 'sort_order': 'desc', + 'sort_key': request.GET.get('sort_key', 'date'), + 'sort_order': request.GET.get('sort_order', 'desc'), } threads, page, num_pages = profiled_user.subscribed_threads(query_params) From 0966e8bdef403501c335b5c11d1f6cb4d401c145 Mon Sep 17 00:00:00 2001 From: Ibrahim Awwal Date: Tue, 18 Sep 2012 15:06:05 -0700 Subject: [PATCH 06/10] Keep thread sorting order stable when sorting by comments or votes by using created_at time as a tiebreaker. This is a stopgap though; the service should handle this. But it looks silly with things shuffling around right now. --- lms/static/coffee/src/discussion/content.coffee | 6 ++++++ lms/static/coffee/src/discussion/discussion.coffee | 10 ++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee index d489782571..4e612dfc40 100644 --- a/lms/static/coffee/src/discussion/content.coffee +++ b/lms/static/coffee/src/discussion/content.coffee @@ -129,6 +129,12 @@ if Backbone? json_attributes = _.clone(@attributes) _.extend(json_attributes, { title: @display_title(), body: @display_body() }) + created_at_date: -> + new Date(@get("created_at")) + + created_at_time: -> + new Date(@get("created_at")).getTime() + class @Comment extends @Content urlMappers: 'reply': -> DiscussionUtil.urlFor('create_sub_comment', @id) diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index 2b4339015f..af74afc6e9 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -66,9 +66,15 @@ if Backbone? sortByVotes: (thread1, thread2) -> thread1_count = parseInt(thread1.get("votes")['up_count']) thread2_count = parseInt(thread2.get("votes")['up_count']) - thread2_count - thread1_count + if thread2_count != thread1_count + thread2_count - thread1_count + else + thread2.created_at_time() - thread1.created_at_time() sortByComments: (thread1, thread2) -> thread1_count = parseInt(thread1.get("comments_count")) thread2_count = parseInt(thread2.get("comments_count")) - thread2_count - thread1_count + if thread2_count != thread1_count + thread2_count - thread1_count + else + thread2.created_at_time() - thread1.created_at_time() From 74ba9319e301bd60ab0e86e12ac699fbf029cb9e Mon Sep 17 00:00:00 2001 From: Ibrahim Awwal Date: Tue, 18 Sep 2012 16:30:37 -0700 Subject: [PATCH 07/10] Fix clicking on commentables in the dropdown. --- .../coffee/src/discussion/discussion.coffee | 5 ++- .../views/discussion_thread_list_view.coffee | 43 +++++++++++-------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index af74afc6e9..b5ddc91e33 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -32,8 +32,9 @@ if Backbone? when 'search' url = DiscussionUtil.urlFor 'search' data['text'] = options.search_text - if options.commentable_ids - data['commentable_ids'] = options.commentable_ids + when 'commentables' + url = DiscussionUtil.urlFor 'search' + data['commentable_ids'] = options.commentable_ids when 'all' url = DiscussionUtil.urlFor 'threads' when 'followed' diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee index 7a6bac1d91..97e7658caf 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee @@ -120,16 +120,18 @@ if Backbone? @$(".post-list").append("
  • Load more
  • ") loadMorePages: (event) -> - event.preventDefault() + if event + event.preventDefault() @$(".more-pages").html('
    ') @$(".more-pages").addClass("loading") options = {} switch @mode when 'search' options.search_text = @current_search - options.commentable_ids = @discussionIds when 'followed' options.user_id = window.user.id + when 'commentables' + options.commentable_ids = @discussionIds @collection.retrieveAnotherPage(@mode, options, {sort_key: @sortBy}) renderThread: (thread) => @@ -273,25 +275,30 @@ if Backbone? @collection.pages = response.num_pages @collection.reset(response.discussion_data) Content.loadContentInfos(response.annotated_content_info) - @displayedCollection.reset(@collection.models) + @displayedCollection.reset(@collection.models)# Don't think this is necessary because it's called on collection.reset if callback? callback() retrieveDiscussions: (discussion_ids) -> @discussionIds = discussion_ids.join(',') - url = DiscussionUtil.urlFor("search") - DiscussionUtil.safeAjax - data: { 'commentable_ids': @discussionIds } - url: url - type: "GET" - success: (response, textStatus) => - @collection.current_page = response.page - @collection.pages = response.num_pages - @collection.reset(response.discussion_data) - Content.loadContentInfos(response.annotated_content_info) - @displayedCollection.reset(@collection.models) + @mode = 'commentables' + @collection.current_page = 0 + @collection.reset() + @loadMorePages() +# url = DiscussionUtil.urlFor("search") +# DiscussionUtil.safeAjax +# data: { 'commentable_ids': @discussionIds } +# url: url +# type: "GET" +# success: (response, textStatus) => +# @collection.current_page = response.page +# @collection.pages = response.num_pages +# @collection.reset(response.discussion_data) +# Content.loadContentInfos(response.annotated_content_info) +# @displayedCollection.reset(@collection.models) retrieveAllThreads: () -> + @mode='all' url = DiscussionUtil.urlFor("threads") DiscussionUtil.safeAjax url: url @@ -301,7 +308,7 @@ if Backbone? @collection.pages = response.num_pages @collection.reset(response.discussion_data) Content.loadContentInfos(response.annotated_content_info) - @displayedCollection.reset(@collection.models) + @displayedCollection.reset(@collection.models) #Don't think this is necessary sortThreads: (event) -> @$(".sort-bar a").removeClass("active") @@ -309,12 +316,12 @@ if Backbone? @sortBy = $(event.target).data("sort") @collection.reset() @collection.current_page = 0 - @loadMorePages(event) @displayedCollection.comparator = switch @sortBy when 'date' then @displayedCollection.sortByDateRecentFirst when 'votes' then @displayedCollection.sortByVotes when 'comments' then @displayedCollection.sortByComments - @displayedCollection.sort() + @loadMorePages(event) + #@displayedCollection.sort() # This should be called automatically and calling this makes the loading indicator go away performSearch: (event) -> if event.which == 13 @@ -355,7 +362,7 @@ if Backbone? # TODO: Perhaps reload user info so that votes can be updated. # In the future we might not load all of a user's votes at once # so this would probably be necessary anyway - @displayedCollection.reset(@collection.models) + @displayedCollection.reset(@collection.models) # Don't think this is necessary clearSearch: (callback, value) -> @$(".post-search-field").val("") From e809fb3b6497aef78933683dfca5821e5ca448e8 Mon Sep 17 00:00:00 2001 From: Ibrahim Awwal Date: Wed, 19 Sep 2012 07:02:24 -0700 Subject: [PATCH 08/10] Use loadMorePages to load All as well. --- .../coffee/src/discussion/discussion.coffee | 1 + .../views/discussion_thread_list_view.coffee | 17 +++++------------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index b5ddc91e33..068cde3de4 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -53,6 +53,7 @@ if Backbone? Content.loadContentInfos(response.annotated_content_info) @reset new_collection @pages = response.num_pages + @current_page = response.page sortByDate: (thread) -> thread.get("created_at") diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee index 97e7658caf..9239620754 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee @@ -298,17 +298,10 @@ if Backbone? # @displayedCollection.reset(@collection.models) retrieveAllThreads: () -> - @mode='all' - url = DiscussionUtil.urlFor("threads") - DiscussionUtil.safeAjax - url: url - type: "GET" - success: (response, textStatus) => - @collection.current_page = response.page - @collection.pages = response.num_pages - @collection.reset(response.discussion_data) - Content.loadContentInfos(response.annotated_content_info) - @displayedCollection.reset(@collection.models) #Don't think this is necessary + @mode = 'all' + @collection.current_page = 0 + @collection.reset() + @loadMorePages() sortThreads: (event) -> @$(".sort-bar a").removeClass("active") @@ -321,7 +314,7 @@ if Backbone? when 'votes' then @displayedCollection.sortByVotes when 'comments' then @displayedCollection.sortByComments @loadMorePages(event) - #@displayedCollection.sort() # This should be called automatically and calling this makes the loading indicator go away + #@displayedCollection.sort() # This should be called automatically and calling manually makes the loading indicator go away performSearch: (event) -> if event.which == 13 From 91de5633b5bef1b04ea88ec418a72d15a58da15e Mon Sep 17 00:00:00 2001 From: Ibrahim Awwal Date: Thu, 20 Sep 2012 04:33:47 -0700 Subject: [PATCH 09/10] DjDT redirect intercept is annoying, especially for tests, so disable it --- lms/envs/dev.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lms/envs/dev.py b/lms/envs/dev.py index d833a5a2fc..9114f099d4 100644 --- a/lms/envs/dev.py +++ b/lms/envs/dev.py @@ -156,6 +156,9 @@ DEBUG_TOOLBAR_PANELS = ( # 'debug_toolbar.panels.profiling.ProfilingDebugPanel', ) +DEBUG_TOOLBAR_CONFIG = { + 'INTERCEPT_REDIRECTS': False +} ############################ FILE UPLOADS (ASKBOT) ############################# DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' MEDIA_ROOT = ENV_ROOT / "uploads" From 57d5ba07c0e71a9c8ca860f20c1372acec8fd1ad Mon Sep 17 00:00:00 2001 From: Ibrahim Awwal Date: Fri, 21 Sep 2012 04:33:16 -0700 Subject: [PATCH 10/10] Remove commented out block and pull some stuff into a function to load the first page of results for a thread query. --- .../views/discussion_thread_list_view.coffee | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee index 529efa2620..e97ca364da 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee @@ -262,7 +262,6 @@ if Backbone? @retrieveAllThreads() else if discussionId == "#following" @retrieveFollowed(event) - # Retrieve following else discussionIds = _.map item.find(".board-name[data-discussion_id]"), (board) -> $(board).data("discussion_id").id @retrieveDiscussions(discussionIds) @@ -284,39 +283,27 @@ if Backbone? retrieveDiscussions: (discussion_ids) -> @discussionIds = discussion_ids.join(',') @mode = 'commentables' - @collection.current_page = 0 - @collection.reset() - @loadMorePages() -# url = DiscussionUtil.urlFor("search") -# DiscussionUtil.safeAjax -# data: { 'commentable_ids': @discussionIds } -# url: url -# type: "GET" -# success: (response, textStatus) => -# @collection.current_page = response.page -# @collection.pages = response.num_pages -# @collection.reset(response.discussion_data) -# Content.loadContentInfos(response.annotated_content_info) -# @displayedCollection.reset(@collection.models) + @retrieveFirstPage() retrieveAllThreads: () -> @mode = 'all' + @retrieveFirstPage() + + retrieveFirstPage: (event)-> @collection.current_page = 0 @collection.reset() - @loadMorePages() + @loadMorePages(event) sortThreads: (event) -> @$(".sort-bar a").removeClass("active") $(event.target).addClass("active") @sortBy = $(event.target).data("sort") - @collection.reset() - @collection.current_page = 0 + @displayedCollection.comparator = switch @sortBy when 'date' then @displayedCollection.sortByDateRecentFirst when 'votes' then @displayedCollection.sortByVotes when 'comments' then @displayedCollection.sortByComments - @loadMorePages(event) - #@displayedCollection.sort() # This should be called automatically and calling manually makes the loading indicator go away + @retrieveFirstPage(event) performSearch: (event) -> if event.which == 13 @@ -392,6 +379,4 @@ if Backbone? retrieveFollowed: (event)=> @mode = 'followed' - @collection.reset() - @collection.current_page = 0 - @loadMorePages(event) + @retrieveFirstPage(event)