Commit 5a242bdb by Rocky Duan

show news & options when submit comment

parent 5ba465f5
......@@ -4,6 +4,8 @@ import logging
import urllib
import itertools
from functools import partial
from django.conf import settings
from django.core.context_processors import csrf
from django.core.urlresolvers import reverse
......@@ -20,6 +22,7 @@ from module_render import toc_for_course, get_module, get_section
from models import StudentModuleCache
from student.models import UserProfile
from multicourse import multicourse_settings
from django_comment_client.utils import get_discussion_title
from util.cache import cache, cache_if_anonymous
from student.models import UserTestGroup, CourseEnrollment
......@@ -27,6 +30,11 @@ from courseware import grades
from courseware.courses import check_course
from xmodule.modulestore.django import modulestore
import comment_client
log = logging.getLogger("mitx.courseware")
template_imports = {'urllib': urllib}
......@@ -259,7 +267,6 @@ def course_info(request, course_id):
return render_to_response('info.html', {'course': course})
@ensure_csrf_cookie
@cache_if_anonymous
def course_about(request, course_id):
......@@ -287,3 +294,24 @@ def university_profile(request, org_id):
template_file = "university_profile/{0}.html".format(org_id).lower()
return render_to_response(template_file, context)
def render_notifications(request, course, notifications):
context = {
'notifications': notifications,
'get_discussion_title': partial(get_discussion_title, request=request, course=course),
'course': course,
}
return render_to_string('notifications.html', context)
@login_required
def news(request, course_id):
course = check_course(course_id)
notifications = comment_client.get_notifications(request.user.id)
context = {
'course': course,
'content': render_notifications(request, course, notifications),
}
return render_to_response('news.html', context)
......@@ -71,8 +71,11 @@ def update_thread(request, course_id, thread_id):
@require_POST
def create_comment(request, course_id, thread_id):
attributes = extract(request.POST, ['body'])
attributes['user_id'] = request.user.id
if request.POST.get('anonymous', 'false').lower() == 'false':
attributes['user_id'] = request.user.id
attributes['course_id'] = course_id
attributes['auto_subscribe'] = bool(request.POST.get('autowatch', False))
print attributes
response = comment_client.create_comment(thread_id, attributes)
return JsonResponse(response)
......@@ -103,8 +106,10 @@ def endorse_comment(request, course_id, comment_id):
@require_POST
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
if request.POST.get('anonymous', 'false').lower() == 'false':
attributes['user_id'] = request.user.id
attributes['course_id'] = course_id
attributes['auto_subscribe'] = bool(request.POST.get('autowatch', False))
response = comment_client.create_sub_comment(comment_id, attributes)
return JsonResponse(response)
......
......@@ -6,79 +6,15 @@ from django.core.context_processors import csrf
from mitxmako.shortcuts import render_to_response, render_to_string
from courseware.courses import check_course
from courseware.models import StudentModuleCache
from courseware.module_render import get_module, get_section
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from importlib import import_module
from django.conf import settings
import comment_client
import dateutil
from dateutil.tz import tzlocal
from datehelper import time_ago_in_words
import operator
import itertools
import json
from django_comment_client.utils import get_categorized_discussion_info
_FULLMODULES = None
_DISCUSSIONINFO = None
def get_full_modules():
global _FULLMODULES
if not _FULLMODULES:
class_path = settings.MODULESTORE['default']['ENGINE']
module_path, _, class_name = class_path.rpartition('.')
class_ = getattr(import_module(module_path), class_name)
modulestore = class_(eager=True, **settings.MODULESTORE['default']['OPTIONS'])
_FULLMODULES = modulestore.modules
return _FULLMODULES
def get_categorized_discussion_info(request, user, course, course_name, url_course_id):
"""
return a dict of the form {category: modules}
"""
global _DISCUSSIONINFO
if not _DISCUSSIONINFO:
_is_course_discussion = lambda x: x[0].dict()['category'] == 'discussion' \
and x[0].dict()['course'] == course_name
_get_module_descriptor = operator.itemgetter(1)
def _get_module(module_descriptor):
print module_descriptor
module = get_module(user, request, module_descriptor.location, student_module_cache)[0]
return module
def _extract_info(module):
return {
'title': module.title,
'discussion_id': module.discussion_id,
'category': module.discussion_category,
}
discussion_module_descriptors = map(_get_module_descriptor,
filter(_is_course_discussion,
get_full_modules().items()))
student_module_cache = StudentModuleCache(user, course)
discussion_info = map(_extract_info, map(_get_module, discussion_module_descriptors))
_DISCUSSIONINFO = dict((category, list(l)) \
for category, l in itertools.groupby(discussion_info, operator.itemgetter('category')))
_DISCUSSIONINFO['General'] = [{
'title': 'General discussion',
'discussion_id': url_course_id,
'category': 'General',
}]
return _DISCUSSIONINFO
import json
def render_accordion(request, course, discussion_info, discussion_id):
context = {
......@@ -118,7 +54,7 @@ def forum_form_discussion(request, course_id, discussion_id):
url_course_id = course_id.replace('/', '_').replace('.', '_')
discussion_info = get_categorized_discussion_info(request, request.user, course, course_name, url_course_id)
discussion_info = get_categorized_discussion_info(request, course)#request.user, course, course_name, url_course_id)
search_text = request.GET.get('text', '')
......
from importlib import import_module
from courseware.models import StudentModuleCache
from courseware.module_render import get_module
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from django.conf import settings
import operator
import itertools
_FULLMODULES = None
_DISCUSSIONINFO = None
def get_full_modules():
global _FULLMODULES
if not _FULLMODULES:
class_path = settings.MODULESTORE['default']['ENGINE']
module_path, _, class_name = class_path.rpartition('.')
class_ = getattr(import_module(module_path), class_name)
modulestore = class_(eager=True, **settings.MODULESTORE['default']['OPTIONS'])
_FULLMODULES = modulestore.modules
return _FULLMODULES
def get_categorized_discussion_info(request, course):
"""
return a dict of the form {category: modules}
"""
global _DISCUSSIONINFO
if not _DISCUSSIONINFO:
initialize_discussion_info(request, course)
return _DISCUSSIONINFO['categorized']
def get_discussion_title(request, course, discussion_id):
global _DISCUSSIONINFO
if not _DISCUSSIONINFO:
initialize_discussion_info(request, course)
title = _DISCUSSIONINFO['by_id'].get(discussion_id, {}).get('title', '(no title)')
if title == '(no title)':
print "title shouldn't be none"
import pdb; pdb.set_trace()
return title
def initialize_discussion_info(request, course):
global _DISCUSSIONINFO
if _DISCUSSIONINFO:
return
course_id = course.id
_, course_name, _ = course_id.split('/')
user = request.user
url_course_id = course_id.replace('/', '_').replace('.', '_')
_is_course_discussion = lambda x: x[0].dict()['category'] == 'discussion' \
and x[0].dict()['course'] == course_name
_get_module_descriptor = operator.itemgetter(1)
def _get_module(module_descriptor):
print module_descriptor
module = get_module(user, request, module_descriptor.location, student_module_cache)[0]
return module
def _extract_info(module):
return {
'title': module.title,
'discussion_id': module.discussion_id,
'category': module.discussion_category,
}
def _pack_with_id(info):
return (info['discussion_id'], info)
discussion_module_descriptors = map(_get_module_descriptor,
filter(_is_course_discussion,
get_full_modules().items()))
student_module_cache = StudentModuleCache(user, course)
discussion_info = map(_extract_info, map(_get_module, discussion_module_descriptors))
_DISCUSSIONINFO = {}
_DISCUSSIONINFO['by_id'] = dict(map(_pack_with_id, discussion_info))
_DISCUSSIONINFO['categorized'] = dict((category, list(l)) \
for category, l in itertools.groupby(discussion_info, operator.itemgetter('category')))
_DISCUSSIONINFO['categorized']['General'] = [{
'title': 'General discussion',
'discussion_id': url_course_id,
'category': 'General',
}]
......@@ -122,6 +122,8 @@ Discussion =
$discussionContent = $content.children(".discussion-content")
$local = generateLocal($discussionContent)
id = $content.attr("_id")
discussionContentHoverIn = ->
status = $discussionContent.attr("status") || "normal"
if status == "normal"
......@@ -138,15 +140,32 @@ Discussion =
$discussionContent.hover(discussionContentHoverIn, discussionContentHoverOut)
handleReply = (elem) ->
editView = $local(".discussion-content-edit")
if editView.length
editView.show()
else
editView = $("<div>").addClass("discussion-content-edit")
editView.append($("<textarea>").addClass("comment-edit"))
textarea = $("<textarea>").addClass("comment-edit")
editView.append(textarea)
anonymousCheckbox = $("<input>").attr("type", "checkbox")
.addClass("discussion-post-anonymously")
.attr("id", "discussion-post-anonymously-#{id}")
anonymousLabel = $("<label>").attr("for", "discussion-post-anonymously-#{id}")
.html("post anonymously")
editView.append(anonymousCheckbox).append(anonymousLabel)
if $discussionContent.parent(".thread").attr("_id") not in $$user_info.subscribed_thread_ids
watchCheckbox = $("<input>").attr("type", "checkbox")
.addClass("discussion-auto-watch")
.attr("id", "discussion-auto-watch-#{id}")
.attr("checked", "")
watchLabel = $("<label>").attr("for", "discussion-auto-watch-#{id}")
.html("watch this thread")
editView.append(watchCheckbox).append(watchLabel)
$discussionContent.append(editView)
cancelReply = generateDiscussionLink("discussion-cancel-reply", "Cancel", handleCancelReply)
submitReply = generateDiscussionLink("discussion-submit-reply", "Submit", handleSubmitReply)
......@@ -166,20 +185,24 @@ Discussion =
handleSubmitReply = (elem) ->
if $content.hasClass("thread")
url = Discussion.urlFor('create_comment', $content.attr("_id"))
url = Discussion.urlFor('create_comment', id)
else if $content.hasClass("comment")
url = Discussion.urlFor('create_sub_comment', $content.attr("_id"))
url = Discussion.urlFor('create_sub_comment', id)
else
return
body = $local(".comment-edit").val()
$.post url, {body: body}, (response, textStatus) ->
anonymous = false || $local(".discussion-post-anonymously").is(":checked")
autowatch = false || $local(".discussion-auto-watch").is(":checked")
$.post url, {body: body, anonymous: anonymous, autowatch: autowatch}, (response, textStatus) ->
if textStatus == "success"
Discussion.handleAnchorAndReload(response)
, 'json'
handleVote = (elem, value) ->
contentType = if $content.hasClass("thread") then "thread" else "comment"
url = Discussion.urlFor("#{value}vote_#{contentType}", $content.attr("_id"))
url = Discussion.urlFor("#{value}vote_#{contentType}", id)
$.post url, {}, (response, textStatus) ->
if textStatus == "success"
Discussion.handleAnchorAndReload(response)
......
......@@ -155,12 +155,13 @@ $discussion_input_width: 60%;
margin-top: 10px;
overflow: hidden;
.discussion-content-edit {
margin-left: $comment_margin_left;
.comment-edit {
@include discussion-font;
width: $discussion_input_width !important;
margin-left: $comment_margin_left;
font-size: $comment_body_size;
margin-top: 10px;
display: block;
}
}
}
......
@mixin news-font {
font-family: "Comic Sans MS", cursive, sans-serif !important;
}
.notifications {
@include news-font;
padding-left: 20px;
padding-top: 20px;
padding-bottom: 20px;
.notification {
@include news-font;
margin-top: 15px;
margin-botton: 15px;
a {
@include news-font;
}
}
}
......@@ -28,3 +28,4 @@
@import 'multicourse/error-pages';
@import 'multicourse/help';
@import 'discussion';
@import 'news';
......@@ -16,6 +16,7 @@ def url_class(url):
% if user.is_authenticated():
<li class="book"><a href="${reverse('book', args=[course.id])}" class="${url_class('book')}">Textbook</a></li>
<li class="discussion"><a href="${reverse('django_comment_client.forum.views.forum_form_discussion', args=[course.id, course.id.replace('/', '_').replace('.', '_')])}" class="${url_class('discussion')}">Discussion</a></li>
<li class="news"><a href="${reverse('news', args=[course.id])}" class="${url_class('news')}">News</a></li>
% endif
<li class="wiki"><a href="${reverse('wiki_root', args=[course.id])}" class="${url_class('wiki')}">Wiki</a></li>
% if user.is_authenticated():
......
......@@ -12,17 +12,19 @@
%>
<div class="thread" _id="${thread['id']}">
<div class="discussion-content">
${render_vote(thread)}
<div class="discussion-right-wrapper clearfix">
<a class="thread-title" name="${thread['id']}" href="${url_for_thread}">${thread['title']}</a>
<div class="discussion-content-view">
<div class="thread-body">${thread['body']}</div>
<div class="info">
${render_info(thread)}
% if edit_thread:
${render_reply()}
${render_edit()}
% endif
<div class="discussion-upper-wrapper clearfix">
${render_vote(thread)}
<div class="discussion-right-wrapper clearfix">
<a class="thread-title" name="${thread['id']}" href="${url_for_thread}">${thread['title']}</a>
<div class="discussion-content-view">
<div class="thread-body">${thread['body']}</div>
<div class="info">
${render_info(thread)}
% if edit_thread:
${render_reply()}
${render_edit()}
% endif
</div>
</div>
</div>
</div>
......@@ -59,7 +61,12 @@
</%def>
<%def name="render_info(content)">
${time_ago_in_words(parse(content['updated_at']))} ago by user No.${content['user_id']}
${time_ago_in_words(parse(content['updated_at']))} ago by
% if content.get('user_id', False):
user No.${content['user_id']}
% else:
anonymous
% endif
</%def>
<%def name="render_reply()">
......
<%inherit file="main.html" />
<%namespace name='static' file='static_content.html'/>
<%block name="bodyclass">courseware news</%block>
<%block name="title"><title>News – MITx 6.002x</title></%block>
<%block name="headextra">
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.js')}"></script>
</%block>
<%block name="js_extra">
</%block>
<%include file="course_navigation.html" args="active_page='news'" />
<section class="container">
<div class="course-wrapper">
<section class="course-content">
${content}
</section>
</div>
</section>
<%! from django.core.urlresolvers import reverse %>
<%
def url_for_thread(thread_id):
return reverse('django_comment_client.forum.views.single_thread', args=[course.id, thread_id])
%>
<%
def url_for_comment(thread_id, comment_id):
return reverse('django_comment_client.forum.views.single_thread', args=[course.id, thread_id]) + "#" + comment_id
%>
<%
def url_for_discussion(discussion_id):
return reverse('django_comment_client.forum.views.forum_form_discussion', args=[course.id, discussion_id])
%>
<%
def discussion_title(discussion_id):
return get_discussion_title(discussion_id=discussion_id)
%>
<div class="notifications">
% for notification in notifications:
${render_notification(notification)}
% endfor
</div>
<%def name="render_notification(notification)">
<div class="notification">
<% info = notification['info'] %>
% if notification['notification_type'] == 'post_reply':
User No.${notification['actor_id']} posted a
<a href="${url_for_comment(info['thread_id'], info['comment_id'])}">comment</a>
to the thread
<a href="${url_for_thread(info['thread_id'])}">${info['thread_title']}</a>
in discussion
<a href="${url_for_discussion(info['commentable_id'])}">${discussion_title(info['commentable_id'])}</a>
% elif notification['notification_type'] == 'post_topic':
User No.${notification['actor_id']} posted a new thread
<a href="${url_for_thread(info['thread_id'])}">${info['thread_title']}</a>
in discussion
<a href="${url_for_discussion(info['commentable_id'])}">${discussion_title(info['commentable_id'])}</a>
% endif
</div>
</%def>
......@@ -126,7 +126,7 @@ $(function() {
%for chapter in courseware_summary:
%if not chapter['chapter'] == "hidden":
<li>
<h2><a href="${reverse('courseware_chapter', args=format_url_params([chapter['course'], chapter['chapter']])) }">
<h2><a href="javascript:void(0)">
${ chapter['chapter'] }</a></h2>
<ol class="sections">
......@@ -138,7 +138,7 @@ $(function() {
percentageString = "{0:.0%}".format( float(earned)/total) if earned > 0 and total > 0 else ""
%>
<h3><a href="${reverse('courseware_section', args=format_url_params([chapter['course'], chapter['chapter'], section['section']])) }">
<h3><a href="javascript:void(0)">
${ section['section'] }</a> ${"({0:.3n}/{1:.3n}) {2}".format( float(earned), float(total), percentageString )}</h3>
${section['format']}
%if 'due' in section and section['due']!="":
......
......@@ -135,6 +135,8 @@ if settings.COURSEWARE_ENABLED:
'courseware.views.profile', name="profile"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/profile/(?P<student_id>[^/]*)/$',
'courseware.views.profile'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/news$',
'courseware.views.news', name="news"),
# discussion
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/discussion/',
......
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