From ff41338cad27b963dfca0a6e44160d6397ec382e Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Sun, 19 Aug 2012 21:58:36 -0700 Subject: [PATCH 01/91] refactor attempt --- .../django_comment_client/forum/views.py | 1 + .../coffee/src/discussion/content.coffee | 412 +----------------- .../coffee/src/discussion/discussion.coffee | 209 ++------- lms/static/coffee/src/discussion/utils.coffee | 251 +---------- .../coffee/src/old_discussion/content.coffee | 409 +++++++++++++++++ .../src/old_discussion/discussion.coffee | 190 ++++++++ .../old_discussion/discussion_module.coffee | 42 ++ .../coffee/src/old_discussion/main.coffee | 23 + .../src/old_discussion/templates.coffee | 73 ++++ .../src/old_discussion/user_profile.coffee | 34 ++ .../coffee/src/old_discussion/utils.coffee | 244 +++++++++++ lms/templates/discussion/_forum.html | 6 +- lms/templates/discussion/_inline.html | 4 +- lms/templates/discussion/_js_data.html | 6 +- .../discussion/_js_dependencies.html | 3 + 15 files changed, 1066 insertions(+), 841 deletions(-) create mode 100644 lms/static/coffee/src/old_discussion/content.coffee create mode 100644 lms/static/coffee/src/old_discussion/discussion.coffee create mode 100644 lms/static/coffee/src/old_discussion/discussion_module.coffee create mode 100644 lms/static/coffee/src/old_discussion/main.coffee create mode 100644 lms/static/coffee/src/old_discussion/templates.coffee create mode 100644 lms/static/coffee/src/old_discussion/user_profile.coffee create mode 100644 lms/static/coffee/src/old_discussion/utils.coffee diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index bf263ffaa1..d76337e8da 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -82,6 +82,7 @@ def render_discussion(request, course_id, threads, *args, **kwargs): 'base_url': base_url, 'query_params': 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 }), } context = dict(context.items() + query_params.items()) return render_to_string(template, context) diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee index 9924e540a2..afd7bd6ff0 100644 --- a/lms/static/coffee/src/discussion/content.coffee +++ b/lms/static/coffee/src/discussion/content.coffee @@ -1,409 +1,7 @@ -if not @Discussion? - @Discussion = {} +$ -> + class Content extends Backbone.Model -Discussion = @Discussion + class Thread extends Content -initializeVote = (content) -> - $content = $(content) - $local = Discussion.generateLocal($content.children(".discussion-content")) - id = $content.attr("_id") - if Discussion.isUpvoted id - $local(".discussion-vote-up").addClass("voted") - else if Discussion.isDownvoted id - $local(".discussion-vote-down").addClass("voted") - -initializeFollowThread = (thread) -> - $thread = $(thread) - id = $thread.attr("_id") - $thread.children(".discussion-content") - .find(".follow-wrapper") - .append(Discussion.subscriptionLink('thread', id)) - -@Discussion = $.extend @Discussion, - - bindContentEvents: (content) -> - - $content = $(content) - $discussionContent = $content.children(".discussion-content") - $local = Discussion.generateLocal($discussionContent) - - id = $content.attr("_id") - - handleReply = (elem) -> - $replyView = $local(".discussion-reply-new") - if $replyView.length - $replyView.show() - else - thread_id = $discussionContent.parents(".thread").attr("_id") - view = - id: id - showWatchCheckbox: not Discussion.isSubscribed(thread_id, "thread") - $discussionContent.append Mustache.render Discussion.replyTemplate, view - Discussion.makeWmdEditor $content, $local, "reply-body" - $local(".discussion-submit-post").click -> handleSubmitReply(this) - $local(".discussion-cancel-post").click -> handleCancelReply(this) - $local(".discussion-reply").hide() - $local(".discussion-edit").hide() - - handleCancelReply = (elem) -> - $replyView = $local(".discussion-reply-new") - if $replyView.length - $replyView.hide() - $local(".discussion-reply").show() - $local(".discussion-edit").show() - - handleSubmitReply = (elem) -> - if $content.hasClass("thread") - url = Discussion.urlFor('create_comment', id) - else if $content.hasClass("comment") - url = Discussion.urlFor('create_sub_comment', id) - else - return - - body = Discussion.getWmdContent $content, $local, "reply-body" - - anonymous = false || $local(".discussion-post-anonymously").is(":checked") - autowatch = false || $local(".discussion-auto-watch").is(":checked") - - Discussion.safeAjax - $elem: $(elem) - url: url - type: "POST" - dataType: 'json' - data: - body: body - anonymous: anonymous - autowatch: autowatch - error: Discussion.formErrorHandler($local(".discussion-errors")) - success: (response, textStatus) -> - Discussion.clearFormErrors($local(".discussion-errors")) - $comment = $(response.html) - $content.children(".comments").prepend($comment) - Discussion.setWmdContent $content, $local, "reply-body", "" - Discussion.setContentInfo response.content['id'], 'can_reply', true - Discussion.setContentInfo response.content['id'], 'editable', true - Discussion.extendContentInfo response.content['id'], response['annotated_content_info'] - Discussion.initializeContent($comment) - Discussion.bindContentEvents($comment) - $local(".discussion-reply-new").hide() - $local(".discussion-reply").show() - $local(".discussion-edit").show() - $discussionContent.attr("status", "normal") - - handleVote = (elem, value) -> - contentType = if $content.hasClass("thread") then "thread" else "comment" - url = Discussion.urlFor("#{value}vote_#{contentType}", id) - Discussion.safeAjax - $elem: $local(".discussion-vote") - url: url - type: "POST" - dataType: "json" - success: (response, textStatus) -> - if textStatus == "success" - $local(".discussion-vote").removeClass("voted") - $local(".discussion-vote-#{value}").addClass("voted") - $local(".discussion-votes-point").html response.votes.point - - handleUnvote = (elem, value) -> - contentType = if $content.hasClass("thread") then "thread" else "comment" - url = Discussion.urlFor("undo_vote_for_#{contentType}", id) - Discussion.safeAjax - $elem: $local(".discussion-vote") - url: url - type: "POST" - dataType: "json" - success: (response, textStatus) -> - if textStatus == "success" - $local(".discussion-vote").removeClass("voted") - $local(".discussion-votes-point").html response.votes.point - - handleCancelEdit = (elem) -> - $local(".discussion-content-edit").hide() - $local(".discussion-content-wrapper").show() - - handleEditThread = (elem) -> - $local(".discussion-content-wrapper").hide() - $editView = $local(".discussion-content-edit") - if $editView.length - $editView.show() - else - view = { - id: id - title: $local(".thread-raw-title").html() - body: $local(".thread-raw-body").html() - tags: $local(".thread-raw-tags").html() - } - $discussionContent.append Mustache.render Discussion.editThreadTemplate, view - Discussion.makeWmdEditor $content, $local, "thread-body-edit" - $local(".thread-tags-edit").tagsInput Discussion.tagsInputOptions() - $local(".discussion-submit-update").unbind("click").click -> handleSubmitEditThread(this) - $local(".discussion-cancel-update").unbind("click").click -> handleCancelEdit(this) - - handleSubmitEditThread = (elem) -> - url = Discussion.urlFor('update_thread', id) - title = $local(".thread-title-edit").val() - body = Discussion.getWmdContent $content, $local, "thread-body-edit" - tags = $local(".thread-tags-edit").val() - Discussion.safeAjax - $elem: $(elem) - url: url - type: "POST" - dataType: 'json' - data: {title: title, body: body, tags: tags}, - error: Discussion.formErrorHandler($local(".discussion-update-errors")) - success: (response, textStatus) -> - Discussion.clearFormErrors($local(".discussion-update-errors")) - $discussionContent.replaceWith(response.html) - Discussion.extendContentInfo response.content['id'], response['annotated_content_info'] - Discussion.initializeContent($content) - Discussion.bindContentEvents($content) - - handleEditComment = (elem) -> - $local(".discussion-content-wrapper").hide() - $editView = $local(".discussion-content-edit") - if $editView.length - $editView.show() - else - view = { id: id, body: $local(".comment-raw-body").html() } - $discussionContent.append Mustache.render Discussion.editCommentTemplate, view - Discussion.makeWmdEditor $content, $local, "comment-body-edit" - $local(".discussion-submit-update").unbind("click").click -> handleSubmitEditComment(this) - $local(".discussion-cancel-update").unbind("click").click -> handleCancelEdit(this) - - handleSubmitEditComment= (elem) -> - url = Discussion.urlFor('update_comment', id) - body = Discussion.getWmdContent $content, $local, "comment-body-edit" - Discussion.safeAjax - $elem: $(elem) - url: url - type: "POST" - dataType: "json" - data: {body: body} - error: Discussion.formErrorHandler($local(".discussion-update-errors")) - success: (response, textStatus) -> - Discussion.clearFormErrors($local(".discussion-update-errors")) - $discussionContent.replaceWith(response.html) - Discussion.extendContentInfo response.content['id'], response['annotated_content_info'] - Discussion.initializeContent($content) - Discussion.bindContentEvents($content) - - handleEndorse = (elem, endorsed) -> - url = Discussion.urlFor('endorse_comment', id) - Discussion.safeAjax - $elem: $(elem) - url: url - type: "POST" - dataType: "json" - data: {endorsed: endorsed} - success: (response, textStatus) -> - if textStatus == "success" - if endorsed - $(content).addClass("endorsed") - else - $(content).removeClass("endorsed") - - $(elem).unbind('click').click -> - handleEndorse(elem, !endorsed) - - handleOpenClose = (elem, text) -> - url = Discussion.urlFor('openclose_thread', id) - closed = undefined - if text.match(/Close/) - closed = true - else if text.match(/[Oo]pen/) - closed = false - else - console.log "Unexpected text " + text + "for open/close thread." - - Discussion.safeAjax - $elem: $(elem) - url: url - type: "POST" - dataType: "json" - data: {closed: closed} - success: (response, textStatus) => - if textStatus == "success" - if closed - $(content).addClass("closed") - $(elem).text "Re-open Thread" - else - $(content).removeClass("closed") - $(elem).text "Close Thread" - error: (response, textStatus, e) -> - console.log e - - handleDelete = (elem) -> - if $content.hasClass("thread") - url = Discussion.urlFor('delete_thread', id) - c = confirm "Are you sure to delete thread \"" + $content.find("a.thread-title").text() + "\"?" - else - url = Discussion.urlFor('delete_comment', id) - c = confirm "Are you sure to delete this comment? " - if c != true - return - Discussion.safeAjax - $elem: $(elem) - url: url - type: "POST" - dataType: "json" - data: {} - success: (response, textStatus) => - if textStatus == "success" - $(content).remove() - error: (response, textStatus, e) -> - console.log e - - handleHideSingleThread = (elem) -> - $threadTitle = $local(".thread-title") - $hideComments = $local(".discussion-hide-comments") - $hideComments.removeClass("discussion-hide-comments") - .addClass("discussion-show-comments") - $content.children(".comments").hide() - $threadTitle.unbind('click').click handleShowSingleThread - $hideComments.unbind('click').click handleShowSingleThread - prevHtml = $hideComments.html() - $hideComments.html prevHtml.replace "Hide", "Show" - - handleShowSingleThread = -> - $threadTitle = $local(".thread-title") - $showComments = $local(".discussion-show-comments") - - if not $showComments.hasClass("first-time") and (not $showComments.length or not $threadTitle.length) - return - - rebindHideEvents = -> - $threadTitle.unbind('click').click handleHideSingleThread - $showComments.unbind('click').click handleHideSingleThread - $showComments.removeClass("discussion-show-comments") - .addClass("discussion-hide-comments") - prevHtml = $showComments.html() - $showComments.html prevHtml.replace "Show", "Hide" - - - if not $showComments.hasClass("first-time") and $content.children(".comments").length - $content.children(".comments").show() - rebindHideEvents() - else - discussion_id = $threadTitle.parents(".discussion").attr("_id") - url = Discussion.urlFor('retrieve_single_thread', discussion_id, id) - Discussion.safeAjax - $elem: $.merge($threadTitle, $showComments) - url: url - type: "GET" - dataType: 'json' - success: (response, textStatus) -> - Discussion.bulkExtendContentInfo response['annotated_content_info'] - $content.append(response['html']) - $content.find(".comment").each (index, comment) -> - Discussion.initializeContent(comment) - Discussion.bindContentEvents(comment) - $showComments.removeClass("first-time") - rebindHideEvents() - - Discussion.bindLocalEvents $local, - - "click .thread-title": -> - handleShowSingleThread(this) - - "click .discussion-show-comments": -> - handleShowSingleThread(this) - - "click .discussion-hide-comments": -> - handleHideSingleThread(this) - - "click .discussion-reply-thread": -> - handleShowSingleThread($local(".thread-title")) - handleReply(this) - - "click .discussion-reply-comment": -> - handleReply(this) - - "click .discussion-cancel-reply": -> - handleCancelReply(this) - - "click .discussion-vote-up": -> - $elem = $(this) - if $elem.hasClass("voted") - handleUnvote($elem) - else - handleVote($elem, "up") - - "click .discussion-vote-down": -> - $elem = $(this) - if $elem.hasClass("voted") - handleUnvote($elem) - else - handleVote($elem, "down") - - "click .admin-endorse": -> - handleEndorse(this, not $content.hasClass("endorsed")) - - "click .admin-openclose": -> - handleOpenClose(this, $(this).text()) - - "click .admin-edit": -> - if $content.hasClass("thread") - handleEditThread(this) - else - handleEditComment(this) - - "click .admin-delete": -> - handleDelete(this) - - initializeContent: (content) -> - - unescapeHighlightTag = (text) -> - text.replace(/\<\;highlight\>\;/g, "") - .replace(/\<\;\/highlight\>\;/g, "") - - stripHighlight = (text, type) -> - text.replace(/\&(amp\;)?lt\;highlight\&(amp\;)?gt\;/g, "") - .replace(/\&(amp\;)?lt\;\/highlight\&(amp\;)?gt\;/g, "") - - - stripLatexHighlight = (text) -> - Discussion.processEachMathAndCode text, stripHighlight - - markdownWithHighlight = (text) -> - converter = Markdown.getMathCompatibleConverter() - unescapeHighlightTag stripLatexHighlight converter.makeHtml text - - $content = $(content) - initializeVote $content - if $content.hasClass("thread") - initializeFollowThread $content - $local = Discussion.generateLocal($content.children(".discussion-content")) - - $contentTitle = $local(".thread-title") - - if $contentTitle.length - $contentTitle.html unescapeHighlightTag stripLatexHighlight $contentTitle.html() - - $contentBody = $local(".content-body") - - $contentBody.html Discussion.postMathJaxProcessor markdownWithHighlight $contentBody.html() - - MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")] - id = $content.attr("_id") - - if $content.hasClass("thread") - discussion_id = $content.attr("_discussion_id") - permalink = Discussion.urlFor("permanent_link_thread", discussion_id, id) - else - thread_id = $content.parents(".thread").attr("_id") - discussion_id = $content.parents(".thread").attr("_discussion_id") - permalink = Discussion.urlFor("permanent_link_comment", discussion_id, thread_id, id) - $local(".discussion-permanent-link").attr "href", permalink - - if not Discussion.getContentInfo id, 'editable' - $local(".admin-edit").remove() - if not Discussion.getContentInfo id, 'can_reply' - $local(".discussion-reply").remove() - if not Discussion.getContentInfo id, 'can_endorse' - $local(".admin-endorse").remove() - if not Discussion.getContentInfo id, 'can_delete' - $local(".admin-delete").remove() - if not Discussion.getContentInfo id, 'can_openclose' - $local(".admin-openclose").remove() - #if not Discussion.getContentInfo id, 'can_vote' - # $local(".discussion-vote").css "visibility", "hidden" + window.Content = Content + window.Thread = Thread diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index 8e98ac3110..d67da04e0e 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -1,190 +1,31 @@ -if not @Discussion? - @Discussion = {} +$ -> + + class Discussion extends Backbone.Collection + model: Thread + initialize: -> + this.bind "add", (item) => + item.collection = this -Discussion = @Discussion + class DiscussionModuleView extends Backbone.View -initializeFollowDiscussion = (discussion) -> - $discussion = $(discussion) - id = $following.attr("_id") - $local = Discussion.generateLocal() - $discussion.children(".discussion-non-content") - .find(".discussion-title-wrapper") - .append(Discussion.subscriptionLink('discussion', id)) + class DiscussionView extends Backbone.View -@Discussion = $.extend @Discussion, + $: (selector) -> + @$local.find(selector) - initializeDiscussion: (discussion) -> - $discussion = $(discussion) - $discussion.find(".thread").each (index, thread) -> - Discussion.initializeContent(thread) - Discussion.bindContentEvents(thread) - $discussion.find(".comment").each (index, comment) -> - Discussion.initializeContent(comment) - Discussion.bindContentEvents(comment) + initialize: -> + @$local = @$el.children(".local") - #initializeFollowDiscussion(discussion) TODO move this somewhere else + events: + "submit .search-wrapper>.discussion-search-form": "search" + "click .discussion-search-link": "search" + "click .discussion-sort-link": "sort" + "click .discussion-paginator>.discussion-page-link": "page" + + $(".discussion-module").each (index, elem) -> + view = new DiscussionModuleView(el: elem) - bindDiscussionEvents: (discussion) -> - - $discussion = $(discussion) - $discussionNonContent = $discussion.children(".discussion-non-content") - $local = Discussion.generateLocal($discussion.children(".discussion-local")) - - id = $discussion.attr("_id") - - handleSubmitNewPost = (elem) -> - title = $local(".new-post-title").val() - body = Discussion.getWmdContent $discussion, $local, "new-post-body" - tags = $local(".new-post-tags").val() - url = Discussion.urlFor('create_thread', id) - Discussion.safeAjax - $elem: $(elem) - url: url - type: "POST" - dataType: 'json' - data: - title: title - body: body - tags: tags - error: Discussion.formErrorHandler($local(".new-post-form-errors")) - success: (response, textStatus) -> - Discussion.clearFormErrors($local(".new-post-form-errors")) - $thread = $(response.html) - $discussion.children(".threads").prepend($thread) - $local(".new-post-title").val("") - Discussion.setWmdContent $discussion, $local, "new-post-body", "" - $local(".new-post-tags").val("") - if $discussion.hasClass("inline-discussion") - $local(".new-post-form").addClass("collapsed") - else if $discussion.hasClass("forum-discussion") - $local(".new-post-form").hide() - - handleCancelNewPost = (elem) -> - if $discussion.hasClass("inline-discussion") - $local(".new-post-form").addClass("collapsed") - else if $discussion.hasClass("forum-discussion") - $local(".new-post-form").hide() - - handleSimilarPost = (elem) -> - $title = $local(".new-post-title") - $wrapper = $local(".new-post-similar-posts-wrapper") - $similarPosts = $local(".new-post-similar-posts") - prevText = $title.attr("prev-text") - text = $title.val() - if text == prevText - if $local(".similar-post").length - $wrapper.show() - else if $.trim(text).length - Discussion.safeAjax - $elem: $(elem) - url: Discussion.urlFor 'search_similar_threads', id - type: "GET" - dateType: 'json' - data: - text: $local(".new-post-title").val() - success: (response, textStatus) -> - $similarPosts.empty() - console.log response - if $.type(response) == "array" and response.length - $wrapper.show() - for thread in response - #singleThreadUrl = Discussion.urlFor 'retrieve_single_thread - $similarPost = $("").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) - - initializeNewPost = -> - view = { discussion_id: id } - $discussionNonContent = $discussion.children(".discussion-non-content") - - if not $local(".wmd-panel").length - $discussionNonContent.append Mustache.render Discussion.newPostTemplate, view - $newPostBody = $local(".new-post-body") - Discussion.makeWmdEditor $discussion, $local, "new-post-body" - - $input = Discussion.getWmdInput($discussion, $local, "new-post-body") - $input.attr("placeholder", "post a new topic...") - if $discussion.hasClass("inline-discussion") - $input.bind 'focus', (e) -> - $local(".new-post-form").removeClass('collapsed') - else if $discussion.hasClass("forum-discussion") - $local(".new-post-form").removeClass('collapsed') - - $local(".new-post-tags").tagsInput Discussion.tagsInputOptions() - - $local(".new-post-title").blur -> - handleSimilarPost(this) - - $local(".hide-similar-posts").click -> - $local(".new-post-similar-posts-wrapper").hide() - - $local(".discussion-submit-post").click -> - handleSubmitNewPost(this) - $local(".discussion-cancel-post").click -> - handleCancelNewPost(this) - - $local(".new-post-form").show() - - handleAjaxReloadDiscussion = (elem, url) -> - if not url then return - $elem = $(elem) - $discussion = $elem.parents("section.discussion") - Discussion.safeAjax - $elem: $elem - url: url - type: "GET" - dataType: 'html' - success: (data, textStatus) -> - $data = $(data) - $parent = $discussion.parent() - $discussion.replaceWith($data) - $discussion = $parent.children(".discussion") - Discussion.initializeDiscussion($discussion) - Discussion.bindDiscussionEvents($discussion) - - handleAjaxSearch = (elem) -> - $elem = $(elem) - url = URI($elem.attr("action")).addSearch({text: $local(".search-input").val()}) - handleAjaxReloadDiscussion($elem, url) - - handleAjaxSort = (elem) -> - $elem = $(elem) - url = $elem.attr("sort-url") - handleAjaxReloadDiscussion($elem, url) - - handleAjaxPage = (elem) -> - $elem = $(elem) - url = $elem.attr("page-url") - handleAjaxReloadDiscussion($elem, url) - - if $discussion.hasClass("inline-discussion") - initializeNewPost() - - if $discussion.hasClass("forum-discussion") - $discussionSidebar = $(".discussion-sidebar") - if $discussionSidebar.length - $sidebarLocal = Discussion.generateLocal($discussionSidebar) - Discussion.bindLocalEvents $sidebarLocal, - "click .sidebar-new-post-button": (event) -> - initializeNewPost() - - Discussion.bindLocalEvents $local, - - "submit .search-wrapper>.discussion-search-form": (event) -> - event.preventDefault() - handleAjaxSearch(this) - - "click .discussion-search-link": -> - handleAjaxSearch($local(".search-wrapper>.discussion-search-form")) - - "click .discussion-sort-link": -> - handleAjaxSort(this) - - $discussion.children(".discussion-paginator").find(".discussion-page-link").unbind('click').click -> - handleAjaxPage(this) + $("section.discussion").each (index, elem) -> + discussionData = DiscussionUtil.getDiscussionData(elem) + discussion = new Discussion(discussionData) + view = new DiscussionView(el: elem, model: discussion) diff --git a/lms/static/coffee/src/discussion/utils.coffee b/lms/static/coffee/src/discussion/utils.coffee index c034b79675..09bac7a335 100644 --- a/lms/static/coffee/src/discussion/utils.coffee +++ b/lms/static/coffee/src/discussion/utils.coffee @@ -1,244 +1,7 @@ -if not @Discussion? - @Discussion = {} - -Discussion = @Discussion - -wmdEditors = {} - -@Discussion = $.extend @Discussion, - - generateLocal: (elem) -> - (selector) -> $(elem).find(selector) - - generateDiscussionLink: (cls, txt, handler) -> - $("").addClass("discussion-link") - .attr("href", "javascript:void(0)") - .addClass(cls).html(txt) - .click -> handler(this) - - urlFor: (name, param, param1, param2) -> - { - follow_discussion : "/courses/#{$$course_id}/discussion/#{param}/follow" - unfollow_discussion : "/courses/#{$$course_id}/discussion/#{param}/unfollow" - create_thread : "/courses/#{$$course_id}/discussion/#{param}/threads/create" - search_similar_threads : "/courses/#{$$course_id}/discussion/#{param}/threads/search_similar" - update_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/update" - create_comment : "/courses/#{$$course_id}/discussion/threads/#{param}/reply" - delete_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/delete" - upvote_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/upvote" - downvote_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/downvote" - undo_vote_for_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/unvote" - follow_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/follow" - unfollow_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/unfollow" - update_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/update" - endorse_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/endorse" - create_sub_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/reply" - delete_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/delete" - upvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/upvote" - downvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/downvote" - undo_vote_for_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/unvote" - upload : "/courses/#{$$course_id}/discussion/upload" - search : "/courses/#{$$course_id}/discussion/forum/search" - tags_autocomplete : "/courses/#{$$course_id}/discussion/threads/tags/autocomplete" - retrieve_discussion : "/courses/#{$$course_id}/discussion/forum/#{param}/inline" - retrieve_single_thread : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}" - update_moderator_status : "/courses/#{$$course_id}/discussion/users/#{param}/update_moderator_status" - openclose_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/close" - permanent_link_thread : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}" - permanent_link_comment : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}##{param2}" - }[name] - - safeAjax: (params) -> - $elem = params.$elem - if $elem.attr("disabled") - return - $elem.attr("disabled", "disabled") - $.ajax(params).always -> - $elem.removeAttr("disabled") - - handleAnchorAndReload: (response) -> - #window.location = window.location.pathname + "#" + response['id'] - window.location.reload() - - bindLocalEvents: ($local, eventsHandler) -> - for eventSelector, handler of eventsHandler - [event, selector] = eventSelector.split(' ') - $local(selector).unbind(event)[event] handler - - tagsInputOptions: -> - autocomplete_url: Discussion.urlFor('tags_autocomplete') - autocomplete: - remoteDataType: 'json' - interactive: true - height: '30px' - width: '100%' - defaultText: "Tag your post: press enter after each tag" - removeWithBackspace: true - - isSubscribed: (id, type) -> - $$user_info? and ( - if type == "thread" - id in $$user_info.subscribed_thread_ids - else if type == "commentable" or type == "discussion" - id in $$user_info.subscribed_commentable_ids - else - id in $$user_info.subscribed_user_ids - ) - - isUpvoted: (id) -> - $$user_info? and (id in $$user_info.upvoted_ids) - - isDownvoted: (id) -> - $$user_info? and (id in $$user_info.downvoted_ids) - - formErrorHandler: (errorsField) -> - (xhr, textStatus, error) -> - response = JSON.parse(xhr.responseText) - if response.errors? and response.errors.length > 0 - errorsField.empty() - for error in response.errors - errorsField.append($("
  • ").addClass("new-post-form-error").html(error)) - - clearFormErrors: (errorsField) -> - errorsField.empty() - - postMathJaxProcessor: (text) -> - RE_INLINEMATH = /^\$([^\$]*)\$/g - RE_DISPLAYMATH = /^\$\$([^\$]*)\$\$/g - Discussion.processEachMathAndCode text, (s, type) -> - if type == 'display' - s.replace RE_DISPLAYMATH, ($0, $1) -> - "\\[" + $1 + "\\]" - else if type == 'inline' - s.replace RE_INLINEMATH, ($0, $1) -> - "\\(" + $1 + "\\)" - else - s - - makeWmdEditor: ($content, $local, cls_identifier) -> - elem = $local(".#{cls_identifier}") - id = $content.attr("_id") - appended_id = "-#{cls_identifier}-#{id}" - imageUploadUrl = Discussion.urlFor('upload') - editor = Markdown.makeWmdEditor elem, appended_id, imageUploadUrl, Discussion.postMathJaxProcessor - wmdEditors["#{cls_identifier}-#{id}"] = editor - editor - - getWmdEditor: ($content, $local, cls_identifier) -> - id = $content.attr("_id") - wmdEditors["#{cls_identifier}-#{id}"] - - getWmdInput: ($content, $local, cls_identifier) -> - id = $content.attr("_id") - $local("#wmd-input-#{cls_identifier}-#{id}") - - getWmdContent: ($content, $local, cls_identifier) -> - Discussion.getWmdInput($content, $local, cls_identifier).val() - - setWmdContent: ($content, $local, cls_identifier, text) -> - Discussion.getWmdInput($content, $local, cls_identifier).val(text) - Discussion.getWmdEditor($content, $local, cls_identifier).refreshPreview() - - getContentInfo: (id, attr) -> - if not window.$$annotated_content_info? - window.$$annotated_content_info = {} - (window.$$annotated_content_info[id] || {})[attr] - - setContentInfo: (id, attr, value) -> - if not window.$$annotated_content_info? - window.$$annotated_content_info = {} - window.$$annotated_content_info[id] ||= {} - window.$$annotated_content_info[id][attr] = value - - extendContentInfo: (id, newInfo) -> - if not window.$$annotated_content_info? - window.$$annotated_content_info = {} - window.$$annotated_content_info[id] = newInfo - bulkExtendContentInfo: (newInfos) -> - if not window.$$annotated_content_info? - window.$$annotated_content_info = {} - window.$$annotated_content_info = $.extend window.$$annotated_content_info, newInfos - - subscriptionLink: (type, id) -> - followLink = -> - Discussion.generateDiscussionLink("discussion-follow-#{type}", "Follow", handleFollow) - - unfollowLink = -> - Discussion.generateDiscussionLink("discussion-unfollow-#{type}", "Unfollow", handleUnfollow) - - handleFollow = (elem) -> - Discussion.safeAjax - $elem: $(elem) - url: Discussion.urlFor("follow_#{type}", id) - type: "POST" - success: (response, textStatus) -> - if textStatus == "success" - $(elem).replaceWith unfollowLink() - dataType: 'json' - - handleUnfollow = (elem) -> - Discussion.safeAjax - $elem: $(elem) - url: Discussion.urlFor("unfollow_#{type}", id) - type: "POST" - success: (response, textStatus) -> - if textStatus == "success" - $(elem).replaceWith followLink() - dataType: 'json' - - if Discussion.isSubscribed(id, type) - unfollowLink() - else - followLink() - - processEachMathAndCode: (text, processor) -> - - codeArchive = [] - - RE_DISPLAYMATH = /^([^\$]*?)\$\$([^\$]*?)\$\$(.*)$/m - RE_INLINEMATH = /^([^\$]*?)\$([^\$]+?)\$(.*)$/m - - ESCAPED_DOLLAR = '@@ESCAPED_D@@' - ESCAPED_BACKSLASH = '@@ESCAPED_B@@' - - processedText = "" - - $div = $("
    ").html(text) - - $div.find("code").each (index, code) -> - codeArchive.push $(code).html() - $(code).html(codeArchive.length - 1) - - text = $div.html() - text = text.replace /\\\$/g, ESCAPED_DOLLAR - - while true - if RE_INLINEMATH.test(text) - text = text.replace RE_INLINEMATH, ($0, $1, $2, $3) -> - processedText += $1 + processor("$" + $2 + "$", 'inline') - $3 - else if RE_DISPLAYMATH.test(text) - text = text.replace RE_DISPLAYMATH, ($0, $1, $2, $3) -> - processedText += $1 + processor("$$" + $2 + "$$", 'display') - $3 - else - processedText += text - break - - text = processedText - text = text.replace(new RegExp(ESCAPED_DOLLAR, 'g'), '\\$') - - text = text.replace /\\\\\\\\/g, ESCAPED_BACKSLASH - text = text.replace /\\begin\{([a-z]*\*?)\}([\s\S]*?)\\end\{\1\}/img, ($0, $1, $2) -> - processor("\\begin{#{$1}}" + $2 + "\\end{#{$1}}") - text = text.replace(new RegExp(ESCAPED_BACKSLASH, 'g'), '\\\\\\\\') - - $div = $("
    ").html(text) - cnt = 0 - $div.find("code").each (index, code) -> - $(code).html(processor(codeArchive[cnt], 'code')) - cnt += 1 - - text = $div.html() - - text +class @DiscussionUtil + @getDiscussionData: (id) -> + if id instanceof $ + id = id.attr("_id") + else if typeof id == "object" + id = $(id).attr("_id") + return $$discussion_data[id] diff --git a/lms/static/coffee/src/old_discussion/content.coffee b/lms/static/coffee/src/old_discussion/content.coffee new file mode 100644 index 0000000000..9924e540a2 --- /dev/null +++ b/lms/static/coffee/src/old_discussion/content.coffee @@ -0,0 +1,409 @@ +if not @Discussion? + @Discussion = {} + +Discussion = @Discussion + +initializeVote = (content) -> + $content = $(content) + $local = Discussion.generateLocal($content.children(".discussion-content")) + id = $content.attr("_id") + if Discussion.isUpvoted id + $local(".discussion-vote-up").addClass("voted") + else if Discussion.isDownvoted id + $local(".discussion-vote-down").addClass("voted") + +initializeFollowThread = (thread) -> + $thread = $(thread) + id = $thread.attr("_id") + $thread.children(".discussion-content") + .find(".follow-wrapper") + .append(Discussion.subscriptionLink('thread', id)) + +@Discussion = $.extend @Discussion, + + bindContentEvents: (content) -> + + $content = $(content) + $discussionContent = $content.children(".discussion-content") + $local = Discussion.generateLocal($discussionContent) + + id = $content.attr("_id") + + handleReply = (elem) -> + $replyView = $local(".discussion-reply-new") + if $replyView.length + $replyView.show() + else + thread_id = $discussionContent.parents(".thread").attr("_id") + view = + id: id + showWatchCheckbox: not Discussion.isSubscribed(thread_id, "thread") + $discussionContent.append Mustache.render Discussion.replyTemplate, view + Discussion.makeWmdEditor $content, $local, "reply-body" + $local(".discussion-submit-post").click -> handleSubmitReply(this) + $local(".discussion-cancel-post").click -> handleCancelReply(this) + $local(".discussion-reply").hide() + $local(".discussion-edit").hide() + + handleCancelReply = (elem) -> + $replyView = $local(".discussion-reply-new") + if $replyView.length + $replyView.hide() + $local(".discussion-reply").show() + $local(".discussion-edit").show() + + handleSubmitReply = (elem) -> + if $content.hasClass("thread") + url = Discussion.urlFor('create_comment', id) + else if $content.hasClass("comment") + url = Discussion.urlFor('create_sub_comment', id) + else + return + + body = Discussion.getWmdContent $content, $local, "reply-body" + + anonymous = false || $local(".discussion-post-anonymously").is(":checked") + autowatch = false || $local(".discussion-auto-watch").is(":checked") + + Discussion.safeAjax + $elem: $(elem) + url: url + type: "POST" + dataType: 'json' + data: + body: body + anonymous: anonymous + autowatch: autowatch + error: Discussion.formErrorHandler($local(".discussion-errors")) + success: (response, textStatus) -> + Discussion.clearFormErrors($local(".discussion-errors")) + $comment = $(response.html) + $content.children(".comments").prepend($comment) + Discussion.setWmdContent $content, $local, "reply-body", "" + Discussion.setContentInfo response.content['id'], 'can_reply', true + Discussion.setContentInfo response.content['id'], 'editable', true + Discussion.extendContentInfo response.content['id'], response['annotated_content_info'] + Discussion.initializeContent($comment) + Discussion.bindContentEvents($comment) + $local(".discussion-reply-new").hide() + $local(".discussion-reply").show() + $local(".discussion-edit").show() + $discussionContent.attr("status", "normal") + + handleVote = (elem, value) -> + contentType = if $content.hasClass("thread") then "thread" else "comment" + url = Discussion.urlFor("#{value}vote_#{contentType}", id) + Discussion.safeAjax + $elem: $local(".discussion-vote") + url: url + type: "POST" + dataType: "json" + success: (response, textStatus) -> + if textStatus == "success" + $local(".discussion-vote").removeClass("voted") + $local(".discussion-vote-#{value}").addClass("voted") + $local(".discussion-votes-point").html response.votes.point + + handleUnvote = (elem, value) -> + contentType = if $content.hasClass("thread") then "thread" else "comment" + url = Discussion.urlFor("undo_vote_for_#{contentType}", id) + Discussion.safeAjax + $elem: $local(".discussion-vote") + url: url + type: "POST" + dataType: "json" + success: (response, textStatus) -> + if textStatus == "success" + $local(".discussion-vote").removeClass("voted") + $local(".discussion-votes-point").html response.votes.point + + handleCancelEdit = (elem) -> + $local(".discussion-content-edit").hide() + $local(".discussion-content-wrapper").show() + + handleEditThread = (elem) -> + $local(".discussion-content-wrapper").hide() + $editView = $local(".discussion-content-edit") + if $editView.length + $editView.show() + else + view = { + id: id + title: $local(".thread-raw-title").html() + body: $local(".thread-raw-body").html() + tags: $local(".thread-raw-tags").html() + } + $discussionContent.append Mustache.render Discussion.editThreadTemplate, view + Discussion.makeWmdEditor $content, $local, "thread-body-edit" + $local(".thread-tags-edit").tagsInput Discussion.tagsInputOptions() + $local(".discussion-submit-update").unbind("click").click -> handleSubmitEditThread(this) + $local(".discussion-cancel-update").unbind("click").click -> handleCancelEdit(this) + + handleSubmitEditThread = (elem) -> + url = Discussion.urlFor('update_thread', id) + title = $local(".thread-title-edit").val() + body = Discussion.getWmdContent $content, $local, "thread-body-edit" + tags = $local(".thread-tags-edit").val() + Discussion.safeAjax + $elem: $(elem) + url: url + type: "POST" + dataType: 'json' + data: {title: title, body: body, tags: tags}, + error: Discussion.formErrorHandler($local(".discussion-update-errors")) + success: (response, textStatus) -> + Discussion.clearFormErrors($local(".discussion-update-errors")) + $discussionContent.replaceWith(response.html) + Discussion.extendContentInfo response.content['id'], response['annotated_content_info'] + Discussion.initializeContent($content) + Discussion.bindContentEvents($content) + + handleEditComment = (elem) -> + $local(".discussion-content-wrapper").hide() + $editView = $local(".discussion-content-edit") + if $editView.length + $editView.show() + else + view = { id: id, body: $local(".comment-raw-body").html() } + $discussionContent.append Mustache.render Discussion.editCommentTemplate, view + Discussion.makeWmdEditor $content, $local, "comment-body-edit" + $local(".discussion-submit-update").unbind("click").click -> handleSubmitEditComment(this) + $local(".discussion-cancel-update").unbind("click").click -> handleCancelEdit(this) + + handleSubmitEditComment= (elem) -> + url = Discussion.urlFor('update_comment', id) + body = Discussion.getWmdContent $content, $local, "comment-body-edit" + Discussion.safeAjax + $elem: $(elem) + url: url + type: "POST" + dataType: "json" + data: {body: body} + error: Discussion.formErrorHandler($local(".discussion-update-errors")) + success: (response, textStatus) -> + Discussion.clearFormErrors($local(".discussion-update-errors")) + $discussionContent.replaceWith(response.html) + Discussion.extendContentInfo response.content['id'], response['annotated_content_info'] + Discussion.initializeContent($content) + Discussion.bindContentEvents($content) + + handleEndorse = (elem, endorsed) -> + url = Discussion.urlFor('endorse_comment', id) + Discussion.safeAjax + $elem: $(elem) + url: url + type: "POST" + dataType: "json" + data: {endorsed: endorsed} + success: (response, textStatus) -> + if textStatus == "success" + if endorsed + $(content).addClass("endorsed") + else + $(content).removeClass("endorsed") + + $(elem).unbind('click').click -> + handleEndorse(elem, !endorsed) + + handleOpenClose = (elem, text) -> + url = Discussion.urlFor('openclose_thread', id) + closed = undefined + if text.match(/Close/) + closed = true + else if text.match(/[Oo]pen/) + closed = false + else + console.log "Unexpected text " + text + "for open/close thread." + + Discussion.safeAjax + $elem: $(elem) + url: url + type: "POST" + dataType: "json" + data: {closed: closed} + success: (response, textStatus) => + if textStatus == "success" + if closed + $(content).addClass("closed") + $(elem).text "Re-open Thread" + else + $(content).removeClass("closed") + $(elem).text "Close Thread" + error: (response, textStatus, e) -> + console.log e + + handleDelete = (elem) -> + if $content.hasClass("thread") + url = Discussion.urlFor('delete_thread', id) + c = confirm "Are you sure to delete thread \"" + $content.find("a.thread-title").text() + "\"?" + else + url = Discussion.urlFor('delete_comment', id) + c = confirm "Are you sure to delete this comment? " + if c != true + return + Discussion.safeAjax + $elem: $(elem) + url: url + type: "POST" + dataType: "json" + data: {} + success: (response, textStatus) => + if textStatus == "success" + $(content).remove() + error: (response, textStatus, e) -> + console.log e + + handleHideSingleThread = (elem) -> + $threadTitle = $local(".thread-title") + $hideComments = $local(".discussion-hide-comments") + $hideComments.removeClass("discussion-hide-comments") + .addClass("discussion-show-comments") + $content.children(".comments").hide() + $threadTitle.unbind('click').click handleShowSingleThread + $hideComments.unbind('click').click handleShowSingleThread + prevHtml = $hideComments.html() + $hideComments.html prevHtml.replace "Hide", "Show" + + handleShowSingleThread = -> + $threadTitle = $local(".thread-title") + $showComments = $local(".discussion-show-comments") + + if not $showComments.hasClass("first-time") and (not $showComments.length or not $threadTitle.length) + return + + rebindHideEvents = -> + $threadTitle.unbind('click').click handleHideSingleThread + $showComments.unbind('click').click handleHideSingleThread + $showComments.removeClass("discussion-show-comments") + .addClass("discussion-hide-comments") + prevHtml = $showComments.html() + $showComments.html prevHtml.replace "Show", "Hide" + + + if not $showComments.hasClass("first-time") and $content.children(".comments").length + $content.children(".comments").show() + rebindHideEvents() + else + discussion_id = $threadTitle.parents(".discussion").attr("_id") + url = Discussion.urlFor('retrieve_single_thread', discussion_id, id) + Discussion.safeAjax + $elem: $.merge($threadTitle, $showComments) + url: url + type: "GET" + dataType: 'json' + success: (response, textStatus) -> + Discussion.bulkExtendContentInfo response['annotated_content_info'] + $content.append(response['html']) + $content.find(".comment").each (index, comment) -> + Discussion.initializeContent(comment) + Discussion.bindContentEvents(comment) + $showComments.removeClass("first-time") + rebindHideEvents() + + Discussion.bindLocalEvents $local, + + "click .thread-title": -> + handleShowSingleThread(this) + + "click .discussion-show-comments": -> + handleShowSingleThread(this) + + "click .discussion-hide-comments": -> + handleHideSingleThread(this) + + "click .discussion-reply-thread": -> + handleShowSingleThread($local(".thread-title")) + handleReply(this) + + "click .discussion-reply-comment": -> + handleReply(this) + + "click .discussion-cancel-reply": -> + handleCancelReply(this) + + "click .discussion-vote-up": -> + $elem = $(this) + if $elem.hasClass("voted") + handleUnvote($elem) + else + handleVote($elem, "up") + + "click .discussion-vote-down": -> + $elem = $(this) + if $elem.hasClass("voted") + handleUnvote($elem) + else + handleVote($elem, "down") + + "click .admin-endorse": -> + handleEndorse(this, not $content.hasClass("endorsed")) + + "click .admin-openclose": -> + handleOpenClose(this, $(this).text()) + + "click .admin-edit": -> + if $content.hasClass("thread") + handleEditThread(this) + else + handleEditComment(this) + + "click .admin-delete": -> + handleDelete(this) + + initializeContent: (content) -> + + unescapeHighlightTag = (text) -> + text.replace(/\<\;highlight\>\;/g, "") + .replace(/\<\;\/highlight\>\;/g, "") + + stripHighlight = (text, type) -> + text.replace(/\&(amp\;)?lt\;highlight\&(amp\;)?gt\;/g, "") + .replace(/\&(amp\;)?lt\;\/highlight\&(amp\;)?gt\;/g, "") + + + stripLatexHighlight = (text) -> + Discussion.processEachMathAndCode text, stripHighlight + + markdownWithHighlight = (text) -> + converter = Markdown.getMathCompatibleConverter() + unescapeHighlightTag stripLatexHighlight converter.makeHtml text + + $content = $(content) + initializeVote $content + if $content.hasClass("thread") + initializeFollowThread $content + $local = Discussion.generateLocal($content.children(".discussion-content")) + + $contentTitle = $local(".thread-title") + + if $contentTitle.length + $contentTitle.html unescapeHighlightTag stripLatexHighlight $contentTitle.html() + + $contentBody = $local(".content-body") + + $contentBody.html Discussion.postMathJaxProcessor markdownWithHighlight $contentBody.html() + + MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")] + id = $content.attr("_id") + + if $content.hasClass("thread") + discussion_id = $content.attr("_discussion_id") + permalink = Discussion.urlFor("permanent_link_thread", discussion_id, id) + else + thread_id = $content.parents(".thread").attr("_id") + discussion_id = $content.parents(".thread").attr("_discussion_id") + permalink = Discussion.urlFor("permanent_link_comment", discussion_id, thread_id, id) + $local(".discussion-permanent-link").attr "href", permalink + + if not Discussion.getContentInfo id, 'editable' + $local(".admin-edit").remove() + if not Discussion.getContentInfo id, 'can_reply' + $local(".discussion-reply").remove() + if not Discussion.getContentInfo id, 'can_endorse' + $local(".admin-endorse").remove() + if not Discussion.getContentInfo id, 'can_delete' + $local(".admin-delete").remove() + if not Discussion.getContentInfo id, 'can_openclose' + $local(".admin-openclose").remove() + #if not Discussion.getContentInfo id, 'can_vote' + # $local(".discussion-vote").css "visibility", "hidden" diff --git a/lms/static/coffee/src/old_discussion/discussion.coffee b/lms/static/coffee/src/old_discussion/discussion.coffee new file mode 100644 index 0000000000..8e98ac3110 --- /dev/null +++ b/lms/static/coffee/src/old_discussion/discussion.coffee @@ -0,0 +1,190 @@ +if not @Discussion? + @Discussion = {} + +Discussion = @Discussion + +initializeFollowDiscussion = (discussion) -> + $discussion = $(discussion) + id = $following.attr("_id") + $local = Discussion.generateLocal() + $discussion.children(".discussion-non-content") + .find(".discussion-title-wrapper") + .append(Discussion.subscriptionLink('discussion', id)) + +@Discussion = $.extend @Discussion, + + initializeDiscussion: (discussion) -> + $discussion = $(discussion) + $discussion.find(".thread").each (index, thread) -> + Discussion.initializeContent(thread) + Discussion.bindContentEvents(thread) + $discussion.find(".comment").each (index, comment) -> + Discussion.initializeContent(comment) + Discussion.bindContentEvents(comment) + + #initializeFollowDiscussion(discussion) TODO move this somewhere else + + bindDiscussionEvents: (discussion) -> + + $discussion = $(discussion) + $discussionNonContent = $discussion.children(".discussion-non-content") + $local = Discussion.generateLocal($discussion.children(".discussion-local")) + + id = $discussion.attr("_id") + + handleSubmitNewPost = (elem) -> + title = $local(".new-post-title").val() + body = Discussion.getWmdContent $discussion, $local, "new-post-body" + tags = $local(".new-post-tags").val() + url = Discussion.urlFor('create_thread', id) + Discussion.safeAjax + $elem: $(elem) + url: url + type: "POST" + dataType: 'json' + data: + title: title + body: body + tags: tags + error: Discussion.formErrorHandler($local(".new-post-form-errors")) + success: (response, textStatus) -> + Discussion.clearFormErrors($local(".new-post-form-errors")) + $thread = $(response.html) + $discussion.children(".threads").prepend($thread) + $local(".new-post-title").val("") + Discussion.setWmdContent $discussion, $local, "new-post-body", "" + $local(".new-post-tags").val("") + if $discussion.hasClass("inline-discussion") + $local(".new-post-form").addClass("collapsed") + else if $discussion.hasClass("forum-discussion") + $local(".new-post-form").hide() + + handleCancelNewPost = (elem) -> + if $discussion.hasClass("inline-discussion") + $local(".new-post-form").addClass("collapsed") + else if $discussion.hasClass("forum-discussion") + $local(".new-post-form").hide() + + handleSimilarPost = (elem) -> + $title = $local(".new-post-title") + $wrapper = $local(".new-post-similar-posts-wrapper") + $similarPosts = $local(".new-post-similar-posts") + prevText = $title.attr("prev-text") + text = $title.val() + if text == prevText + if $local(".similar-post").length + $wrapper.show() + else if $.trim(text).length + Discussion.safeAjax + $elem: $(elem) + url: Discussion.urlFor 'search_similar_threads', id + type: "GET" + dateType: 'json' + data: + text: $local(".new-post-title").val() + success: (response, textStatus) -> + $similarPosts.empty() + console.log response + if $.type(response) == "array" and response.length + $wrapper.show() + for thread in response + #singleThreadUrl = Discussion.urlFor 'retrieve_single_thread + $similarPost = $("").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) + + initializeNewPost = -> + view = { discussion_id: id } + $discussionNonContent = $discussion.children(".discussion-non-content") + + if not $local(".wmd-panel").length + $discussionNonContent.append Mustache.render Discussion.newPostTemplate, view + $newPostBody = $local(".new-post-body") + Discussion.makeWmdEditor $discussion, $local, "new-post-body" + + $input = Discussion.getWmdInput($discussion, $local, "new-post-body") + $input.attr("placeholder", "post a new topic...") + if $discussion.hasClass("inline-discussion") + $input.bind 'focus', (e) -> + $local(".new-post-form").removeClass('collapsed') + else if $discussion.hasClass("forum-discussion") + $local(".new-post-form").removeClass('collapsed') + + $local(".new-post-tags").tagsInput Discussion.tagsInputOptions() + + $local(".new-post-title").blur -> + handleSimilarPost(this) + + $local(".hide-similar-posts").click -> + $local(".new-post-similar-posts-wrapper").hide() + + $local(".discussion-submit-post").click -> + handleSubmitNewPost(this) + $local(".discussion-cancel-post").click -> + handleCancelNewPost(this) + + $local(".new-post-form").show() + + handleAjaxReloadDiscussion = (elem, url) -> + if not url then return + $elem = $(elem) + $discussion = $elem.parents("section.discussion") + Discussion.safeAjax + $elem: $elem + url: url + type: "GET" + dataType: 'html' + success: (data, textStatus) -> + $data = $(data) + $parent = $discussion.parent() + $discussion.replaceWith($data) + $discussion = $parent.children(".discussion") + Discussion.initializeDiscussion($discussion) + Discussion.bindDiscussionEvents($discussion) + + handleAjaxSearch = (elem) -> + $elem = $(elem) + url = URI($elem.attr("action")).addSearch({text: $local(".search-input").val()}) + handleAjaxReloadDiscussion($elem, url) + + handleAjaxSort = (elem) -> + $elem = $(elem) + url = $elem.attr("sort-url") + handleAjaxReloadDiscussion($elem, url) + + handleAjaxPage = (elem) -> + $elem = $(elem) + url = $elem.attr("page-url") + handleAjaxReloadDiscussion($elem, url) + + if $discussion.hasClass("inline-discussion") + initializeNewPost() + + if $discussion.hasClass("forum-discussion") + $discussionSidebar = $(".discussion-sidebar") + if $discussionSidebar.length + $sidebarLocal = Discussion.generateLocal($discussionSidebar) + Discussion.bindLocalEvents $sidebarLocal, + "click .sidebar-new-post-button": (event) -> + initializeNewPost() + + Discussion.bindLocalEvents $local, + + "submit .search-wrapper>.discussion-search-form": (event) -> + event.preventDefault() + handleAjaxSearch(this) + + "click .discussion-search-link": -> + handleAjaxSearch($local(".search-wrapper>.discussion-search-form")) + + "click .discussion-sort-link": -> + handleAjaxSort(this) + + $discussion.children(".discussion-paginator").find(".discussion-page-link").unbind('click').click -> + handleAjaxPage(this) diff --git a/lms/static/coffee/src/old_discussion/discussion_module.coffee b/lms/static/coffee/src/old_discussion/discussion_module.coffee new file mode 100644 index 0000000000..d449533ba9 --- /dev/null +++ b/lms/static/coffee/src/old_discussion/discussion_module.coffee @@ -0,0 +1,42 @@ +if not @Discussion? + @Discussion = {} + +Discussion = @Discussion + +@Discussion = $.extend @Discussion, + initializeDiscussionModule: (elem) -> + $discussionModule = $(elem) + $local = Discussion.generateLocal($discussionModule) + handleShowDiscussion = (elem) -> + $elem = $(elem) + if not $local("section.discussion").length + discussion_id = $elem.attr("discussion_id") + url = Discussion.urlFor 'retrieve_discussion', discussion_id + Discussion.safeAjax + $elem: $elem + url: url + type: "GET" + success: (data, textStatus, xhr) -> + $discussionModule.append(data) + discussion = $local("section.discussion") + Discussion.initializeDiscussion(discussion) + Discussion.bindDiscussionEvents(discussion) + $elem.html("Hide Discussion") + $elem.unbind('click').click -> + handleHideDiscussion(this) + dataType: 'html' + else + $local("section.discussion").show() + $elem.html("Hide Discussion") + $elem.unbind('click').click -> + handleHideDiscussion(this) + + handleHideDiscussion = (elem) -> + $local("section.discussion").hide() + $elem = $(elem) + $elem.html("Show Discussion") + $elem.unbind('click').click -> + handleShowDiscussion(this) + + $local(".discussion-show").click -> + handleShowDiscussion(this) diff --git a/lms/static/coffee/src/old_discussion/main.coffee b/lms/static/coffee/src/old_discussion/main.coffee new file mode 100644 index 0000000000..f9b11b72e3 --- /dev/null +++ b/lms/static/coffee/src/old_discussion/main.coffee @@ -0,0 +1,23 @@ +$ -> + + #toggle = -> + # $('.course-wrapper').toggleClass('closed') + + #Discussion = window.Discussion + #if $('#accordion').length + # active = $('#accordion ul:has(li.active)').index('#accordion ul') + # $('#accordion').bind('accordionchange', @log).accordion + # active: if active >= 0 then active else 1 + # header: 'h3' + # autoHeight: false + # $('#open_close_accordion a').click toggle + # $('#accordion').show() + + #$(".discussion-module").each (index, elem) -> + # Discussion.initializeDiscussionModule(elem) + + #$("section.discussion").each (index, discussion) -> + # Discussion.initializeDiscussion(discussion) + # Discussion.bindDiscussionEvents(discussion) + + #Discussion.initializeUserProfile($(".discussion-sidebar>.user-profile")) diff --git a/lms/static/coffee/src/old_discussion/templates.coffee b/lms/static/coffee/src/old_discussion/templates.coffee new file mode 100644 index 0000000000..4d43442a16 --- /dev/null +++ b/lms/static/coffee/src/old_discussion/templates.coffee @@ -0,0 +1,73 @@ +if not @Discussion? + @Discussion = {} + +Discussion = @Discussion + + +@Discussion = $.extend @Discussion, + + newPostTemplate: """ + + """ + + replyTemplate: """ +
    +
      +
      + + + {{#showWatchCheckbox}} + + + {{/showWatchCheckbox}} +
      +
      + Cancel + Submit +
      +
      + """ + + editThreadTemplate: """ +
      +
        + +
        {{body}}
        + +
        + Cancel + Update +
        +
        + """ + + editCommentTemplate: """ +
        +
          +
          {{body}}
          +
          + Cancel + Update +
          +
          + """ diff --git a/lms/static/coffee/src/old_discussion/user_profile.coffee b/lms/static/coffee/src/old_discussion/user_profile.coffee new file mode 100644 index 0000000000..0cff708ae6 --- /dev/null +++ b/lms/static/coffee/src/old_discussion/user_profile.coffee @@ -0,0 +1,34 @@ +if not @Discussion? + @Discussion = {} + +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) -> diff --git a/lms/static/coffee/src/old_discussion/utils.coffee b/lms/static/coffee/src/old_discussion/utils.coffee new file mode 100644 index 0000000000..c034b79675 --- /dev/null +++ b/lms/static/coffee/src/old_discussion/utils.coffee @@ -0,0 +1,244 @@ +if not @Discussion? + @Discussion = {} + +Discussion = @Discussion + +wmdEditors = {} + +@Discussion = $.extend @Discussion, + + generateLocal: (elem) -> + (selector) -> $(elem).find(selector) + + generateDiscussionLink: (cls, txt, handler) -> + $("").addClass("discussion-link") + .attr("href", "javascript:void(0)") + .addClass(cls).html(txt) + .click -> handler(this) + + urlFor: (name, param, param1, param2) -> + { + follow_discussion : "/courses/#{$$course_id}/discussion/#{param}/follow" + unfollow_discussion : "/courses/#{$$course_id}/discussion/#{param}/unfollow" + create_thread : "/courses/#{$$course_id}/discussion/#{param}/threads/create" + search_similar_threads : "/courses/#{$$course_id}/discussion/#{param}/threads/search_similar" + update_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/update" + create_comment : "/courses/#{$$course_id}/discussion/threads/#{param}/reply" + delete_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/delete" + upvote_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/upvote" + downvote_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/downvote" + undo_vote_for_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/unvote" + follow_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/follow" + unfollow_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/unfollow" + update_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/update" + endorse_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/endorse" + create_sub_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/reply" + delete_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/delete" + upvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/upvote" + downvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/downvote" + undo_vote_for_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/unvote" + upload : "/courses/#{$$course_id}/discussion/upload" + search : "/courses/#{$$course_id}/discussion/forum/search" + tags_autocomplete : "/courses/#{$$course_id}/discussion/threads/tags/autocomplete" + retrieve_discussion : "/courses/#{$$course_id}/discussion/forum/#{param}/inline" + retrieve_single_thread : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}" + update_moderator_status : "/courses/#{$$course_id}/discussion/users/#{param}/update_moderator_status" + openclose_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/close" + permanent_link_thread : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}" + permanent_link_comment : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}##{param2}" + }[name] + + safeAjax: (params) -> + $elem = params.$elem + if $elem.attr("disabled") + return + $elem.attr("disabled", "disabled") + $.ajax(params).always -> + $elem.removeAttr("disabled") + + handleAnchorAndReload: (response) -> + #window.location = window.location.pathname + "#" + response['id'] + window.location.reload() + + bindLocalEvents: ($local, eventsHandler) -> + for eventSelector, handler of eventsHandler + [event, selector] = eventSelector.split(' ') + $local(selector).unbind(event)[event] handler + + tagsInputOptions: -> + autocomplete_url: Discussion.urlFor('tags_autocomplete') + autocomplete: + remoteDataType: 'json' + interactive: true + height: '30px' + width: '100%' + defaultText: "Tag your post: press enter after each tag" + removeWithBackspace: true + + isSubscribed: (id, type) -> + $$user_info? and ( + if type == "thread" + id in $$user_info.subscribed_thread_ids + else if type == "commentable" or type == "discussion" + id in $$user_info.subscribed_commentable_ids + else + id in $$user_info.subscribed_user_ids + ) + + isUpvoted: (id) -> + $$user_info? and (id in $$user_info.upvoted_ids) + + isDownvoted: (id) -> + $$user_info? and (id in $$user_info.downvoted_ids) + + formErrorHandler: (errorsField) -> + (xhr, textStatus, error) -> + response = JSON.parse(xhr.responseText) + if response.errors? and response.errors.length > 0 + errorsField.empty() + for error in response.errors + errorsField.append($("
        • ").addClass("new-post-form-error").html(error)) + + clearFormErrors: (errorsField) -> + errorsField.empty() + + postMathJaxProcessor: (text) -> + RE_INLINEMATH = /^\$([^\$]*)\$/g + RE_DISPLAYMATH = /^\$\$([^\$]*)\$\$/g + Discussion.processEachMathAndCode text, (s, type) -> + if type == 'display' + s.replace RE_DISPLAYMATH, ($0, $1) -> + "\\[" + $1 + "\\]" + else if type == 'inline' + s.replace RE_INLINEMATH, ($0, $1) -> + "\\(" + $1 + "\\)" + else + s + + makeWmdEditor: ($content, $local, cls_identifier) -> + elem = $local(".#{cls_identifier}") + id = $content.attr("_id") + appended_id = "-#{cls_identifier}-#{id}" + imageUploadUrl = Discussion.urlFor('upload') + editor = Markdown.makeWmdEditor elem, appended_id, imageUploadUrl, Discussion.postMathJaxProcessor + wmdEditors["#{cls_identifier}-#{id}"] = editor + editor + + getWmdEditor: ($content, $local, cls_identifier) -> + id = $content.attr("_id") + wmdEditors["#{cls_identifier}-#{id}"] + + getWmdInput: ($content, $local, cls_identifier) -> + id = $content.attr("_id") + $local("#wmd-input-#{cls_identifier}-#{id}") + + getWmdContent: ($content, $local, cls_identifier) -> + Discussion.getWmdInput($content, $local, cls_identifier).val() + + setWmdContent: ($content, $local, cls_identifier, text) -> + Discussion.getWmdInput($content, $local, cls_identifier).val(text) + Discussion.getWmdEditor($content, $local, cls_identifier).refreshPreview() + + getContentInfo: (id, attr) -> + if not window.$$annotated_content_info? + window.$$annotated_content_info = {} + (window.$$annotated_content_info[id] || {})[attr] + + setContentInfo: (id, attr, value) -> + if not window.$$annotated_content_info? + window.$$annotated_content_info = {} + window.$$annotated_content_info[id] ||= {} + window.$$annotated_content_info[id][attr] = value + + extendContentInfo: (id, newInfo) -> + if not window.$$annotated_content_info? + window.$$annotated_content_info = {} + window.$$annotated_content_info[id] = newInfo + bulkExtendContentInfo: (newInfos) -> + if not window.$$annotated_content_info? + window.$$annotated_content_info = {} + window.$$annotated_content_info = $.extend window.$$annotated_content_info, newInfos + + subscriptionLink: (type, id) -> + followLink = -> + Discussion.generateDiscussionLink("discussion-follow-#{type}", "Follow", handleFollow) + + unfollowLink = -> + Discussion.generateDiscussionLink("discussion-unfollow-#{type}", "Unfollow", handleUnfollow) + + handleFollow = (elem) -> + Discussion.safeAjax + $elem: $(elem) + url: Discussion.urlFor("follow_#{type}", id) + type: "POST" + success: (response, textStatus) -> + if textStatus == "success" + $(elem).replaceWith unfollowLink() + dataType: 'json' + + handleUnfollow = (elem) -> + Discussion.safeAjax + $elem: $(elem) + url: Discussion.urlFor("unfollow_#{type}", id) + type: "POST" + success: (response, textStatus) -> + if textStatus == "success" + $(elem).replaceWith followLink() + dataType: 'json' + + if Discussion.isSubscribed(id, type) + unfollowLink() + else + followLink() + + processEachMathAndCode: (text, processor) -> + + codeArchive = [] + + RE_DISPLAYMATH = /^([^\$]*?)\$\$([^\$]*?)\$\$(.*)$/m + RE_INLINEMATH = /^([^\$]*?)\$([^\$]+?)\$(.*)$/m + + ESCAPED_DOLLAR = '@@ESCAPED_D@@' + ESCAPED_BACKSLASH = '@@ESCAPED_B@@' + + processedText = "" + + $div = $("
          ").html(text) + + $div.find("code").each (index, code) -> + codeArchive.push $(code).html() + $(code).html(codeArchive.length - 1) + + text = $div.html() + text = text.replace /\\\$/g, ESCAPED_DOLLAR + + while true + if RE_INLINEMATH.test(text) + text = text.replace RE_INLINEMATH, ($0, $1, $2, $3) -> + processedText += $1 + processor("$" + $2 + "$", 'inline') + $3 + else if RE_DISPLAYMATH.test(text) + text = text.replace RE_DISPLAYMATH, ($0, $1, $2, $3) -> + processedText += $1 + processor("$$" + $2 + "$$", 'display') + $3 + else + processedText += text + break + + text = processedText + text = text.replace(new RegExp(ESCAPED_DOLLAR, 'g'), '\\$') + + text = text.replace /\\\\\\\\/g, ESCAPED_BACKSLASH + text = text.replace /\\begin\{([a-z]*\*?)\}([\s\S]*?)\\end\{\1\}/img, ($0, $1, $2) -> + processor("\\begin{#{$1}}" + $2 + "\\end{#{$1}}") + text = text.replace(new RegExp(ESCAPED_BACKSLASH, 'g'), '\\\\\\\\') + + $div = $("
          ").html(text) + cnt = 0 + $div.find("code").each (index, code) -> + $(code).html(processor(codeArchive[cnt], 'code')) + cnt += 1 + + text = $div.html() + + text diff --git a/lms/templates/discussion/_forum.html b/lms/templates/discussion/_forum.html index 21dd182002..3248d1e3c9 100644 --- a/lms/templates/discussion/_forum.html +++ b/lms/templates/discussion/_forum.html @@ -2,7 +2,7 @@
          -
          +
          <%include file="_search_bar.html" />
          @@ -13,13 +13,13 @@
          % else: - <%include file="_sort.html" /> +
          <%include file="_sort.html" />
          % for thread in threads: ${renderer.render_content_with_comments(thread)} % endfor
          - <%include file="_paginator.html" /> +
          <%include file="_paginator.html" />
          % endif
          diff --git a/lms/templates/discussion/_inline.html b/lms/templates/discussion/_inline.html index 1737e2e558..d49a25a7ff 100644 --- a/lms/templates/discussion/_inline.html +++ b/lms/templates/discussion/_inline.html @@ -2,7 +2,7 @@
          -
          +
          % for thread in threads: @@ -10,7 +10,7 @@ % endfor
          - <%include file="_paginator.html" /> +
          <%include file="_paginator.html" />
          <%include file="_js_data.html" /> diff --git a/lms/templates/discussion/_js_data.html b/lms/templates/discussion/_js_data.html index 3e1dc4c280..2dc3759114 100644 --- a/lms/templates/discussion/_js_data.html +++ b/lms/templates/discussion/_js_data.html @@ -3,8 +3,12 @@ diff --git a/lms/templates/discussion/_js_dependencies.html b/lms/templates/discussion/_js_dependencies.html index 474f1abfe3..7ee4549028 100644 --- a/lms/templates/discussion/_js_dependencies.html +++ b/lms/templates/discussion/_js_dependencies.html @@ -26,5 +26,8 @@ + + + From 3b02e6a1ee6e89d3144cd1d9c072c03a4711445b Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Mon, 20 Aug 2012 09:02:18 -0400 Subject: [PATCH 02/91] started new nav styles --- lms/static/sass/course/base/_base.scss | 6 ++++++ lms/static/sass/course/courseware/_courseware.scss | 14 ++++++++++++++ .../sass/course/layout/_courseware_header.scss | 13 ++++++------- .../sass/course/layout/_courseware_subnav.scss | 8 +------- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/lms/static/sass/course/base/_base.scss b/lms/static/sass/course/base/_base.scss index 034e047754..56713ca429 100644 --- a/lms/static/sass/course/base/_base.scss +++ b/lms/static/sass/course/base/_base.scss @@ -1,5 +1,6 @@ body { min-width: 980px; + background: #f7f7f7; } body, h1, h2, h3, h4, h5, h6, p, p a:link, p a:visited, a { @@ -11,6 +12,11 @@ table { table-layout: fixed; } +.content-wrapper { + background: none; + border: none; +} + .container { padding: lh(2); diff --git a/lms/static/sass/course/courseware/_courseware.scss b/lms/static/sass/course/courseware/_courseware.scss index 198902c146..32a2c2a2af 100644 --- a/lms/static/sass/course/courseware/_courseware.scss +++ b/lms/static/sass/course/courseware/_courseware.scss @@ -3,8 +3,22 @@ html { max-height: 100%; } +div.info-wrapper { + padding: 40px; + box-sizing: border-box; + border-radius: 3px; + background: #fff; + @include box-shadow(0 1px 25px rgba(0, 0, 0, .1)); +} + div.course-wrapper { @extend .table-wrapper; + padding: 40px; + box-sizing: border-box; + border-radius: 3px; + background: #fff; + @include box-shadow(0 1px 25px rgba(0, 0, 0, .1)); + ul, ol { list-style: none; diff --git a/lms/static/sass/course/layout/_courseware_header.scss b/lms/static/sass/course/layout/_courseware_header.scss index dfa30b43a0..676ed5c58f 100644 --- a/lms/static/sass/course/layout/_courseware_header.scss +++ b/lms/static/sass/course/layout/_courseware_header.scss @@ -2,7 +2,7 @@ nav.course-material { @include clearfix; @include box-sizing(border-box); background: #f6f6f6; - border-bottom: 1px solid rgb(200,200,200); + border-bottom: none; margin: 0px auto 0px; padding: 0px; width: 100%; @@ -28,7 +28,7 @@ nav.course-material { text-align: center; padding: 8px 13px 12px; font-size: 14px; - font-weight: 400; + font-weight: bold; text-decoration: none; text-shadow: 0 1px rgb(255,255,255); @@ -37,11 +37,6 @@ nav.course-material { } &.active { - background: rgb(255,255,255); - border: 1px solid rgb(200,200,200); - border-bottom: 0px; - @include border-top-radius(4px); - @include box-shadow(0 2px 0 0 rgba(255,255,255, 1)); color: $blue; } } @@ -58,6 +53,10 @@ nav.course-material { } .global { + background: none !important; + border: none !important; + box-shadow: none !important; + .find-courses-button { display: none; } diff --git a/lms/static/sass/course/layout/_courseware_subnav.scss b/lms/static/sass/course/layout/_courseware_subnav.scss index 21f6187a83..c06b1435f3 100644 --- a/lms/static/sass/course/layout/_courseware_subnav.scss +++ b/lms/static/sass/course/layout/_courseware_subnav.scss @@ -1,8 +1,7 @@ nav.course-material { @include clearfix; @include box-sizing(border-box); - background: #f6f6f6; - border-bottom: 1px solid rgb(200,200,200); + background: none; margin: 0px auto 0px; padding: 0px; width: 100%; @@ -37,11 +36,6 @@ nav.course-material { } &.active { - background: rgb(255,255,255); - border: 1px solid rgb(200,200,200); - border-bottom: 0px; - @include border-top-radius(4px); - @include box-shadow(0 2px 0 0 rgba(255,255,255, 1)); color: $blue; } } From 8b4b1fddb5ac36ddbc03236434997bf4d91e011c Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Mon, 20 Aug 2012 10:15:28 -0400 Subject: [PATCH 03/91] added new wrapper styles --- lms/static/sass/course/base/_base.scss | 2 +- lms/static/sass/course/courseware/_courseware.scss | 6 +++--- lms/templates/courseware.html | 2 +- lms/templates/gradebook.html | 2 +- lms/templates/info.html | 2 +- lms/templates/profile.html | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lms/static/sass/course/base/_base.scss b/lms/static/sass/course/base/_base.scss index 56713ca429..c05a09570c 100644 --- a/lms/static/sass/course/base/_base.scss +++ b/lms/static/sass/course/base/_base.scss @@ -18,7 +18,7 @@ table { } .container { - padding: lh(2); + padding: 1.4em lh(2); > div { display: table; diff --git a/lms/static/sass/course/courseware/_courseware.scss b/lms/static/sass/course/courseware/_courseware.scss index 32a2c2a2af..1082930b13 100644 --- a/lms/static/sass/course/courseware/_courseware.scss +++ b/lms/static/sass/course/courseware/_courseware.scss @@ -3,12 +3,12 @@ html { max-height: 100%; } -div.info-wrapper { +div.inner-container { padding: 40px; box-sizing: border-box; border-radius: 3px; background: #fff; - @include box-shadow(0 1px 25px rgba(0, 0, 0, .1)); + @include box-shadow(0 0 1px 1px rgba(0, 0, 0, .08), 0 1px 10px rgba(0, 0, 0, 0.1)); } div.course-wrapper { @@ -17,7 +17,7 @@ div.course-wrapper { box-sizing: border-box; border-radius: 3px; background: #fff; - @include box-shadow(0 1px 25px rgba(0, 0, 0, .1)); + @include box-shadow(0 0 1px 1px rgba(0, 0, 0, .08), 0 1px 10px rgba(0, 0, 0, 0.1)); ul, ol { diff --git a/lms/templates/courseware.html b/lms/templates/courseware.html index a14f35d154..6bb6c74862 100644 --- a/lms/templates/courseware.html +++ b/lms/templates/courseware.html @@ -32,7 +32,7 @@ <%include file="course_navigation.html" args="active_page='courseware'" />
          -
          +
          close diff --git a/lms/templates/gradebook.html b/lms/templates/gradebook.html index 787fc23c9a..895f7ea004 100644 --- a/lms/templates/gradebook.html +++ b/lms/templates/gradebook.html @@ -31,7 +31,7 @@ <%include file="course_navigation.html" args="active_page=''" />
          -
          +

          Gradebook

          diff --git a/lms/templates/info.html b/lms/templates/info.html index a04e31896f..58bfbd6933 100644 --- a/lms/templates/info.html +++ b/lms/templates/info.html @@ -20,7 +20,7 @@ $(document).ready(function(){
          -
          +
          % if user.is_authenticated():

          Course Updates & News

          diff --git a/lms/templates/profile.html b/lms/templates/profile.html index ca27920a1b..e0639b0e00 100644 --- a/lms/templates/profile.html +++ b/lms/templates/profile.html @@ -113,7 +113,7 @@ $(function() { <%include file="course_navigation.html" args="active_page='profile'" />
          -
          +
          From e9e6b1828ed0e90bf3762f4c4475a3a5b47f2319 Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Mon, 20 Aug 2012 10:41:34 -0400 Subject: [PATCH 04/91] new header styles --- lms/static/sass/course/base/_base.scss | 8 +++++--- lms/static/sass/course/courseware/_courseware.scss | 8 -------- lms/static/sass/course/layout/_courseware_header.scss | 5 +++++ lms/templates/courseware.html | 2 +- lms/templates/gradebook.html | 2 +- lms/templates/info.html | 2 +- lms/templates/profile.html | 2 +- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/lms/static/sass/course/base/_base.scss b/lms/static/sass/course/base/_base.scss index c05a09570c..6bd42e63cd 100644 --- a/lms/static/sass/course/base/_base.scss +++ b/lms/static/sass/course/base/_base.scss @@ -18,12 +18,14 @@ table { } .container { - padding: 1.4em lh(2); + padding: 1.4em 0; > div { - display: table; width: 100%; - table-layout: fixed; + box-sizing: border-box; + border-radius: 3px; + background: #fff; + @include box-shadow(0 0 1px 1px rgba(0, 0, 0, .08), 0 1px 10px rgba(0, 0, 0, 0.1)); } } diff --git a/lms/static/sass/course/courseware/_courseware.scss b/lms/static/sass/course/courseware/_courseware.scss index 1082930b13..7905bd81e6 100644 --- a/lms/static/sass/course/courseware/_courseware.scss +++ b/lms/static/sass/course/courseware/_courseware.scss @@ -3,14 +3,6 @@ html { max-height: 100%; } -div.inner-container { - padding: 40px; - box-sizing: border-box; - border-radius: 3px; - background: #fff; - @include box-shadow(0 0 1px 1px rgba(0, 0, 0, .08), 0 1px 10px rgba(0, 0, 0, 0.1)); -} - div.course-wrapper { @extend .table-wrapper; padding: 40px; diff --git a/lms/static/sass/course/layout/_courseware_header.scss b/lms/static/sass/course/layout/_courseware_header.scss index 676ed5c58f..7a2a04a5db 100644 --- a/lms/static/sass/course/layout/_courseware_header.scss +++ b/lms/static/sass/course/layout/_courseware_header.scss @@ -17,6 +17,7 @@ nav.course-material { @include border-top-radius(4px); @include clearfix; padding: 10px 0 0 0; + margin-left: 10px; li { float: left; @@ -57,6 +58,10 @@ nav.course-material { border: none !important; box-shadow: none !important; + .logo { + margin-left: 13px !important; + } + .find-courses-button { display: none; } diff --git a/lms/templates/courseware.html b/lms/templates/courseware.html index 6bb6c74862..a14f35d154 100644 --- a/lms/templates/courseware.html +++ b/lms/templates/courseware.html @@ -32,7 +32,7 @@ <%include file="course_navigation.html" args="active_page='courseware'" />
          -
          +
          close diff --git a/lms/templates/gradebook.html b/lms/templates/gradebook.html index 895f7ea004..787fc23c9a 100644 --- a/lms/templates/gradebook.html +++ b/lms/templates/gradebook.html @@ -31,7 +31,7 @@ <%include file="course_navigation.html" args="active_page=''" />
          -
          +

          Gradebook

          diff --git a/lms/templates/info.html b/lms/templates/info.html index 58bfbd6933..a04e31896f 100644 --- a/lms/templates/info.html +++ b/lms/templates/info.html @@ -20,7 +20,7 @@ $(document).ready(function(){
          -
          +
          % if user.is_authenticated():

          Course Updates & News

          diff --git a/lms/templates/profile.html b/lms/templates/profile.html index e0639b0e00..ca27920a1b 100644 --- a/lms/templates/profile.html +++ b/lms/templates/profile.html @@ -113,7 +113,7 @@ $(function() { <%include file="course_navigation.html" args="active_page='profile'" />
          -
          +
          From 16190843e7bc4f4d0a4b797b5ac562bd4b7d996f Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Mon, 20 Aug 2012 13:56:51 -0400 Subject: [PATCH 05/91] started reworking sidebar styles --- lms/static/images/bg-texture.png | Bin 0 -> 7766 bytes lms/static/sass/base/_variables.scss | 2 ++ lms/static/sass/course.scss | 1 + lms/static/sass/course/_info.scss | 23 ++++++++++++------ lms/static/sass/course/base/_base.scss | 13 +++++++--- lms/static/sass/course/base/_extends.scss | 9 ++++--- .../sass/course/courseware/_courseware.scss | 6 ----- .../course/layout/_courseware_header.scss | 8 +++--- lms/static/sass/course/layout/_footer.scss | 4 +++ 9 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 lms/static/images/bg-texture.png create mode 100644 lms/static/sass/course/layout/_footer.scss diff --git a/lms/static/images/bg-texture.png b/lms/static/images/bg-texture.png new file mode 100644 index 0000000000000000000000000000000000000000..909af2b7ffd30e4ccb5ed9851874c1fc8bd8a96d GIT binary patch literal 7766 zcmbVxbyQSe+xAdONDC;S3@ISpH8dz7jYx{b2m?$2LwAFKG^lioNH;@Ch=8=@Fmy=@ zL+6Lj^ZefL`{P;fAKzK$oW1wG@9Vl_-}|h+)``^7dPYi2PYeJ6NY$Pz>)p0Ce;-2J z+xJZXF5YeP5UFB>)Q7)Cx?4Jf0SY#7D=<(EYH17B16$g7I`x8O002BYh=CE(Nb`l1 zH5|%k`B#U}1B$ps0{}8|9tcZo2QU(71-6C2KrDNxHWnbn2E_78R8v3`p#-*rJoj=2 z>w9S#SbI5GOWLr=$pU3Oq;3hIV5B9`1L_EKk@5hs{EJuWw*Obm&jS3H3eo|@@^7b% zG`yC?YB*CddO65)c&P7r4E}cm+kIM8%|p#DV|0SZ=L3 z+q{+1Q&#sK`LF$43uN(hW+r_{W0p`~OyTDzYt#8-kE$ctv+uZ%{hW;wvdLyOn47pttOGjn6 zwJQ`1L#io*SZ-(dY#=sLf(imk%1WXt3d%x)ViJOa5<)@>DvE;20^-6dq7o9~|8V>_ ztg@)0xQN&-UQtz9P*6osQbb%-Qdm+%L{(KtSVc_fAFLY81!)Ph2LEFha%=ZLSjGPp zE2ZQNwnW064d8Ice=0!74vvJo*ufD%C4Dg3Pf1ol_J^>=~(9j`Lj8R7=EQFVqx zf&a>|6y(2npr9-wDx#n)DWW1I^xq@@H`eC=Cu{t-&hY;&kN>A!{t?~Q;NR~56#wnu zf3^=8cDrYsZ#PE=tjY}lpbt?~Rxt3G*~auHu&GdmST@~G6$ zX|TUrW?YkhtX@^c!a6$u)zue1@j6a#JA?NFtA$OE~?InA&Ro*rj?tgt|%anPto>){=I%B5!DTppdd&ZTP~quOq+y@x>aAf`;X=nU3D`}fGYulC+k(!8FG zEYk5S)M@i?+pI(Ey;|vczf3vQh38SLsmN>>GIM`Ge4Z`TW!iJhm%%IX_j}D{G`wvG zYlFdDp0BQ_QnqU=nI!L%7+whPt*s|!x~h)rw|;n~ zC?_}MK12}1Oq2kjmh*?I4y+39VEaFwdHTcR7k-q8IzB|Xvf8Py`!2Hvxm=x5t4K2) zm%4P?>|D1DhOg&mo{v=qFM;+H%1^MmfDN0}Qa=>)8Bj2PACG{_sQ zs(tnC^wY~=Yw2B;aOx8FDzf$DpmcGyKbOB{wtcK{OiMx%l$-p;#gU5I{evltb+-g^1^r(K`5w$`wSq4nZqO0gbV{zkSf z?Dg{sE|Gl>bkk%1(F^RxWbu#&o|=een%wU0oOnd5)@#y|wBfOurXFmpr1SUDG%WV? z!W^)?*qrDrE{V-|$p09O7MC$uRtp6QmM4+)!E3bgry1dWo|%)r3^;hZzVErjldP@K z&F!jLnUmZF>xz288e~v8Qvf`7)=OQb_4nq7?3{j0&9M>~ifblNGM~ZY-s(7?6bHYQTQ{iE`1H5QK#?+^5>>br=!Cf z?^T7rJ>W86l_OUV-J=L8J=Wd*s0SeB%@A-5i+ZJ`bj3qzhu`M!2d-;8%gp%dnL-f0 zZadc^pjezJq#v~Xi8-R52r+8jiASi=n2qJ}TsjYweIb}7PxD$60JGufTzkd;^}+Bw zCXpivWtcKu7v|X9ji6qeS4w-qmr@W_w?0{YJs{jz(=L=Ivf$$cLSj;F0xN1k+9V(hoRGFvZxUInEVZ>ufQa1i zT)KQKYfy*6F)`XxNDr_>*Eehr$m7I6(QTAO_T)K`R_3@&<-T}T#)^KCn{(0|B-PL+ znLpBD9ITPnl-xxUCC}&Spsh_XMR(dNA#ViVr{Hd%F=z27mmOFtyNK#}5v(SB(K@>l z#Zx$mO*v+vv?t*cx1@O3HPm;Wu9_`~|Im@B-#ofifa>1T3mlSOfWk?3nWt#^?I-l4 z)`GFws*nd5$%*O$B>Jf}8Z*UZ5b^Mic=b__#+!Ww8)*?o=@B>DC$nb`5hTj)1(EpN z&2H$xb}x6lQ(4DiGmEsfvsPNR4L3V;Lb_?GGo2{9Mj^!sg0(XbMenIhl=|2wOOwbS zsc_*-@+r!~`)Yuq0AkkBU#GP{I+l2z*L+mXiIo4tPP@lvQ2B6W)mYF*P)515_4>*FyOVRp!g_pB^P#XKwYc(!$vLBEg4YBeP9Yc%DdU3`eXMQY!c zWf|OgL6(gXlS-F&35pDRmJZ(xAm`!`S#&`QH?VH3O>YtzVV)Fll#{*({lXWcHrg8- zRa|WDS~>;9xq^$p+oxI%{N&3GS;zau=WFwqT68th<~r(*^eJRvR22|dlH?$YKl6g^ zaDW<@m|0am`ccm4sptpe+1W#vNZ|4dVuHlW7cLOy>U&PxgV4iuGagU4e2B4tThPp? ze0nM+1&hpd#N>Pt%+leD%@y?kRp9BpOM}B{$`VT*Vp%p?>LAD!kImT~gB-74qzVPn zoFG;mAXiQ*@4EW8r(qwP8@}U?u^iMEmwkI*9TWp*upO6mevww-OxfQ6Cm`fn@|{7b za$XPXN891%eowEMM!pwCdX8h`38I$NO?uD(#-)7yLJvv$xDKkzGp36{n+m0w*c2 z&6qaW6&4%xSlLIpfgfNB^(Slu^VZI=+<>;p$KHCBH5atk0H>cnIqWW%Qu5CFqvI1* z#ohN!+)LP%3q_;)HR0n$(pm6O%rC*0BA*5oxEJD0BOw~p&YB-rpzO4zuhna*77WNG zzvV6dIsL&(hV#pZm;}|K$dKPkU=XKw%{)&dKsqMyA2 z?LM&dGX+<<8*=qVg8Tc_3POEb!h-eg>_L^{bT@6vcmkUNEf9#H^hs{%t1%I4fIt`v z#|Vvkot2{=r)W0n^T6j@3x;Ic4%9s5TW24kUYE%`ILk9fb{AOTP8e+Ki*)4Cf3^Vx z(5f0;<(BgDiK}VP?~KJdk8Sor!efB3_Y`GpLhuMlJ#i2)Y(|0#(oY@&Ja=m@b zh!HphA~W%nHC|QtW7kNdS=T#WA2PrBw&#c*=0n9*ZJdp^u&D=SXLImRbl;#kTAoZp zu#RE2#s;uP8ZD@_vK9hTDsyK$Dd@|!FixEsrjBAhsDxLaB~jaS`XHFbBV|w4q-2bJ zY@0)uOwZ1VwcFrRyPYhTrCy3WJ(2qSsS3Pl?h7sFhfLanvPFWV$8nl$gPUp99O+3g zVI1nMwOVC?T;Akm*8TcbL1QTf?zM#eiN(FOHpWWa@=qalTEQs|!CkwUK zOakL@w!o&JRcs?V7fxmDrXKzc9?!c^ z*U;FW1$FOzu0WyNEoz3g3f-+65e9^m0TU%%2QSpJ#tmvhaU(eZ{-7 zst>$#{3EoqsrLXfaeN6OF7TSZ2-@z}muV6qIKU+Wx1}+-YFfxJeMz@&sy&o%D;}4% za`|4glNe-`avW9g%`FA6MnF|bRBeSb_$a{IX*0;K4x@slesQ-oii*^O;UXb4Wqqf2 z=J!kVeOomY9zC zhvWFi#xo7v{5^^Cn&b!)oJn^KQ&g(U5As{(*D-dcFwU@g$B&4I=)5~A_@E=lxDD%wXP`+`%`zWLN?e(VD~sDgfc+cyk) zITll{F6B$xnmCelvZLPdb0(ByYgrXSJ{4}7&{R1+Pg5OS&bHa>1?~QMmJF~M18JSN z(NE051&1$@xxyQ1%KDq-URjD$U}wnHWMox&t?U;0!#wq4OW=6Q9XI~IbDn)d?}CLF zpFAT&yBNfgd)t1*Gcg4BUEKGQ->`GFGF)rRU4?Av^z_&kpTMUj!>PE=^-AlW_>9oj z1PO)Cn9Pa!h1A(BBVDk{pMAZfoomtCO(n?_X2TPk3k2bKC5pp4)D4CT1(X)Y`n<~O z#28qwV~De&OyVnX{+F|(0qUy-gRQyKpCYEXXXsD1~{3TJVDJ`Dv8P#s|S3f+7N*8`xsZU!ou!ZNZoGI}F)oO|vz0 zAQ9~}qK&BGpEhg&a~(N`*PM5I2=aoFl8C&h?JpHMzPkgC_6WAz7+kMWCLQm+Dwg(2 zc^GM$fRyELvP{(Tc_U__v|8od{6<5(9(d|A8hr`s_Uk{3ov__r-F&8QoYyKsY*$kQ z{p5mB60&u7E^Gk@2-$`8BXc=buO7nz> zzAn2l<}VcceD8Qu2nJ^HNL3&9BSH>v}_p!WuD2Bh{TAv6nnts zmf7id-^P*p7*h`wq>Z{9#diHP(rC$z)!amx#jL$Sa#)i`>SWk}OYc?KFc=8->S+vQ zU3X>sl-NQN7Svp8Y4C^s$ai4+HPa5}p{Ph6mS>`L);ip9OZrV%g<`>+x4L_p;G5D( zvyXFP?33<+7}^gH0~JT(qUYNDwP(3ACS-i-%?MuLXP5lmVt)0>s=_mJf(uOfTX}sa z)?%}AQ?AaLq+Oio@=quEIl3vSBF;Z)YqWQ>Fg2#)Q>K{oxY^T<&kKcTjdi?9P(a^dhqF8~b6!jTur5edUI7Z z(_s8zGc7C()NbxRiXq#4;~E@g^x2AhTDxGGituoG_AkYyh8%|vB_8g- z758w}*j?v1`gR^QC3mdUgeG7_wOwyqZ^WD3elhG)eTng4pc{7Fa+jZbr@K_6npMib zK{ER#ki@Mh3`23i{EiEM@y5h+UE3%j>GlNb#;LkNIC@zWice?gz?gkKmD0r& zD?D36=JCX=xidumXH73zGX}ul-qw%eM=@?9@BlW;JxIzPFnA^7ow;*Sx&MEm|r z^!J8S6%4}ro=ZBZqLV^|lcUK{j0Jn1N^&W+onfs#iv2jB%=`NO-MRvw-IM0rfF?_9< z*Zvx;<8k5`f&nl?Zlun@Q5CMQs8yR7DH|nm_h2yS^o@5w%{??NLQpE(r2gLNyRw`g z#Sd1i_G+|`@27yc_H~skGU3$mrNb)K9DRpcBaCx2;u$o#f{tmr z^utb4X=XGx8J>zTcX7Z!I(rY&tg46cVSO2kt)y|mx{pj~&04F{8Vo9r&1lhreC%7G z`v`K5hHX4c%)wohI&GBh&$m^Ca{|X*6t(Ff5rA~fZtY&7;7)k1VeI2?AfKK}7+SUc z;88+RA->>(n8WXy4}+j(QQW1dkyIzJj(~bcWU7ttT^2X`cOIaLwJ6Ywy}G*(nHK!OIJ{4I0pYmu5I0Qe zuejj}SmYar`qX@u9W+@aS|`%t<5*4v$~T_-<9e7b&`&Lmz0Tb~O75r?9M`kf_$CTJ zL)S64>2AblFUV{BXKA{s)ix;`_k5gOyaU1l5ZVt?`hfjp(=!7 z=P=AY^ngXiX=gnBpekxVPP+4hk z*TA#b$?<8Qo-@k4|BYKBHxP6G@$(Mc+(gF297}(4Dvgyo)T2q1sevY%)}yGDr=T~F zjxn&^)!!>5IrKzl38O#%V&V0#pq5UVq*4~|nr}_m4yjK0GhG0!r%K`PJ04IUAD56lxM1`D|3KCOmc<=9*>&C!AJOpDuH9FJ#WSrvPlJjS-%^X;L_;bw|};rQg;9e>4! zbirDpyX)#1t3UgNs_z4E2^xB4B7(1*3htD}iA^uzz2{U=^Z z6usQsZAW8Cu*B_b!3}&_Xm>|-a$7B2Ba#@7N1=R zZ#F+pd}cS=ijX89m{vi%$+uw1yk#RUoo?Xa_w7%dN`*(MYOjp-I--KBS({JJ#nFr0 z{td|d@}TW$`A<{T0TJOygoY>KsW%IT5GX=C1lkeY!EbZT0L|%v5hG(T!$7mTJArch zlVIb@ut6k;!@IhPCk!eD+pJT5#!rSSVv5sFrg%<@18n~U6eUeh06^x*>ZmMH7))9prOPd&`0GC*MFRnhObp4nrA`%`rvSBi^AmS>}C zTS05yu^@KVy(0&a*1fiy#})6+kUHNLQJAX;NA$A4JC>-&iHQC>^5-u7e05E-$uAv1 z6Wgc>%m!;0O>xrWJ!imnw{AM_Dc`e4+c+19HUbf}Onprx)3hEt&a`WhJbC%qx=`-j zjRx-+anSD5v~YV6`o_Ix-~G(gQ#KpV9z*95t38#6VEl6T>z+cj>FLTNWtK1RjK6={ zES`PPW*PFTBk3G2e~ltP!1Hw84{%l@6qCa^lo*;`Pv@nvg)LoQ1$1?!kUnO(?lQNN zp7p>j;LK|_q4+$}W-Inp&(T(C#4y>N7fF5p1IKP)aCH&*#KYy74mBqxYpsPC1mt|7 zsIu?(b^9ER`2nDq&Tc?2AY4&&tDWKG+0^vU%-{GPXZF^jaLs`uXzRxY2=o zAC&d??&jdn`GQD_TBlj65ab5kg+Y{5E#pDFpWKWyR_Kl0`6~vh&R6S#{WBhst@#;-BFJgNISQh(zaSi2h{vhO@Y#ppe~R2`mT{+Z;ELF=u9F50y)^i}?h^>4aR zOZsDpv3@c}^KNy7ID^z%j~h9zp(pRdlI@5UZ5i&Jv}deF^Q~39H07h#|Bh3QOY8yZ z?5^5%+a=h&(0+B9m5WKO4qw;lM2srdVX&Y4MWWF6`G{f=o!ck#D?9)$rJ2mkt8h1z zbS53T)#1FGlNM#BWuV79&l||_`UKH}k!_a##&=YfQlTYs;7Wf{g^qn6R1crOmb;Ga zo6cLU^KNKwinyY)^_K4rT8uI9riCi{dzzgcc#(4>Z!QQx@Y%X|Ym4dT>@~mr_L+tm z!^rLTQc8D>YXhCsPIjtXFY(+M%mo#NXx;#R Yzn8q1gBo1=`=?h;MN7F%@lDYG0lfgid;kCd literal 0 HcmV?d00001 diff --git a/lms/static/sass/base/_variables.scss b/lms/static/sass/base/_variables.scss index 7ad30f0c91..50c5c07b5a 100644 --- a/lms/static/sass/base/_variables.scss +++ b/lms/static/sass/base/_variables.scss @@ -21,6 +21,7 @@ $pink: rgb(182,37,104); $yellow: rgb(255, 252, 221); $error-red: rgb(253, 87, 87); $border-color: #C8C8C8; +$sidebar-color: #f6f6f6; // old variables @@ -28,3 +29,4 @@ $light-gray: #ddd; $dark-gray: #333; $mit-red: #993333; $text-color: $dark-gray; + diff --git a/lms/static/sass/course.scss b/lms/static/sass/course.scss index d3a74cb91b..ba40de32e4 100644 --- a/lms/static/sass/course.scss +++ b/lms/static/sass/course.scss @@ -11,6 +11,7 @@ // Course base / layout styles @import 'course/layout/courseware_header'; +@import 'course/layout/footer'; @import 'course/base/base'; @import 'course/base/extends'; @import 'module/module-styles.scss'; diff --git a/lms/static/sass/course/_info.scss b/lms/static/sass/course/_info.scss index 1651ad4da8..886c46c9e7 100644 --- a/lms/static/sass/course/_info.scss +++ b/lms/static/sass/course/_info.scss @@ -68,23 +68,31 @@ div.info-wrapper { section.handouts { @extend .sidebar; - border-left: 1px solid $border-color; @include border-radius(0 4px 4px 0); border-right: 0; @include box-shadow(none); + font-size: 14px; h1 { - @extend .bottom-border; margin-bottom: 0; - padding: lh(.5) lh(.5); + padding: 20px 26px; + font-size: 18px; + font-style: normal; + font-weight: bold; } ol { li { + padding: 0 26px; + margin-bottom: 14px; + a { display: block; - padding-left: lh(.5); - padding-right: 0; + padding: 0; + + &:hover { + background: transparent; + } } &.expandable, @@ -98,7 +106,6 @@ div.info-wrapper { } &.multiple { - padding: lh(.5) 0 lh(.5) lh(.5); a { @include inline-block; @@ -133,7 +140,7 @@ div.info-wrapper { } div.hitarea { - background-image: url('../images/treeview-default.gif'); + background-image: url('../images/treeview-default.gif') no-repeat; display: block; height: 100%; margin-left: 0; @@ -165,7 +172,7 @@ div.info-wrapper { @include box-shadow(none); color: #aaa; font-size: 1em; - margin-bottom: em(6); + margin-bottom: em(6); } p { diff --git a/lms/static/sass/course/base/_base.scss b/lms/static/sass/course/base/_base.scss index 6bd42e63cd..780520a3fc 100644 --- a/lms/static/sass/course/base/_base.scss +++ b/lms/static/sass/course/base/_base.scss @@ -1,6 +1,6 @@ body { min-width: 980px; - background: #f7f7f7; + background: url(../images/bg-texture.png) #f7f7f7; } body, h1, h2, h3, h4, h5, h6, p, p a:link, p a:visited, a { @@ -12,6 +12,13 @@ table { table-layout: fixed; } +a { + &:hover { + color: $pink; + text-decoration: none !important; + } +} + .content-wrapper { background: none; border: none; @@ -36,6 +43,6 @@ form { } ::selection, ::-moz-selection, ::-webkit-selection { - background:#444; - color:#fff; + background: #444; + color: #fff; } diff --git a/lms/static/sass/course/base/_extends.scss b/lms/static/sass/course/base/_extends.scss index 04eaf73094..3604f784f9 100644 --- a/lms/static/sass/course/base/_extends.scss +++ b/lms/static/sass/course/base/_extends.scss @@ -28,7 +28,7 @@ h1.top-header { .content { @include box-sizing(border-box); display: table-cell; - padding: lh(); + padding: 2.5em; vertical-align: top; width: flex-grid(9) + flex-gutter(); @@ -38,13 +38,14 @@ h1.top-header { } .sidebar { - border-right: 1px solid #C8C8C8; + // border-right: 1px solid #C8C8C8; @include box-sizing(border-box); display: table-cell; font-family: $sans-serif; position: relative; vertical-align: top; width: flex-grid(3); + background: $sidebar-color; h1, h2 { font-size: em(20); @@ -85,7 +86,7 @@ h1.top-header { } &.active { - @extend .bottom-border; + // @extend .bottom-border; color: #000; font-weight: bold; @@ -101,7 +102,7 @@ h1.top-header { padding-left: 0; li { - @extend .bottom-border; + // @extend .bottom-border; @extend .clearfix; background: none; position: relative; diff --git a/lms/static/sass/course/courseware/_courseware.scss b/lms/static/sass/course/courseware/_courseware.scss index 7905bd81e6..198902c146 100644 --- a/lms/static/sass/course/courseware/_courseware.scss +++ b/lms/static/sass/course/courseware/_courseware.scss @@ -5,12 +5,6 @@ html { div.course-wrapper { @extend .table-wrapper; - padding: 40px; - box-sizing: border-box; - border-radius: 3px; - background: #fff; - @include box-shadow(0 0 1px 1px rgba(0, 0, 0, .08), 0 1px 10px rgba(0, 0, 0, 0.1)); - ul, ol { list-style: none; diff --git a/lms/static/sass/course/layout/_courseware_header.scss b/lms/static/sass/course/layout/_courseware_header.scss index 7a2a04a5db..ca8b9918d2 100644 --- a/lms/static/sass/course/layout/_courseware_header.scss +++ b/lms/static/sass/course/layout/_courseware_header.scss @@ -1,7 +1,6 @@ nav.course-material { @include clearfix; @include box-sizing(border-box); - background: #f6f6f6; border-bottom: none; margin: 0px auto 0px; padding: 0px; @@ -24,7 +23,7 @@ nav.course-material { list-style: none; a { - color: darken($lighter-base-font-color, 20%); + color: #555; display: block; text-align: center; padding: 8px 13px 12px; @@ -34,7 +33,7 @@ nav.course-material { text-shadow: 0 1px rgb(255,255,255); &:hover { - color: $base-font-color; + color: #333; } &.active { @@ -72,6 +71,7 @@ nav.course-material { float: left; font-size: 0.9em; font-weight: 600; + color: #888; line-height: 40px; letter-spacing: 0; text-transform: none; @@ -83,7 +83,7 @@ nav.course-material { .provider { font: inherit; font-weight: bold; - color: #6d6d6d; + color: #555; } } } \ No newline at end of file diff --git a/lms/static/sass/course/layout/_footer.scss b/lms/static/sass/course/layout/_footer.scss new file mode 100644 index 0000000000..7abf35a819 --- /dev/null +++ b/lms/static/sass/course/layout/_footer.scss @@ -0,0 +1,4 @@ +footer { + border: none; + box-shadow: none; +} \ No newline at end of file From 0f6c31b4bd67dee2459b887d5f299d58cad4a80b Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Mon, 20 Aug 2012 15:51:10 -0400 Subject: [PATCH 06/91] started courseware sidebar styling --- lms/static/sass/course/_info.scss | 39 ++++++++----- lms/static/sass/course/base/_base.scss | 3 +- lms/static/sass/course/base/_extends.scss | 2 +- .../sass/course/courseware/_sidebar.scss | 58 ++++++++++--------- lms/templates/accordion.html | 32 +++++----- 5 files changed, 77 insertions(+), 57 deletions(-) diff --git a/lms/static/sass/course/_info.scss b/lms/static/sass/course/_info.scss index 886c46c9e7..9a64c9d042 100644 --- a/lms/static/sass/course/_info.scss +++ b/lms/static/sass/course/_info.scss @@ -75,7 +75,7 @@ div.info-wrapper { h1 { margin-bottom: 0; - padding: 20px 26px; + padding: 32px 26px 20px 26px; font-size: 18px; font-style: normal; font-weight: bold; @@ -83,8 +83,7 @@ div.info-wrapper { ol { li { - padding: 0 26px; - margin-bottom: 14px; + margin: 0 26px 14px 26px; a { display: block; @@ -97,11 +96,25 @@ div.info-wrapper { &.expandable, &.collapsable { + margin: 0 16px 14px 16px; + @include transition(all .2s); + h4 { color: $blue; font-size: 1em; font-weight: normal; - padding: lh(.25) 0 lh(.25) lh(1.5); + padding-left: 30px; + } + } + + &.collapsable { + background: #fff; + border-radius: 3px; + padding: 14px 0; + @include box-shadow(0 0 1px 1px rgba(0, 0, 0, .1), 0 1px 3px rgba(0, 0, 0, .25)); + + h4 { + margin-bottom: 16px; } } @@ -123,10 +136,10 @@ div.info-wrapper { li { border-bottom: 0; - border-top: 1px solid $border-color; - @include box-shadow(inset 0 1px 0 #eee); - font-size: 1em; - padding: lh(.5) 0 lh(.5) lh(.5); + border-top: 1px solid #e6e6e6; + font-size: 0.9em; + margin: 0; + padding: 15px 30px; a { @include inline-block; @@ -144,7 +157,7 @@ div.info-wrapper { display: block; height: 100%; margin-left: 0; - max-height: 30px; + max-height: 20px; position: absolute; width: 100%; @@ -159,20 +172,20 @@ div.info-wrapper { } &.expandable-hitarea { - background-position: -72px 7px; + background-position: -72px 0px; } &.collapsable-hitarea { - background-position: -55px -15px; + background-position: -55px -23px; } } h3 { border-bottom: 0; @include box-shadow(none); - color: #aaa; + color: #888; font-size: 1em; - margin-bottom: em(6); + margin-bottom: 0; } p { diff --git a/lms/static/sass/course/base/_base.scss b/lms/static/sass/course/base/_base.scss index 780520a3fc..a7a59c7bf3 100644 --- a/lms/static/sass/course/base/_base.scss +++ b/lms/static/sass/course/base/_base.scss @@ -31,8 +31,9 @@ a { width: 100%; box-sizing: border-box; border-radius: 3px; + border: 1px solid #ccc; background: #fff; - @include box-shadow(0 0 1px 1px rgba(0, 0, 0, .08), 0 1px 10px rgba(0, 0, 0, 0.1)); + @include box-shadow(0 1px 10px rgba(0, 0, 0, 0.1)); } } diff --git a/lms/static/sass/course/base/_extends.scss b/lms/static/sass/course/base/_extends.scss index 3604f784f9..7087fd3568 100644 --- a/lms/static/sass/course/base/_extends.scss +++ b/lms/static/sass/course/base/_extends.scss @@ -28,7 +28,7 @@ h1.top-header { .content { @include box-sizing(border-box); display: table-cell; - padding: 2.5em; + padding: 2em 2.5em; vertical-align: top; width: flex-grid(9) + flex-gutter(); diff --git a/lms/static/sass/course/courseware/_sidebar.scss b/lms/static/sass/course/courseware/_sidebar.scss index 51e9cbd90d..e516939297 100644 --- a/lms/static/sass/course/courseware/_sidebar.scss +++ b/lms/static/sass/course/courseware/_sidebar.scss @@ -1,6 +1,12 @@ section.course-index { @extend .sidebar; @extend .tran; + @include border-radius(3px 0 0 3px); + border-right: 1px solid #ddd; + + #open_close_accordion { + display: none; + } header { max-height: 47px; @@ -11,10 +17,11 @@ section.course-index { } div#accordion { + width: auto; + font-size: 14px; + h3 { @include border-radius(0); - border-top: 1px solid lighten($border-color, 10%); - font-size: em(16, 18); margin: 0; overflow: hidden; @@ -24,7 +31,6 @@ section.course-index { &:hover { color: #666; - background: #f6f6f6; } &.ui-state-hover { @@ -40,6 +46,7 @@ section.course-index { a { @include border-radius(0); @include box-shadow(none); + padding-left: 19px; } &.ui-state-active { @@ -52,28 +59,42 @@ section.course-index { } span.ui-icon { + left: 0; background-image: url("/static/images/ui-icons_222222_256x240.png"); opacity: .3; } } } + .chapter { + padding: 11px 14px; + @include linear-gradient(top, #f9f9f9, #eee); + box-shadow: 0 1px 0 #fff inset, 0 -1px 0 rgba(0, 0, 0, .1) inset; + + &:first-child { + border-radius: 3px 0 0 0; + } + + &:last-child { + border-radius: 0 0 0 3px; + } + } + ul.ui-accordion-content { background: transparent; border: none; @include border-radius(0); - font-size: em(14, 18); margin: 0; - padding: 1em 1.5em; + padding: 9px 0 9px 9px; li { border-bottom: 0; @include border-radius(0); margin-bottom: lh(.5); + line-height: 1.4; a { background: transparent; - border: 1px solid transparent; @include border-radius(4px); display: block; padding: 5px 36px 5px 10px; @@ -92,26 +113,7 @@ section.course-index { } } - &:after { - background: transparent; - border-right: 1px solid rgb(180,180,180); - border-top: 1px solid rgb(180,180,180); - content: ""; - display: block; - height: 12px; - margin-top: -6px; - opacity: 0; - position: absolute; - right: 30px; - top: 50%; - @include transform(rotate(45deg)); - width: 12px; - } - &:hover { - @include background-image(linear-gradient(-90deg, rgba(245,245,245, 0.4), rgba(230,230,230, 0.4))); - border-color: rgb(200,200,200); - &:after { opacity: 1; right: 15px; @@ -134,10 +136,12 @@ section.course-index { } &.active { - font-weight: bold; + font-weight: bold; > a { - border-color: rgb(200,200,200); + border: none; + @include box-shadow(0 1px 0 rgba(255, 255, 255, .35) inset); + @include linear-gradient(top, #ddd, #bbb); &:after { opacity: 1; diff --git a/lms/templates/accordion.html b/lms/templates/accordion.html index 1f514fe4a4..0d3dee9495 100644 --- a/lms/templates/accordion.html +++ b/lms/templates/accordion.html @@ -1,22 +1,24 @@ <%! from django.core.urlresolvers import reverse %> <%def name="make_chapter(chapter)"> -

          ${chapter['display_name']} -

          + % for chapter in toc: From ee92ba0752f68a49fb10a3e3763677627ceef9a4 Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Mon, 20 Aug 2012 17:56:51 -0400 Subject: [PATCH 07/91] new sequence nav; new courseware sidebar; new textbook styling; fixed gradebook glitch --- lms/static/sass/course/_gradebook.scss | 3 ++ lms/static/sass/course/_info.scss | 2 +- lms/static/sass/course/_textbook.scss | 21 ++++++------ lms/static/sass/course/base/_extends.scss | 2 +- .../sass/course/courseware/_courseware.scss | 11 +++++++ .../sass/course/courseware/_sidebar.scss | 32 ++++++++++++------- 6 files changed, 49 insertions(+), 22 deletions(-) diff --git a/lms/static/sass/course/_gradebook.scss b/lms/static/sass/course/_gradebook.scss index cd3205149c..d8be7d779a 100644 --- a/lms/static/sass/course/_gradebook.scss +++ b/lms/static/sass/course/_gradebook.scss @@ -6,6 +6,9 @@ div.gradebook-wrapper { section.gradebook-content { @extend .content; + display: block; + width: 100%; + @include clearfix; .student-search { padding: 0 20px 0 15px; diff --git a/lms/static/sass/course/_info.scss b/lms/static/sass/course/_info.scss index 9a64c9d042..8cc5293246 100644 --- a/lms/static/sass/course/_info.scss +++ b/lms/static/sass/course/_info.scss @@ -69,7 +69,7 @@ div.info-wrapper { section.handouts { @extend .sidebar; @include border-radius(0 4px 4px 0); - border-right: 0; + border-left: 1px solid #ddd; @include box-shadow(none); font-size: 14px; diff --git a/lms/static/sass/course/_textbook.scss b/lms/static/sass/course/_textbook.scss index 8e88f8befd..73a215a46c 100644 --- a/lms/static/sass/course/_textbook.scss +++ b/lms/static/sass/course/_textbook.scss @@ -1,10 +1,17 @@ div.book-wrapper { @extend .table-wrapper; + #open_close_accordion { + display: none; + } + section.book-sidebar { @extend .sidebar; @extend .tran; @include box-sizing(border-box); + padding: 10px 0; + border-radius: 3px 0 0 3px; + border-right: 1px solid #ccc; ul#booknav { font-size: em(14); @@ -33,7 +40,7 @@ div.book-wrapper { li { background: none; border-bottom: 0; - padding-left: lh(); + padding-left: lh(); a { padding: 0; @@ -50,7 +57,7 @@ div.book-wrapper { div.hitarea { background-image: url('../images/treeview-default.gif'); - margin-left: -22px; + position: relative; top: 4px; @@ -63,27 +70,23 @@ div.book-wrapper { ul { background: none; margin-top: lh(.25); - border-top: 1px solid $border-color; padding-top: lh(.25); li { - padding-bottom: lh(.25); + padding-bottom: 10px; } } } > li { - border-bottom: 1px solid $border-color; - padding: 7px 7px 7px 30px; + padding: 5px 6px; + margin: 0 16px 5px 25px; } } } section.book { @extend .content; - padding-bottom: 0; - padding-right: 0; - padding-top: 0; nav { @extend .clearfix; diff --git a/lms/static/sass/course/base/_extends.scss b/lms/static/sass/course/base/_extends.scss index 7087fd3568..35ea19ee91 100644 --- a/lms/static/sass/course/base/_extends.scss +++ b/lms/static/sass/course/base/_extends.scss @@ -54,7 +54,7 @@ h1.top-header { text-transform: none; font-family: $sans-serif; text-align: left; - font-style: italic; + font-style: normal; } a { diff --git a/lms/static/sass/course/courseware/_courseware.scss b/lms/static/sass/course/courseware/_courseware.scss index 198902c146..28bd817607 100644 --- a/lms/static/sass/course/courseware/_courseware.scss +++ b/lms/static/sass/course/courseware/_courseware.scss @@ -12,6 +12,7 @@ div.course-wrapper { section.course-content { @extend .content; + padding-top: 0 !important; @include border-radius(0 4px 4px 0); h1 { @@ -45,6 +46,7 @@ div.course-wrapper { ol.vert-mod { padding: 0; margin: 0; + line-height: 1.4; > li { @extend .clearfix; @@ -210,3 +212,12 @@ div.course-wrapper { } } } + +.xmodule_VideoModule { + margin-bottom: 30px; + + +} + + + diff --git a/lms/static/sass/course/courseware/_sidebar.scss b/lms/static/sass/course/courseware/_sidebar.scss index e516939297..7c884bf5d3 100644 --- a/lms/static/sass/course/courseware/_sidebar.scss +++ b/lms/static/sass/course/courseware/_sidebar.scss @@ -90,8 +90,7 @@ section.course-index { li { border-bottom: 0; @include border-radius(0); - margin-bottom: lh(.5); - line-height: 1.4; + margin-bottom: 4px; a { background: transparent; @@ -105,20 +104,18 @@ section.course-index { font-weight: bold; font-family: $sans-serif; margin-bottom: 0; + line-height: 1.3; span.subtitle { color: #666; + font-size: 13px; font-weight: normal; display: block; } - } + } &:hover { - &:after { - opacity: 1; - right: 15px; - @include transition(); - } + background: #fff; > a p { color: #333; @@ -136,12 +133,25 @@ section.course-index { } &.active { - font-weight: bold; + font-weight: bold; + + &:after { + content: '›'; + position: absolute; + top: 50%; + right: 20px; + margin-top: -13px; + font-size: 30px; + font-weight: normal; + color: #333; + opacity: 0; + @include transition(); + } > a { - border: none; + border: 1px solid #bbb; @include box-shadow(0 1px 0 rgba(255, 255, 255, .35) inset); - @include linear-gradient(top, #ddd, #bbb); + @include linear-gradient(top, #e6e6e6, #d6d6d6); &:after { opacity: 1; From a1ff34571e47ef6a7b4365ef6f7169599344e14f Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Mon, 20 Aug 2012 18:01:57 -0400 Subject: [PATCH 08/91] sequence nav changes were lost?! temporary fix --- lms/static/sass/course/courseware/_courseware.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/lms/static/sass/course/courseware/_courseware.scss b/lms/static/sass/course/courseware/_courseware.scss index 28bd817607..459987e6b2 100644 --- a/lms/static/sass/course/courseware/_courseware.scss +++ b/lms/static/sass/course/courseware/_courseware.scss @@ -12,7 +12,6 @@ div.course-wrapper { section.course-content { @extend .content; - padding-top: 0 !important; @include border-radius(0 4px 4px 0); h1 { From 88d514e28d3dd12287b4d2d9587410fd4b1dc342 Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Mon, 20 Aug 2012 18:15:09 -0400 Subject: [PATCH 09/91] fixed merge conflict in extends --- lms/static/sass/course/base/_extends.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lms/static/sass/course/base/_extends.scss b/lms/static/sass/course/base/_extends.scss index 9604a6a7e6..f04d879bc3 100644 --- a/lms/static/sass/course/base/_extends.scss +++ b/lms/static/sass/course/base/_extends.scss @@ -37,11 +37,7 @@ h1.top-header { .content { @include box-sizing(border-box); display: table-cell; -<<<<<<< HEAD padding: 2em 2.5em; -======= - padding-right: lh(); ->>>>>>> master vertical-align: top; width: flex-grid(9) + flex-gutter(); From dc599c469f68d4835f65e37be106bbdb40c4fec8 Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Mon, 20 Aug 2012 15:26:07 -0700 Subject: [PATCH 10/91] fixed test --- lms/djangoapps/django_comment_client/tests.py | 19 +++++- lms/static/templates/_content.mustache | 62 +++++++++++++++++++ .../discussion/_js_dependencies.html | 1 + 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 lms/static/templates/_content.mustache diff --git a/lms/djangoapps/django_comment_client/tests.py b/lms/djangoapps/django_comment_client/tests.py index ac18c5b5bf..2f62c2f301 100644 --- a/lms/djangoapps/django_comment_client/tests.py +++ b/lms/djangoapps/django_comment_client/tests.py @@ -1,8 +1,12 @@ from django.contrib.auth.models import User from django.utils import unittest +from student.models import CourseEnrollment + +from django.db.models.signals import m2m_changed, pre_delete, pre_save, post_delete, post_save +from django.dispatch.dispatcher import _make_id import string import random -from .permissions import student_role, moderator_role, add_permission, has_permission +from .permissions import has_permission, assign_default_role from .models import Role, Permission @@ -11,6 +15,17 @@ class PermissionsTestCase(unittest.TestCase): return ''.join(random.choice(chars) for x in range(length)) def setUp(self): + + sender_receivers_to_keep = [ + (assign_default_role, CourseEnrollment), + ] + super(PermissionsTestCase, self).setUp(sender_receivers_to_keep=sender_receivers_to_keep) + + self.course_id = "MITx/6.002x/2012_Fall" + + self.moderator_role = Role.objects.get_or_create(name="Moderator", course_id=self.course_id)[0] + self.student_role = Role.objects.get_or_create(name="Student", course_id=self.course_id)[0] + self.student = User.objects.create(username=self.random_str(), password="123456", email="john@yahoo.com") self.moderator = User.objects.create(username=self.random_str(), @@ -33,4 +48,4 @@ class PermissionsTestCase(unittest.TestCase): self.assertTrue(has_permission(self.moderator, name)) add_permission(self.student, name) - self.assertTrue(has_permission(self.student, name)) \ No newline at end of file + self.assertTrue(has_permission(self.student, name)) diff --git a/lms/static/templates/_content.mustache b/lms/static/templates/_content.mustache new file mode 100644 index 0000000000..8f3a284510 --- /dev/null +++ b/lms/static/templates/_content.mustache @@ -0,0 +1,62 @@ +
          +
          +
          + +
          {{content.votes.point}}
          + +
          +
          + + {{#thread}} + {{{content.displayed_title}}} + + {{/thread}} +
          + +
          {{{content.displayed_body}}}
          + + {{#thread}} +
          + {{#content.tags}} + {{.}} + {{/content.tags}} +
          + + {{/thread}} +
          +
          + sometime by + {{#content.anonymous}} + anonymous + {{/content.anonymous}} + {{^content.anonymous}} + {{content.username}} + {{/content.anonymous}} +
          +
          + {{#thread}} + {{#partial_comments}} + Show all comments ({{content.comments_count}} total) + {{/partial_comments}} + {{^partial_comments}} + Show {{##pluralize}}{{content.comments_count}} comment{{/pluralize}} + {{/partial_comments}} + {{/thread}} +
          + +
          +
          +
          +
          +
          diff --git a/lms/templates/discussion/_js_dependencies.html b/lms/templates/discussion/_js_dependencies.html index 7ee4549028..18da17ba06 100644 --- a/lms/templates/discussion/_js_dependencies.html +++ b/lms/templates/discussion/_js_dependencies.html @@ -31,3 +31,4 @@ + From d189a49ee52032add505ca7d482b2c5ac81c841d Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Mon, 20 Aug 2012 17:32:36 -0700 Subject: [PATCH 11/91] continue working on backbone & got vote migrated and working --- .../django_comment_client/helpers.py | 19 +- .../src/backbone_discussion/content.coffee | 131 ++++++++- .../src/backbone_discussion/discussion.coffee | 55 ++-- .../src/backbone_discussion/main.coffee | 9 + .../src/backbone_discussion/utils.coffee | 255 ++++++++++++++++++ lms/templates/courseware/courseware.html | 3 +- lms/templates/discussion/_content.mustache | 4 +- .../discussion/_content_renderer.html | 2 +- .../discussion/_js_body_dependencies.html | 4 + .../discussion/_js_dependencies.html | 3 - .../discussion/_js_head_dependencies.html | 17 ++ lms/templates/discussion/index.html | 3 +- 12 files changed, 466 insertions(+), 39 deletions(-) create mode 100644 lms/templates/discussion/_js_body_dependencies.html create mode 100644 lms/templates/discussion/_js_head_dependencies.html diff --git a/lms/djangoapps/django_comment_client/helpers.py b/lms/djangoapps/django_comment_client/helpers.py index a168f53021..b3eff7026d 100644 --- a/lms/djangoapps/django_comment_client/helpers.py +++ b/lms/djangoapps/django_comment_client/helpers.py @@ -1,11 +1,15 @@ from django.core.urlresolvers import reverse +from django.template.defaultfilters import escapejs +from django.conf import settings from mitxmako.shortcuts import render_to_string -from utils import * from mustache_helpers import mustache_helpers from functools import partial +from utils import * + import pystache_custom as pystache import urllib +import os def pluralize(singular_term, count): if int(count) >= 2: @@ -18,6 +22,19 @@ def show_if(text, condition): else: return '' +# TODO there should be a better way to handle this +def include_mustache_templates(): + mustache_dir = settings.PROJECT_ROOT / 'templates' / 'discussion' + valid_file_name = lambda file_name: file_name.endswith('.mustache') + read_file = lambda file_name: (file_name, open(mustache_dir / file_name, "r").read()) + strip_file_name = lambda x: (x[0].rpartition('.')[0], x[1]) + wrap_in_tag = lambda x: "".format(x[0], escapejs(x[1])) + + 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 render_content(content, additional_context={}): content_info = { 'displayed_title': content.get('highlighted_title') or content.get('title', ''), diff --git a/lms/static/coffee/src/backbone_discussion/content.coffee b/lms/static/coffee/src/backbone_discussion/content.coffee index afd7bd6ff0..6d67846346 100644 --- a/lms/static/coffee/src/backbone_discussion/content.coffee +++ b/lms/static/coffee/src/backbone_discussion/content.coffee @@ -1,7 +1,128 @@ -$ -> - class Content extends Backbone.Model +class @Content extends Backbone.Model - class Thread extends Content + template: -> DiscussionUtil.getTemplate('_content') - window.Content = Content - window.Thread = Thread + actions: + editable: '.admin-edit' + can_reply: '.discussion-reply' + can_endorse: '.admin-endorse' + can_delete: '.admin-delete' + can_openclose: '.admin-openclose' + + isUpvoted: -> + DiscussionUtil.isUpvoted @id + + isDownvoted: -> + DiscussionUtil.isDownvoted @id + + can: (action) -> + DiscussionUtil.getContentInfo @id, action + +class @ContentView extends Backbone.View + + $: (selector) -> + @$local.find(selector) + + showSingleThread: (event) -> + + unvote: (event) -> + url = DiscussionUtil.urlFor("undo_vote_for_#{@model.get('type')}", @model.id) + DiscussionUtil.safeAjax + $elem: @$(".discussion-vote") + url: url + type: "POST" + dataType: "json" + success: (response, textStatus) => + if textStatus == "success" + @$(".discussion-vote").removeClass("voted") + @$(".discussion-votes-point").html response.votes.point + + vote: (event) -> + $elem = $(event.target) + if $elem.hasClass("voted") + @unvote(event) + else + value = $elem.attr("value") + url = Discussion.urlFor("#{value}vote_#{@model.get('type')}", @model.id) + DiscussionUtil.safeAjax + $elem: @$(".discussion-vote") + url: url + type: "POST" + dataType: "json" + success: (response, textStatus) => + if textStatus == "success" + @$(".discussion-vote").removeClass("voted") + @$(".discussion-vote-#{value}").addClass("voted") + @$(".discussion-votes-point").html response.votes.point + + hideSingleThread: -> + + reply: -> + + cancelReply: -> + + endorse: -> + + close: -> + + edit: -> + + delete: -> + + events: + "click .thread-title": "showSingleThread" + "click .discussion-show-comments": "showSingleThread" + "click .discussion-hide-comments": "hideSingleThread" + "click .discussion-reply-thread": "reply" + "click .discussion-reply-comment": "reply" + "click .discussion-cancel-reply": "cancelReply" + "click .discussion-vote-up": "vote" + "click .discussion-vote-down": "vote" + "click .admin-endorse": "endorse" + "click .admin-openclose": "close" + "click .admin-edit": "edit" + "click .admin-delete": "delete" + + initLocal: -> + @$local = @$el.children(".local") + + initFollowThread: -> + $el.children(".discussion-content") + .find(".follow-wrapper") + .append(DiscussionUtil.subscriptionLink('thread', id)) + + initVote: -> + if @model.isUpvoted() + @$(".discussion-vote-up").addClass("voted") + else if @model.isDownvoted() + @$(".discussion-vote-down").addClass("voted") + + initBody: -> + $contentBody = @$(".content-body") + $contentBody.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight $contentBody.html() + MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")] + + initActions: -> + for action, elemSelector of @model.actions + if not @model.can action + @$(elemSelector).remove() + + initTimeago: -> + @$("span.timeago").timeago() + + initialize: -> + @model.view = @ + @initLocal() + @initVote() + @initTimeago() + @initBody() + @initActions() + +class @Thread extends @Content + +class @ThreadView extends @ContentView + +class @Comment extends @Content + +class @Comments extends Backbone.Collection + model: Comment diff --git a/lms/static/coffee/src/backbone_discussion/discussion.coffee b/lms/static/coffee/src/backbone_discussion/discussion.coffee index d67da04e0e..39ce545dae 100644 --- a/lms/static/coffee/src/backbone_discussion/discussion.coffee +++ b/lms/static/coffee/src/backbone_discussion/discussion.coffee @@ -1,31 +1,36 @@ -$ -> - - class Discussion extends Backbone.Collection - model: Thread - initialize: -> - this.bind "add", (item) => - item.collection = this +class @Discussion extends Backbone.Collection + model: Thread + initialize: -> + this.bind "add", (item) => + item.collection = this - class DiscussionModuleView extends Backbone.View + find: (id) -> + _.first @where(id: id) - class DiscussionView extends Backbone.View +class @DiscussionModuleView extends Backbone.View - $: (selector) -> - @$local.find(selector) +class @DiscussionView extends Backbone.View - initialize: -> - @$local = @$el.children(".local") + $: (selector) -> + @$local.find(selector) - events: - "submit .search-wrapper>.discussion-search-form": "search" - "click .discussion-search-link": "search" - "click .discussion-sort-link": "sort" - "click .discussion-paginator>.discussion-page-link": "page" - - $(".discussion-module").each (index, elem) -> - view = new DiscussionModuleView(el: elem) + initLocal: -> + @$local = @$el.children(".local") - $("section.discussion").each (index, elem) -> - discussionData = DiscussionUtil.getDiscussionData(elem) - discussion = new Discussion(discussionData) - view = new DiscussionView(el: elem, model: discussion) + initialize: -> + @initLocal() + @model.view = @ + @$el.children(".threads").children(".thread").each (index, elem) => + threadView = new ThreadView el: elem, model: @model.find $(elem).attr("_id") + + search: -> + + sort: -> + + page: -> + + events: + "submit .search-wrapper>.discussion-search-form": "search" + "click .discussion-search-link": "search" + "click .discussion-sort-link": "sort" + "click .discussion-paginator>.discussion-page-link": "page" diff --git a/lms/static/coffee/src/backbone_discussion/main.coffee b/lms/static/coffee/src/backbone_discussion/main.coffee index e69de29bb2..fe214e80a3 100644 --- a/lms/static/coffee/src/backbone_discussion/main.coffee +++ b/lms/static/coffee/src/backbone_discussion/main.coffee @@ -0,0 +1,9 @@ +$ -> + + $(".discussion-module").each (index, elem) -> + view = new DiscussionModuleView(el: elem) + + $("section.discussion").each (index, elem) -> + discussionData = DiscussionUtil.getDiscussionData(elem) + discussion = new Discussion(discussionData) + view = new DiscussionView(el: elem, model: discussion) diff --git a/lms/static/coffee/src/backbone_discussion/utils.coffee b/lms/static/coffee/src/backbone_discussion/utils.coffee index 09bac7a335..9820a63272 100644 --- a/lms/static/coffee/src/backbone_discussion/utils.coffee +++ b/lms/static/coffee/src/backbone_discussion/utils.coffee @@ -1,7 +1,262 @@ class @DiscussionUtil + + @wmdEditors: {} + + @getTemplate: (id) -> + $("script##{id}").html() + @getDiscussionData: (id) -> if id instanceof $ id = id.attr("_id") else if typeof id == "object" id = $(id).attr("_id") return $$discussion_data[id] + + @getContentInfo: (id, attr) -> + if not window.$$annotated_content_info? + window.$$annotated_content_info = {} + (window.$$annotated_content_info[id] || {})[attr] + + @setContentInfo: (id, attr, value) -> + if not window.$$annotated_content_info? + window.$$annotated_content_info = {} + window.$$annotated_content_info[id] ||= {} + window.$$annotated_content_info[id][attr] = value + + @extendContentInfo: (id, newInfo) -> + if not window.$$annotated_content_info? + window.$$annotated_content_info = {} + window.$$annotated_content_info[id] = newInfo + + @bulkExtendContentInfo: (newInfos) -> + if not window.$$annotated_content_info? + window.$$annotated_content_info = {} + window.$$annotated_content_info = $.extend window.$$annotated_content_info, newInfos + + @generateDiscussionLink: (cls, txt, handler) -> + $("").addClass("discussion-link") + .attr("href", "javascript:void(0)") + .addClass(cls).html(txt) + .click -> handler(this) + + @urlFor: (name, param, param1, param2) -> + { + follow_discussion : "/courses/#{$$course_id}/discussion/#{param}/follow" + unfollow_discussion : "/courses/#{$$course_id}/discussion/#{param}/unfollow" + create_thread : "/courses/#{$$course_id}/discussion/#{param}/threads/create" + search_similar_threads : "/courses/#{$$course_id}/discussion/#{param}/threads/search_similar" + update_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/update" + create_comment : "/courses/#{$$course_id}/discussion/threads/#{param}/reply" + delete_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/delete" + upvote_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/upvote" + downvote_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/downvote" + undo_vote_for_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/unvote" + follow_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/follow" + unfollow_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/unfollow" + update_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/update" + endorse_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/endorse" + create_sub_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/reply" + delete_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/delete" + upvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/upvote" + downvote_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/downvote" + undo_vote_for_comment : "/courses/#{$$course_id}/discussion/comments/#{param}/unvote" + upload : "/courses/#{$$course_id}/discussion/upload" + search : "/courses/#{$$course_id}/discussion/forum/search" + tags_autocomplete : "/courses/#{$$course_id}/discussion/threads/tags/autocomplete" + retrieve_discussion : "/courses/#{$$course_id}/discussion/forum/#{param}/inline" + retrieve_single_thread : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}" + update_moderator_status : "/courses/#{$$course_id}/discussion/users/#{param}/update_moderator_status" + openclose_thread : "/courses/#{$$course_id}/discussion/threads/#{param}/close" + permanent_link_thread : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}" + permanent_link_comment : "/courses/#{$$course_id}/discussion/forum/#{param}/threads/#{param1}##{param2}" + }[name] + + @safeAjax: (params) -> + $elem = params.$elem + if $elem.attr("disabled") + return + $elem.attr("disabled", "disabled") + $.ajax(params).always -> + $elem.removeAttr("disabled") + + @handleAnchorAndReload: (response) -> + #window.location = window.location.pathname + "#" + response['id'] + window.location.reload() + + @bindLocalEvents: ($local, eventsHandler) -> + for eventSelector, handler of eventsHandler + [event, selector] = eventSelector.split(' ') + $local(selector).unbind(event)[event] handler + + @tagsInputOptions: -> + autocomplete_url: @urlFor('tags_autocomplete') + autocomplete: + remoteDataType: 'json' + interactive: true + height: '30px' + width: '100%' + defaultText: "Tag your post: press enter after each tag" + removeWithBackspace: true + + @isSubscribed: (id, type) -> + $$user_info? and ( + if type == "thread" + id in $$user_info.subscribed_thread_ids + else if type == "commentable" or type == "discussion" + id in $$user_info.subscribed_commentable_ids + else + id in $$user_info.subscribed_user_ids + ) + + @isUpvoted: (id) -> + $$user_info? and (id in $$user_info.upvoted_ids) + + @isDownvoted: (id) -> + $$user_info? and (id in $$user_info.downvoted_ids) + + @formErrorHandler: (errorsField) -> + (xhr, textStatus, error) -> + response = JSON.parse(xhr.responseText) + if response.errors? and response.errors.length > 0 + errorsField.empty() + for error in response.errors + errorsField.append($("
        • ").addClass("new-post-form-error").html(error)) + + @clearFormErrors: (errorsField) -> + errorsField.empty() + + @postMathJaxProcessor: (text) -> + RE_INLINEMATH = /^\$([^\$]*)\$/g + RE_DISPLAYMATH = /^\$\$([^\$]*)\$\$/g + @processEachMathAndCode text, (s, type) -> + if type == 'display' + s.replace RE_DISPLAYMATH, ($0, $1) -> + "\\[" + $1 + "\\]" + else if type == 'inline' + s.replace RE_INLINEMATH, ($0, $1) -> + "\\(" + $1 + "\\)" + else + s + + @makeWmdEditor: ($content, $local, cls_identifier) -> + elem = $local(".#{cls_identifier}") + id = $content.attr("_id") + appended_id = "-#{cls_identifier}-#{id}" + imageUploadUrl = @urlFor('upload') + editor = Markdown.makeWmdEditor elem, appended_id, imageUploadUrl, @postMathJaxProcessor + @wmdEditors["#{cls_identifier}-#{id}"] = editor + editor + + @getWmdEditor: ($content, $local, cls_identifier) -> + id = $content.attr("_id") + @wmdEditors["#{cls_identifier}-#{id}"] + + @getWmdInput: ($content, $local, cls_identifier) -> + id = $content.attr("_id") + $local("#wmd-input-#{cls_identifier}-#{id}") + + @getWmdContent: ($content, $local, cls_identifier) -> + @getWmdInput($content, $local, cls_identifier).val() + + @setWmdContent: ($content, $local, cls_identifier, text) -> + @getWmdInput($content, $local, cls_identifier).val(text) + @getWmdEditor($content, $local, cls_identifier).refreshPreview() + + @subscriptionLink: (type, id) -> + followLink = -> + @generateDiscussionLink("discussion-follow-#{type}", "Follow", handleFollow) + + unfollowLink = -> + @generateDiscussionLink("discussion-unfollow-#{type}", "Unfollow", handleUnfollow) + + handleFollow = (elem) -> + @safeAjax + $elem: $(elem) + url: @urlFor("follow_#{type}", id) + type: "POST" + success: (response, textStatus) -> + if textStatus == "success" + $(elem).replaceWith unfollowLink() + dataType: 'json' + + handleUnfollow = (elem) -> + @safeAjax + $elem: $(elem) + url: @urlFor("unfollow_#{type}", id) + type: "POST" + success: (response, textStatus) -> + if textStatus == "success" + $(elem).replaceWith followLink() + dataType: 'json' + + if @isSubscribed(id, type) + unfollowLink() + else + followLink() + + @processEachMathAndCode: (text, processor) -> + + codeArchive = [] + + RE_DISPLAYMATH = /^([^\$]*?)\$\$([^\$]*?)\$\$(.*)$/m + RE_INLINEMATH = /^([^\$]*?)\$([^\$]+?)\$(.*)$/m + + ESCAPED_DOLLAR = '@@ESCAPED_D@@' + ESCAPED_BACKSLASH = '@@ESCAPED_B@@' + + processedText = "" + + $div = $("
          ").html(text) + + $div.find("code").each (index, code) -> + codeArchive.push $(code).html() + $(code).html(codeArchive.length - 1) + + text = $div.html() + text = text.replace /\\\$/g, ESCAPED_DOLLAR + + while true + if RE_INLINEMATH.test(text) + text = text.replace RE_INLINEMATH, ($0, $1, $2, $3) -> + processedText += $1 + processor("$" + $2 + "$", 'inline') + $3 + else if RE_DISPLAYMATH.test(text) + text = text.replace RE_DISPLAYMATH, ($0, $1, $2, $3) -> + processedText += $1 + processor("$$" + $2 + "$$", 'display') + $3 + else + processedText += text + break + + text = processedText + text = text.replace(new RegExp(ESCAPED_DOLLAR, 'g'), '\\$') + + text = text.replace /\\\\\\\\/g, ESCAPED_BACKSLASH + text = text.replace /\\begin\{([a-z]*\*?)\}([\s\S]*?)\\end\{\1\}/img, ($0, $1, $2) -> + processor("\\begin{#{$1}}" + $2 + "\\end{#{$1}}") + text = text.replace(new RegExp(ESCAPED_BACKSLASH, 'g'), '\\\\\\\\') + + $div = $("
          ").html(text) + cnt = 0 + $div.find("code").each (index, code) -> + $(code).html(processor(codeArchive[cnt], 'code')) + cnt += 1 + + text = $div.html() + + text + + @unescapeHighlightTag: (text) -> + text.replace(/\<\;highlight\>\;/g, "") + .replace(/\<\;\/highlight\>\;/g, "") + + @stripHighlight: (text) -> + text.replace(/\&(amp\;)?lt\;highlight\&(amp\;)?gt\;/g, "") + .replace(/\&(amp\;)?lt\;\/highlight\&(amp\;)?gt\;/g, "") + + @stripLatexHighlight: (text) -> + @processEachMathAndCode text, @stripHighlight + + @markdownWithHighlight: (text) -> + converter = Markdown.getMathCompatibleConverter() + @unescapeHighlightTag @stripLatexHighlight converter.makeHtml text diff --git a/lms/templates/courseware/courseware.html b/lms/templates/courseware/courseware.html index fdc590bf92..0e2428a1c5 100644 --- a/lms/templates/courseware/courseware.html +++ b/lms/templates/courseware/courseware.html @@ -5,6 +5,7 @@ <%block name="headextra"> <%static:css group='course'/> + <%include file="discussion/_js_head_dependencies.html" /> <%block name="js_extra"> @@ -21,7 +22,7 @@ <%static:js group='courseware'/> - <%include file="discussion/_js_dependencies.html" /> + <%include file="discussion/_js_body_dependencies.html" /> diff --git a/lms/templates/discussion/_content.mustache b/lms/templates/discussion/_content.mustache index 8f3a284510..95e95a8c4b 100644 --- a/lms/templates/discussion/_content.mustache +++ b/lms/templates/discussion/_content.mustache @@ -1,9 +1,9 @@
          - +
          {{content.votes.point}}
          - +
            diff --git a/lms/templates/discussion/_content_renderer.html b/lms/templates/discussion/_content_renderer.html index 88b87da4a2..9734165944 100644 --- a/lms/templates/discussion/_content_renderer.html +++ b/lms/templates/discussion/_content_renderer.html @@ -6,7 +6,7 @@ <%def name="render_content_with_comments(content)">
            - ${render_content(content)} +
            ${render_content(content)}
            % if content.get('children') is not None: ${render_comments(content['children'])} % endif diff --git a/lms/templates/discussion/_js_body_dependencies.html b/lms/templates/discussion/_js_body_dependencies.html new file mode 100644 index 0000000000..86265c7cb4 --- /dev/null +++ b/lms/templates/discussion/_js_body_dependencies.html @@ -0,0 +1,4 @@ +<%! from django_comment_client.helpers import include_mustache_templates %> + +<%include file="/mathjax_include.html" /> +${include_mustache_templates()} diff --git a/lms/templates/discussion/_js_dependencies.html b/lms/templates/discussion/_js_dependencies.html index b6e65f43c6..bde873fee1 100644 --- a/lms/templates/discussion/_js_dependencies.html +++ b/lms/templates/discussion/_js_dependencies.html @@ -1,7 +1,5 @@ <%namespace name='static' file='../static_content.html'/> -<%include file="/mathjax_include.html" /> - @@ -17,4 +15,3 @@ - diff --git a/lms/templates/discussion/_js_head_dependencies.html b/lms/templates/discussion/_js_head_dependencies.html new file mode 100644 index 0000000000..bde873fee1 --- /dev/null +++ b/lms/templates/discussion/_js_head_dependencies.html @@ -0,0 +1,17 @@ +<%namespace name='static' file='../static_content.html'/> + + + + + + + + + + + + + + + + diff --git a/lms/templates/discussion/index.html b/lms/templates/discussion/index.html index 46bc35f34b..b0ca1a2ffa 100644 --- a/lms/templates/discussion/index.html +++ b/lms/templates/discussion/index.html @@ -5,10 +5,11 @@ <%block name="headextra"> <%static:css group='course'/> +<%include file="_js_head_dependencies.html" /> <%block name="js_extra"> -<%include file="_js_dependencies.html" /> +<%include file="_js_body_dependencies.html" /> <%include file="../courseware/course_navigation.html" args="active_page='discussion'" /> From a905ac9e1d51323aeaad82a1e29aaad6a5f54165 Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Mon, 20 Aug 2012 22:03:46 -0700 Subject: [PATCH 12/91] updated backbone & single thread retrieval now working --- common/static/js/vendor/backbone-min.js | 66 +++++++++--------- .../django_comment_client/forum/views.py | 2 + lms/envs/dev.py | 1 + .../src/backbone_discussion/content.coffee | 68 +++++++++++++++++-- .../src/backbone_discussion/discussion.coffee | 8 ++- .../src/backbone_discussion/main.coffee | 3 +- .../discussion/_content_renderer.html | 2 +- 7 files changed, 108 insertions(+), 42 deletions(-) diff --git a/common/static/js/vendor/backbone-min.js b/common/static/js/vendor/backbone-min.js index c1c0d4fff2..91f29ffafb 100644 --- a/common/static/js/vendor/backbone-min.js +++ b/common/static/js/vendor/backbone-min.js @@ -4,35 +4,37 @@ // Backbone may be freely distributed under the MIT license. // For all details and documentation: // http://backbonejs.org -(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks= -{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g= -z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent= -{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null== -b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent: -b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)}; -a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error, -h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t(); -return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending= -{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length|| -!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator); -this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c=b))this.iframe=i('
          From 960cf5792b6265dc90d6d306b9a626a4169c6e67 Mon Sep 17 00:00:00 2001 From: kimth Date: Wed, 22 Aug 2012 15:12:49 -0400 Subject: [PATCH 76/91] Dynamically disable syllabus per-course --- common/lib/xmodule/xmodule/course_module.py | 5 +++++ lms/templates/courseware/course_navigation.html | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 5cc4a09165..f8496dd67c 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -3,6 +3,7 @@ import time import logging import requests from lxml import etree +from path import path # NOTE (THK): Only used for detecting presence of syllabus from xmodule.util.decorators import lazyproperty from xmodule.graders import load_grading_policy @@ -75,6 +76,10 @@ class CourseDescriptor(SequenceDescriptor): # NOTE: relies on the modulestore to call set_grading_policy() right after # init. (Modulestore is in charge of figuring out where to load the policy from) + # NOTE (THK): This is a last-minute addition for Fall 2012 launch to dynamically + # disable the syllabus content for courses that do not provide a syllabus + self.syllabus_present = self.system.resources_fs.exists(path('syllabus')) + def set_grading_policy(self, policy_str): """Parse the policy specified in policy_str, and save it""" diff --git a/lms/templates/courseware/course_navigation.html b/lms/templates/courseware/course_navigation.html index 63fec53c4c..af2100f042 100644 --- a/lms/templates/courseware/course_navigation.html +++ b/lms/templates/courseware/course_navigation.html @@ -19,7 +19,7 @@ def url_class(url):
          1. Courseware
          2. Course Info
          3. -% if settings.MITX_FEATURES.get('ENABLE_SYLLABUS'): +% if course.syllabus_present:
          4. Syllabus
          5. % endif % if user.is_authenticated(): From 5b7ae1a99b211253266e25e24d52d82dc5ec546d Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 22 Aug 2012 15:14:25 -0400 Subject: [PATCH 77/91] Add more excludes to rakefile --- rakefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rakefile b/rakefile index 9eaa4534f2..053abf56a8 100644 --- a/rakefile +++ b/rakefile @@ -200,6 +200,8 @@ task :package do "--exclude=**/.git/**", "--exclude=**/*.pyc", "--exclude=**/reports/**", + "--exclude=**/test_root/**", + "--exclude=**/.coverage/**", "-C", "#{REPO_ROOT}", "--provides=#{PACKAGE_NAME}", "--name=#{NORMALIZED_DEPLOY_NAME}", From 64788a73aadf3787c9d04d6ce4b78e2270d0b533 Mon Sep 17 00:00:00 2001 From: kimth Date: Wed, 22 Aug 2012 15:22:58 -0400 Subject: [PATCH 78/91] Remove ENABLE_SYLLABUS flag from envs/common --- lms/envs/common.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lms/envs/common.py b/lms/envs/common.py index a217f0e7b9..bc49de6d76 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -55,10 +55,6 @@ MITX_FEATURES = { # course_ids (see dev_int.py for an example) 'SUBDOMAIN_COURSE_LISTINGS' : False, - # TODO: This will be removed once course-specific tabs are in place. see - # courseware/courses.py - 'ENABLE_SYLLABUS' : True, - 'ENABLE_TEXTBOOK' : True, 'ENABLE_DISCUSSION' : False, 'ENABLE_DISCUSSION_SERVICE': True, From f4f8b7bf73c08ba1cd375b3df9ebd594bb1b5fa4 Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Wed, 22 Aug 2012 15:39:24 -0400 Subject: [PATCH 79/91] Added delete page and padding and hid th on overflow --- lms/static/sass/course/wiki/_wiki.scss | 5 +++++ lms/templates/wiki/edit.html | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lms/static/sass/course/wiki/_wiki.scss b/lms/static/sass/course/wiki/_wiki.scss index 9178f7719f..9868989eff 100644 --- a/lms/static/sass/course/wiki/_wiki.scss +++ b/lms/static/sass/course/wiki/_wiki.scss @@ -757,6 +757,7 @@ section.wiki { th, td { border-bottom: 1px solid $light-gray; padding: 8px; + overflow: hidden; } tr:nth-child(even) { @@ -860,6 +861,10 @@ section.wiki { } } + section.delete { + padding: 40px; + } + diff --git a/lms/templates/wiki/edit.html b/lms/templates/wiki/edit.html index 3d36cf2869..f4bd7d138f 100644 --- a/lms/templates/wiki/edit.html +++ b/lms/templates/wiki/edit.html @@ -39,7 +39,7 @@ {% trans "Back to editor" %}
          -
          +
          {% endblock %} From cca5fe2f040a063d2db267dba263cd3fa23ea257 Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Wed, 22 Aug 2012 15:40:56 -0400 Subject: [PATCH 80/91] Delete page --- lms/templates/wiki/delete.html | 64 ++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 lms/templates/wiki/delete.html diff --git a/lms/templates/wiki/delete.html b/lms/templates/wiki/delete.html new file mode 100644 index 0000000000..6cb2b36707 --- /dev/null +++ b/lms/templates/wiki/delete.html @@ -0,0 +1,64 @@ +{% extends "wiki/base.html" %} +{% load wiki_tags i18n sekizai_tags %} +{% load url from future %} + +{% block pagetitle %}{% trans "Delete article" %}{% endblock %} + +{% block wiki_contents %} +
          +

          {% trans "Delete" %} "{{ article.current_revision.title }}"

          + + {% if cannot_delete_root %} +

          {% trans "You cannot delete a root article." %}

          +

          {% trans "Go back" %}

          + {% else %} + + {% if cannot_delete_children %} + +

          {% trans "You cannot delete this article because you do not have permission to delete articles with children. Try to remove the children manually one-by-one." %}

          + + {% endif %} + + {% if delete_children %} + +

          {% trans "You are deleting an article. This means that its children will be deleted as well. If you choose to purge, children will also be purged!" %}

          + +

          {% trans "Articles that will be deleted" %}

          + +
            + {% for child in delete_children %} +
          • {{ child.article }}
          • + {% if delete_children_more %} +
          • {% trans "...and more!" %}
          • + {% endif %} + {% endfor %} +
          + + {% endif %} + + {% if not cannot_delete_children %} +

          {% trans "You are deleting an article. Please confirm." %}

          + +
          + {% wiki_form delete_form %} + +
          + + + + {% trans "Go back" %} + +
          +
          + {% endif %} + + {% endif %} +
          + +{% endblock %} + From a301abd5adeb0a0346014007982b2bb113289b4a Mon Sep 17 00:00:00 2001 From: Piotr Mitros Date: Wed, 22 Aug 2012 15:49:48 -0400 Subject: [PATCH 81/91] Enrollment hack for allowing Berkeley to add their students. --- lms/djangoapps/courseware/views.py | 42 ++++++++++++++++++++++++++++++ lms/urls.py | 3 ++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index 98444c176d..f67f79cf11 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -393,3 +393,45 @@ def instructor_dashboard(request, course_id): context = {'course': course, 'staff_access': True,} return render_to_response('courseware/instructor_dashboard.html', context) + +@ensure_csrf_cookie +@cache_control(no_cache=True, no_store=True, must_revalidate=True) +def enroll_students(request, course_id): + course = get_course_with_access(request.user, course_id, 'staff') + ''' Allows a staff member to enroll students in a course. + + This is a short-term hack for Berkeley courses launching fall + 2012. In the long term, we would like functionality like this, but + we would like both the instructor and the student to agree. Right + now, this allows any instructor to add students to their course, + which we do not want. + + It is poorly written and poorly tested, but it's designed to be + stripped out. + ''' + + course = get_course_with_access(request.user, course_id, 'staff') + existing_students = [ce.user.email for ce in CourseEnrollment.objects.filter(course_id = course_id)] + print request.POST + if 'new_students' in request.POST: + new_students = request.POST['new_students'].split('\n') + else: + new_students = [] + new_students = [s.strip() for s in new_students] + + added_students = [] + rejected_students = [] + + for student in new_students: + try: + nce = CourseEnrollment(user=User.objects.get(email = student), course_id = course_id) + nce.save() + added_students.append(student) + except: + rejected_students.append(student) + + return render_to_response("enroll_students.html", {'course':course_id, + 'existing_students': existing_students, + 'added_students': added_students, + 'rejected_students': rejected_students, + 'debug':new_students}) diff --git a/lms/urls.py b/lms/urls.py index 6e6ad4300e..af21b8bf45 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -154,7 +154,8 @@ if settings.COURSEWARE_ENABLED: 'courseware.views.gradebook', name='gradebook'), url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/grade_summary$', 'courseware.views.grade_summary', name='grade_summary'), - + url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/enroll_students$', + 'courseware.views.enroll_students', name='enroll_students'), ) # discussion forums live within courseware, so courseware must be enabled first From 7c242939f2bce439473b4ed205370ef6e10fd14e Mon Sep 17 00:00:00 2001 From: ike Date: Wed, 22 Aug 2012 15:51:08 -0400 Subject: [PATCH 82/91] fix staff debug view, and also add new source_link --- common/djangoapps/xmodule_modifiers.py | 3 +++ lms/templates/staff_problem_info.html | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/common/djangoapps/xmodule_modifiers.py b/common/djangoapps/xmodule_modifiers.py index 4b3050e227..86443520c2 100644 --- a/common/djangoapps/xmodule_modifiers.py +++ b/common/djangoapps/xmodule_modifiers.py @@ -112,11 +112,14 @@ def add_histogram(get_html, module, user): edit_link = "%s/%s/tree/master/%s" % (giturl,data_dir,filepath) else: edit_link = False + source_file = module.metadata.get('source_file','') # source used to generate the problem XML, eg latex or word staff_context = {'definition': module.definition.get('data'), 'metadata': json.dumps(module.metadata, indent=4), 'location': module.location, 'xqa_key': module.metadata.get('xqa_key',''), + 'source_file' : source_file, + 'source_url': '%s/%s/tree/master/%s' % (giturl,data_dir,source_file), 'category': str(module.__class__.__name__), 'element_id': module.location.html_id().replace('-','_'), 'edit_link': edit_link, diff --git a/lms/templates/staff_problem_info.html b/lms/templates/staff_problem_info.html index f91decf876..47194aa6fd 100644 --- a/lms/templates/staff_problem_info.html +++ b/lms/templates/staff_problem_info.html @@ -31,9 +31,12 @@ ${module_content}

          Staff Debug

          -
          +
          location = ${location | h} github = ${edit_link | h} +%if source_file: +source_url = ${source_file | h} +%endif definition =
          ${definition | h}
          metadata = ${metadata | h} category = ${category | h} From a694d1034ee6ec9ba177bc66fd4296b71e125587 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Wed, 22 Aug 2012 15:57:44 -0400 Subject: [PATCH 83/91] We use django-wiki's built-in wikilinks markdown extension. They were clashing. --- .../plugins/markdownedx/mdx_wikipath.py | 92 ------------------- .../plugins/markdownedx/wiki_plugin.py | 7 +- lms/envs/common.py | 1 + repo-requirements.txt | 2 +- 4 files changed, 4 insertions(+), 98 deletions(-) delete mode 100755 lms/djangoapps/course_wiki/plugins/markdownedx/mdx_wikipath.py diff --git a/lms/djangoapps/course_wiki/plugins/markdownedx/mdx_wikipath.py b/lms/djangoapps/course_wiki/plugins/markdownedx/mdx_wikipath.py deleted file mode 100755 index 4c6f6fadd7..0000000000 --- a/lms/djangoapps/course_wiki/plugins/markdownedx/mdx_wikipath.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python - -''' -Wikipath Extension for Python-Markdown -====================================== - -Converts [Link Name](wiki:ArticleName) to relative links pointing to article. Requires Python-Markdown 2.0+ - -Basic usage: - - >>> import markdown - >>> text = "Some text with a [Link Name](wiki:ArticleName)." - >>> html = markdown.markdown(text, ['wikipath(base_url="/wiki/view/")']) - >>> html - u'

          Some text with a Link Name.

          ' - -Dependencies: -* [Python 2.3+](http://python.org) -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) -''' - - -import markdown -try: - # Markdown 2.1.0 changed from 2.0.3. We try importing the new version first, - # but import the 2.0.3 version if it fails - from markdown.util import etree -except: - from markdown import etree - - -class WikiPathExtension(markdown.Extension): - def __init__(self, configs): - # set extension defaults - self.config = { - 'base_url' : ['/', 'String to append to beginning of URL.'], - 'html_class' : ['wikipath', 'CSS hook. Leave blank for none.'] - } - - # Override defaults with user settings - for key, value in configs : - # self.config[key][0] = value - self.setConfig(key, value) - - - def extendMarkdown(self, md, md_globals): - self.md = md - - # append to end of inline patterns - WIKI_RE = r'\[(?P.+?)\]\(wiki:(?P[a-zA-Z\d/_-]*)\)' - wikiPathPattern = WikiPath(WIKI_RE, self.config) - wikiPathPattern.md = md - md.inlinePatterns.add('wikipath', wikiPathPattern, " Date: Wed, 22 Aug 2012 16:09:09 -0400 Subject: [PATCH 84/91] fixed firefox outline bugs --- common/lib/xmodule/xmodule/css/sequence/display.scss | 12 +++++++++++- common/lib/xmodule/xmodule/css/video/display.scss | 10 ++++++++++ lms/static/sass/course/base/_base.scss | 2 +- .../sass/course/layout/_courseware_header.scss | 11 ++++++++--- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/common/lib/xmodule/xmodule/css/sequence/display.scss b/common/lib/xmodule/xmodule/css/sequence/display.scss index 90b1ff53a4..32b27d1e78 100644 --- a/common/lib/xmodule/xmodule/css/sequence/display.scss +++ b/common/lib/xmodule/xmodule/css/sequence/display.scss @@ -73,6 +73,11 @@ nav.sequence-nav { padding: 0; position: relative; @include transition(); + outline: 0; + + &:focus { + outline: 0; + } &:hover { background-color: #fff; @@ -211,7 +216,7 @@ nav.sequence-nav { ul { position: absolute; top: 0; - list-style: none; + list-style: none !important; height: 100%; right: 0; top: 0; @@ -238,6 +243,11 @@ nav.sequence-nav { width: 40px; text-indent: -9999px; @include transition(all, .2s, $ease-in-out-quad); + outline: 0; + + &:focus { + outline: 0; + } &:hover { opacity: .5; diff --git a/common/lib/xmodule/xmodule/css/video/display.scss b/common/lib/xmodule/xmodule/css/video/display.scss index fb78f46d77..504d5df8cf 100644 --- a/common/lib/xmodule/xmodule/css/video/display.scss +++ b/common/lib/xmodule/xmodule/css/video/display.scss @@ -101,6 +101,11 @@ div.video { @include transition(background-color, opacity); width: 14px; background: url('../images/vcr.png') 15px 15px no-repeat; + outline: 0; + + &:focus { + outline: 0; + } &:empty { height: 46px; @@ -171,6 +176,11 @@ div.video { @include transition(); -webkit-font-smoothing: antialiased; width: 116px; + outline: 0; + + &:focus { + outline: 0; + } h3 { color: #999; diff --git a/lms/static/sass/course/base/_base.scss b/lms/static/sass/course/base/_base.scss index e6bd70c338..1edb9aa7ba 100644 --- a/lms/static/sass/course/base/_base.scss +++ b/lms/static/sass/course/base/_base.scss @@ -1,7 +1,7 @@ body { min-width: 980px; min-height: 100%; - background: url(../images/bg-texture.png) #ddd; + background: url(../images/bg-texture.png) #d6d6d6; } body, h1, h2, h3, h4, h5, h6, p, p a:link, p a:visited, a, label { diff --git a/lms/static/sass/course/layout/_courseware_header.scss b/lms/static/sass/course/layout/_courseware_header.scss index 84ededfabb..aa5d07fc44 100644 --- a/lms/static/sass/course/layout/_courseware_header.scss +++ b/lms/static/sass/course/layout/_courseware_header.scss @@ -32,15 +32,20 @@ nav.course-material { font-size: 14px; font-weight: bold; text-decoration: none; + // text-shadow: 0 1px 0 rgba(0, 0, 0, .4); &:hover { color: #333; - background: rgba(255, 255, 255, .4); + background: rgba(255, 255, 255, .6); } &.active { - color: $blue; - background: rgba(255, 255, 255, .8); + // background: rgba(0, 0, 0, .2); + @include linear-gradient(top, rgba(0, 0, 0, .4), rgba(0, 0, 0, .25)); + background-color: transparent; + @include box-shadow(0 1px 0 rgba(255, 255, 255, .5), 0 1px 1px rgba(0, 0, 0, .3) inset); + color: #fff; + text-shadow: 0 1px 0 rgba(0, 0, 0, .4); } } } From 3777cd2f5efece32c7bf735d42ca43e2451088f5 Mon Sep 17 00:00:00 2001 From: Piotr Mitros Date: Wed, 22 Aug 2012 16:15:24 -0400 Subject: [PATCH 85/91] Added missing file --- lms/templates/enroll_students.html | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 lms/templates/enroll_students.html diff --git a/lms/templates/enroll_students.html b/lms/templates/enroll_students.html new file mode 100644 index 0000000000..2f0b6ccc01 --- /dev/null +++ b/lms/templates/enroll_students.html @@ -0,0 +1,29 @@ +

          Student Enrollment Form

          + +

          Course: ${ course } + +

          + +

          Add new students

          + + +
          + +

          Existing students: + +

          ${ existing_students } + +

          New students added: +${ added_students } + +

          Students rejected: +${ rejected_students } + +

          Debug: +

          ${ debug } + + +

          foo +

          bar +

          biff From 69a3ea63bcd0497bc9786b4d805eab6294b7a33a Mon Sep 17 00:00:00 2001 From: Piotr Mitros Date: Wed, 22 Aug 2012 16:32:14 -0400 Subject: [PATCH 86/91] Fixed minor issues (removed two unnecessary lines) --- lms/djangoapps/courseware/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index f67f79cf11..640dd7c08d 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -397,7 +397,6 @@ def instructor_dashboard(request, course_id): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) def enroll_students(request, course_id): - course = get_course_with_access(request.user, course_id, 'staff') ''' Allows a staff member to enroll students in a course. This is a short-term hack for Berkeley courses launching fall @@ -412,7 +411,7 @@ def enroll_students(request, course_id): course = get_course_with_access(request.user, course_id, 'staff') existing_students = [ce.user.email for ce in CourseEnrollment.objects.filter(course_id = course_id)] - print request.POST + if 'new_students' in request.POST: new_students = request.POST['new_students'].split('\n') else: From 3b76fe16d899230b79bb153ca3674fd5508a5256 Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Wed, 22 Aug 2012 14:04:42 -0700 Subject: [PATCH 87/91] make inline page working --- .../django_comment_client/forum/views.py | 8 ++- .../src/backbone_discussion/content.coffee | 2 +- .../src/backbone_discussion/discussion.coffee | 12 ++-- .../discussion_module.coffee | 65 ++++++++----------- .../src/backbone_discussion/main.coffee | 2 +- .../src/backbone_discussion/utils.coffee | 4 -- lms/templates/courseware/courseware.html | 4 +- 7 files changed, 45 insertions(+), 52 deletions(-) diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index 8f154f2bd6..4fab42564b 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -125,7 +125,11 @@ def inline_discussion(request, course_id, discussion_id): threads, query_params = get_threads(request, course_id, discussion_id) html = render_inline_discussion(request, course_id, threads, discussion_id=discussion_id, \ query_params=query_params) - return utils.HtmlResponse(html) + + return utils.JsonResponse({ + 'html': html, + 'discussionData': threads, + }) def render_search_bar(request, course_id, discussion_id=None, text=''): if not discussion_id: @@ -142,8 +146,6 @@ def forum_form_discussion(request, course_id): threads, query_params = get_threads(request, course_id) content = render_forum_discussion(request, course_id, threads, discussion_id=_general_discussion_id(course_id), query_params=query_params) - - if request.is_ajax(): return utils.JsonResponse({ 'html': content, diff --git a/lms/static/coffee/src/backbone_discussion/content.coffee b/lms/static/coffee/src/backbone_discussion/content.coffee index 3fde83bfc2..31f903e965 100644 --- a/lms/static/coffee/src/backbone_discussion/content.coffee +++ b/lms/static/coffee/src/backbone_discussion/content.coffee @@ -175,7 +175,7 @@ class @ContentView extends Backbone.View data: body: body anonymous: anonymous - autowatch: autowatch + auto_subscribe: autowatch error: DiscussionUtil.formErrorHandler @$(".discussion-errors") success: (response, textStatus) => DiscussionUtil.clearFormErrors @$(".discussion-errors") diff --git a/lms/static/coffee/src/backbone_discussion/discussion.coffee b/lms/static/coffee/src/backbone_discussion/discussion.coffee index d38ea47d85..7a664dfe0a 100644 --- a/lms/static/coffee/src/backbone_discussion/discussion.coffee +++ b/lms/static/coffee/src/backbone_discussion/discussion.coffee @@ -15,8 +15,6 @@ class @Discussion extends Backbone.Collection @add model model -class @DiscussionModuleView extends Backbone.View - class @DiscussionView extends Backbone.View $: (selector) -> @@ -39,11 +37,13 @@ class @DiscussionView extends Backbone.View @newPost() reload: ($elem, url) -> + console.log "here" if not url then return DiscussionUtil.get $elem, url, {}, (response, textStatus) => - $discussion = $(response.html) + console.log response $parent = @$el.parent() - @$el.replaceWith($discussion) + @$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) @@ -111,6 +111,8 @@ class @DiscussionView extends Backbone.View 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) @@ -121,6 +123,8 @@ class @DiscussionView extends Backbone.View 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")) diff --git a/lms/static/coffee/src/backbone_discussion/discussion_module.coffee b/lms/static/coffee/src/backbone_discussion/discussion_module.coffee index d449533ba9..036aba7b3a 100644 --- a/lms/static/coffee/src/backbone_discussion/discussion_module.coffee +++ b/lms/static/coffee/src/backbone_discussion/discussion_module.coffee @@ -1,42 +1,33 @@ -if not @Discussion? - @Discussion = {} - -Discussion = @Discussion - -@Discussion = $.extend @Discussion, - initializeDiscussionModule: (elem) -> - $discussionModule = $(elem) - $local = Discussion.generateLocal($discussionModule) - handleShowDiscussion = (elem) -> - $elem = $(elem) - if not $local("section.discussion").length +class @DiscussionModuleView extends Backbone.View + events: + "click .discussion-show": "toggleDiscussion" + toggleDiscussion: (event) -> + console.log "toggle" + 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 + else + $elem = $(event.target) discussion_id = $elem.attr("discussion_id") - url = Discussion.urlFor 'retrieve_discussion', discussion_id + url = DiscussionUtil.urlFor 'retrieve_discussion', discussion_id Discussion.safeAjax $elem: $elem url: url type: "GET" - success: (data, textStatus, xhr) -> - $discussionModule.append(data) - discussion = $local("section.discussion") - Discussion.initializeDiscussion(discussion) - Discussion.bindDiscussionEvents(discussion) - $elem.html("Hide Discussion") - $elem.unbind('click').click -> - handleHideDiscussion(this) - dataType: 'html' - else - $local("section.discussion").show() - $elem.html("Hide Discussion") - $elem.unbind('click').click -> - handleHideDiscussion(this) - - handleHideDiscussion = (elem) -> - $local("section.discussion").hide() - $elem = $(elem) - $elem.html("Show Discussion") - $elem.unbind('click').click -> - handleShowDiscussion(this) - - $local(".discussion-show").click -> - handleShowDiscussion(this) + 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 diff --git a/lms/static/coffee/src/backbone_discussion/main.coffee b/lms/static/coffee/src/backbone_discussion/main.coffee index 1b2b04e2ed..2266e136ed 100644 --- a/lms/static/coffee/src/backbone_discussion/main.coffee +++ b/lms/static/coffee/src/backbone_discussion/main.coffee @@ -7,7 +7,7 @@ $ -> view = new DiscussionModuleView(el: elem) $("section.discussion").each (index, elem) -> - discussionData = DiscussionUtil.getDiscussionData(elem) + discussionData = DiscussionUtil.getDiscussionData($(elem).attr("_id")) discussion = new Discussion() discussion.reset(discussionData, {silent: false}) view = new DiscussionView(el: elem, model: discussion) diff --git a/lms/static/coffee/src/backbone_discussion/utils.coffee b/lms/static/coffee/src/backbone_discussion/utils.coffee index ce99354a94..0610ade1f8 100644 --- a/lms/static/coffee/src/backbone_discussion/utils.coffee +++ b/lms/static/coffee/src/backbone_discussion/utils.coffee @@ -6,10 +6,6 @@ class @DiscussionUtil $("script##{id}").html() @getDiscussionData: (id) -> - if id instanceof $ - id = id.attr("_id") - else if typeof id == "object" - id = $(id).attr("_id") return $$discussion_data[id] @addContent: (id, content) -> window.$$contents[id] = content diff --git a/lms/templates/courseware/courseware.html b/lms/templates/courseware/courseware.html index c1fc6a74cf..141d05352c 100644 --- a/lms/templates/courseware/courseware.html +++ b/lms/templates/courseware/courseware.html @@ -5,7 +5,7 @@ <%block name="headextra"> <%static:css group='course'/> - <%include file="discussion/_js_head_dependencies.html" /> + <%include file="../discussion/_js_head_dependencies.html" /> <%block name="js_extra"> @@ -23,7 +23,7 @@ <%static:js group='courseware'/> - <%include file="discussion/_js_body_dependencies.html" /> + <%include file="../discussion/_js_body_dependencies.html" /> From 1a7b5b8564abb459f2f9c4b87beee039902f2e21 Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Wed, 22 Aug 2012 17:05:42 -0400 Subject: [PATCH 88/91] fixed tutorial sequence nav bug --- common/lib/xmodule/xmodule/css/sequence/display.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/lib/xmodule/xmodule/css/sequence/display.scss b/common/lib/xmodule/xmodule/css/sequence/display.scss index 32b27d1e78..05002e881d 100644 --- a/common/lib/xmodule/xmodule/css/sequence/display.scss +++ b/common/lib/xmodule/xmodule/css/sequence/display.scss @@ -355,3 +355,10 @@ nav.sequence-bottom { } } } + +div.course-wrapper section.course-content ol.vert-mod > li ul.sequence-nav-buttons { + list-style: none !important; +} + + + From 3cbaba19610dcf178bf6812ae548428917e2f587 Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Wed, 22 Aug 2012 14:11:19 -0700 Subject: [PATCH 89/91] fixed autosubscribe --- lms/lib/comment_client/comment.py | 5 +++++ lms/static/coffee/src/backbone_discussion/content.coffee | 1 - lms/static/coffee/src/backbone_discussion/discussion.coffee | 4 ---- .../coffee/src/backbone_discussion/discussion_module.coffee | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lms/lib/comment_client/comment.py b/lms/lib/comment_client/comment.py index f8c29ba80d..52a0aef70f 100644 --- a/lms/lib/comment_client/comment.py +++ b/lms/lib/comment_client/comment.py @@ -1,5 +1,6 @@ from utils import * +from thread import Thread import models import settings @@ -23,6 +24,10 @@ class Comment(models.Model): base_url = "{prefix}/comments".format(prefix=settings.PREFIX) type = 'comment' + @property + def thread(self): + return Thread(id=self.thread_id, type='thread') + @classmethod def url_for_comments(cls, params={}): if params.get('thread_id'): diff --git a/lms/static/coffee/src/backbone_discussion/content.coffee b/lms/static/coffee/src/backbone_discussion/content.coffee index 31f903e965..d427a348b3 100644 --- a/lms/static/coffee/src/backbone_discussion/content.coffee +++ b/lms/static/coffee/src/backbone_discussion/content.coffee @@ -87,7 +87,6 @@ class @ContentView extends Backbone.View @$(".discussion-follow-thread").removeClass("discussion-unfollow-thread").html("Follow") ability: (ability) -> - console.log "triggered" for action, elemSelector of @model.actions if not ability[action] @$(elemSelector).parent().remove() diff --git a/lms/static/coffee/src/backbone_discussion/discussion.coffee b/lms/static/coffee/src/backbone_discussion/discussion.coffee index 7a664dfe0a..04eed9bf9c 100644 --- a/lms/static/coffee/src/backbone_discussion/discussion.coffee +++ b/lms/static/coffee/src/backbone_discussion/discussion.coffee @@ -37,10 +37,8 @@ class @DiscussionView extends Backbone.View @newPost() reload: ($elem, url) -> - console.log "here" if not url then return DiscussionUtil.get $elem, url, {}, (response, textStatus) => - console.log response $parent = @$el.parent() @$el.replaceWith(response.html) $discussion = $parent.find("section.discussion") @@ -49,7 +47,6 @@ class @DiscussionView extends Backbone.View DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info) loadSimilarPost: (event) -> - console.log "loading" $title = @$(".new-post-title") $wrapper = @$(".new-post-similar-posts-wrapper") $similarPosts = @$(".new-post-similar-posts") @@ -63,7 +60,6 @@ class @DiscussionView extends Backbone.View url = DiscussionUtil.urlFor 'search_similar_threads', @model.id data = { text: @$(".new-post-title").val() } DiscussionUtil.get $elem, url, data, (response, textStatus) => - console.log response $similarPosts.empty() if $.type(response) == "array" and response.length $wrapper.show() diff --git a/lms/static/coffee/src/backbone_discussion/discussion_module.coffee b/lms/static/coffee/src/backbone_discussion/discussion_module.coffee index 036aba7b3a..38f44be1f1 100644 --- a/lms/static/coffee/src/backbone_discussion/discussion_module.coffee +++ b/lms/static/coffee/src/backbone_discussion/discussion_module.coffee @@ -2,7 +2,6 @@ class @DiscussionModuleView extends Backbone.View events: "click .discussion-show": "toggleDiscussion" toggleDiscussion: (event) -> - console.log "toggle" if @showed @$("section.discussion").hide() $(event.target).html("Show Discussion") From 0d689b31641b97dee01347d0fef2359a3f1b174b Mon Sep 17 00:00:00 2001 From: kimth Date: Wed, 22 Aug 2012 17:50:21 -0400 Subject: [PATCH 90/91] Quick workaround on syllabus_present that broke staging --- lms/templates/courseware/course_navigation.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/templates/courseware/course_navigation.html b/lms/templates/courseware/course_navigation.html index af2100f042..5df09207b1 100644 --- a/lms/templates/courseware/course_navigation.html +++ b/lms/templates/courseware/course_navigation.html @@ -19,7 +19,7 @@ def url_class(url):

          1. Courseware
          2. Course Info
          3. -% if course.syllabus_present: +% if hasattr(course,'syllabus_present') and course.syllabus_present:
          4. Syllabus
          5. % endif % if user.is_authenticated(): From 631aaaf5ba826a3346325c6476aca7165d4ab99e Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Wed, 22 Aug 2012 15:51:45 -0700 Subject: [PATCH 91/91] removed unnecessary file --- lms/static/templates/_content.mustache | 62 -------------------------- 1 file changed, 62 deletions(-) delete mode 100644 lms/static/templates/_content.mustache diff --git a/lms/static/templates/_content.mustache b/lms/static/templates/_content.mustache deleted file mode 100644 index 8f3a284510..0000000000 --- a/lms/static/templates/_content.mustache +++ /dev/null @@ -1,62 +0,0 @@ -
            -
            -
            - -
            {{content.votes.point}}
            - -
            -
            - - {{#thread}} - {{{content.displayed_title}}} - - {{/thread}} -
            - -
            {{{content.displayed_body}}}
            - - {{#thread}} -
            - {{#content.tags}} - {{.}} - {{/content.tags}} -
            - - {{/thread}} -
            -
            - sometime by - {{#content.anonymous}} - anonymous - {{/content.anonymous}} - {{^content.anonymous}} - {{content.username}} - {{/content.anonymous}} -
            -
            - {{#thread}} - {{#partial_comments}} - Show all comments ({{content.comments_count}} total) - {{/partial_comments}} - {{^partial_comments}} - Show {{##pluralize}}{{content.comments_count}} comment{{/pluralize}} - {{/partial_comments}} - {{/thread}} -
            - -
            -
            -
            -
            -