diff --git a/common/static/images/spinner-on-grey.gif b/common/static/images/spinner-on-grey.gif new file mode 100644 index 0000000000..f43d52e4f4 Binary files /dev/null and b/common/static/images/spinner-on-grey.gif differ diff --git a/common/static/images/spinner.gif b/common/static/images/spinner.gif index b2f94cd12c..e23eb78abe 100644 Binary files a/common/static/images/spinner.gif and b/common/static/images/spinner.gif differ diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index f9e1baa8ed..2c919acc48 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -227,6 +227,7 @@ def forum_form_discussion(request, course_id): 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info),escapedict), 'course_id': course.id, 'category_map': category_map, + 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), } # print "start rendering.." return render_to_response('discussion/index.html', context) @@ -273,6 +274,8 @@ def single_thread(request, course_id, discussion_id, thread_id): thread['courseware_location'] = courseware_context['courseware_location'] thread['courseware_title'] = courseware_context['courseware_title'] + threads = [utils.safe_content(thread) for thread in threads] + #recent_active_threads = cc.search_recent_active_threads( # course_id, # recursive=False, @@ -304,6 +307,7 @@ def single_thread(request, course_id, discussion_id, thread_id): 'thread_id': thread_id, 'threads': saxutils.escape(json.dumps(threads), escapedict), 'category_map': category_map, + 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), } return render_to_response('discussion/single_thread.html', context) @@ -314,10 +318,12 @@ def user_profile(request, course_id, user_id): course = get_course_with_access(request.user, course_id, 'load') try: profiled_user = cc.User(id=user_id, course_id=course_id) + query_params = { 'page': request.GET.get('page', 1), - 'per_page': INLINE_THREADS_PER_PAGE, - } + 'per_page': THREADS_PER_PAGE, # more than threads_per_page to show more activities + } + threads, page, num_pages = profiled_user.active_threads(query_params) query_params['page'] = page query_params['num_pages'] = num_pages diff --git a/lms/djangoapps/django_comment_client/utils.py b/lms/djangoapps/django_comment_client/utils.py index 1a24bdf565..4ceea6b82c 100644 --- a/lms/djangoapps/django_comment_client/utils.py +++ b/lms/djangoapps/django_comment_client/utils.py @@ -39,6 +39,14 @@ def strip_blank(dic): def merge_dict(dic1, dic2): return dict(dic1.items() + dic2.items()) +def get_role_ids(course_id): + roles = Role.objects.filter(course_id=course_id) + staff = list(User.objects.filter(is_staff=True).values_list('id', flat=True)) + roles_with_ids = {'Staff': staff} + for role in roles: + roles_with_ids[role.name] = list(role.users.values_list('id', flat=True)) + return roles_with_ids + def get_full_modules(): global _FULLMODULES if not _FULLMODULES: @@ -273,7 +281,7 @@ def safe_content(content): 'created_at', 'updated_at', 'depth', 'type', 'commentable_id', 'comments_count', 'at_position_list', 'children', 'highlighted_title', 'highlighted_body', - 'courseware_title', 'courseware_location' + 'courseware_title', 'courseware_location', 'tags' ] if content.get('anonymous') is False: diff --git a/lms/static/coffee/src/discussion/discussion_module_view.coffee b/lms/static/coffee/src/discussion/discussion_module_view.coffee index d7a555f004..2d2f3eaa04 100644 --- a/lms/static/coffee/src/discussion/discussion_module_view.coffee +++ b/lms/static/coffee/src/discussion/discussion_module_view.coffee @@ -9,7 +9,8 @@ if Backbone? paginationTemplate: -> DiscussionUtil.getTemplate("_pagination") page_re: /\?discussion_page=(\d+)/ initialize: -> - # Set the page if it was set in the URL. This is used to allow deep linking to pages + @toggleDiscussionBtn = @$(".discussion-show") + # Set the page if it was set in the URL. This is used to allow deep linking to pages match = @page_re.exec(window.location.href) if match @page = parseInt(match[1]) @@ -18,31 +19,38 @@ if Backbone? toggleNewPost: (event) -> event.preventDefault() - if @newPostForm.is(':hidden') + if !@newPostForm + @toggleDiscussion() + @isWaitingOnNewPost = true; + return + if @showed @newPostForm.slideDown(300) else - @newPostForm.slideUp(300) + @newPostForm.show() + @toggleDiscussionBtn.addClass('shown') + @toggleDiscussionBtn.find('.button-text').html("Hide Discussion") + @$("section.discussion").slideDown() + @showed = true hideNewPost: (event) -> event.preventDefault() @newPostForm.slideUp(300) toggleDiscussion: (event) -> - thisButton = $(event.target).closest('a') if @showed @$("section.discussion").slideUp() - thisButton.removeClass('shown') - thisButton.find('.button-text').html("Show Discussion") + @toggleDiscussionBtn.removeClass('shown') + @toggleDiscussionBtn.find('.button-text').html("Show Discussion") @showed = false else - thisButton.addClass('shown') - thisButton.find('.button-text').html("Hide Discussion") + @toggleDiscussionBtn.addClass('shown') + @toggleDiscussionBtn.find('.button-text').html("Hide Discussion") if @retrieved @$("section.discussion").slideDown() @showed = true else - $elem = $(event.target) + $elem = @toggleDiscussionBtn @loadPage $elem loadPage: ($elem)=> @@ -59,7 +67,7 @@ if Backbone? renderDiscussion: ($elem, response, textStatus, discussionId) => window.user = new DiscussionUser(response.user_info) Content.loadContentInfos(response.annotated_content_info) - $elem.html("Hide Discussion") + # $elem.html("Hide Discussion") @discussion = new Discussion() @discussion.reset(response.discussion_data, {silent: false}) $discussion = $(Mustache.render $("script#_inline_discussion").html(), {'threads':response.discussion_data, 'discussionId': discussionId}) @@ -77,6 +85,8 @@ if Backbone? @retrieved = true @showed = true @renderPagination(2, response.num_pages) + if @isWaitingOnNewPost + @newPostForm.show() addThread: (thread, collection, options) => # TODO: When doing pagination, this will need to repaginate. Perhaps just reload page 1? diff --git a/lms/static/coffee/src/discussion/discussion_router.coffee b/lms/static/coffee/src/discussion/discussion_router.coffee index 53ada7a596..14ee1b52f3 100644 --- a/lms/static/coffee/src/discussion/discussion_router.coffee +++ b/lms/static/coffee/src/discussion/discussion_router.coffee @@ -26,12 +26,16 @@ if Backbone? @thread = @discussion.get(thread_id) @setActiveThread() if(@main) + @main.cleanup() @main.undelegateEvents() @main = new DiscussionThreadView(el: $(".discussion-column"), model: @thread) @main.render() @main.on "thread:responses:rendered", => @nav.updateSidebar() + @main.on "tag:selected", (tag) => + search = "[#{tag}]" + @nav.setAndSearchFor(search) navigateToThread: (thread_id) => thread = @discussion.get(thread_id) diff --git a/lms/static/coffee/src/discussion/utils.coffee b/lms/static/coffee/src/discussion/utils.coffee index 07a23a4ca6..52a0b510ac 100644 --- a/lms/static/coffee/src/discussion/utils.coffee +++ b/lms/static/coffee/src/discussion/utils.coffee @@ -15,6 +15,11 @@ class @DiscussionUtil @getTemplate: (id) -> $("script##{id}").html() + @isStaff: (user_id) -> + ids = $("#discussion-container").data("roles") + staff = _.union(ids['Staff'], ids['Moderator'], ids['Administrator']) + _.include(staff, parseInt(user_id)) + @bulkUpdateContentInfo: (infos) -> for id, info of infos Content.getContent(id).updateInfo(info) @@ -71,7 +76,7 @@ class @DiscussionUtil params["loadingCallback"].apply(params["$loading"]) else params["$loading"].loading() - $.ajax(params).always -> + request = $.ajax(params).always -> if $elem $elem.removeAttr("disabled") if params["$loading"] @@ -79,6 +84,7 @@ class @DiscussionUtil params["loadedCallback"].apply(params["$loading"]) else params["$loading"].loaded() + return request @get: ($elem, url, data, success) -> @safeAjax diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee index fef8b311a7..e0502d892a 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee @@ -142,7 +142,10 @@ if Backbone? setTimeout (-> @$(".post-search-field").focus()), 200 toggleTopicDrop: (event) => + event.preventDefault() event.stopPropagation() + if @current_search != "" + @clearSearch() @$(".search").removeClass('is-open') @$(".browse").addClass('is-open') @$(".browse").toggleClass('is-dropped') @@ -235,6 +238,11 @@ if Backbone? text = @$(".post-search-field").val() @searchFor(text) + setAndSearchFor: (text) -> + @showSearch() + @$(".post-search-field").val(text) + @searchFor(text) + searchFor: (text, callback, value) -> @current_search = text url = DiscussionUtil.urlFor("search") diff --git a/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee b/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee index f56a47b85d..d53a447880 100644 --- a/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee +++ b/lms/static/coffee/src/discussion/views/discussion_thread_view.coffee @@ -3,6 +3,7 @@ if Backbone? events: "click .discussion-submit-post": "submitComment" + "click .thread-tag": "tagSelected" $: (selector) -> @$el.find(selector) @@ -14,19 +15,39 @@ if Backbone? render: -> @template = _.template($("#thread-template").html()) @$el.html(@template(@model.toJSON())) + @$el.find(".loading").hide() @delegateEvents() @renderShowView() @renderAttrs() + @renderTags() @$("span.timeago").timeago() @makeWmdEditor "reply-body" @renderResponses() @ - renderResponses: -> - DiscussionUtil.safeAjax + cleanup: -> + if @responsesRequest? + @responsesRequest.abort() + + renderTags: -> + tags = $('
') + for tag in @model.get("tags") + tags.append("#{tag}") + @$(".post-body").after(tags) + + tagSelected: (e) -> + @trigger "tag:selected", $(e.target).html() + + + renderResponses: -> + setTimeout(=> + @$el.find(".loading").show() + , 200) + @responsesRequest = DiscussionUtil.safeAjax url: "/courses/#{$$course_id}/discussion/forum/#{@model.get('commentable_id')}/threads/#{@model.id}" success: (data, textStatus, xhr) => + @responsesRequest = null @$el.find(".loading").remove() Content.loadContentInfos(data['annotated_content_info']) comments = new Comments(data['content']['children']) diff --git a/lms/static/coffee/src/discussion/views/response_comment_view.coffee b/lms/static/coffee/src/discussion/views/response_comment_view.coffee index 34cbaac7ad..fbd3b917a1 100644 --- a/lms/static/coffee/src/discussion/views/response_comment_view.coffee +++ b/lms/static/coffee/src/discussion/views/response_comment_view.coffee @@ -18,6 +18,7 @@ if Backbone? @initLocal() @delegateEvents() @renderAttrs() + @markAsStaff() @$el.find(".timeago").timeago() @convertMath() @ @@ -29,3 +30,7 @@ if Backbone? body.children("p").each (index, elem) -> $(elem).replaceWith($(elem).html()) MathJax.Hub.Queue ["Typeset", MathJax.Hub, body[0]] + + markAsStaff: -> + if DiscussionUtil.isStaff(@model.get("user_id")) + @$el.find("a").after('staff') diff --git a/lms/static/coffee/src/discussion/views/thread_response_view.coffee b/lms/static/coffee/src/discussion/views/thread_response_view.coffee index f4719800cc..c6f0643de6 100644 --- a/lms/static/coffee/src/discussion/views/thread_response_view.coffee +++ b/lms/static/coffee/src/discussion/views/thread_response_view.coffee @@ -18,6 +18,7 @@ if Backbone? @renderAttrs() @$el.find(".posted-details").timeago() @convertMath() + @markAsStaff() @renderComments() @ @@ -26,6 +27,11 @@ if Backbone? element.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight element.html() MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]] + markAsStaff: -> + if DiscussionUtil.isStaff(@model.get("user_id")) + @$el.addClass("staff") + @$el.prepend('
staff
') + renderComments: -> comments = new Comments() comments.comparator = (comment) -> diff --git a/lms/static/sass/_discussion.scss b/lms/static/sass/_discussion.scss index 35632af9dc..e75c05c140 100644 --- a/lms/static/sass/_discussion.scss +++ b/lms/static/sass/_discussion.scss @@ -274,20 +274,23 @@ body.discussion { width: 100%; margin-bottom: 20px; @include clearfix; + @include box-sizing(border-box); + + .form-row { + margin-top: 20px; + } + .post-cancel { @include white-button; - border-color: #444; float: left; margin: 10px 0 0 15px; } .post-update { - @include blue-button; float: left; height: 37px; margin-top: 10px; - border-color: #333; padding-bottom: 2px; &:hover { @@ -301,13 +304,28 @@ body.discussion { padding: 0 10px; box-sizing: border-box; border-radius: 3px; - border: 1px solid #333; + border: 1px solid #aaa; font-size: 16px; - font-family: 'Open Sans', sans-serif; + font-family: $sans-serif; color: #333; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) inset; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15) inset; } + .tagsinput { + padding: 10px; + @include box-sizing(border-box); + border: 1px solid #aaa; + border-radius: 3px; + background: #fff; + font-family: 'Monaco', monospace; + font-size: 13px; + line-height: 1.6; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) inset; + + span.tag { + margin-bottom: 0; + } + } } .new-post-form { @@ -1006,8 +1024,14 @@ body.discussion { .post-list { background-color: #ddd; + .loading { padding: 15px 0; + background: #f6f6f6; + + .loading-animation { + background-image: url(../images/spinner-on-grey.gif); + } } a { @@ -1231,6 +1255,12 @@ body.discussion { font-style: italic; color: #888; + .username { + display: block; + font-size: 16px; + font-weight: 700; + } + span { font-style: italic; } @@ -1309,7 +1339,7 @@ body.discussion { > li { position: relative; margin: 0 -10px 30px; - padding: 26px 30px 30px; + padding: 26px 30px 20px; border-radius: 3px; border: 1px solid #b2b2b2; box-shadow: 0 1px 3px rgba(0, 0, 0, .15); @@ -1573,6 +1603,7 @@ body.discussion { .discussion-reply-new { padding: 20px; @include clearfix; + @include transition(opacity .2s); h4 { font-size: 16px; @@ -1619,8 +1650,9 @@ body.discussion { .discussion-module { @extend .discussion-body; + position: relative; margin: 20px 0; - padding: 20px 20px 28px 20px; + padding: 20px; background: #f6f6f6 !important; border-radius: 3px; @@ -1633,10 +1665,13 @@ body.discussion { } } + .loading-animation { + background-image: url(../images/spinner-on-grey.gif); + } + .discussion-show { - display: block; - width: 200px; - margin: auto; + position: relative; + top: 3px; font-size: 14px; text-align: center; @@ -1660,10 +1695,11 @@ body.discussion { .new-post-btn { display: inline-block; + float: right; } section.discussion { - margin-top: 20px; + margin-top: 30px; .threads { margin-top: 20px; @@ -1715,6 +1751,16 @@ body.discussion { padding-bottom: 0; margin-bottom: 15px; + .posted-details { + margin-top: 4px; + + .username { + display: inline; + font-size: 14px; + font-weight: 700; + } + } + h3 { font-size: 19px; font-weight: 700; @@ -1758,13 +1804,16 @@ body.discussion { margin-top: 10px; header { - padding-bottom: 0em; - margin-bottom: 5px; + padding-bottom: 0; + margin-bottom: 15px; .posted-by { - font-size: 0.8em; + float: left; + margin-right: 5px; + font-size: 16px; } } + .response-body { margin-bottom: 0.2em; font-size: 14px; @@ -2169,4 +2218,4 @@ body.discussion { .discussion-user-threads { @extend .discussion-module -} +} \ No newline at end of file diff --git a/lms/templates/discussion/_discussion_module.html b/lms/templates/discussion/_discussion_module.html index 5c44495c08..bb172093f6 100644 --- a/lms/templates/discussion/_discussion_module.html +++ b/lms/templates/discussion/_discussion_module.html @@ -2,4 +2,5 @@
Show Discussion + New Post
diff --git a/lms/templates/discussion/_underscore_templates.html b/lms/templates/discussion/_underscore_templates.html index 2c3585bb58..6c951b2253 100644 --- a/lms/templates/discussion/_underscore_templates.html +++ b/lms/templates/discussion/_underscore_templates.html @@ -25,10 +25,11 @@ + ${'<%- votes["up_count"] %>'}

${'<%- title %>'}

- ${'<%- created_at %>'} by ${"<% if (!obj.anonymous) { %>"} - ${'<%- username %>'} + ${'<%- username %>'} ${"<% } else {print('anonymous');} %>"} + ${'<%- created_at %>'} + diff --git a/lms/templates/discussion/index.html b/lms/templates/discussion/index.html index 53cbe2d9ed..a0cdcdcb44 100644 --- a/lms/templates/discussion/index.html +++ b/lms/templates/discussion/index.html @@ -23,7 +23,7 @@ -

+
@@ -36,4 +36,4 @@
<%include file="_underscore_templates.html" /> -<%include file="_thread_list_template.html" /> \ No newline at end of file +<%include file="_thread_list_template.html" /> diff --git a/lms/templates/discussion/mustache/_inline_discussion.mustache b/lms/templates/discussion/mustache/_inline_discussion.mustache index de15431ca1..22c0564426 100644 --- a/lms/templates/discussion/mustache/_inline_discussion.mustache +++ b/lms/templates/discussion/mustache/_inline_discussion.mustache @@ -1,5 +1,5 @@
- New Post +
diff --git a/lms/templates/discussion/mustache/_inline_thread.mustache b/lms/templates/discussion/mustache/_inline_thread.mustache index 3875c9e7bc..a874b728ce 100644 --- a/lms/templates/discussion/mustache/_inline_thread.mustache +++ b/lms/templates/discussion/mustache/_inline_thread.mustache @@ -5,13 +5,15 @@ + {{votes.up_count}}

{{title}}

- {{created_at}} by {{#user}} - {{username}} + {{username}} {{/user}} {{^user}} anonymous {{/user}} + + {{created_at}} + diff --git a/lms/templates/discussion/single_thread.html b/lms/templates/discussion/single_thread.html index 2ddeae4613..0cc1f28b24 100644 --- a/lms/templates/discussion/single_thread.html +++ b/lms/templates/discussion/single_thread.html @@ -24,7 +24,7 @@ <%include file="_new_post.html" /> -

+