Merge branch 'master' into feature/tomg/fall-design
This commit is contained in:
@@ -185,7 +185,7 @@ def choicegroup(element, value, status, render_template, msg=''):
|
||||
if choice.text is not None:
|
||||
ctext += choice.text # TODO: fix order?
|
||||
choices.append((choice.get("name"), ctext))
|
||||
context = {'id': eid, 'value': value, 'state': status, 'input_type': type, 'choices': choices, 'inline': True, 'name_array_suffix': ''}
|
||||
context = {'id': eid, 'value': value, 'state': status, 'input_type': type, 'choices': choices, 'name_array_suffix': ''}
|
||||
html = render_template("choicegroup.html", context)
|
||||
return etree.XML(html)
|
||||
|
||||
@@ -226,7 +226,7 @@ def radiogroup(element, value, status, render_template, msg=''):
|
||||
|
||||
choices = extract_choices(element)
|
||||
|
||||
context = {'id': eid, 'value': value, 'state': status, 'input_type': 'radio', 'choices': choices, 'inline': False, 'name_array_suffix': '[]'}
|
||||
context = {'id': eid, 'value': value, 'state': status, 'input_type': 'radio', 'choices': choices, 'name_array_suffix': '[]'}
|
||||
|
||||
html = render_template("choicegroup.html", context)
|
||||
return etree.XML(html)
|
||||
@@ -244,7 +244,7 @@ def checkboxgroup(element, value, status, render_template, msg=''):
|
||||
|
||||
choices = extract_choices(element)
|
||||
|
||||
context = {'id': eid, 'value': value, 'state': status, 'input_type': 'checkbox', 'choices': choices, 'inline': False, 'name_array_suffix': '[]'}
|
||||
context = {'id': eid, 'value': value, 'state': status, 'input_type': 'checkbox', 'choices': choices, 'name_array_suffix': '[]'}
|
||||
|
||||
html = render_template("choicegroup.html", context)
|
||||
return etree.XML(html)
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
checked="true"
|
||||
% endif
|
||||
/> ${choice_description} </label>
|
||||
% if not inline:
|
||||
<br/>
|
||||
% endif
|
||||
% endfor
|
||||
<span id="answer_${id}"></span>
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import functools
|
||||
|
||||
import comment_client as cc
|
||||
import django_comment_client.utils as utils
|
||||
import django_comment_client.settings as cc_settings
|
||||
|
||||
|
||||
from django.core import exceptions
|
||||
from django.contrib.auth.decorators import login_required
|
||||
@@ -15,13 +17,11 @@ from django.views.decorators.http import require_POST, require_GET
|
||||
from django.views.decorators import csrf
|
||||
from django.core.files.storage import get_storage_class
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from mitxmako.shortcuts import render_to_response, render_to_string
|
||||
from courseware.courses import get_course_with_access
|
||||
|
||||
|
||||
from django_comment_client.utils import JsonResponse, JsonError, extract
|
||||
|
||||
from django_comment_client.permissions import check_permissions_by_view
|
||||
@@ -115,6 +115,9 @@ def _create_comment(request, course_id, thread_id=None, parent_id=None):
|
||||
@login_required
|
||||
@permitted
|
||||
def create_comment(request, course_id, thread_id):
|
||||
if cc_settings.MAX_COMMENT_DEPTH is not None:
|
||||
if cc_settings.MAX_COMMENT_DEPTH < 0:
|
||||
return JsonError("Comment level too deep")
|
||||
return _create_comment(request, course_id, thread_id=thread_id)
|
||||
|
||||
@require_POST
|
||||
@@ -159,6 +162,9 @@ def openclose_thread(request, course_id, thread_id):
|
||||
@login_required
|
||||
@permitted
|
||||
def create_sub_comment(request, course_id, comment_id):
|
||||
if cc_settings.MAX_COMMENT_DEPTH is not None:
|
||||
if cc_settings.MAX_COMMENT_DEPTH <= cc.Comment.find(comment_id).depth:
|
||||
return JsonError("Comment level too deep")
|
||||
return _create_comment(request, course_id, parent_id=comment_id)
|
||||
|
||||
@require_POST
|
||||
@@ -282,7 +288,7 @@ def update_moderator_status(request, course_id, user_id):
|
||||
'course_id': course_id,
|
||||
'user': request.user,
|
||||
'django_user': user,
|
||||
'discussion_user': discussion_user.to_dict(),
|
||||
'profiled_user': discussion_user.to_dict(),
|
||||
}
|
||||
return JsonResponse({
|
||||
'html': render_to_string('discussion/ajax_user_profile.html', context)
|
||||
@@ -298,10 +304,13 @@ def search_similar_threads(request, course_id, commentable_id):
|
||||
'text': text,
|
||||
'commentable_id': commentable_id,
|
||||
}
|
||||
result = cc.search_similar_threads(course_id, recursive=False, query_params=query_params)
|
||||
return JsonResponse(result)
|
||||
threads = cc.search_similar_threads(course_id, recursive=False, query_params=query_params)
|
||||
else:
|
||||
return JsonResponse([])
|
||||
theads = []
|
||||
context = { 'threads': map(utils.extend_content, threads) }
|
||||
return JsonResponse({
|
||||
'html': render_to_string('discussion/_similar_posts.html', context)
|
||||
})
|
||||
|
||||
@require_GET
|
||||
def tags_autocomplete(request, course_id):
|
||||
@@ -334,8 +343,8 @@ def upload(request, course_id):#ajax upload file to a question or answer
|
||||
# check file type
|
||||
f = request.FILES['file-upload']
|
||||
file_extension = os.path.splitext(f.name)[1].lower()
|
||||
if not file_extension in settings.DISCUSSION_ALLOWED_UPLOAD_FILE_TYPES:
|
||||
file_types = "', '".join(settings.DISCUSSION_ALLOWED_UPLOAD_FILE_TYPES)
|
||||
if not file_extension in cc_settings.ALLOWED_UPLOAD_FILE_TYPES:
|
||||
file_types = "', '".join(cc_settings.ALLOWED_UPLOAD_FILE_TYPES)
|
||||
msg = _("allowed file types are '%(file_types)s'") % \
|
||||
{'file_types': file_types}
|
||||
raise exceptions.PermissionDenied(msg)
|
||||
@@ -354,15 +363,16 @@ def upload(request, course_id):#ajax upload file to a question or answer
|
||||
# check file size
|
||||
# byte
|
||||
size = file_storage.size(new_file_name)
|
||||
if size > settings.ASKBOT_MAX_UPLOAD_FILE_SIZE:
|
||||
if size > cc_settings.MAX_UPLOAD_FILE_SIZE:
|
||||
file_storage.delete(new_file_name)
|
||||
msg = _("maximum upload file size is %(file_size)sK") % \
|
||||
{'file_size': settings.ASKBOT_MAX_UPLOAD_FILE_SIZE}
|
||||
{'file_size': cc_settings.MAX_UPLOAD_FILE_SIZE}
|
||||
raise exceptions.PermissionDenied(msg)
|
||||
|
||||
except exceptions.PermissionDenied, e:
|
||||
error = unicode(e)
|
||||
except Exception, e:
|
||||
print e
|
||||
logging.critical(unicode(e))
|
||||
error = _('Error uploading file. Please contact the site administrator. Thank you.')
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ def render_discussion(request, course_id, threads, *args, **kwargs):
|
||||
'base_url': base_url,
|
||||
'query_params': strip_blank(strip_none(extract(query_params, ['page', 'sort_key', 'sort_order', 'tags', 'text']))),
|
||||
'annotated_content_info': json.dumps(annotated_content_info),
|
||||
'discussion_data': json.dumps({ discussion_id: threads }),
|
||||
'discussion_data': json.dumps({ (discussion_id or user_id): threads })
|
||||
}
|
||||
context = dict(context.items() + query_params.items())
|
||||
return render_to_string(template, context)
|
||||
@@ -250,7 +250,10 @@ def user_profile(request, course_id, user_id):
|
||||
content = render_user_discussion(request, course_id, threads, user_id=user_id, query_params=query_params)
|
||||
|
||||
if request.is_ajax():
|
||||
return utils.HtmlResponse(content)
|
||||
return utils.JsonResponse({
|
||||
'html': content,
|
||||
'discussionData': threads,
|
||||
})
|
||||
else:
|
||||
context = {
|
||||
'course': course,
|
||||
|
||||
@@ -6,13 +6,14 @@ from django.core.urlresolvers import reverse
|
||||
from functools import partial
|
||||
|
||||
from utils import *
|
||||
import django_comment_client.settings as cc_settings
|
||||
|
||||
import pystache_custom as pystache
|
||||
import urllib
|
||||
import os
|
||||
|
||||
def pluralize(singular_term, count):
|
||||
if int(count) >= 2:
|
||||
if int(count) >= 2 or int(count) == 0:
|
||||
return singular_term + 's'
|
||||
return singular_term
|
||||
|
||||
@@ -33,26 +34,19 @@ def include_mustache_templates():
|
||||
file_contents = map(read_file, filter(valid_file_name, os.listdir(mustache_dir)))
|
||||
return '\n'.join(map(wrap_in_tag, map(strip_file_name, file_contents)))
|
||||
|
||||
def permalink(content):
|
||||
if content['type'] == 'thread':
|
||||
return reverse('django_comment_client.forum.views.single_thread',
|
||||
args=[content['course_id'], content['commentable_id'], content['id']])
|
||||
else:
|
||||
return reverse('django_comment_client.forum.views.single_thread',
|
||||
args=[content['course_id'], content['commentable_id'], content['thread_id']]) + '#' + content['id']
|
||||
|
||||
def render_content(content, additional_context={}):
|
||||
content_info = {
|
||||
'displayed_title': content.get('highlighted_title') or content.get('title', ''),
|
||||
'displayed_body': content.get('highlighted_body') or content.get('body', ''),
|
||||
'raw_tags': ','.join(content.get('tags', [])),
|
||||
'permalink': permalink(content),
|
||||
}
|
||||
|
||||
context = {
|
||||
'content': merge_dict(content, content_info),
|
||||
'content': extend_content(content),
|
||||
content['type']: True,
|
||||
}
|
||||
if cc_settings.MAX_COMMENT_DEPTH is not None:
|
||||
if content['type'] == 'thread':
|
||||
if cc_settings.MAX_COMMENT_DEPTH < 0:
|
||||
context['max_depth'] = True
|
||||
elif content['type'] == 'comment':
|
||||
if cc_settings.MAX_COMMENT_DEPTH <= content['depth']:
|
||||
context['max_depth'] = True
|
||||
context = merge_dict(context, additional_context)
|
||||
partial_mustache_helpers = {k: partial(v, content) for k, v in mustache_helpers.items()}
|
||||
context = merge_dict(context, partial_mustache_helpers)
|
||||
|
||||
@@ -7,7 +7,8 @@ import inspect
|
||||
|
||||
def pluralize(content, text):
|
||||
num, word = text.split(' ')
|
||||
if int(num or '0') >= 2:
|
||||
num = int(num or '0')
|
||||
if num >= 2 or num == 0:
|
||||
return word + 's'
|
||||
else:
|
||||
return word
|
||||
|
||||
10
lms/djangoapps/django_comment_client/settings.py
Normal file
10
lms/djangoapps/django_comment_client/settings.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.conf import settings
|
||||
|
||||
MAX_COMMENT_DEPTH = None
|
||||
MAX_UPLOAD_FILE_SIZE = 1024 * 1024 #result in bytes
|
||||
ALLOWED_UPLOAD_FILE_TYPES = ('.jpg', '.jpeg', '.gif', '.bmp', '.png', '.tiff')
|
||||
|
||||
if hasattr(settings, 'DISCUSSION_SETTINGS'):
|
||||
MAX_COMMENT_DEPTH = settings.DISCUSSION_SETTINGS.get('MAX_COMMENT_DEPTH')
|
||||
MAX_UPLOAD_FILE_SIZE = settings.DISCUSSION_SETTINGS.get('MAX_UPLOAD_FILE_SIZE') or MAX_UPLOAD_FILE_SIZE
|
||||
ALLOWED_UPLOAD_FILE_TYPES = settings.DISCUSSION_SETTINGS.get('ALLOWED_UPLOAD_FILE_TYPES') or ALLOWED_UPLOAD_FILE_TYPES
|
||||
@@ -21,6 +21,8 @@ import pystache_custom as pystache
|
||||
_FULLMODULES = None
|
||||
_DISCUSSIONINFO = None
|
||||
|
||||
|
||||
|
||||
def extract(dic, keys):
|
||||
return {k: dic.get(k) for k in keys}
|
||||
|
||||
@@ -197,3 +199,20 @@ def url_for_tags(course_id, tags):
|
||||
def render_mustache(template_name, dictionary, *args, **kwargs):
|
||||
template = middleware.lookup['main'].get_template(template_name).source
|
||||
return pystache.render(template, dictionary)
|
||||
|
||||
def permalink(content):
|
||||
if content['type'] == 'thread':
|
||||
return reverse('django_comment_client.forum.views.single_thread',
|
||||
args=[content['course_id'], content['commentable_id'], content['id']])
|
||||
else:
|
||||
return reverse('django_comment_client.forum.views.single_thread',
|
||||
args=[content['course_id'], content['commentable_id'], content['thread_id']]) + '#' + content['id']
|
||||
|
||||
def extend_content(content):
|
||||
content_info = {
|
||||
'displayed_title': content.get('highlighted_title') or content.get('title', ''),
|
||||
'displayed_body': content.get('highlighted_body') or content.get('body', ''),
|
||||
'raw_tags': ','.join(content.get('tags', [])),
|
||||
'permalink': permalink(content),
|
||||
}
|
||||
return merge_dict(content, content_info)
|
||||
|
||||
@@ -68,4 +68,4 @@ if 'COURSE_ID' in ENV_TOKENS:
|
||||
ASKBOT_URL = "courses/{0}/discussions/".format(ENV_TOKENS['COURSE_ID'])
|
||||
|
||||
COMMENTS_SERVICE_URL = ENV_TOKENS["COMMENTS_SERVICE_URL"]
|
||||
|
||||
COMMENTS_SERVICE_KEY = ENV_TOKENS["COMMENTS_SERVICE_KEY"]
|
||||
|
||||
@@ -38,6 +38,10 @@ ASKBOT_ENABLED = False
|
||||
GENERATE_RANDOM_USER_CREDENTIALS = False
|
||||
PERFSTATS = False
|
||||
|
||||
DISCUSSION_SETTINGS = {
|
||||
'MAX_COMMENT_DEPTH': 2,
|
||||
}
|
||||
|
||||
# Features
|
||||
MITX_FEATURES = {
|
||||
'SAMPLE' : False,
|
||||
|
||||
@@ -92,6 +92,8 @@ SUBDOMAIN_BRANDING = {
|
||||
'harvard': 'HarvardX',
|
||||
}
|
||||
|
||||
COMMENTS_SERVICE_KEY = "PUT_YOUR_API_KEY_HERE"
|
||||
|
||||
################################ LMS Migration #################################
|
||||
MITX_FEATURES['ENABLE_LMS_MIGRATION'] = True
|
||||
MITX_FEATURES['ACCESS_REQUIRE_STAFF_FOR_COURSE'] = False # require that user be in the staff_* group to be able to enroll
|
||||
|
||||
@@ -7,4 +7,7 @@ else:
|
||||
|
||||
PREFIX = SERVICE_HOST + '/api/v1'
|
||||
|
||||
API_KEY = "PUT_YOUR_API_KEY_HERE"
|
||||
if hasattr(settings, "COMMENTS_SERVICE_KEY"):
|
||||
API_KEY = settings.COMMENTS_SERVICE_KEY
|
||||
else:
|
||||
API_KEY = "PUT_YOUR_API_KEY_HERE"
|
||||
|
||||
@@ -1,410 +1,431 @@
|
||||
class @Content extends Backbone.Model
|
||||
if Backbone?
|
||||
class @Content extends Backbone.Model
|
||||
|
||||
template: -> DiscussionUtil.getTemplate('_content')
|
||||
template: -> DiscussionUtil.getTemplate('_content')
|
||||
|
||||
actions:
|
||||
editable: '.admin-edit'
|
||||
can_reply: '.discussion-reply'
|
||||
can_endorse: '.admin-endorse'
|
||||
can_delete: '.admin-delete'
|
||||
can_openclose: '.admin-openclose'
|
||||
|
||||
urlMappers: {}
|
||||
actions:
|
||||
editable: '.admin-edit'
|
||||
can_reply: '.discussion-reply'
|
||||
can_endorse: '.admin-endorse'
|
||||
can_delete: '.admin-delete'
|
||||
can_openclose: '.admin-openclose'
|
||||
|
||||
urlMappers: {}
|
||||
|
||||
urlFor: (name) ->
|
||||
@urlMappers[name].apply(@)
|
||||
urlFor: (name) ->
|
||||
@urlMappers[name].apply(@)
|
||||
|
||||
can: (action) ->
|
||||
DiscussionUtil.getContentInfo @id, action
|
||||
can: (action) ->
|
||||
DiscussionUtil.getContentInfo @id, action
|
||||
|
||||
updateInfo: (info) ->
|
||||
@set('ability', info.ability)
|
||||
@set('voted', info.voted)
|
||||
@set('subscribed', info.subscribed)
|
||||
updateInfo: (info) ->
|
||||
@set('ability', info.ability)
|
||||
@set('voted', info.voted)
|
||||
@set('subscribed', info.subscribed)
|
||||
|
||||
addComment: (comment, options) ->
|
||||
options ||= {}
|
||||
if not options.silent
|
||||
addComment: (comment, options) ->
|
||||
options ||= {}
|
||||
if not options.silent
|
||||
thread = @get('thread')
|
||||
comments_count = parseInt(thread.get('comments_count'))
|
||||
thread.set('comments_count', comments_count + 1)
|
||||
@get('children').push comment
|
||||
model = new Comment $.extend {}, comment, { thread: @get('thread') }
|
||||
@get('comments').add model
|
||||
model
|
||||
|
||||
removeComment: (comment) ->
|
||||
thread = @get('thread')
|
||||
comments_count = parseInt(thread.get('comments_count'))
|
||||
thread.set('comments_count', comments_count + 1)
|
||||
@get('children').push comment
|
||||
model = new Comment $.extend {}, comment, { thread: @get('thread') }
|
||||
@get('comments').add model
|
||||
model
|
||||
thread.set('comments_count', comments_count - 1 - comment.getCommentsCount())
|
||||
|
||||
removeComment: (comment) ->
|
||||
thread = @get('thread')
|
||||
comments_count = parseInt(thread.get('comments_count'))
|
||||
thread.set('comments_count', comments_count - 1 - comment.getCommentsCount())
|
||||
resetComments: (children) ->
|
||||
@set 'children', []
|
||||
@set 'comments', new Comments()
|
||||
for comment in (children || [])
|
||||
@addComment comment, { silent: true }
|
||||
|
||||
resetComments: (children) ->
|
||||
@set 'children', []
|
||||
@set 'comments', new Comments()
|
||||
for comment in (children || [])
|
||||
@addComment comment, { silent: true }
|
||||
|
||||
initialize: ->
|
||||
DiscussionUtil.addContent @id, @
|
||||
@resetComments(@get('children'))
|
||||
|
||||
|
||||
class @ContentView extends Backbone.View
|
||||
|
||||
$: (selector) ->
|
||||
@$local.find(selector)
|
||||
|
||||
partial:
|
||||
endorsed: (endorsed) ->
|
||||
if endorsed
|
||||
@$el.addClass("endorsed")
|
||||
else
|
||||
@$el.removeClass("endorsed")
|
||||
|
||||
closed: (closed) -> # we should just re-render the whole thread, or update according to new abilities
|
||||
if closed
|
||||
@$el.addClass("closed")
|
||||
@$(".admin-openclose").text "Re-open Thread"
|
||||
else
|
||||
@$el.removeClass("closed")
|
||||
@$(".admin-openclose").text "Close Thread"
|
||||
|
||||
voted: (voted) ->
|
||||
@$(".discussion-vote-up").removeClass("voted") if voted != "up"
|
||||
@$(".discussion-vote-down").removeClass("voted") if voted != "down"
|
||||
@$(".discussion-vote-#{voted}").addClass("voted") if voted in ["up", "down"]
|
||||
|
||||
votes_point: (votes_point) ->
|
||||
@$(".discussion-votes-point").html(votes_point)
|
||||
|
||||
comments_count: (comments_count) ->
|
||||
@$(".comments-count").html(comments_count)
|
||||
initialize: ->
|
||||
DiscussionUtil.addContent @id, @
|
||||
@resetComments(@get('children'))
|
||||
|
||||
subscribed: (subscribed) ->
|
||||
if subscribed
|
||||
@$(".discussion-follow-thread").addClass("discussion-unfollow-thread").html("Unfollow")
|
||||
|
||||
class @ContentView extends Backbone.View
|
||||
|
||||
$: (selector) ->
|
||||
@$local.find(selector)
|
||||
|
||||
partial:
|
||||
endorsed: (endorsed) ->
|
||||
if endorsed
|
||||
@$el.addClass("endorsed")
|
||||
else
|
||||
@$el.removeClass("endorsed")
|
||||
|
||||
closed: (closed) -> # we should just re-render the whole thread, or update according to new abilities
|
||||
if closed
|
||||
@$el.addClass("closed")
|
||||
@$(".admin-openclose").text "Re-open Thread"
|
||||
else
|
||||
@$el.removeClass("closed")
|
||||
@$(".admin-openclose").text "Close Thread"
|
||||
|
||||
voted: (voted) ->
|
||||
@$(".discussion-vote-up").removeClass("voted") if voted != "up"
|
||||
@$(".discussion-vote-down").removeClass("voted") if voted != "down"
|
||||
@$(".discussion-vote-#{voted}").addClass("voted") if voted in ["up", "down"]
|
||||
|
||||
votes_point: (votes_point) ->
|
||||
@$(".discussion-votes-point").html(votes_point)
|
||||
|
||||
comments_count: (comments_count) ->
|
||||
@$(".comments-count").html(comments_count)
|
||||
|
||||
subscribed: (subscribed) ->
|
||||
if subscribed
|
||||
@$(".discussion-follow-thread").addClass("discussion-unfollow-thread").html("Unfollow")
|
||||
else
|
||||
@$(".discussion-follow-thread").removeClass("discussion-unfollow-thread").html("Follow")
|
||||
|
||||
ability: (ability) ->
|
||||
for action, elemSelector of @model.actions
|
||||
if not ability[action]
|
||||
@$(elemSelector).parent().remove()
|
||||
|
||||
$discussionContent: ->
|
||||
@_discussionContent ||= @$el.children(".discussion-content")
|
||||
|
||||
$showComments: ->
|
||||
@_showComments ||= @$(".discussion-show-comments")
|
||||
|
||||
updateShowComments: ->
|
||||
if @showed
|
||||
@$showComments().html @$showComments().html().replace "Show", "Hide"
|
||||
else
|
||||
@$(".discussion-follow-thread").removeClass("discussion-unfollow-thread").html("Follow")
|
||||
@$showComments().html @$showComments().html().replace "Hide", "Show"
|
||||
|
||||
ability: (ability) ->
|
||||
for action, elemSelector of @model.actions
|
||||
if not ability[action]
|
||||
@$(elemSelector).parent().remove()
|
||||
|
||||
$discussionContent: ->
|
||||
@_discussionContent ||= @$el.children(".discussion-content")
|
||||
|
||||
$showComments: ->
|
||||
@_showComments ||= @$(".discussion-show-comments")
|
||||
|
||||
updateShowComments: ->
|
||||
if @showed
|
||||
@$showComments().html @$showComments().html().replace "Show", "Hide"
|
||||
else
|
||||
@$showComments().html @$showComments().html().replace "Hide", "Show"
|
||||
|
||||
retrieved: ->
|
||||
@$showComments().hasClass("retrieved")
|
||||
|
||||
hideSingleThread: (event) ->
|
||||
@$el.children(".comments").hide()
|
||||
@showed = false
|
||||
@updateShowComments()
|
||||
|
||||
showSingleThread: (event) ->
|
||||
if @retrieved()
|
||||
@$el.children(".comments").show()
|
||||
@showed = true
|
||||
retrieved: ->
|
||||
@$showComments().hasClass("retrieved")
|
||||
|
||||
hideSingleThread: (event) ->
|
||||
@$el.children(".comments").hide()
|
||||
@showed = false
|
||||
@updateShowComments()
|
||||
else
|
||||
$elem = $.merge @$(".thread-title"), @$showComments()
|
||||
url = @model.urlFor('retrieve')
|
||||
DiscussionUtil.get $elem, url, {}, (response, textStatus) =>
|
||||
|
||||
showSingleThread: (event) ->
|
||||
if @retrieved()
|
||||
@$el.children(".comments").show()
|
||||
@showed = true
|
||||
@updateShowComments()
|
||||
@$showComments().addClass("retrieved")
|
||||
@$el.children(".comments").replaceWith response.html
|
||||
@model.resetComments response.content.children
|
||||
@initCommentViews()
|
||||
DiscussionUtil.bulkUpdateContentInfo response.annotated_content_info
|
||||
|
||||
toggleSingleThread: (event) ->
|
||||
if @showed
|
||||
@hideSingleThread(event)
|
||||
else
|
||||
@showSingleThread(event)
|
||||
|
||||
initCommentViews: ->
|
||||
@$el.children(".comments").children(".comment").each (index, elem) =>
|
||||
model = @model.get('comments').find $(elem).attr("_id")
|
||||
if not model.view
|
||||
commentView = new CommentView el: elem, model: model
|
||||
|
||||
reply: ->
|
||||
if @model.get('type') == 'thread'
|
||||
@showSingleThread()
|
||||
$replyView = @$(".discussion-reply-new")
|
||||
if $replyView.length
|
||||
$replyView.show()
|
||||
else
|
||||
view = {}
|
||||
view.id = @model.id
|
||||
view.showWatchCheckbox = not @model.get('thread').get('subscribed')
|
||||
html = Mustache.render DiscussionUtil.getTemplate('_reply'), view
|
||||
@$discussionContent().append html
|
||||
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "reply-body"
|
||||
@$(".discussion-submit-post").click $.proxy(@submitReply, @)
|
||||
@$(".discussion-cancel-post").click $.proxy(@cancelReply, @)
|
||||
@$(".discussion-reply").hide()
|
||||
@$(".discussion-edit").hide()
|
||||
|
||||
submitReply: (event) ->
|
||||
url = @model.urlFor('reply')
|
||||
|
||||
body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "reply-body"
|
||||
|
||||
anonymous = false || @$(".discussion-post-anonymously").is(":checked")
|
||||
autowatch = false || @$(".discussion-auto-watch").is(":checked")
|
||||
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $(event.target)
|
||||
url: url
|
||||
type: "POST"
|
||||
dataType: 'json'
|
||||
data:
|
||||
body: body
|
||||
anonymous: anonymous
|
||||
auto_subscribe: autowatch
|
||||
error: DiscussionUtil.formErrorHandler @$(".discussion-errors")
|
||||
success: (response, textStatus) =>
|
||||
DiscussionUtil.clearFormErrors @$(".discussion-errors")
|
||||
$comment = $(response.html)
|
||||
@$el.children(".comments").prepend $comment
|
||||
DiscussionUtil.setWmdContent @$el, $.proxy(@$, @), "reply-body", ""
|
||||
comment = @model.addComment response.content
|
||||
commentView = new CommentView el: $comment[0], model: comment
|
||||
comment.updateInfo response.annotated_content_info
|
||||
@cancelReply()
|
||||
|
||||
cancelReply: ->
|
||||
$replyView = @$(".discussion-reply-new")
|
||||
if $replyView.length
|
||||
$replyView.hide()
|
||||
@$(".discussion-reply").show()
|
||||
@$(".discussion-edit").show()
|
||||
|
||||
unvote: (event) ->
|
||||
url = @model.urlFor('unvote')
|
||||
$elem = @$(".discussion-vote")
|
||||
DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
|
||||
@model.set('voted', '')
|
||||
@model.set('votes_point', response.votes.point)
|
||||
|
||||
vote: (event, value) ->
|
||||
url = @model.urlFor("#{value}vote")
|
||||
$elem = @$(".discussion-vote")
|
||||
DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
|
||||
@model.set('voted', value)
|
||||
@model.set('votes_point', response.votes.point)
|
||||
|
||||
toggleVote: (event) ->
|
||||
$elem = $(event.target)
|
||||
value = $elem.attr("value")
|
||||
if @model.get("voted") == value
|
||||
@unvote(event)
|
||||
else
|
||||
@vote(event, value)
|
||||
|
||||
toggleEndorse: (event) ->
|
||||
$elem = $(event.target)
|
||||
url = @model.urlFor('endorse')
|
||||
endorsed = @model.get('endorsed')
|
||||
data = { endorsed: not endorsed }
|
||||
DiscussionUtil.post $elem, url, data, (response, textStatus) =>
|
||||
@model.set('endorsed', not endorsed)
|
||||
|
||||
toggleFollow: (event) ->
|
||||
$elem = $(event.target)
|
||||
subscribed = @model.get('subscribed')
|
||||
if subscribed
|
||||
url = @model.urlFor('unfollow')
|
||||
else
|
||||
url = @model.urlFor('follow')
|
||||
DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
|
||||
@model.set('subscribed', not subscribed)
|
||||
|
||||
toggleClosed: (event) ->
|
||||
$elem = $(event.target)
|
||||
url = @model.urlFor('close')
|
||||
closed = @model.get('closed')
|
||||
data = { closed: not closed }
|
||||
DiscussionUtil.post $elem, url, data, (response, textStatus) =>
|
||||
@model.set('closed', not closed)
|
||||
|
||||
edit: (event) ->
|
||||
@$(".discussion-content-wrapper").hide()
|
||||
$editView = @$(".discussion-content-edit")
|
||||
if $editView.length
|
||||
$editView.show()
|
||||
else
|
||||
view = {}
|
||||
view.id = @model.id
|
||||
if @model.get('type') == 'thread'
|
||||
view.title = @$(".thread-raw-title").html()
|
||||
view.body = @$(".thread-raw-body").html()
|
||||
view.tags = @$(".thread-raw-tags").html()
|
||||
else
|
||||
view.body = @$(".comment-raw-body").html()
|
||||
@$discussionContent().append Mustache.render DiscussionUtil.getTemplate("_edit_#{@model.get('type')}"), view
|
||||
Discussion.makeWmdEditor @$el, $.proxy(@$, @), "#{@model.get('type')}-body-edit"
|
||||
@$(".thread-tags-edit").tagsInput DiscussionUtil.tagsInputOptions()
|
||||
@$(".discussion-submit-update").unbind("click").click $.proxy(@submitEdit, @)
|
||||
@$(".discussion-cancel-update").unbind("click").click $.proxy(@cancelEdit, @)
|
||||
$elem = $.merge @$(".thread-title"), @$showComments()
|
||||
url = @model.urlFor('retrieve')
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
$loading: $(event.target) if event
|
||||
type: "GET"
|
||||
url: url
|
||||
success: (response, textStatus) =>
|
||||
@showed = true
|
||||
@updateShowComments()
|
||||
@$showComments().addClass("retrieved")
|
||||
@$el.children(".comments").replaceWith response.html
|
||||
@model.resetComments response.content.children
|
||||
@initCommentViews()
|
||||
DiscussionUtil.bulkUpdateContentInfo response.annotated_content_info
|
||||
|
||||
submitEdit: (event) ->
|
||||
toggleSingleThread: (event) ->
|
||||
if @showed
|
||||
@hideSingleThread(event)
|
||||
else
|
||||
@showSingleThread(event)
|
||||
|
||||
initCommentViews: ->
|
||||
@$el.children(".comments").children(".comment").each (index, elem) =>
|
||||
model = @model.get('comments').find $(elem).attr("_id")
|
||||
if not model.view
|
||||
commentView = new CommentView el: elem, model: model
|
||||
|
||||
url = @model.urlFor('update')
|
||||
data = {}
|
||||
if @model.get('type') == 'thread'
|
||||
data.title = @$(".thread-title-edit").val()
|
||||
data.body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "thread-body-edit"
|
||||
data.tags = @$(".thread-tags-edit").val()
|
||||
else
|
||||
data.body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "comment-body-edit"
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $(event.target)
|
||||
url: url
|
||||
type: "POST"
|
||||
dataType: 'json'
|
||||
data: data
|
||||
error: DiscussionUtil.formErrorHandler @$(".discussion-update-errors")
|
||||
success: (response, textStatus) =>
|
||||
DiscussionUtil.clearFormErrors @$(".discussion-update-errors")
|
||||
@$discussionContent().replaceWith(response.html)
|
||||
@model.set response.content
|
||||
@model.updateInfo response.annotated_content_info
|
||||
reply: ->
|
||||
if @model.get('type') == 'thread'
|
||||
@showSingleThread()
|
||||
$replyView = @$(".discussion-reply-new")
|
||||
if $replyView.length
|
||||
$replyView.show()
|
||||
else
|
||||
view = {}
|
||||
view.id = @model.id
|
||||
view.showWatchCheckbox = not @model.get('thread').get('subscribed')
|
||||
html = Mustache.render DiscussionUtil.getTemplate('_reply'), view
|
||||
@$discussionContent().append html
|
||||
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "reply-body"
|
||||
@$(".discussion-submit-post").click $.proxy(@submitReply, @)
|
||||
@$(".discussion-cancel-post").click $.proxy(@cancelReply, @)
|
||||
@$(".discussion-reply").hide()
|
||||
@$(".discussion-edit").hide()
|
||||
|
||||
cancelEdit: (event) ->
|
||||
@$(".discussion-content-edit").hide()
|
||||
@$(".discussion-content-wrapper").show()
|
||||
submitReply: (event) ->
|
||||
url = @model.urlFor('reply')
|
||||
|
||||
delete: (event) ->
|
||||
url = @model.urlFor('delete')
|
||||
if @model.get('type') == 'thread'
|
||||
c = confirm "Are you sure to delete thread \"#{@model.get('title')}\"?"
|
||||
else
|
||||
c = confirm "Are you sure to delete this comment? "
|
||||
if not c
|
||||
return
|
||||
$elem = $(event.target)
|
||||
DiscussionUtil.post $elem, url, {}, (response, textStatus) =>
|
||||
@$el.remove()
|
||||
@model.get('thread').removeComment(@model)
|
||||
body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "reply-body"
|
||||
|
||||
anonymous = false || @$(".discussion-post-anonymously").is(":checked")
|
||||
autowatch = false || @$(".discussion-auto-watch").is(":checked")
|
||||
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $(event.target)
|
||||
$loading: $(event.target) if event
|
||||
url: url
|
||||
type: "POST"
|
||||
dataType: 'json'
|
||||
data:
|
||||
body: body
|
||||
anonymous: anonymous
|
||||
auto_subscribe: autowatch
|
||||
error: DiscussionUtil.formErrorHandler @$(".discussion-errors")
|
||||
success: (response, textStatus) =>
|
||||
DiscussionUtil.clearFormErrors @$(".discussion-errors")
|
||||
$comment = $(response.html)
|
||||
@$el.children(".comments").prepend $comment
|
||||
DiscussionUtil.setWmdContent @$el, $.proxy(@$, @), "reply-body", ""
|
||||
comment = @model.addComment response.content
|
||||
commentView = new CommentView el: $comment[0], model: comment
|
||||
comment.updateInfo response.annotated_content_info
|
||||
@cancelReply()
|
||||
|
||||
cancelReply: ->
|
||||
$replyView = @$(".discussion-reply-new")
|
||||
if $replyView.length
|
||||
$replyView.hide()
|
||||
@$(".discussion-reply").show()
|
||||
@$(".discussion-edit").show()
|
||||
|
||||
unvote: (event) ->
|
||||
url = @model.urlFor('unvote')
|
||||
$elem = @$(".discussion-vote")
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
type: "POST"
|
||||
success: (response, textStatus) =>
|
||||
@model.set('voted', '')
|
||||
@model.set('votes_point', response.votes.point)
|
||||
|
||||
vote: (event, value) ->
|
||||
url = @model.urlFor("#{value}vote")
|
||||
$elem = @$(".discussion-vote")
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
type: "POST"
|
||||
success: (response, textStatus) =>
|
||||
@model.set('voted', value)
|
||||
@model.set('votes_point', response.votes.point)
|
||||
|
||||
toggleVote: (event) ->
|
||||
$elem = $(event.target)
|
||||
value = $elem.attr("value")
|
||||
if @model.get("voted") == value
|
||||
@unvote(event)
|
||||
else
|
||||
@vote(event, value)
|
||||
|
||||
toggleEndorse: (event) ->
|
||||
$elem = $(event.target)
|
||||
url = @model.urlFor('endorse')
|
||||
endorsed = @model.get('endorsed')
|
||||
data = { endorsed: not endorsed }
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
data: data
|
||||
type: "POST"
|
||||
success: (response, textStatus) =>
|
||||
@model.set('endorsed', not endorsed)
|
||||
|
||||
toggleFollow: (event) ->
|
||||
$elem = $(event.target)
|
||||
subscribed = @model.get('subscribed')
|
||||
if subscribed
|
||||
url = @model.urlFor('unfollow')
|
||||
else
|
||||
url = @model.urlFor('follow')
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
type: "POST"
|
||||
success: (response, textStatus) =>
|
||||
@model.set('subscribed', not subscribed)
|
||||
|
||||
toggleClosed: (event) ->
|
||||
$elem = $(event.target)
|
||||
url = @model.urlFor('close')
|
||||
closed = @model.get('closed')
|
||||
data = { closed: not closed }
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
type: "POST"
|
||||
data: data
|
||||
success: (response, textStatus) =>
|
||||
@model.set('closed', not closed)
|
||||
|
||||
edit: (event) ->
|
||||
@$(".discussion-content-wrapper").hide()
|
||||
$editView = @$(".discussion-content-edit")
|
||||
if $editView.length
|
||||
$editView.show()
|
||||
else
|
||||
view = {}
|
||||
view.id = @model.id
|
||||
if @model.get('type') == 'thread'
|
||||
view.title = @$(".thread-raw-title").html()
|
||||
view.body = @$(".thread-raw-body").html()
|
||||
view.tags = @$(".thread-raw-tags").html()
|
||||
else
|
||||
view.body = @$(".comment-raw-body").html()
|
||||
@$discussionContent().append Mustache.render DiscussionUtil.getTemplate("_edit_#{@model.get('type')}"), view
|
||||
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "#{@model.get('type')}-body-edit"
|
||||
@$(".thread-tags-edit").tagsInput DiscussionUtil.tagsInputOptions()
|
||||
@$(".discussion-submit-update").unbind("click").click $.proxy(@submitEdit, @)
|
||||
@$(".discussion-cancel-update").unbind("click").click $.proxy(@cancelEdit, @)
|
||||
|
||||
submitEdit: (event) ->
|
||||
|
||||
url = @model.urlFor('update')
|
||||
data = {}
|
||||
if @model.get('type') == 'thread'
|
||||
data.title = @$(".thread-title-edit").val()
|
||||
data.body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "thread-body-edit"
|
||||
data.tags = @$(".thread-tags-edit").val()
|
||||
else
|
||||
data.body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "comment-body-edit"
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $(event.target)
|
||||
$loading: $(event.target) if event
|
||||
url: url
|
||||
type: "POST"
|
||||
dataType: 'json'
|
||||
data: data
|
||||
error: DiscussionUtil.formErrorHandler @$(".discussion-update-errors")
|
||||
success: (response, textStatus) =>
|
||||
DiscussionUtil.clearFormErrors @$(".discussion-update-errors")
|
||||
@$discussionContent().replaceWith(response.html)
|
||||
@model.set response.content
|
||||
@model.updateInfo response.annotated_content_info
|
||||
|
||||
cancelEdit: (event) ->
|
||||
@$(".discussion-content-edit").hide()
|
||||
@$(".discussion-content-wrapper").show()
|
||||
|
||||
delete: (event) ->
|
||||
url = @model.urlFor('delete')
|
||||
if @model.get('type') == 'thread'
|
||||
c = confirm "Are you sure to delete thread \"#{@model.get('title')}\"?"
|
||||
else
|
||||
c = confirm "Are you sure to delete this comment? "
|
||||
if not c
|
||||
return
|
||||
$elem = $(event.target)
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
success: (response, textStatus) =>
|
||||
@$el.remove()
|
||||
@model.get('thread').removeComment(@model)
|
||||
|
||||
events:
|
||||
"click .discussion-follow-thread": "toggleFollow"
|
||||
"click .thread-title": "toggleSingleThread"
|
||||
"click .discussion-show-comments": "toggleSingleThread"
|
||||
"click .discussion-reply-thread": "reply"
|
||||
"click .discussion-reply-comment": "reply"
|
||||
"click .discussion-cancel-reply": "cancelReply"
|
||||
"click .discussion-vote-up": "toggleVote"
|
||||
"click .discussion-vote-down": "toggleVote"
|
||||
"click .admin-endorse": "toggleEndorse"
|
||||
"click .admin-openclose": "toggleClosed"
|
||||
"click .admin-edit": "edit"
|
||||
"click .admin-delete": "delete"
|
||||
|
||||
initLocal: ->
|
||||
@$local = @$el.children(".local")
|
||||
@$delegateElement = @$local
|
||||
|
||||
initTitle: ->
|
||||
$contentTitle = @$(".thread-title")
|
||||
if $contentTitle.length
|
||||
$contentTitle.html DiscussionUtil.unescapeHighlightTag DiscussionUtil.stripLatexHighlight $contentTitle.html()
|
||||
|
||||
initBody: ->
|
||||
$contentBody = @$(".content-body")
|
||||
$contentBody.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight $contentBody.html()
|
||||
MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")]
|
||||
|
||||
initTimeago: ->
|
||||
@$("span.timeago").timeago()
|
||||
|
||||
renderPartial: ->
|
||||
for attr, value of @model.changedAttributes()
|
||||
if @partial[attr]
|
||||
@partial[attr].apply(@, [value])
|
||||
|
||||
initBindings: ->
|
||||
@model.view = @
|
||||
@model.bind('change', @renderPartial, @)
|
||||
|
||||
initialize: ->
|
||||
@initBindings()
|
||||
@initLocal()
|
||||
@initTimeago()
|
||||
@initTitle()
|
||||
@initBody()
|
||||
@initCommentViews()
|
||||
|
||||
events:
|
||||
"click .discussion-follow-thread": "toggleFollow"
|
||||
"click .thread-title": "toggleSingleThread"
|
||||
"click .discussion-show-comments": "toggleSingleThread"
|
||||
"click .discussion-reply-thread": "reply"
|
||||
"click .discussion-reply-comment": "reply"
|
||||
"click .discussion-cancel-reply": "cancelReply"
|
||||
"click .discussion-vote-up": "toggleVote"
|
||||
"click .discussion-vote-down": "toggleVote"
|
||||
"click .admin-endorse": "toggleEndorse"
|
||||
"click .admin-openclose": "toggleClosed"
|
||||
"click .admin-edit": "edit"
|
||||
"click .admin-delete": "delete"
|
||||
class @Thread extends @Content
|
||||
urlMappers:
|
||||
'retrieve' : -> DiscussionUtil.urlFor('retrieve_single_thread', @discussion.id, @id)
|
||||
'reply' : -> DiscussionUtil.urlFor('create_comment', @id)
|
||||
'unvote' : -> DiscussionUtil.urlFor("undo_vote_for_#{@get('type')}", @id)
|
||||
'upvote' : -> DiscussionUtil.urlFor("upvote_#{@get('type')}", @id)
|
||||
'downvote' : -> DiscussionUtil.urlFor("downvote_#{@get('type')}", @id)
|
||||
'close' : -> DiscussionUtil.urlFor('openclose_thread', @id)
|
||||
'update' : -> DiscussionUtil.urlFor('update_thread', @id)
|
||||
'delete' : -> DiscussionUtil.urlFor('delete_thread', @id)
|
||||
'follow' : -> DiscussionUtil.urlFor('follow_thread', @id)
|
||||
'unfollow' : -> DiscussionUtil.urlFor('unfollow_thread', @id)
|
||||
|
||||
initLocal: ->
|
||||
@$local = @$el.children(".local")
|
||||
@$delegateElement = @$local
|
||||
initialize: ->
|
||||
@set('thread', @)
|
||||
super()
|
||||
|
||||
initTitle: ->
|
||||
$contentTitle = @$(".thread-title")
|
||||
if $contentTitle.length
|
||||
$contentTitle.html DiscussionUtil.unescapeHighlightTag DiscussionUtil.stripLatexHighlight $contentTitle.html()
|
||||
class @ThreadView extends @ContentView
|
||||
|
||||
initBody: ->
|
||||
$contentBody = @$(".content-body")
|
||||
$contentBody.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight $contentBody.html()
|
||||
MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")]
|
||||
class @Comment extends @Content
|
||||
urlMappers:
|
||||
'reply': -> DiscussionUtil.urlFor('create_sub_comment', @id)
|
||||
'unvote': -> DiscussionUtil.urlFor("undo_vote_for_#{@get('type')}", @id)
|
||||
'upvote': -> DiscussionUtil.urlFor("upvote_#{@get('type')}", @id)
|
||||
'downvote': -> DiscussionUtil.urlFor("downvote_#{@get('type')}", @id)
|
||||
'endorse': -> DiscussionUtil.urlFor('endorse_comment', @id)
|
||||
'update': -> DiscussionUtil.urlFor('update_comment', @id)
|
||||
'delete': -> DiscussionUtil.urlFor('delete_comment', @id)
|
||||
|
||||
initTimeago: ->
|
||||
@$("span.timeago").timeago()
|
||||
getCommentsCount: ->
|
||||
count = 0
|
||||
@get('comments').each (comment) ->
|
||||
count += comment.getCommentsCount() + 1
|
||||
count
|
||||
|
||||
initPermalink: ->
|
||||
@$(".discussion-permanent-link").attr "href", @model.permalink()
|
||||
class @CommentView extends @ContentView
|
||||
|
||||
renderPartial: ->
|
||||
for attr, value of @model.changedAttributes()
|
||||
if @partial[attr]
|
||||
@partial[attr].apply(@, [value])
|
||||
class @Comments extends Backbone.Collection
|
||||
|
||||
initBindings: ->
|
||||
@model.view = @
|
||||
@model.bind('change', @renderPartial, @)
|
||||
model: Comment
|
||||
|
||||
initialize: ->
|
||||
@initBindings()
|
||||
@initLocal()
|
||||
@initTimeago()
|
||||
@initTitle()
|
||||
@initBody()
|
||||
@initCommentViews()
|
||||
|
||||
class @Thread extends @Content
|
||||
urlMappers:
|
||||
'retrieve' : -> DiscussionUtil.urlFor('retrieve_single_thread', @discussion.id, @id)
|
||||
'reply' : -> DiscussionUtil.urlFor('create_comment', @id)
|
||||
'unvote' : -> DiscussionUtil.urlFor("undo_vote_for_#{@get('type')}", @id)
|
||||
'upvote' : -> DiscussionUtil.urlFor("upvote_#{@get('type')}", @id)
|
||||
'downvote' : -> DiscussionUtil.urlFor("downvote_#{@get('type')}", @id)
|
||||
'close' : -> DiscussionUtil.urlFor('openclose_thread', @id)
|
||||
'update' : -> DiscussionUtil.urlFor('update_thread', @id)
|
||||
'delete' : -> DiscussionUtil.urlFor('delete_thread', @id)
|
||||
'follow' : -> DiscussionUtil.urlFor('follow_thread', @id)
|
||||
'unfollow' : -> DiscussionUtil.urlFor('unfollow_thread', @id)
|
||||
initialize: ->
|
||||
@bind "add", (item) =>
|
||||
item.collection = @
|
||||
|
||||
initialize: ->
|
||||
@set('thread', @)
|
||||
super()
|
||||
|
||||
permalink: ->
|
||||
discussion_id = @get('commentable_id')
|
||||
return Discussion.urlFor("permanent_link_thread", discussion_id, @id)
|
||||
|
||||
class @ThreadView extends @ContentView
|
||||
|
||||
class @Comment extends @Content
|
||||
urlMappers:
|
||||
'reply': -> DiscussionUtil.urlFor('create_sub_comment', @id)
|
||||
'unvote': -> DiscussionUtil.urlFor("undo_vote_for_#{@get('type')}", @id)
|
||||
'upvote': -> DiscussionUtil.urlFor("upvote_#{@get('type')}", @id)
|
||||
'downvote': -> DiscussionUtil.urlFor("downvote_#{@get('type')}", @id)
|
||||
'endorse': -> DiscussionUtil.urlFor('endorse_comment', @id)
|
||||
'update': -> DiscussionUtil.urlFor('update_comment', @id)
|
||||
'delete': -> DiscussionUtil.urlFor('delete_comment', @id)
|
||||
|
||||
permalink: ->
|
||||
thread_id = @get('thread').id
|
||||
discussion_id = @get('thread').get('commentable_id')
|
||||
return Discussion.urlFor("permanent_link_comment", discussion_id, thread_id, @id)
|
||||
|
||||
getCommentsCount: ->
|
||||
count = 0
|
||||
@get('comments').each (comment) ->
|
||||
count += comment.getCommentsCount() + 1
|
||||
count
|
||||
|
||||
class @CommentView extends @ContentView
|
||||
|
||||
class @Comments extends Backbone.Collection
|
||||
|
||||
model: Comment
|
||||
|
||||
initialize: ->
|
||||
@bind "add", (item) =>
|
||||
item.collection = @
|
||||
|
||||
find: (id) ->
|
||||
_.first @where(id: id)
|
||||
find: (id) ->
|
||||
_.first @where(id: id)
|
||||
|
||||
@@ -1,167 +1,178 @@
|
||||
class @Discussion extends Backbone.Collection
|
||||
model: Thread
|
||||
if Backbone?
|
||||
class @Discussion extends Backbone.Collection
|
||||
model: Thread
|
||||
|
||||
initialize: ->
|
||||
DiscussionUtil.addDiscussion @id, @
|
||||
@bind "add", (item) =>
|
||||
item.discussion = @
|
||||
initialize: ->
|
||||
DiscussionUtil.addDiscussion @id, @
|
||||
@bind "add", (item) =>
|
||||
item.discussion = @
|
||||
|
||||
find: (id) ->
|
||||
_.first @where(id: id)
|
||||
find: (id) ->
|
||||
_.first @where(id: id)
|
||||
|
||||
addThread: (thread, options) ->
|
||||
options ||= {}
|
||||
model = new Thread thread
|
||||
@add model
|
||||
model
|
||||
addThread: (thread, options) ->
|
||||
options ||= {}
|
||||
model = new Thread thread
|
||||
@add model
|
||||
model
|
||||
|
||||
class @DiscussionView extends Backbone.View
|
||||
class @DiscussionView extends Backbone.View
|
||||
|
||||
$: (selector) ->
|
||||
@$local.find(selector)
|
||||
$: (selector) ->
|
||||
@$local.find(selector)
|
||||
|
||||
initLocal: ->
|
||||
@$local = @$el.children(".local")
|
||||
@$delegateElement = @$local
|
||||
initLocal: ->
|
||||
@$local = @$el.children(".local")
|
||||
@$delegateElement = @$local
|
||||
|
||||
initialize: ->
|
||||
@initLocal()
|
||||
@model.id = @$el.attr("_id")
|
||||
@model.view = @
|
||||
@$el.children(".threads").children(".thread").each (index, elem) =>
|
||||
threadView = new ThreadView el: elem, model: @model.find $(elem).attr("_id")
|
||||
if @$el.hasClass("forum-discussion")
|
||||
$(".discussion-sidebar").find(".sidebar-new-post-button")
|
||||
.unbind('click').click $.proxy @newPost, @
|
||||
else if @$el.hasClass("inline-discussion")
|
||||
@newPost()
|
||||
initialize: ->
|
||||
@initLocal()
|
||||
@model.id = @$el.attr("_id")
|
||||
@model.view = @
|
||||
@$el.children(".threads").children(".thread").each (index, elem) =>
|
||||
threadView = new ThreadView el: elem, model: @model.find $(elem).attr("_id")
|
||||
if @$el.hasClass("forum-discussion")
|
||||
$(".discussion-sidebar").find(".sidebar-new-post-button")
|
||||
.unbind('click').click $.proxy @newPost, @
|
||||
else if @$el.hasClass("inline-discussion")
|
||||
@newPost()
|
||||
|
||||
reload: ($elem, url) ->
|
||||
if not url then return
|
||||
DiscussionUtil.get $elem, url, {}, (response, textStatus) =>
|
||||
$parent = @$el.parent()
|
||||
@$el.replaceWith(response.html)
|
||||
$discussion = $parent.find("section.discussion")
|
||||
@model.reset(response.discussionData, { silent: false })
|
||||
view = new DiscussionView el: $discussion[0], model: @model
|
||||
DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
|
||||
reload: ($elem, url) ->
|
||||
if not url then return
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
$loading: $elem
|
||||
url: url
|
||||
type: "GET"
|
||||
success: (response, textStatus) =>
|
||||
$parent = @$el.parent()
|
||||
@$el.replaceWith(response.html)
|
||||
$discussion = $parent.find("section.discussion")
|
||||
@model.reset(response.discussionData, { silent: false })
|
||||
view = new DiscussionView el: $discussion[0], model: @model
|
||||
DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
|
||||
$("html, body").animate({ scrollTop: 0 }, 0)
|
||||
|
||||
loadSimilarPost: (event) ->
|
||||
$title = @$(".new-post-title")
|
||||
$wrapper = @$(".new-post-similar-posts-wrapper")
|
||||
$similarPosts = @$(".new-post-similar-posts")
|
||||
prevText = $title.attr("prev-text")
|
||||
text = $title.val()
|
||||
if text == prevText
|
||||
if @$(".similar-post").length
|
||||
$wrapper.show()
|
||||
else if $.trim(text).length
|
||||
$elem = $(event.target)
|
||||
url = DiscussionUtil.urlFor 'search_similar_threads', @model.id
|
||||
data = { text: @$(".new-post-title").val() }
|
||||
DiscussionUtil.get $elem, url, data, (response, textStatus) =>
|
||||
$similarPosts.empty()
|
||||
if $.type(response) == "array" and response.length
|
||||
loadSimilarPost: (event) ->
|
||||
console.log "loading similar"
|
||||
$title = @$(".new-post-title")
|
||||
$wrapper = @$(".new-post-similar-posts-wrapper")
|
||||
$similarPosts = @$(".new-post-similar-posts")
|
||||
prevText = $title.attr("prev-text")
|
||||
text = $title.val()
|
||||
if text == prevText
|
||||
if @$(".similar-post").length
|
||||
$wrapper.show()
|
||||
for thread in response
|
||||
$similarPost = $("<a>").addClass("similar-post")
|
||||
.html(thread["title"])
|
||||
.attr("href", "javascript:void(0)") #TODO
|
||||
.appendTo($similarPosts)
|
||||
else
|
||||
$wrapper.hide()
|
||||
else
|
||||
$wrapper.hide()
|
||||
$title.attr("prev-text", text)
|
||||
else if $.trim(text).length
|
||||
$elem = $(event.target)
|
||||
url = DiscussionUtil.urlFor 'search_similar_threads', @model.id
|
||||
data = { text: @$(".new-post-title").val() }
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
data: data
|
||||
dataType: 'json'
|
||||
success: (response, textStatus) =>
|
||||
$wrapper.html(response.html)
|
||||
if $wrapper.find(".similar-post").length
|
||||
$wrapper.show()
|
||||
$wrapper.find(".hide-similar-posts").click =>
|
||||
$wrapper.hide()
|
||||
else
|
||||
$wrapper.hide()
|
||||
$title.attr("prev-text", text)
|
||||
|
||||
|
||||
newPost: ->
|
||||
if not @$(".wmd-panel").length
|
||||
view = { discussion_id: @model.id }
|
||||
@$el.children(".discussion-non-content").append Mustache.render DiscussionUtil.getTemplate("_new_post"), view
|
||||
$newPostBody = @$(".new-post-body")
|
||||
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "new-post-body"
|
||||
newPost: ->
|
||||
if not @$(".wmd-panel").length
|
||||
view = { discussion_id: @model.id }
|
||||
@$el.children(".discussion-non-content").append Mustache.render DiscussionUtil.getTemplate("_new_post"), view
|
||||
$newPostBody = @$(".new-post-body")
|
||||
DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "new-post-body"
|
||||
|
||||
$input = DiscussionUtil.getWmdInput @$el, $.proxy(@$, @), "new-post-body"
|
||||
$input.attr("placeholder", "post a new topic...")
|
||||
if @$el.hasClass("inline-discussion")
|
||||
$input.bind 'focus', (e) =>
|
||||
$input = DiscussionUtil.getWmdInput @$el, $.proxy(@$, @), "new-post-body"
|
||||
$input.attr("placeholder", "post a new topic...")
|
||||
if @$el.hasClass("inline-discussion")
|
||||
$input.bind 'focus', (e) =>
|
||||
@$(".new-post-form").removeClass('collapsed')
|
||||
else if @$el.hasClass("forum-discussion")
|
||||
@$(".new-post-form").removeClass('collapsed')
|
||||
else if @$el.hasClass("forum-discussion")
|
||||
@$(".new-post-form").removeClass('collapsed')
|
||||
|
||||
@$(".new-post-tags").tagsInput DiscussionUtil.tagsInputOptions()
|
||||
@$(".new-post-tags").tagsInput DiscussionUtil.tagsInputOptions()
|
||||
|
||||
@$(".new-post-title").blur $.proxy(@loadSimilarPost, @)
|
||||
@$(".new-post-title").blur $.proxy(@loadSimilarPost, @)
|
||||
|
||||
@$(".hide-similar-posts").click =>
|
||||
@$(".new-post-similar-posts-wrapper").hide()
|
||||
@$(".hide-similar-posts").click =>
|
||||
@$(".new-post-similar-posts-wrapper").hide()
|
||||
|
||||
@$(".discussion-submit-post").click $.proxy(@submitNewPost, @)
|
||||
@$(".discussion-cancel-post").click $.proxy(@cancelNewPost, @)
|
||||
|
||||
|
||||
@$(".new-post-form").show()
|
||||
|
||||
submitNewPost: (event) ->
|
||||
title = @$(".new-post-title").val()
|
||||
body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "new-post-body"
|
||||
tags = @$(".new-post-tags").val()
|
||||
anonymous = false || @$(".discussion-post-anonymously").is(":checked")
|
||||
autowatch = false || @$(".discussion-auto-watch").is(":checked")
|
||||
url = DiscussionUtil.urlFor('create_thread', @model.id)
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $(event.target)
|
||||
url: url
|
||||
type: "POST"
|
||||
dataType: 'json'
|
||||
data:
|
||||
title: title
|
||||
body: body
|
||||
tags: tags
|
||||
anonymous: anonymous
|
||||
auto_subscribe: autowatch
|
||||
error: DiscussionUtil.formErrorHandler(@$(".new-post-form-errors"))
|
||||
success: (response, textStatus) =>
|
||||
DiscussionUtil.clearFormErrors(@$(".new-post-form-errors"))
|
||||
$thread = $(response.html)
|
||||
@$el.children(".threads").prepend($thread)
|
||||
|
||||
@$(".new-post-title").val("")
|
||||
DiscussionUtil.setWmdContent @$el, $.proxy(@$, @), "new-post-body", ""
|
||||
@$(".new-post-tags").val("")
|
||||
@$(".new-post-tags").importTags("")
|
||||
|
||||
thread = @model.addThread response.content
|
||||
threadView = new ThreadView el: $thread[0], model: thread
|
||||
thread.updateInfo response.annotated_content_info
|
||||
@cancelNewPost()
|
||||
@$(".discussion-submit-post").click $.proxy(@submitNewPost, @)
|
||||
@$(".discussion-cancel-post").click $.proxy(@cancelNewPost, @)
|
||||
|
||||
|
||||
cancelNewPost: (event) ->
|
||||
if @$el.hasClass("inline-discussion")
|
||||
@$(".new-post-form").addClass("collapsed")
|
||||
else if @$el.hasClass("forum-discussion")
|
||||
@$(".new-post-form").hide()
|
||||
@$(".new-post-form").show()
|
||||
|
||||
search: (event) ->
|
||||
event.preventDefault()
|
||||
$elem = $(event.target)
|
||||
url = URI($elem.attr("action")).addSearch({text: @$(".search-input").val()})
|
||||
@reload($elem, url)
|
||||
submitNewPost: (event) ->
|
||||
title = @$(".new-post-title").val()
|
||||
body = DiscussionUtil.getWmdContent @$el, $.proxy(@$, @), "new-post-body"
|
||||
tags = @$(".new-post-tags").val()
|
||||
anonymous = false || @$(".discussion-post-anonymously").is(":checked")
|
||||
autowatch = false || @$(".discussion-auto-watch").is(":checked")
|
||||
url = DiscussionUtil.urlFor('create_thread', @model.id)
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $(event.target)
|
||||
$loading: $(event.target) if event
|
||||
url: url
|
||||
type: "POST"
|
||||
dataType: 'json'
|
||||
data:
|
||||
title: title
|
||||
body: body
|
||||
tags: tags
|
||||
anonymous: anonymous
|
||||
auto_subscribe: autowatch
|
||||
error: DiscussionUtil.formErrorHandler(@$(".new-post-form-errors"))
|
||||
success: (response, textStatus) =>
|
||||
DiscussionUtil.clearFormErrors(@$(".new-post-form-errors"))
|
||||
$thread = $(response.html)
|
||||
@$el.children(".threads").prepend($thread)
|
||||
|
||||
sort: ->
|
||||
$elem = $(event.target)
|
||||
url = $elem.attr("sort-url")
|
||||
@reload($elem, url)
|
||||
@$(".new-post-similar-posts").empty()
|
||||
@$(".new-post-similar-posts-wrapper").hide()
|
||||
@$(".new-post-title").val("").attr("prev-text", "")
|
||||
DiscussionUtil.setWmdContent @$el, $.proxy(@$, @), "new-post-body", ""
|
||||
@$(".new-post-tags").val("")
|
||||
@$(".new-post-tags").importTags("")
|
||||
|
||||
page: (event) ->
|
||||
$elem = $(event.target)
|
||||
url = $elem.attr("page-url")
|
||||
@reload($elem, url)
|
||||
thread = @model.addThread response.content
|
||||
threadView = new ThreadView el: $thread[0], model: thread
|
||||
thread.updateInfo response.annotated_content_info
|
||||
@cancelNewPost()
|
||||
|
||||
|
||||
events:
|
||||
"submit .search-wrapper>.discussion-search-form": "search"
|
||||
"click .discussion-search-link": "search"
|
||||
"click .discussion-sort-link": "sort"
|
||||
"click .discussion-page-link": "page"
|
||||
cancelNewPost: (event) ->
|
||||
if @$el.hasClass("inline-discussion")
|
||||
@$(".new-post-form").addClass("collapsed")
|
||||
else if @$el.hasClass("forum-discussion")
|
||||
@$(".new-post-form").hide()
|
||||
|
||||
search: (event) ->
|
||||
event.preventDefault()
|
||||
$elem = $(event.target)
|
||||
url = URI($elem.attr("action")).addSearch({text: @$(".search-input").val()})
|
||||
@reload($elem, url)
|
||||
|
||||
sort: ->
|
||||
$elem = $(event.target)
|
||||
url = $elem.attr("sort-url")
|
||||
@reload($elem, url)
|
||||
|
||||
page: (event) ->
|
||||
$elem = $(event.target)
|
||||
url = $elem.attr("page-url")
|
||||
@reload($elem, url)
|
||||
|
||||
events:
|
||||
"submit .search-wrapper>.discussion-search-form": "search"
|
||||
"click .discussion-search-link": "search"
|
||||
"click .discussion-sort-link": "sort"
|
||||
"click .discussion-page-link": "page"
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
class @DiscussionModuleView extends Backbone.View
|
||||
events:
|
||||
"click .discussion-show": "toggleDiscussion"
|
||||
toggleDiscussion: (event) ->
|
||||
if @showed
|
||||
@$("section.discussion").hide()
|
||||
$(event.target).html("Show Discussion")
|
||||
@showed = false
|
||||
else
|
||||
if @retrieved
|
||||
@$("section.discussion").show()
|
||||
$(event.target).html("Hide Discussion")
|
||||
@showed = true
|
||||
if Backbone?
|
||||
class @DiscussionModuleView extends Backbone.View
|
||||
events:
|
||||
"click .discussion-show": "toggleDiscussion"
|
||||
toggleDiscussion: (event) ->
|
||||
if @showed
|
||||
@$("section.discussion").hide()
|
||||
$(event.target).html("Show Discussion")
|
||||
@showed = false
|
||||
else
|
||||
$elem = $(event.target)
|
||||
discussion_id = $elem.attr("discussion_id")
|
||||
url = DiscussionUtil.urlFor 'retrieve_discussion', discussion_id
|
||||
Discussion.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
type: "GET"
|
||||
dataType: 'json'
|
||||
success: (response, textStatus) =>
|
||||
@$el.append(response.html)
|
||||
$discussion = @$el.find("section.discussion")
|
||||
$(event.target).html("Hide Discussion")
|
||||
discussion = new Discussion()
|
||||
discussion.reset(response.discussionData, {silent: false})
|
||||
view = new DiscussionView(el: $discussion[0], model: discussion)
|
||||
DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
|
||||
@retrieved = true
|
||||
@showed = true
|
||||
if @retrieved
|
||||
@$("section.discussion").show()
|
||||
$(event.target).html("Hide Discussion")
|
||||
@showed = true
|
||||
else
|
||||
$elem = $(event.target)
|
||||
discussion_id = $elem.attr("discussion_id")
|
||||
url = DiscussionUtil.urlFor 'retrieve_discussion', discussion_id
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
$loading: $elem
|
||||
url: url
|
||||
type: "GET"
|
||||
dataType: 'json'
|
||||
success: (response, textStatus) =>
|
||||
@$el.append(response.html)
|
||||
$discussion = @$el.find("section.discussion")
|
||||
$(event.target).html("Hide Discussion")
|
||||
discussion = new Discussion()
|
||||
discussion.reset(response.discussionData, {silent: false})
|
||||
view = new DiscussionView(el: $discussion[0], model: discussion)
|
||||
DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
|
||||
@retrieved = true
|
||||
@showed = true
|
||||
|
||||
@@ -12,4 +12,10 @@ $ ->
|
||||
discussion.reset(discussionData, {silent: false})
|
||||
view = new DiscussionView(el: elem, model: discussion)
|
||||
|
||||
DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
|
||||
if window.$$annotated_content_info?
|
||||
DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info)
|
||||
|
||||
$userProfile = $(".discussion-sidebar>.user-profile")
|
||||
if $userProfile.length
|
||||
console.log "initialize user profile"
|
||||
view = new DiscussionUserProfileView(el: $userProfile[0])
|
||||
|
||||
@@ -1,34 +1,29 @@
|
||||
if not @Discussion?
|
||||
@Discussion = {}
|
||||
class @DiscussionUserProfileView extends Backbone.View
|
||||
toggleModeratorStatus: (event) ->
|
||||
confirmValue = confirm("Are you sure?")
|
||||
if not confirmValue then return
|
||||
$elem = $(event.target)
|
||||
if $elem.hasClass("sidebar-promote-moderator-button")
|
||||
isModerator = true
|
||||
else if $elem.hasClass("sidebar-revoke-moderator-button")
|
||||
isModerator = false
|
||||
else
|
||||
console.error "unrecognized moderator status"
|
||||
return
|
||||
url = DiscussionUtil.urlFor('update_moderator_status', $$profiled_user_id)
|
||||
DiscussionUtil.safeAjax
|
||||
$elem: $elem
|
||||
url: url
|
||||
type: "POST"
|
||||
dataType: 'json'
|
||||
data:
|
||||
is_moderator: isModerator
|
||||
error: (response, textStatus, e) ->
|
||||
console.log e
|
||||
success: (response, textStatus) =>
|
||||
parent = @$el.parent()
|
||||
@$el.replaceWith(response.html)
|
||||
view = new DiscussionUserProfileView el: parent.children(".user-profile")
|
||||
|
||||
Discussion = @Discussion
|
||||
|
||||
@Discussion = $.extend @Discussion,
|
||||
initializeUserProfile: ($userProfile) ->
|
||||
$local = Discussion.generateLocal $userProfile
|
||||
|
||||
handleUpdateModeratorStatus = (elem, isModerator) ->
|
||||
confirmValue = confirm("Are you sure?")
|
||||
if not confirmValue then return
|
||||
url = Discussion.urlFor('update_moderator_status', $$profiled_user_id)
|
||||
Discussion.safeAjax
|
||||
$elem: $(elem)
|
||||
url: url
|
||||
type: "POST"
|
||||
dataType: 'json'
|
||||
data:
|
||||
is_moderator: isModerator
|
||||
error: (response, textStatus, e) ->
|
||||
console.log e
|
||||
success: (response, textStatus) ->
|
||||
parent = $userProfile.parent()
|
||||
$userProfile.replaceWith(response.html)
|
||||
Discussion.initializeUserProfile parent.children(".user-profile")
|
||||
|
||||
Discussion.bindLocalEvents $local,
|
||||
"click .sidebar-revoke-moderator-button": (event) ->
|
||||
handleUpdateModeratorStatus(this, false)
|
||||
"click .sidebar-promote-moderator-button": (event) ->
|
||||
handleUpdateModeratorStatus(this, true)
|
||||
|
||||
initializeUserActiveDiscussion: ($discussion) ->
|
||||
events:
|
||||
"click .sidebar-toggle-moderator-button": "toggleModeratorStatus"
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
$ ->
|
||||
$.fn.extend
|
||||
loading: ->
|
||||
$(this).after("<span class='discussion-loading'></span>")
|
||||
loaded: ->
|
||||
$(this).parent().children(".discussion-loading").remove()
|
||||
|
||||
class @DiscussionUtil
|
||||
|
||||
@wmdEditors: {}
|
||||
@@ -62,9 +69,16 @@ class @DiscussionUtil
|
||||
$elem = params.$elem
|
||||
if $elem.attr("disabled")
|
||||
return
|
||||
$elem.attr("disabled", "disabled")
|
||||
params["beforeSend"] = ->
|
||||
$elem.attr("disabled", "disabled")
|
||||
if params["$loading"]
|
||||
console.log "loading"
|
||||
params["$loading"].loading()
|
||||
$.ajax(params).always ->
|
||||
$elem.removeAttr("disabled")
|
||||
if params["$loading"]
|
||||
console.log "loaded"
|
||||
params["$loading"].loaded()
|
||||
|
||||
@get: ($elem, url, data, success) ->
|
||||
@safeAjax
|
||||
|
||||
BIN
lms/static/images/discussion/loading.gif
Normal file
BIN
lms/static/images/discussion/loading.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 809 B |
@@ -35,7 +35,13 @@ $tag-text-color: #5b614f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.discussion-loading {
|
||||
background-image: url(../images/discussion/loading.gif);
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin-left: 2px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/*** Discussions ***/
|
||||
|
||||
@@ -49,8 +55,6 @@ $tag-text-color: #5b614f;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** Sidebar ***/
|
||||
|
||||
.sidebar-module {
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
@import 'course/courseware/sidebar';
|
||||
@import 'course/courseware/amplifier';
|
||||
|
||||
|
||||
// course-specific courseware (all styles in these files should be gated by a
|
||||
// course-specific class). This should be replaced with a better way of
|
||||
// providing course-specific styling.
|
||||
@import "course/courseware/courses/_cs188.scss";
|
||||
|
||||
// wiki
|
||||
@import "course/wiki/basic-html";
|
||||
@import "course/wiki/sidebar";
|
||||
|
||||
32
lms/static/sass/course/courseware/courses/_cs188.scss
Normal file
32
lms/static/sass/course/courseware/courses/_cs188.scss
Normal file
@@ -0,0 +1,32 @@
|
||||
body.cs188 {
|
||||
|
||||
.course-content{
|
||||
|
||||
.project {
|
||||
ul, ol {
|
||||
margin-top: 3px;
|
||||
list-style: disc;
|
||||
ul, ol {
|
||||
margin: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h3, h4 {
|
||||
font-weight: bold;
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
p, .code_snippet {
|
||||
margin-bottom: 1.416em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
## <script type="text/javascript" src="${static.url('js/vendor/CodeMirror-2.25/mode/python/python.js')}"></script>
|
||||
|
||||
<%static:js group='courseware'/>
|
||||
<%static:js group='discussion'/>
|
||||
|
||||
<%include file="../discussion/_js_body_dependencies.html" />
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<%! import django_comment_client.helpers as helpers %>
|
||||
|
||||
<%def name="render_content(content)">
|
||||
${helpers.render_content(content)}
|
||||
<%def name="render_content(content, *args, **kwargs)">
|
||||
${helpers.render_content(content, *args, **kwargs)}
|
||||
</%def>
|
||||
|
||||
<%def name="render_content_with_comments(content)">
|
||||
<%def name="render_content_with_comments(content, *args, **kwargs)">
|
||||
<div class="${content['type']}${helpers.show_if(' endorsed', content.get('endorsed'))}" _id="${content['id']}" _discussion_id="${content.get('commentable_id', '')}" _author_id="${helpers.show_if(content['user_id'], not content.get('anonymous'))}">
|
||||
${render_content(content)}
|
||||
${render_comments(content.get('children', []))}
|
||||
${render_content(content, *args, **kwargs)}
|
||||
${render_comments(content.get('children', []), *args, **kwargs)}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="render_comments(comments)">
|
||||
<%def name="render_comments(comments, *args, **kwargs)">
|
||||
<div class="comments">
|
||||
% for comment in comments:
|
||||
${render_content_with_comments(comment)}
|
||||
${render_content_with_comments(comment, *args, **kwargs)}
|
||||
% endfor
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
9
lms/templates/discussion/_similar_posts.html
Normal file
9
lms/templates/discussion/_similar_posts.html
Normal file
@@ -0,0 +1,9 @@
|
||||
% if len(threads) > 0:
|
||||
Similar Posts:
|
||||
<a class="hide-similar-posts" href="javascript:void(0)">Hide</a>
|
||||
<div class="new-post-similar-posts">
|
||||
% for thread in threads:
|
||||
<a class="similar-post" href="${thread['permalink']}">${thread['title']}</a>
|
||||
% endfor
|
||||
</div>
|
||||
% endif
|
||||
@@ -1,12 +1,12 @@
|
||||
<%namespace name="renderer" file="_thread.html"/>
|
||||
<%namespace name="renderer" file="_content_renderer.html"/>
|
||||
|
||||
<section class="discussion user-active-discussion">
|
||||
<section class="discussion user-active-discussion" _id="${user_id}">
|
||||
|
||||
<div class="discussion-non-content discussion-local"></div>
|
||||
<div class="discussion-non-content local"></div>
|
||||
|
||||
<div class="threads">
|
||||
% for thread in threads:
|
||||
${renderer.render_thread(course_id, thread, show_comments=True)}
|
||||
${renderer.render_content_with_comments(thread, {'partial_comments': True})}
|
||||
% endfor
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%! from django_comment_client.utils import pluralize %>
|
||||
<%! from django_comment_client.helpers import pluralize %>
|
||||
<%! from django_comment_client.permissions import has_permission, check_permissions_by_view %>
|
||||
<%! from operator import attrgetter %>
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<div class="sidebar-comments-count"><span>${profiled_user['comments_count']}</span> ${pluralize('comment', profiled_user['comments_count'])}</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-revoke-moderator-button">Revoke Moderator provileges</a>
|
||||
<a href="javascript:void(0)" class="sidebar-toggle-moderator-button sidebar-revoke-moderator-button">Revoke Moderator provileges</a>
|
||||
% else:
|
||||
<a href="javascript:void(0)" class="sidebar-promote-moderator-button">Promote to Moderator</a>
|
||||
<a href="javascript:void(0)" class="sidebar-toggle-moderator-button sidebar-promote-moderator-button">Promote to Moderator</a>
|
||||
% endif
|
||||
% endif
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<%inherit file="../main.html" />
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
<%block name="bodyclass">discussion</%block>
|
||||
<%block name="title"><title>Discussion – MITx 6.002x</title></%block>
|
||||
<%block name="title"><title>Discussion – ${course.number}</title></%block>
|
||||
|
||||
<%block name="headextra">
|
||||
<%static:css group='course'/>
|
||||
@@ -32,7 +32,7 @@
|
||||
</section>
|
||||
|
||||
<section class="course-content">
|
||||
${content}
|
||||
${content.decode('utf-8')}
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
anonymous
|
||||
{{/content.anonymous}}
|
||||
{{^content.anonymous}}
|
||||
{{content.username}}
|
||||
<a href="{{##url_for_user}}{{content.user_id}}{{/url_for_user}}">{{content.username}}</a>
|
||||
{{/content.anonymous}}
|
||||
</div>
|
||||
<div class="show-comments-wrapper">
|
||||
@@ -51,7 +51,9 @@
|
||||
{{/thread}}
|
||||
</div>
|
||||
<ul class="discussion-actions">
|
||||
<li><a class="discussion-link discussion-reply discussion-reply-{{content.type}}" href="javascript:void(0)">Reply</a></li>
|
||||
{{^max_depth}}
|
||||
<li><a class="discussion-link discussion-reply discussion-reply-{{content.type}}" href="javascript:void(0)">Reply</a></li>
|
||||
{{/max_depth}}
|
||||
{{#thread}}
|
||||
<li><div class="follow-wrapper"><a class="discussion-link discussion-follow-thread" href="javascript:void(0)">Follow</a></div></li>
|
||||
{{/thread}}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
<form class="new-post-form collapsed" id="new-post-form" style="display: block; ">
|
||||
<ul class="new-post-form-errors discussion-errors"></ul>
|
||||
<input type="text" class="new-post-title title-input" placeholder="Title" />
|
||||
<div class="new-post-similar-posts-wrapper" style="display: none">
|
||||
Similar Posts:
|
||||
<a class="hide-similar-posts" href="javascript:void(0)">Hide</a>
|
||||
<div class="new-post-similar-posts"></div>
|
||||
</div>
|
||||
<div class="new-post-similar-posts-wrapper" style="display: none"></div>
|
||||
<div class="new-post-body reply-body"></div>
|
||||
<input class="new-post-tags" placeholder="Tags" />
|
||||
<div class="post-options">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<%inherit file="../main.html" />
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
<%block name="bodyclass">discussion</%block>
|
||||
<%block name="title"><title>Discussion – MITx 6.002x</title></%block>
|
||||
<%block name="title"><title>Discussion – ${course.number}</title></%block>
|
||||
|
||||
<%block name="headextra">
|
||||
<%static:css group='course'/>
|
||||
@@ -30,7 +30,7 @@
|
||||
</section>
|
||||
|
||||
<section class="course-content">
|
||||
${content}
|
||||
${content.decode('utf-8')}
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
<%! from django.template.defaultfilters import escapejs %>
|
||||
<%namespace name="renderer" file="_thread.html"/>
|
||||
|
||||
<%inherit file="../main.html" />
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
<%block name="bodyclass">discussion</%block>
|
||||
<%block name="title"><title>Discussion – MITx 6.002x</title></%block>
|
||||
<%block name="title"><title>Discussion – ${course.number}</title></%block>
|
||||
|
||||
<%block name="headextra">
|
||||
<%static:css group='course'/>
|
||||
<%include file="_js_head_dependencies.html" />
|
||||
</%block>
|
||||
|
||||
<%block name="js_extra">
|
||||
<%include file="_js_dependencies.html" />
|
||||
<%include file="_js_body_dependencies.html" />
|
||||
<%static:js group='discussion'/>
|
||||
</%block>
|
||||
|
||||
<%include file="/courseware/course_navigation.html" args="active_page='discussion'" />
|
||||
<%include file="../courseware/course_navigation.html" args="active_page='discussion'" />
|
||||
|
||||
<section class="container">
|
||||
<div class="course-wrapper">
|
||||
|
||||
Reference in New Issue
Block a user