Commit 21438356 by Rocky Duan

Merge branch 'feature/tomg/new-discussions' of github.com:MITx/mitx into…

Merge branch 'feature/tomg/new-discussions' of github.com:MITx/mitx into feature/tomg/new-discussions

Conflicts:
	lms/static/coffee/src/discussion/views/discussion_thread_view.coffee
	lms/static/coffee/src/discussion/views/thread_list_item_view.coffee
parents 1bbb4111 1caef344
......@@ -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')
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,
)
#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),
......
......@@ -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):
......
......@@ -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"
......
......@@ -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)
......
......@@ -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)
......@@ -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(/&lt;mark&gt;/g, "<mark>").replace(/&lt;\/mark&gt;/g, "</mark>"))
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(/&lt;mark&gt;/g, "<mark>").replace(/&lt;\/mark&gt;/g, "</mark>"))
......@@ -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);
......
......@@ -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;
}
......
......@@ -29,7 +29,7 @@
<div class="left-column">
<label>Create new post in:</label>
<div class="form-topic-drop">
<a href="#" class="topic_dropdown_button">Homework / Week 1 <span class="drop-arrow"></span></a>
<a href="#" class="topic_dropdown_button">All<span class="drop-arrow"></span></a>
<div class="topic_menu_wrapper">
<div class="topic_menu_search">
<input type="text" class="form-topic-drop-search-input" placeholder="filter topics">
......
......@@ -2,7 +2,7 @@
<div class="browse-search">
<div class="browse is-open">
<a href="#" class="browse-topic-drop-icon"></a>
<a href="#" class="browse-topic-drop-btn"><span class="current-board">Homework / Week 1</span> <span class="drop-arrow">▾</span></a>
<a href="#" class="browse-topic-drop-btn"><span class="current-board">All</span> <span class="drop-arrow">▾</span></a>
</div>
<%include file="_filter_dropdown.html" />
<div class="search">
......
......@@ -13,6 +13,11 @@
<div class="post-body">
${'<%- body %>'}
</div>
${'<% if (obj.courseware_location) { %>'}
<div class="post-context">
(this post is about <a href="../../jump_to/${'<%- courseware_location %>'}">${'<%- courseware_title %>'}</a>)
</div>
${'<% } %>'}
<div class="post-status-closed" style="display: none">
This thread is closed.
</div>
......@@ -25,6 +30,9 @@
<ol class="responses">
<li class="loading"><div class="loading-animation"></div></li>
</ol>
<div class="post-status-closed bottom-post-status" style="display: none">
This thread is closed.
</div>
<form class="discussion-reply-new" data-id="${'<%- id %>'}">
<h4>Post a response:</h4>
<ul class="discussion-errors"></ul>
......
......@@ -15,7 +15,7 @@
<div class="sidebar-comments-count"><span>${profiled_user['comments_count'] | h}</span> ${pluralize('comment', profiled_user['comments_count']) | h}</div>
% if check_permissions_by_view(user, course.id, content=None, name='update_moderator_status'):
% if "Moderator" in role_names:
<a href="javascript:void(0)" class="sidebar-toggle-moderator-button sidebar-revoke-moderator-button">Revoke Moderator provileges</a>
<a href="javascript:void(0)" class="sidebar-toggle-moderator-button sidebar-revoke-moderator-button">Revoke Moderator rights</a>
% else:
<a href="javascript:void(0)" class="sidebar-toggle-moderator-button sidebar-promote-moderator-button">Promote to Moderator</a>
% endif
......
......@@ -26,7 +26,12 @@
<section class="discussion container" id="discussion-container" data-course-id="${course_id}" data-user-info="${user_info}" data-threads="${threads}">
<div class="discussion-body">
<div class="sidebar"></div>
<div class="discussion-column"></div>
<div class="discussion-column">
<div class="blank-slate">
<h1>${course.title} discussion forum</h1>
</div>
</div>
</div>
</section>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment