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.
This commit is contained in:
@@ -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<user_id>\w+)/following$', 'following_threads', name='following_threads'),
|
||||
url(r'users/(?P<user_id>\w+)$', 'user_profile', name='user_profile'),
|
||||
url(r'(?P<discussion_id>[\w\-]+)/threads/(?P<thread_id>\w+)$', 'single_thread', name='single_thread'),
|
||||
url(r'(?P<discussion_id>[\w\-]+)/inline$', 'inline_discussion', name='inline_discussion'),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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('<div class="loading-animation"></div>')
|
||||
@$(".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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -27,12 +27,17 @@
|
||||
<div class="browse-topic-drop-search">
|
||||
<input type="text" class="browse-topic-drop-search-input" placeholder="filter topics">
|
||||
</div>
|
||||
<ul class="browse-topic-drop-menu">
|
||||
<ul class="browse-topic-drop-menu">
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="board-name" data-discussion_id='#all'>All</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="board-name" data-discussion_id='#following'>Following</span>
|
||||
</a>
|
||||
</li>
|
||||
${render_dropdown(category_map)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user