diff --git a/common/lib/xmodule/xmodule/discussion_module.py b/common/lib/xmodule/xmodule/discussion_module.py index ffc0597b19..24ba4ad206 100644 --- a/common/lib/xmodule/xmodule/discussion_module.py +++ b/common/lib/xmodule/xmodule/discussion_module.py @@ -19,6 +19,7 @@ class DiscussionModule(XModule): 'discussion_id': self.discussion_id, 'search_bar': '', 'user_info': comment_client.get_user_info(self.user_id, raw=True), + 'course_id': self.course_id, } return self.system.render_template('discussion/inline.html', context) @@ -30,8 +31,9 @@ class DiscussionModule(XModule): xml_data = etree.fromstring(definition['data']) self.discussion_id = xml_data.attrib['id'] self.title = xml_data.attrib['for'] - self.category = xml_data.attrib['category'] + self.discussion_category = xml_data.attrib['discussion_category'] self.user_id = instance_state['user_id'] + self.course_id = instance_state['course_id'] class DiscussionDescriptor(RawDescriptor): module_class = DiscussionModule diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 5f9fc8da82..ad85bfbf4f 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -122,6 +122,13 @@ def get_module(user, request, location, student_module_cache, position=None): ''' descriptor = modulestore().get_item(location) + user_id = user.id + + import re + course_id = re.search(r'^/courses/(?P[^/]+/[^/]+/[^/]+)/', request.path) + if course_id: + course_id = course_id.group('course_id') + instance_module = student_module_cache.lookup(descriptor.category, descriptor.location.url()) shared_state_key = getattr(descriptor, 'shared_state_key', None) @@ -130,8 +137,12 @@ def get_module(user, request, location, student_module_cache, position=None): else: shared_module = None - instance_state = instance_module.state if instance_module is not None else None - instance_state = json.dumps(dict(json.loads(instance_state).items() + [("user_id", user.id)])) + instance_state = instance_module.state if instance_module is not None else {} + + instance_hash = json.loads(instance_state) if isinstance(instance_state, str) or isinstance(instance_state, unicode) \ + else instance_state + + instance_state = json.dumps(dict(instance_hash.items() + [("user_id", user.id), ("course_id", course_id)])) shared_state = shared_module.state if shared_module is not None else None # TODO (vshnayder): fix hardcoded urls (use reverse) diff --git a/lms/djangoapps/django_comment_client/base/views.py b/lms/djangoapps/django_comment_client/base/views.py index 518751162d..a148d80020 100644 --- a/lms/djangoapps/django_comment_client/base/views.py +++ b/lms/djangoapps/django_comment_client/base/views.py @@ -52,41 +52,41 @@ def extract(dic, keys): @login_required @require_POST -def create_thread(request, commentable_id): +def create_thread(request, course_id, commentable_id): attributes = extract(request.POST, ['body', 'title']) attributes['user_id'] = request.user.id - attributes['course_id'] = "1" # TODO either remove this or pass this parameter somehow + attributes['course_id'] = course_id response = comment_client.create_thread(commentable_id, attributes) return JsonResponse(response) @thread_author_only @login_required @require_POST -def update_thread(request, thread_id): +def update_thread(request, course_id, thread_id): attributes = extract(request.POST, ['body', 'title']) response = comment_client.update_thread(thread_id, attributes) return JsonResponse(response) @login_required @require_POST -def create_comment(request, thread_id): +def create_comment(request, course_id, thread_id): attributes = extract(request.POST, ['body']) attributes['user_id'] = request.user.id - attributes['course_id'] = "1" # TODO either remove this or pass this parameter somehow + attributes['course_id'] = course_id response = comment_client.create_comment(thread_id, attributes) return JsonResponse(response) @thread_author_only @login_required @require_POST -def delete_thread(request, thread_id): +def delete_thread(request, course_id, thread_id): response = comment_client.delete_thread(thread_id) return JsonResponse(response) @thread_author_only @login_required @require_POST -def update_comment(request, comment_id): +def update_comment(request, course_id, comment_id): attributes = extract(request.POST, ['body']) response = comment_client.update_comment(comment_id, attributes) return JsonResponse(response) @@ -94,14 +94,14 @@ def update_comment(request, comment_id): @instructor_only @login_required @require_POST -def endorse_comment(request, comment_id): +def endorse_comment(request, course_id, comment_id): attributes = extract(request.POST, ['endorsed']) response = comment_client.update_comment(comment_id, attributes) return JsonResponse(response) @login_required @require_POST -def create_sub_comment(request, comment_id): +def create_sub_comment(request, course_id, comment_id): attributes = extract(request.POST, ['body']) attributes['user_id'] = request.user.id attributes['course_id'] = "1" # TODO either remove this or pass this parameter somehow @@ -111,69 +111,69 @@ def create_sub_comment(request, comment_id): @comment_author_only @login_required @require_POST -def delete_comment(request, comment_id): +def delete_comment(request, course_id, comment_id): response = comment_client.delete_comment(comment_id) return JsonResponse(response) @login_required @require_POST -def vote_for_comment(request, comment_id, value): +def vote_for_comment(request, course_id, comment_id, value): user_id = request.user.id response = comment_client.vote_for_comment(comment_id, user_id, value) return JsonResponse(response) @login_required @require_POST -def vote_for_thread(request, thread_id, value): +def vote_for_thread(request, course_id, thread_id, value): user_id = request.user.id response = comment_client.vote_for_thread(thread_id, user_id, value) return JsonResponse(response) @login_required @require_POST -def watch_thread(request, thread_id): +def watch_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, commentable_id): +def watch_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, followed_user_id): +def follow(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, thread_id): +def unwatch_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, commentable_id): +def unwatch_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, followed_user_id): +def unfollow(request, course_id, followed_user_id): user_id = request.user.id response = comment_client.unfollow(user_id, followed_user_id) return JsonResponse(response) @login_required @require_GET -def search(request): +def search(request, course_id): text = request.GET.get('text', None) commentable_id = request.GET.get('commentable_id', None) response = comment_client.search(text, commentable_id) diff --git a/lms/djangoapps/django_comment_client/forum/urls.py b/lms/djangoapps/django_comment_client/forum/urls.py index 3a724ebee4..0fd4f0e320 100644 --- a/lms/djangoapps/django_comment_client/forum/urls.py +++ b/lms/djangoapps/django_comment_client/forum/urls.py @@ -4,5 +4,5 @@ import django_comment_client.forum.views urlpatterns = patterns('django_comment_client.forum.views', url(r'search$', 'search', name='search'), url(r'threads/(?P\w+)$', 'single_thread', name='single_thread'), - url(r'(?P[\w\.\-]+)/(?P\w+)$', 'forum_form_discussion', name='forum_form_discussion'), + url(r'(?P\w+)$', 'forum_form_discussion', name='forum_form_discussion'), ) diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index dfff24e5bf..c2a136d6cf 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -58,7 +58,7 @@ def get_categorized_discussion_info(request, user, course, course_name, url_cour return { 'title': module.title, 'discussion_id': module.discussion_id, - 'category': module.category, + 'category': module.discussion_category, } discussion_module_descriptors = map(_get_module_descriptor, @@ -90,27 +90,28 @@ def render_accordion(request, course, discussion_info, discussion_id): return render_to_string('discussion/accordion.html', context) -def render_discussion(request, threads, discussion_id=None, search_text=''): +def render_discussion(request, course_id, threads, discussion_id=None, search_text=''): context = { 'threads': threads, 'discussion_id': discussion_id, - 'search_bar': render_search_bar(request, discussion_id, text=search_text), + 'search_bar': render_search_bar(request, course_id, discussion_id, text=search_text), 'user_info': comment_client.get_user_info(request.user.id, raw=True), + 'course_id': course_id, } return render_to_string('discussion/inline.html', context) -def render_search_bar(request, discussion_id=None, text=''): +def render_search_bar(request, course_id, discussion_id=None, text=''): if not discussion_id: return '' context = { 'discussion_id': discussion_id, 'text': text, + 'course_id': course_id, } return render_to_string('discussion/search_bar.html', context) def forum_form_discussion(request, course_id, discussion_id): - course_id = course_id.replace('-', '/') course = check_course(course_id) _, course_name, _ = course_id.split('/') @@ -131,39 +132,46 @@ def forum_form_discussion(request, course_id, discussion_id): 'COURSE_TITLE': course.title, 'course': course, 'init': '', - 'content': render_discussion(request, threads, discussion_id, search_text), + 'content': render_discussion(request, course_id, threads, discussion_id, search_text), 'accordion': render_accordion(request, course, discussion_info, discussion_id), } return render_to_response('discussion/index.html', context) -def render_single_thread(request, thread_id): +def render_single_thread(request, course_id, thread_id): context = { 'thread': comment_client.get_thread(thread_id, recursive=True), 'user_info': comment_client.get_user_info(request.user.id, raw=True), + 'course_id': course_id, } return render_to_string('discussion/single_thread.html', context) -def single_thread(request, thread_id): +def single_thread(request, course_id, thread_id): + + course = check_course(course_id) context = { 'csrf': csrf(request)['csrf_token'], 'init': '', - 'content': render_single_thread(request, thread_id), + 'content': render_single_thread(request, course_id, thread_id), 'accordion': '', 'user_info': comment_client.get_user_info(request.user.id, raw=True), + 'course': course, } return render_to_response('discussion/index.html', context) -def search(request): +def search(request, course_id): + + course = check_course(course_id) text = request.GET.get('text', None) threads = comment_client.search(text) context = { 'csrf': csrf(request)['csrf_token'], 'init': '', - 'content': render_discussion(request, threads, search_text=text), + 'content': render_discussion(request, course_id, threads, search_text=text), 'accordion': '', + 'course': course, } return render_to_response('discussion/index.html', context) diff --git a/lms/static/coffee/src/discussion.coffee b/lms/static/coffee/src/discussion.coffee index fe0007a8f0..44aebb7241 100644 --- a/lms/static/coffee/src/discussion.coffee +++ b/lms/static/coffee/src/discussion.coffee @@ -26,23 +26,23 @@ Discussion = urlFor: (name, param) -> { - watch_commentable : "/discussions/#{param}/watch" - unwatch_commentable : "/discussions/#{param}/unwatch" - create_thread : "/discussions/#{param}/threads/create" - update_thread : "/discussions/threads/#{param}/update" - create_comment : "/discussions/threads/#{param}/reply" - delete_thread : "/discussions/threads/#{param}/delete" - upvote_thread : "/discussions/threads/#{param}/upvote" - downvote_thread : "/discussions/threads/#{param}/downvote" - watch_thread : "/discussions/threads/#{param}/watch" - unwatch_thread : "/discussions/threads/#{param}/unwatch" - update_comment : "/discussions/comments/#{param}/update" - endorse_comment : "/discussions/comments/#{param}/endorse" - create_sub_comment : "/discussions/comments/#{param}/reply" - delete_comment : "/discussions/comments/#{param}/delete" - upvote_comment : "/discussions/comments/#{param}/upvote" - downvote_comment : "/discussions/comments/#{param}/downvote" - search : "/discussions/forum/search" + watch_commentable : "/courses/#{$$course_id}/discussion/#{param}/watch" + unwatch_commentable : "/courses/#{$$course_id}/discussion/#{param}/unwatch" + 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" + 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" + delete_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/delete" + upvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/upvote" + downvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/downvote" + search : "/courses/#{$$course_id}/discussion/forum/search" }[name] handleAnchorAndReload: (response) -> @@ -54,11 +54,36 @@ Discussion = $content = $(content) $local = generateLocal($content.children(".discussion-content")) id = $content.attr("_id") - if id in user_info.upvoted_ids + if id in $$user_info.upvoted_ids $local(".discussion-vote-up").addClass("voted") - else if id in user_info.downvoted_ids + else if id in $$user_info.downvoted_ids $local(".discussion-vote-down").addClass("voted") + initializeWatchDiscussion = (discussion) -> + $discussion = $(discussion) + id = $discussion.attr("_id") + $local = 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 = generateDiscussionLink("discussion-unwatch-discussion", "Unwatch", handleUnwatchDiscussion) + $local(".discussion-title-wrapper").append(unwatchDiscussion) + else + watchDiscussion = generateDiscussionLink("discussion-watch-discussion", "Watch", handleWatchDiscussion) + $local(".discussion-title-wrapper").append(watchDiscussion) initializeWatchThreads = (index, thread) -> $thread = $(thread) @@ -67,7 +92,6 @@ Discussion = handleWatchThread = (elem) -> url = Discussion.urlFor('watch_thread', id) - console.log url $.post url, {}, (response, textStatus) -> if textStatus == "success" Discussion.handleAnchorAndReload(response) @@ -80,16 +104,17 @@ Discussion = Discussion.handleAnchorAndReload(response) , 'json' - if id in user_info.subscribed_thread_ids + if id in $$user_info.subscribed_thread_ids unwatchThread = generateDiscussionLink("discussion-unwatch-thread", "Unwatch", handleUnwatchThread) $local(".info").append(unwatchThread) else watchThread = generateDiscussionLink("discussion-watch-thread", "Watch", handleWatchThread) $local(".info").append(watchThread) - if user_info? + if $$user_info? $(discussion).find(".comment").each(initializeVote) $(discussion).find(".thread").each(initializeVote).each(initializeWatchThreads) + initializeWatchDiscussion(discussion) bindContentEvents: (content) -> diff --git a/lms/static/sass/_discussion.scss b/lms/static/sass/_discussion.scss index 724d7b15cf..cf669a10c2 100644 --- a/lms/static/sass/_discussion.scss +++ b/lms/static/sass/_discussion.scss @@ -37,6 +37,7 @@ $discussion_input_width: 60%; @include discussion-font; } } + .discussion-title { @include discussion-font; @include discussion-clickable; @@ -45,6 +46,22 @@ $discussion_input_width: 60%; margin-bottom: 20px; display: block; } + .discussion-title-wrapper { + .discussion-watch-discussion, .discussion-unwatch-discussion { + display: none; + @include discussion-font; + margin-left: 5px; + font-size: $comment_info_size; + } + .discussion-title { + display: inline-block; + } + &:hover { + .discussion-watch-discussion, .discussion-unwatch-discussion { + display: inline-block; + } + } + } .discussion-votes { margin-right: 8px; margin-top: 5px; diff --git a/lms/templates/course_navigation.html b/lms/templates/course_navigation.html index beaea5e2a7..4682fc423f 100644 --- a/lms/templates/course_navigation.html +++ b/lms/templates/course_navigation.html @@ -15,7 +15,7 @@ def url_class(url):
  • Course Info
  • % if user.is_authenticated():
  • Textbook
  • -
  • Discussion
  • +
  • Discussion
  • % endif
  • Wiki
  • % if user.is_authenticated(): diff --git a/lms/templates/discussion/accordion.html b/lms/templates/discussion/accordion.html index 99c72a8f96..c072705d55 100644 --- a/lms/templates/discussion/accordion.html +++ b/lms/templates/discussion/accordion.html @@ -1,23 +1,23 @@ <%! from django.core.urlresolvers import reverse %> <% -def url_for(board): - return reverse('django_comment_client.forum.views.forum_form_discussion', args=[course.id.replace('/', '-'), board['discussion_id']]) +def url_for(commentable): + return reverse('django_comment_client.forum.views.forum_form_discussion', args=[course.id, commentable['discussion_id']]) %> -<%def name="make_category(category, boards)"> +<%def name="make_category(category, commentables)">

    ${category}

    -% for category, boards in discussion_info.items(): - ${make_category(category, boards)} +% for category, commentables in discussion_info.items(): + ${make_category(category, commentables)} % endfor diff --git a/lms/templates/discussion/index.html b/lms/templates/discussion/index.html index 7b35863377..2beac5ddd9 100644 --- a/lms/templates/discussion/index.html +++ b/lms/templates/discussion/index.html @@ -10,7 +10,7 @@ <%block name="js_extra"> -##<%include file="../course_navigation.html" args="active_page='discussion'" /> +<%include file="../course_navigation.html" args="active_page='discussion'" />
    diff --git a/lms/templates/discussion/inline.html b/lms/templates/discussion/inline.html index d9d5bb8df1..779a08e6f6 100644 --- a/lms/templates/discussion/inline.html +++ b/lms/templates/discussion/inline.html @@ -1,8 +1,10 @@ <%namespace name="renderer" file="thread.html"/> -
    +
    - Discussion + ${search_bar}
    @@ -11,11 +13,11 @@
    % for thread in threads: - ${renderer.render_thread(thread, edit_thread=False, show_comments=False)} + ${renderer.render_thread(course_id, thread, edit_thread=False, show_comments=False)} % endfor
    - diff --git a/lms/templates/discussion/search_bar.html b/lms/templates/discussion/search_bar.html index dfc6ab5e2f..bf3ec6a02f 100644 --- a/lms/templates/discussion/search_bar.html +++ b/lms/templates/discussion/search_bar.html @@ -2,7 +2,7 @@ <% def url_for_search(): - return reverse('django_comment_client.forum.views.search') + return reverse('django_comment_client.forum.views.search', args=[course_id]) %>
    diff --git a/lms/templates/discussion/single_thread.html b/lms/templates/discussion/single_thread.html index 0545b2ba4a..3e320b68ca 100644 --- a/lms/templates/discussion/single_thread.html +++ b/lms/templates/discussion/single_thread.html @@ -2,10 +2,10 @@
    Discussion - ${renderer.render_thread(thread, edit_thread=True, show_comments=True)} + ${renderer.render_thread(course_id, thread, edit_thread=True, show_comments=True)}
    - diff --git a/lms/templates/discussion/thread.html b/lms/templates/discussion/thread.html index f76d3de8df..fbfa0f19c8 100644 --- a/lms/templates/discussion/thread.html +++ b/lms/templates/discussion/thread.html @@ -2,13 +2,13 @@ <%! from datehelper import time_ago_in_words %> <%! from dateutil.parser import parse %> -<%def name="render_thread(thread, edit_thread=False, show_comments=False)"> +<%def name="render_thread(course_id, thread, edit_thread=False, show_comments=False)"> <% if show_comments: url_for_thread = "" else: thread_id = thread['id'] - url_for_thread = reverse('django_comment_client.forum.views.single_thread', args=[thread_id]) + url_for_thread = reverse('django_comment_client.forum.views.single_thread', args=[course_id, thread_id]) %>
    diff --git a/lms/urls.py b/lms/urls.py index 73b440d331..796aea5bb4 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -135,6 +135,10 @@ if settings.COURSEWARE_ENABLED: 'courseware.views.profile', name="profile"), url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/profile/(?P[^/]*)/$', 'courseware.views.profile'), + + # discussion + url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/discussion/', + include('django_comment_client.urls')), ) # Multicourse wiki @@ -156,8 +160,6 @@ if settings.ASKBOT_ENABLED: # url(r'^robots.txt$', include('robots.urls')), ) -# discussion -urlpatterns += (url(r'^discussions/', include('django_comment_client.urls')), ) if settings.DEBUG: ## Jasmine