diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index 4607cc10c5..af896f6f80 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -13,7 +13,7 @@ from courseware.access import has_access from urllib import urlencode from operator import methodcaller from django_comment_client.permissions import check_permissions_by_view -from django_comment_client.utils import merge_dict, extract, strip_none, strip_blank +from django_comment_client.utils import merge_dict, extract, strip_none, strip_blank, get_courseware_context import json import django_comment_client.utils as utils @@ -71,6 +71,15 @@ def render_discussion(request, course_id, threads, *args, **kwargs): annotated_content_info = reduce(merge_dict, map(infogetter, threads), {}) + if discussion_type != 'inline': + course = get_course_with_access(request.user, course_id, 'load') + + for thread in threads: + courseware_context = get_courseware_context(thread, course) + if courseware_context: + thread['courseware_location'] = courseware_context['courseware_location'] + thread['courseware_title'] = courseware_context['courseware_title'] + context = { 'threads': threads, 'discussion_id': discussion_id, @@ -231,15 +240,23 @@ def single_thread(request, course_id, discussion_id, thread_id): category_map = utils.get_discussion_category_map(course) threads, query_params = get_threads(request, course_id) - recent_active_threads = cc.search_recent_active_threads( - course_id, - recursive=False, - query_params={'follower_id': request.user.id}, - ) + course = get_course_with_access(request.user, course_id, 'load') - trending_tags = cc.search_trending_tags( - course_id, - ) + for thread in threads: + courseware_context = get_courseware_context(thread, course) + if courseware_context: + thread['courseware_location'] = courseware_context['courseware_location'] + thread['courseware_title'] = courseware_context['courseware_title'] + + #recent_active_threads = cc.search_recent_active_threads( + # course_id, + # recursive=False, + # query_params={'follower_id': request.user.id}, + #) + + #trending_tags = cc.search_trending_tags( + # course_id, + #) user_info = cc.User.from_django_user(request.user).to_dict() escapedict = {'"': '"'} @@ -256,8 +273,8 @@ def single_thread(request, course_id, discussion_id, thread_id): 'user_info': saxutils.escape(json.dumps(user_info),escapedict), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course': course, - 'recent_active_threads': recent_active_threads, - 'trending_tags': trending_tags, + #'recent_active_threads': recent_active_threads, + #'trending_tags': trending_tags, 'course_id': course.id, 'thread_id': thread_id, 'threads': saxutils.escape(json.dumps(threads), escapedict), diff --git a/lms/djangoapps/django_comment_client/utils.py b/lms/djangoapps/django_comment_client/utils.py index 373611380d..b0a570caad 100644 --- a/lms/djangoapps/django_comment_client/utils.py +++ b/lms/djangoapps/django_comment_client/utils.py @@ -124,10 +124,7 @@ def initialize_discussion_info(course): category_map['entries'][topic] = {"id": entry["id"], "sort_key": entry.get("sort_key", topic)} - sort_map_entries(category_map) - #for level in category_map["subcategories"].values(): - # sort_map_entries(level) _DISCUSSIONINFO = {} @@ -135,12 +132,15 @@ def initialize_discussion_info(course): _DISCUSSIONINFO['category_map'] = category_map - # TODO delete me when you've used me - #_DISCUSSIONINFO['categorized']['General'] = [{ - # 'title': 'General', - # 'discussion_id': url_course_id, - # 'category': 'General', - #}] +def get_courseware_context(content, course): + id_map = get_discussion_id_map(course) + id = content['commentable_id'] + content_info = None + if id in id_map: + location = id_map[id]["location"].url() + title = id_map[id]["title"] + content_info = { "courseware_location": location, "courseware_title": title} + return content_info class JsonResponse(HttpResponse): def __init__(self, data=None): diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee index 3e175d3634..25f6ba5156 100644 --- a/lms/static/coffee/src/discussion/content.coffee +++ b/lms/static/coffee/src/discussion/content.coffee @@ -449,6 +449,9 @@ if Backbone? @set('thread', @) super() + comment: -> + @set("comments_count", parseInt(@get("comments_count")) + 1) + follow: -> @set('subscribed', true) @trigger "thread:follow" diff --git a/lms/static/coffee/src/discussion/discussion_router.coffee b/lms/static/coffee/src/discussion/discussion_router.coffee index 3a183e4d3b..8d5dd27733 100644 --- a/lms/static/coffee/src/discussion/discussion_router.coffee +++ b/lms/static/coffee/src/discussion/discussion_router.coffee @@ -15,8 +15,9 @@ class @DiscussionRouter extends Backbone.Router @newPostView.on "thread:created", @navigateToThread allThreads: -> + @nav.updateSidebar() # TODO: Do something reasonable here - $(".discussion-column").html("No thread selected.") + # $(".discussion-column").html($('#blank-slate-template').html()) setActiveThread: => if @thread @@ -30,6 +31,8 @@ class @DiscussionRouter extends Backbone.Router @main = new DiscussionThreadView(el: $(".discussion-column"), model: @thread) @main.render() + @main.on "thread:responses:rendered", => + @nav.updateSidebar() navigateToThread: (thread_id) => thread = @discussion.get(thread_id) 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 fd7f7d0017..741d3fb385 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 @@ -10,6 +10,46 @@ class @DiscussionThreadListView extends Backbone.View initialize: -> @displayedCollection = new Discussion(@collection.models) + @collection.on "change", @reloadDisplayedCollection + @sidebar_padding = 10 + @sidebar_header_height = 87 + + reloadDisplayedCollection: => + @displayedCollection.reset(@collection.models) + @updateSidebar() + + updateSidebar: => + + scrollTop = $(window).scrollTop(); + windowHeight = $(window).height(); + + discussionBody = $(".discussion-article") + discussionsBodyTop = discussionBody.offset().top; + discussionsBodyBottom = discussionsBodyTop + discussionBody.outerHeight(); + + sidebar = $(".sidebar") + if scrollTop > discussionsBodyTop - @sidebar_padding + sidebar.addClass('fixed'); + sidebar.css('top', @sidebar_padding); + else + sidebar.removeClass('fixed'); + sidebar.css('top', '0'); + + sidebarWidth = .32 * $(".discussion-body").width() - 10; + sidebar.css('width', sidebarWidth + 'px'); + + sidebarHeight = windowHeight - Math.max(discussionsBodyTop - scrollTop, @sidebar_padding) + + topOffset = scrollTop + windowHeight + discussionBottomOffset = discussionsBodyBottom + @sidebar_padding + amount = Math.max(topOffset - discussionBottomOffset, 0) + + sidebarHeight = sidebarHeight - @sidebar_padding - amount + sidebar.css 'height', Math.min(Math.max(sidebarHeight, 400), discussionBody.outerHeight()) + + postListWrapper = @$('.post-list-wrapper') + postListWrapper.css('height', (sidebarHeight - @sidebar_header_height - 4) + 'px'); + # Because we want the behavior that when the body is clicked the menu is # closed, we need to ignore clicks in the search field and stop propagation. @@ -20,6 +60,10 @@ class @DiscussionThreadListView extends Backbone.View render: -> @timer = 0 @$el.html(@template()) + + $(window).bind "scroll", @updateSidebar + $(window).bind "resize", @updateSidebar + @displayedCollection.on "reset", @renderThreads @displayedCollection.on "thread:remove", @renderThreads @renderThreads() @@ -60,9 +104,11 @@ class @DiscussionThreadListView extends Backbone.View if @$(".browse").hasClass('is-dropped') @$(".browse-topic-drop-menu-wrapper").show() $('body').bind 'click', @toggleTopicDrop + $('body').bind 'keyup', @setActiveItem else @$(".browse-topic-drop-menu-wrapper").hide() $('body').unbind 'click', @toggleTopicDrop + $('body').unbind 'keyup', @setActiveItem setTopic: (event) -> item = $(event.target).closest('a') @@ -102,10 +148,6 @@ class @DiscussionThreadListView extends Backbone.View @displayedCollection.comparator = @displayedCollection.sortByComments @displayedCollection.sort() - delay: (callback, ms) => - clearTimeout(@timer) - @timer = setTimeout(callback, ms) - performSearch: (event) -> if event.which == 13 event.preventDefault() @@ -120,3 +162,35 @@ class @DiscussionThreadListView extends Backbone.View if textStatus == 'success' @collection.reset(response.discussion_data) @displayedCollection.reset(@collection.models) + + + setActiveItem: (event) -> + if event.which == 13 + console.log($(".browse-topic-drop-menu-wrapper .focused")) + $(".browse-topic-drop-menu-wrapper .focused").click() + return + if event.which != 40 && event.which != 38 + return + event.preventDefault() + + items = $(".browse-topic-drop-menu-wrapper a").not(".hidden") + totalItems = items.length + index = $(".browse-topic-drop-menu-wrapper .focused").parent().index() + # index = parseInt($(".browse-topic-drop-menu-wrapper").attr("data-focused")) || 0 + + + if event.which == 40 + index = index + 1 + else if event.which == 38 + index = index - 1 + if index == totalItems + index = 0 + + console.log(index) + + $(".browse-topic-drop-menu-wrapper .focused").removeClass("focused") + $(".browse-topic-drop-menu-wrapper li").eq(index).find('a').addClass("focused") + # $(items[index]).addClass("focused") + $(".browse-topic-drop-menu-wrapper").attr("data-focused", index) + + diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee index 52a663153d..d1fb404b7b 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee @@ -10,12 +10,18 @@ class @DiscussionThreadView extends DiscussionContentView template: _.template($("#thread-template").html()) + initLocal: -> + @$local = @$el.children(".discussion-article").children(".local") + @$delegateElement = @$local + initialize: -> super() @model.on "change", @updateModelDetails render: -> @$el.html(@template(@model.toJSON())) + @initLocal() + @delegateEvents() @renderDogear() @renderVoted() @renderAttrs() @@ -23,6 +29,8 @@ class @DiscussionThreadView extends DiscussionContentView Markdown.makeWmdEditor @$(".reply-body"), "", DiscussionUtil.urlFor("upload"), (text) -> DiscussionUtil.postMathJaxProcessor(text) @convertMath() @renderResponses() + @highlight @$(".post-body") + @highlight @$("h1") @ renderDogear: -> @@ -48,19 +56,20 @@ class @DiscussionThreadView extends DiscussionContentView DiscussionUtil.safeAjax url: "/courses/#{$$course_id}/discussion/forum/#{@model.get('commentable_id')}/threads/#{@model.id}" success: (data, textStatus, xhr) => - @$(".loading").remove() + @$el.find(".loading").remove() Content.loadContentInfos(data['annotated_content_info']) comments = new Comments(data['content']['children']) comments.each @renderResponse + @trigger "thread:responses:rendered" renderResponse: (response) => view = new ThreadResponseView(model: response) view.on "comment:add", @addComment view.render() - @$(".responses").append(view.el) + @$el.find(".responses").append(view.el) addComment: => - + @model.comment() toggleVote: (event) -> event.preventDefault() @@ -72,6 +81,7 @@ class @DiscussionThreadView extends DiscussionContentView toggleFollowing: (event) -> $elem = $(event.target) url = null + console.log "follow" if not @model.get('subscribed') @model.follow() url = @model.urlFor("follow") @@ -129,6 +139,7 @@ class @DiscussionThreadView extends DiscussionContentView if not confirm "Are you sure to delete thread \"#{@model.get('title')}\"?" return @model.remove() + @$el.empty() $elem = $(event.target) DiscussionUtil.safeAjax $elem: $elem @@ -162,3 +173,6 @@ class @DiscussionThreadView extends DiscussionContentView type: "POST" success: (response, textStatus) => @model.set('endorsed', not endorsed) + + highlight: (el) -> + el.html(el.html().replace(/<mark>/g, "").replace(/<\/mark>/g, "")) diff --git a/lms/static/coffee/src/discussion/views/thread_list_item_view.coffee b/lms/static/coffee/src/discussion/views/thread_list_item_view.coffee index a437dbd593..83f7ebff28 100644 --- a/lms/static/coffee/src/discussion/views/thread_list_item_view.coffee +++ b/lms/static/coffee/src/discussion/views/thread_list_item_view.coffee @@ -1,8 +1,10 @@ class @ThreadListItemView extends Backbone.View tagName: "li" template: _.template($("#thread-list-item-template").html()) + events: "click a": "threadSelected" + initialize: -> @model.on "change", @render @model.on "thread:remove", @threadRemoved @@ -10,10 +12,12 @@ class @ThreadListItemView extends Backbone.View @model.on "thread:unfollow", @unfollow @model.on "comment:add", @addComment @model.on "comment:remove", @removeComment + render: => @$el.html(@template(@model.toJSON())) if window.user.following(@model) @follow() + @highlight @$(".title") @ threadSelected: (event) -> @@ -34,3 +38,9 @@ class @ThreadListItemView extends Backbone.View removeComment: (comment) => @$(".comments-count").html(@model.get('comments_count')) + + #addComment: => + # @$(".comments-count").html(parseInt(@$(".comments-count").html()) + 1) + + highlight: (el) -> + el.html(el.html().replace(/<mark>/g, "").replace(/<\/mark>/g, "")) diff --git a/lms/static/js/discussions-temp.js b/lms/static/js/discussions-temp.js index 71a5c16e67..ffc953f7e2 100644 --- a/lms/static/js/discussions-temp.js +++ b/lms/static/js/discussions-temp.js @@ -67,13 +67,13 @@ $(document).ready(function() { $body.delegate('.browse-topic-drop-search-input, .form-topic-drop-search-input', 'keyup', filterDrop); - $(window).bind('resize', updateSidebar); - $(window).bind('scroll', updateSidebar); - $('.discussion-column').bind("input", function (e) { - console.log("resized"); - updateSidebar(); - }) - updateSidebar(); +// $(window).bind('resize', updateSidebar); +// $(window).bind('scroll', updateSidebar); +// $('.discussion-column').bind("input", function (e) { +// console.log("resized"); +// updateSidebar(); +// }) +// updateSidebar(); }); function filterDrop(e) { @@ -132,7 +132,7 @@ function filterDrop(e) { return; } - $items.hide(); + $items.addClass('hidden'); $items.each(function(i) { var thisText = $(this).children().not('.unread').text(); $(this).parents('ul').siblings('a').not('.unread').each(function(i) { @@ -147,7 +147,7 @@ function filterDrop(e) { } if(test) { - $(this).show(); + $(this).removeClass('hidden'); // show children $(this).parent().find('a').show(); @@ -225,6 +225,7 @@ function showTopicDrop(e) { $topicDrop.show(); $browse.unbind('click', showTopicDrop); + $body.bind('keyup', setActiveDropItem); $browse.bind('click', hideTopicDrop); setTimeout(function() { $body.bind('click', hideTopicDrop); diff --git a/lms/static/sass/_discussion.scss b/lms/static/sass/_discussion.scss index a00639792e..2070abb8ce 100644 --- a/lms/static/sass/_discussion.scss +++ b/lms/static/sass/_discussion.scss @@ -732,8 +732,14 @@ body.discussion { line-height: 22px; color: #fff; @include clearfix; + @include transition(none); - &:hover { + &.hidden { + display: none; + } + + &:hover, + &.focused { background-color: #636363; } @@ -1022,6 +1028,16 @@ body.discussion { + .bottom-post-status { + padding: 30px; + font-size: 20px; + font-weight: 700; + color: #ccc; + text-align: center; + } + + + .discussion-column { float: right; @@ -1038,9 +1054,17 @@ body.discussion { } } + .blank-slate h1 { + margin-top: 195px; + text-align: center; + color: #ccc; + } + + .blank-slate, .discussion-article { position: relative; padding: 40px; + min-height: 468px; h1 { margin-bottom: 10px; @@ -1056,6 +1080,12 @@ body.discussion { color: #888; } + .post-context{ + margin-top: 20px; + font-size: 12px; + color: #888; + } + p + p { margin-top: 20px; } diff --git a/lms/templates/discussion/_new_post.html b/lms/templates/discussion/_new_post.html index dde21c5a95..8081829ec1 100644 --- a/lms/templates/discussion/_new_post.html +++ b/lms/templates/discussion/_new_post.html @@ -29,7 +29,7 @@
- Homework / Week 1 + All