From bc005e5f467d65514e3dfb2936c6a66795a85d0b Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Thu, 23 Aug 2012 07:29:35 -0400 Subject: [PATCH 01/42] adjusted dashboard sidebar to accommodate long emails --- lms/static/sass/multicourse/_dashboard.scss | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lms/static/sass/multicourse/_dashboard.scss b/lms/static/sass/multicourse/_dashboard.scss index 9581f5e016..f37c772aef 100644 --- a/lms/static/sass/multicourse/_dashboard.scss +++ b/lms/static/sass/multicourse/_dashboard.scss @@ -68,8 +68,8 @@ @include clearfix; border-bottom: 1px dotted rgb(220,220,220); list-style: none; - margin-bottom: 20px; - padding-bottom: 10px; + margin-bottom: 15px; + padding-bottom: 17px; &:hover { .title .icon { @@ -77,16 +77,20 @@ } } + span { + display: block; + } + span.title { color: $lighter-base-font-color; - float: left; font-family: $sans-serif; + font-size: 13px; .icon { background-size: cover; float: left; height: 19px; - margin: 2px 8px 0 0; + margin: 0 6px 0 0; opacity: 0.6; @include transition(all, 0.15s, linear); width: 19px; @@ -112,7 +116,10 @@ span.data { color: $lighter-base-font-color; font-weight: 700; - margin-left: 12px; + margin-left: 26px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; } } } From 163eb9d49f2f5be274ca2e2acc1fbbc3441dfe9b Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Thu, 23 Aug 2012 08:24:54 -0400 Subject: [PATCH 02/42] fixed bottom sequence nav buttons and firefox outling bug --- .../xmodule/xmodule/css/sequence/display.scss | 42 ++++++++++++------- .../course/layout/_courseware_header.scss | 4 +- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/common/lib/xmodule/xmodule/css/sequence/display.scss b/common/lib/xmodule/xmodule/css/sequence/display.scss index 05002e881d..ba8e94f851 100644 --- a/common/lib/xmodule/xmodule/css/sequence/display.scss +++ b/common/lib/xmodule/xmodule/css/sequence/display.scss @@ -298,14 +298,16 @@ nav.sequence-bottom { ul { @extend .clearfix; - border: 1px solid $border-color; - @include border-radius(3px); @include inline-block(); - width: 100px; + width: 103px; li { float: left; - width: 50%; + width: 50px; + height: 44px; + border: 1px solid #ccc; + @include linear-gradient(top, #eee, #ddd); + @include box-shadow(0 1px 0 rgba(255, 255, 255, .7) inset); &.prev, &.next { margin-bottom: 0; @@ -318,38 +320,39 @@ nav.sequence-bottom { padding: lh(.5) 4px; text-indent: -9999px; @include transition(all, .2s, $ease-in-out-quad); + outline: 0; &:hover { - background-color: #ddd; - color: #000; opacity: .5; - text-decoration: none; + background-position: center 15px; } &.disabled { opacity: .4; } + + &:focus { + outline: 0; + } } } &.prev { + border-radius: 35px 0 0 35px; + a { background-image: url('../images/sequence-nav/previous-icon.png'); - border-right: 1px solid lighten(#c6c6c6, 10%); - - &:hover { - background-color: none; - } + background-position: center 15px; } } &.next { + border-radius: 0 35px 35px 0; + border-left: none; + a { background-image: url('../images/sequence-nav/next-icon.png'); - - &:hover { - background-color: none; - } + background-position: center 15px; } } } @@ -360,5 +363,12 @@ div.course-wrapper section.course-content ol.vert-mod > li ul.sequence-nav-butto list-style: none !important; } +.xmodule_SequenceModule nav.sequence-bottom ul li.next a, +.xmodule_SequenceModule nav.sequence-bottom ul li.prev a { + outline: 0; + &:focus { + outline: 0; + } +} diff --git a/lms/static/sass/course/layout/_courseware_header.scss b/lms/static/sass/course/layout/_courseware_header.scss index 95af67473a..3122281271 100644 --- a/lms/static/sass/course/layout/_courseware_header.scss +++ b/lms/static/sass/course/layout/_courseware_header.scss @@ -86,7 +86,7 @@ header.global.slim { height: 40px; position: absolute; right: 3px; - top: -8px; + top: 0; width: 1px; } @@ -97,7 +97,7 @@ header.global.slim { height: 40px; position: absolute; right: 0px; - top: -12px; + top: 0; width: 1px; } } From 2c1571d66cbee8630b5ba3c8cb41cfbe241ca1f2 Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Thu, 23 Aug 2012 10:04:00 -0400 Subject: [PATCH 03/42] applied line height to course content wrapper to avoid strange line heights with improper markup --- lms/static/sass/course/courseware/_courseware.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/lms/static/sass/course/courseware/_courseware.scss b/lms/static/sass/course/courseware/_courseware.scss index 5621396679..92d8194f24 100644 --- a/lms/static/sass/course/courseware/_courseware.scss +++ b/lms/static/sass/course/courseware/_courseware.scss @@ -13,6 +13,7 @@ div.course-wrapper { section.course-content { @extend .content; padding: 40px; + line-height: 1.6; h1 { margin: 0 0 lh(); From 07eead234939ea433a8c05cdb6f9523970469059 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Thu, 23 Aug 2012 10:11:31 -0400 Subject: [PATCH 04/42] Updated django-wiki to better handle relative wiki links. --- lms/envs/common.py | 2 ++ repo-requirements.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lms/envs/common.py b/lms/envs/common.py index d2397c166d..49921a8626 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -322,6 +322,8 @@ WIKI_CAN_CHANGE_PERMISSIONS = lambda article, user: user.is_staff or user.is_sup WIKI_CAN_ASSIGN = lambda article, user: user.is_staff or user.is_superuser WIKI_USE_BOOTSTRAP_SELECT_WIDGET = False +WIKI_LINK_LIVE_LOOKUPS = False +WIKI_LINK_DEFAULT_LEVEL = 2 ################################# Jasmine ################################### JASMINE_TEST_DIRECTORY = PROJECT_ROOT + '/static/coffee' diff --git a/repo-requirements.txt b/repo-requirements.txt index b93dc2837f..fc6ae1b301 100644 --- a/repo-requirements.txt +++ b/repo-requirements.txt @@ -1,6 +1,6 @@ -e git://github.com/MITx/django-staticfiles.git@6d2504e5c8#egg=django-staticfiles -e git://github.com/MITx/django-pipeline.git#egg=django-pipeline --e git://github.com/benjaoming/django-wiki.git@533c7fc#egg=django-wiki +-e git://github.com/benjaoming/django-wiki.git@7e42bce#egg=django-wiki -e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev -e common/lib/capa -e common/lib/xmodule From 773055cf54d4b974118d7198df5697b2ef069576 Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Thu, 23 Aug 2012 10:28:31 -0400 Subject: [PATCH 05/42] fix wiki falsh message --- lms/static/sass/course/wiki/_wiki.scss | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lms/static/sass/course/wiki/_wiki.scss b/lms/static/sass/course/wiki/_wiki.scss index bcc2c8855d..d15f408e13 100644 --- a/lms/static/sass/course/wiki/_wiki.scss +++ b/lms/static/sass/course/wiki/_wiki.scss @@ -885,8 +885,7 @@ section.wiki { .alert { position: relative; - top: -15px; - margin-bottom: 24px; + margin: 24px 40px; padding: 8px 12px; border: 1px solid #EBE8BF; border-radius: 3px; @@ -903,6 +902,10 @@ section.wiki { } } + .main-article .alert { + margin: 0 0 24px; + } + .missing { max-width: 400px; margin: lh(2) auto; From 5950520e71ec18c243aab608e606e35308256fa9 Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Thu, 23 Aug 2012 11:11:22 -0400 Subject: [PATCH 06/42] tweaked sequence nav and discussion profile sidebar --- .../lib/xmodule/xmodule/css/sequence/display.scss | 13 ++++++------- .../xmodule/xmodule/js/src/sequence/display.coffee | 1 + lms/static/js/jquery.sequence.js | 5 ----- lms/static/sass/_discussion.scss | 5 +++++ lms/templates/seq_module.html | 3 --- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/common/lib/xmodule/xmodule/css/sequence/display.scss b/common/lib/xmodule/xmodule/css/sequence/display.scss index ba8e94f851..7b8dc5f57c 100644 --- a/common/lib/xmodule/xmodule/css/sequence/display.scss +++ b/common/lib/xmodule/xmodule/css/sequence/display.scss @@ -61,13 +61,12 @@ nav.sequence-nav { min-width: 20px; a { - width: 34px; - height: 34px; - margin: 4px auto; - background-position: center 10px; + width: 100%; + height: 42px; + margin: 0; + background-position: center 14px; background-repeat: no-repeat; border: 1px solid transparent; - @include border-radius(35px); cursor: pointer; display: block; padding: 0; @@ -82,7 +81,7 @@ nav.sequence-nav { &:hover { background-color: #fff; background-repeat: no-repeat; - background-position: center 10px; + background-position: center 14px; } &.active { @@ -103,7 +102,7 @@ nav.sequence-nav { &:hover { background-color: #fff; background-repeat: no-repeat; - background-position: center 10px; + background-position: center 14px; } } diff --git a/common/lib/xmodule/xmodule/js/src/sequence/display.coffee b/common/lib/xmodule/xmodule/js/src/sequence/display.coffee index 38291be2ef..35a33a9ea1 100644 --- a/common/lib/xmodule/xmodule/js/src/sequence/display.coffee +++ b/common/lib/xmodule/xmodule/js/src/sequence/display.coffee @@ -55,6 +55,7 @@ class @Sequence element.removeClass('progress-none') .removeClass('progress-some') .removeClass('progress-done') + switch progress when 'none' then element.addClass('progress-none') when 'in_progress' then element.addClass('progress-some') diff --git a/lms/static/js/jquery.sequence.js b/lms/static/js/jquery.sequence.js index 5406444eed..fbefb8a8ce 100644 --- a/lms/static/js/jquery.sequence.js +++ b/lms/static/js/jquery.sequence.js @@ -27,11 +27,6 @@ var SequenceNav = function($element) { var offset = e.pageX - mouseOrigin; var targetLeft = clamp(listOrigin + offset, -maxScroll, 0); - console.log('---------------'); - console.log('offset: ' + offset); - console.log('target left: ' + targetLeft); - console.log('max: ' + maxScroll); - updateHorizontalPosition(targetLeft); setShadows(targetLeft); diff --git a/lms/static/sass/_discussion.scss b/lms/static/sass/_discussion.scss index 717770f459..b44f8120b9 100644 --- a/lms/static/sass/_discussion.scss +++ b/lms/static/sass/_discussion.scss @@ -150,10 +150,15 @@ $tag-text-color: #5b614f; //user profile + .user-profile { + @extend .sidebar; + } + .sidebar-username { font-size: 1.5em; font-weight: bold; line-height: 1.5em; + margin-top: 20px; } .sidebar-user-roles { diff --git a/lms/templates/seq_module.html b/lms/templates/seq_module.html index 9e26543fbd..8ff3e096dd 100644 --- a/lms/templates/seq_module.html +++ b/lms/templates/seq_module.html @@ -40,9 +40,6 @@ From 0b309ea40af0561256c769db0c45d950f9d4ac9f Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Thu, 23 Aug 2012 11:27:54 -0400 Subject: [PATCH 07/42] Can't return Http404, have to raise it. --- lms/djangoapps/courseware/module_render.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 7967452647..51fc5f46c3 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -346,10 +346,10 @@ def xqueue_callback(request, course_id, userid, id, dispatch): get = request.POST.copy() for key in ['xqueue_header', 'xqueue_body']: if not get.has_key(key): - return Http404 + raise Http404 header = json.loads(get['xqueue_header']) if not isinstance(header, dict) or not header.has_key('lms_key'): - return Http404 + raise Http404 # Retrieve target StudentModule user = User.objects.get(id=userid) From f2c5a064aa230b7b39134191fe0d359a39b87ee5 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Thu, 23 Aug 2012 11:49:23 -0400 Subject: [PATCH 08/42] Pull the http or https from the forwarded proto for the xqueue callback url --- lms/djangoapps/courseware/module_render.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 51fc5f46c3..da9828fb12 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -199,7 +199,10 @@ def _get_module(user, request, location, student_module_cache, course_id, positi ) # Fully qualified callback URL for external queueing system - xqueue_callback_url = request.build_absolute_uri('/')[:-1] # Trailing slash provided by reverse + xqueue_callback_url = '{proto}://{host}'.format( + host=request.get_host(), + proto=request.META.get('HTTP_X_FORWARDED_PROTO', 'https' if request.is_secure() else 'http') + ) xqueue_callback_url += reverse('xqueue_callback', kwargs=dict(course_id=course_id, userid=str(user.id), From 48e7966ef2a2b5ff184d3a37a3037142e0c33c22 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Thu, 23 Aug 2012 09:11:13 -0700 Subject: [PATCH 09/42] partial credit for an inputfield --- common/lib/capa/capa/correctmap.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/common/lib/capa/capa/correctmap.py b/common/lib/capa/capa/correctmap.py index c727626a33..eb6ef2d00c 100644 --- a/common/lib/capa/capa/correctmap.py +++ b/common/lib/capa/capa/correctmap.py @@ -73,10 +73,12 @@ class CorrectMap(object): return answer_id in self.cmap and self.cmap[answer_id]['queuekey'] == test_key def get_npoints(self, answer_id): - if self.is_correct(answer_id): - npoints = self.cmap[answer_id].get('npoints', 1) # default to 1 point if correct - return npoints or 1 - return 0 # if not correct, return 0 + npoints = self.get_property(answer_id, 'npoints') + if npoints is not None: + return npoints + elif self.is_correct(answer_id): + return 1 + return 0 # if not correct and no points have been assigned, return 0 def set_property(self, answer_id, property, value): if answer_id in self.cmap: self.cmap[answer_id][property] = value From ed6317f37c9b0e09006cf51680e0ca087b4ea6a4 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Thu, 23 Aug 2012 10:03:36 -0700 Subject: [PATCH 10/42] getting rid of terrible hack in favor of re enabling the dispatcher to sync w/ the comment service --- common/djangoapps/student/models.py | 2 +- lms/djangoapps/django_comment_client/base/views.py | 2 +- lms/djangoapps/django_comment_client/forum/views.py | 6 +++--- lms/lib/comment_client/user.py | 10 ---------- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 55204f19f6..9cde878d21 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -273,7 +273,7 @@ def add_user_to_default_group(user, group): utg.users.add(User.objects.get(username=user)) utg.save() -# @receiver(post_save, sender=User) +@receiver(post_save, sender=User) def update_user_information(sender, instance, created, **kwargs): try: cc_user = cc.User.from_django_user(instance) diff --git a/lms/djangoapps/django_comment_client/base/views.py b/lms/djangoapps/django_comment_client/base/views.py index 280e6d2780..dd9da857c6 100644 --- a/lms/djangoapps/django_comment_client/base/views.py +++ b/lms/djangoapps/django_comment_client/base/views.py @@ -51,7 +51,7 @@ def ajax_content_response(request, course_id, content, template_name): 'content': content, } html = render_to_string(template_name, context) - user_info = cc.User.from_django_user(request.user).safe_attributes() + user_info = cc.User.from_django_user(request.user).to_dict() annotated_content_info = utils.get_annotated_content_info(course_id, content, request.user, user_info) return JsonResponse({ 'html': html, diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index 38f8fa985d..eda574cb6e 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -64,7 +64,7 @@ def render_discussion(request, course_id, threads, *args, **kwargs): 'user': (lambda: reverse('django_comment_client.forum.views.user_profile', args=[course_id, user_id])), }[discussion_type]() - user_info = cc.User.from_django_user(request.user).safe_attributes() + user_info = cc.User.from_django_user(request.user).to_dict() def infogetter(thread): return utils.get_annotated_content_infos(course_id, thread, request.user, user_info) @@ -176,7 +176,7 @@ def render_single_thread(request, discussion_id, course_id, thread_id): thread = cc.Thread.find(thread_id).retrieve(recursive=True).to_dict() - user_info = cc.User.from_django_user(request.user).safe_attributes() + user_info = cc.User.from_django_user(request.user).to_dict() annotated_content_info = utils.get_annotated_content_infos(course_id, thread=thread, user=request.user, user_info=user_info) @@ -194,7 +194,7 @@ def single_thread(request, course_id, discussion_id, thread_id): if request.is_ajax(): - user_info = cc.User.from_django_user(request.user).safe_attributes() + user_info = cc.User.from_django_user(request.user).to_dict() thread = cc.Thread.find(thread_id).retrieve(recursive=True) annotated_content_info = utils.get_annotated_content_infos(course_id, thread, request.user, user_info=user_info) context = {'thread': thread.to_dict(), 'course_id': course_id} diff --git a/lms/lib/comment_client/user.py b/lms/lib/comment_client/user.py index 1f61e9d625..ae4abf91b7 100644 --- a/lms/lib/comment_client/user.py +++ b/lms/lib/comment_client/user.py @@ -33,16 +33,6 @@ class User(models.Model): params = {'source_type': source.type, 'source_id': source.id} response = perform_request('delete', _url_for_subscription(self.id), params) - # TODO this is a hack to compensate for the fact that synchronization isn't - # happening properly. - def safe_attributes(self): - try: - return self.to_dict() - except: - self.save() - self._retrieve() - return self.to_dict() - def vote(self, voteable, value): if voteable.type == 'thread': url = _url_for_vote_thread(voteable.id) From 3e6f3430b6ac6ccb0c66ea3b1b33ea7bb04e6aab Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Thu, 23 Aug 2012 10:06:27 -0700 Subject: [PATCH 11/42] remove blank slate after new post --- common/djangoapps/student/models.py | 2 +- lms/static/coffee/src/discussion/discussion.coffee | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 55204f19f6..9cde878d21 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -273,7 +273,7 @@ def add_user_to_default_group(user, group): utg.users.add(User.objects.get(username=user)) utg.save() -# @receiver(post_save, sender=User) +@receiver(post_save, sender=User) def update_user_information(sender, instance, created, **kwargs): try: cc_user = cc.User.from_django_user(instance) diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index d4b6a8a9c4..8f9639d344 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -109,6 +109,7 @@ if Backbone? @$(".discussion-cancel-post").click $.proxy(@cancelNewPost, @) + @$el.children(".blank").hide() @$(".new-post-form").show() submitNewPost: (event) -> @@ -136,6 +137,8 @@ if Backbone? $thread = $(response.html) @$el.children(".threads").prepend($thread) + @$el.children(".blank").remove() + @$(".new-post-similar-posts").empty() @$(".new-post-similar-posts-wrapper").hide() @$(".new-post-title").val("").attr("prev-text", "") @@ -154,6 +157,7 @@ if Backbone? @$(".new-post-form").addClass("collapsed") else if @$el.hasClass("forum-discussion") @$(".new-post-form").hide() + @$el.children(".blank").show() search: (event) -> event.preventDefault() From 9a3a4f4a8841a8655d992adb7d271df555f6cfa2 Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Thu, 23 Aug 2012 13:10:14 -0400 Subject: [PATCH 12/42] set active accordion state --- lms/static/coffee/src/navigation.coffee | 8 +++++++- lms/static/sass/course/courseware/_sidebar.scss | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lms/static/coffee/src/navigation.coffee b/lms/static/coffee/src/navigation.coffee index 1d6b8a8117..94d53c17c4 100644 --- a/lms/static/coffee/src/navigation.coffee +++ b/lms/static/coffee/src/navigation.coffee @@ -13,9 +13,10 @@ class @Navigation active: active header: 'h3' autoHeight: false + $('#accordion .ui-state-active').closest('.chapter').addClass('is-open') $('#open_close_accordion a').click @toggle - $('#accordion').show() + $('#accordion a').click @setChapter log: (event, ui) -> log_event 'accordion', @@ -24,3 +25,8 @@ class @Navigation toggle: -> $('.course-wrapper').toggleClass('closed') + + setChapter: -> + $('#accordion .is-open').removeClass('is-open') + $(this).closest('.chapter').addClass('is-open') + \ No newline at end of file diff --git a/lms/static/sass/course/courseware/_sidebar.scss b/lms/static/sass/course/courseware/_sidebar.scss index 9f570711a7..52a639d9a6 100644 --- a/lms/static/sass/course/courseware/_sidebar.scss +++ b/lms/static/sass/course/courseware/_sidebar.scss @@ -75,6 +75,10 @@ section.course-index { @include box-shadow(0 1px 0 #fff inset, 0 -1px 0 rgba(0, 0, 0, .1) inset); @include transition(background-color .1s); + &.is-open { + background: #fff; + } + &:first-child { border-radius: 3px 0 0 0; } From 8274aa112fc36932fa7797668b4903713c5a7b6d Mon Sep 17 00:00:00 2001 From: ichuang Date: Thu, 23 Aug 2012 13:12:46 -0400 Subject: [PATCH 13/42] the admin page should be loaded based on settings.DEBUG, not askbot --- lms/urls.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lms/urls.py b/lms/urls.py index ca5b01fa2c..4e4eee4f43 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -197,7 +197,6 @@ if settings.QUICKEDIT: if settings.ASKBOT_ENABLED: urlpatterns += (url(r'^%s' % settings.ASKBOT_URL, include('askbot.urls')), \ - url(r'^admin/', include(admin.site.urls)), \ url(r'^settings/', include('askbot.deps.livesettings.urls')), \ url(r'^followit/', include('followit.urls')), \ # url(r'^robots.txt$', include('robots.urls')), @@ -206,8 +205,10 @@ if settings.ASKBOT_ENABLED: if settings.DEBUG: - ## Jasmine - urlpatterns=urlpatterns + (url(r'^_jasmine/', include('django_jasmine.urls')),) + ## Jasmine and admin + urlpatterns=urlpatterns + (url(r'^_jasmine/', include('django_jasmine.urls')), + url(r'^admin/', include(admin.site.urls)), + ) if settings.MITX_FEATURES.get('AUTH_USE_OPENID'): urlpatterns += ( From bc63b99a10e60de67e37dd6933323e6fa15cb5d7 Mon Sep 17 00:00:00 2001 From: ichuang Date: Thu, 23 Aug 2012 13:13:58 -0400 Subject: [PATCH 14/42] disable subdomain filtering in dev_ike (should allow non-fall2012 courses) --- lms/envs/dev_ike.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lms/envs/dev_ike.py b/lms/envs/dev_ike.py index 3ae141a037..297b179fae 100644 --- a/lms/envs/dev_ike.py +++ b/lms/envs/dev_ike.py @@ -16,6 +16,8 @@ WIKI_ENABLED = False MITX_FEATURES['ENABLE_TEXTBOOK'] = False MITX_FEATURES['ENABLE_DISCUSSION'] = False MITX_FEATURES['ACCESS_REQUIRE_STAFF_FOR_COURSE'] = True # require that user be in the staff_* group to be able to enroll +MITX_FEATURES['SUBDOMAIN_COURSE_LISTINGS'] = False +MITX_FEATURES['SUBDOMAIN_BRANDING'] = False MITX_FEATURES['DISABLE_START_DATES'] = True # MITX_FEATURES['USE_DJANGO_PIPELINE']=False # don't recompile scss From a7f1e7c1477612ac7432e6727d0b0295975d94b0 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Thu, 23 Aug 2012 11:37:46 -0700 Subject: [PATCH 15/42] use the request's hostname rather than settings.SITE_NAME for activation emails --- common/djangoapps/mitxmako/middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/djangoapps/mitxmako/middleware.py b/common/djangoapps/mitxmako/middleware.py index 50f2840a05..64cb2e5415 100644 --- a/common/djangoapps/mitxmako/middleware.py +++ b/common/djangoapps/mitxmako/middleware.py @@ -46,4 +46,4 @@ class MakoMiddleware(object): global requestcontext requestcontext = RequestContext(request) requestcontext['is_secure'] = request.is_secure() - requestcontext['site'] = settings.SITE_NAME + requestcontext['site'] = request.get_host() From 202edb0ecb57dc49f76e4df5764ac8f974bc68d4 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Thu, 23 Aug 2012 11:47:30 -0700 Subject: [PATCH 16/42] making password reset email link have the current site's domain rather than edx.org hardcoded. --- common/djangoapps/student/views.py | 2 +- lms/templates/registration/password_reset_email.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index e7864337b3..ce15a2a003 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -511,7 +511,7 @@ def password_reset(request): form.save(use_https = request.is_secure(), from_email = settings.DEFAULT_FROM_EMAIL, request = request, - domain_override = settings.SITE_NAME) + domain_override = request.get_host()) return HttpResponse(json.dumps({'success':True, 'value': render_to_string('registration/password_reset_done.html', {})})) else: diff --git a/lms/templates/registration/password_reset_email.html b/lms/templates/registration/password_reset_email.html index dba3879281..6d906c84ff 100644 --- a/lms/templates/registration/password_reset_email.html +++ b/lms/templates/registration/password_reset_email.html @@ -3,7 +3,7 @@ {% trans "Please go to the following page and choose a new password:" %} {% block reset_link %} -https://www.edx.org{% url 'django.contrib.auth.views.password_reset_confirm' uidb36=uid token=token %} +https://{{domain}}{% url 'django.contrib.auth.views.password_reset_confirm' uidb36=uid token=token %} {% endblock %} {% trans "Your username, in case you've forgotten:" %} {{ user.username }} From b494441e7a687b0bcbaacc0e12195878158bf61f Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Thu, 23 Aug 2012 16:40:48 -0400 Subject: [PATCH 17/42] started new gradebook functionality --- lms/static/js/jquery.gradebook.js | 33 ++++++++- lms/static/sass/course/_gradebook.scss | 25 +++---- lms/templates/courseware/gradebook.html | 94 +++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 15 deletions(-) diff --git a/lms/static/js/jquery.gradebook.js b/lms/static/js/jquery.gradebook.js index 187e58189f..c2c3553006 100644 --- a/lms/static/js/jquery.gradebook.js +++ b/lms/static/js/jquery.gradebook.js @@ -4,13 +4,16 @@ var Gradebook = function($element) { var _this = this; var $element = $element; + var $body = $('body'); var $grades = $element.find('.grades'); + var $studentTable = $element.find('.student-table'); var $gradeTable = $element.find('.grade-table'); + var $search = $element.find('.student-search-field'); var $leftShadow = $('
'); var $rightShadow = $('
'); var tableHeight = $gradeTable.height(); var maxScroll = $gradeTable.width() - $grades.width(); - var $body = $('body'); + var mouseOrigin; var tableOrigin; @@ -58,12 +61,30 @@ var Gradebook = function($element) { var targetLeft = clamp($gradeTable.position().left, -maxScroll, 0); updateHorizontalPosition(targetLeft); setShadows(targetLeft); - } + }; var updateHorizontalPosition = function(left) { $gradeTable.css({ 'left': left + 'px' }); + }; + + var highlightRow = function(e) { + $element.find('.highlight').removeClass('highlight'); + + var index = $(this).index(); + $studentTable.find('tr').eq(index + 1).addClass('highlight'); + $gradeTable.find('tr').eq(index + 1).addClass('highlight'); + }; + + var filter = function(e) { + var term = $(this).val(); + if(term.length > 0) { + $studentTable.find('tr').hide(); + $studentTable.find('tbody tr').not(':contains(' + term + '), :nth-child(1)').hide(); + } else { + $studentTable.find('tbody tr').show(); + } } $leftShadow.css('height', tableHeight + 'px'); @@ -72,5 +93,11 @@ var Gradebook = function($element) { setShadows(0); $grades.css('height', tableHeight); $gradeTable.bind('mousedown', startDrag); + $element.find('tr').bind('mouseover', highlightRow); + $search.bind('keyup', filter); $(window).bind('resize', updateWidths); -} \ No newline at end of file +} + + + + diff --git a/lms/static/sass/course/_gradebook.scss b/lms/static/sass/course/_gradebook.scss index 5f6c88d918..9817188d34 100644 --- a/lms/static/sass/course/_gradebook.scss +++ b/lms/static/sass/course/_gradebook.scss @@ -133,18 +133,6 @@ div.gradebook-wrapper { box-shadow: 0 1px 0 $table-border-color inset, 0 2px 0 rgba(255, 255, 255, .7) inset; border-left: 1px solid #ccc; - // &:before { - // content: ''; - // display: block; - // position: absolute; - // left: 0; - // top: 0; - // z-index: 9999; - // width: 1px; - // height: 50px; - // @include linear-gradient(top, rgba(0, 0, 0, 0) 30%, rgba(0, 0, 0, .15)); - // } - &:first-child { border-radius: 5px 0 0 0; box-shadow: 1px 1px 0 $table-border-color inset, 1px 2px 0 rgba(255, 255, 255, .7) inset; @@ -205,6 +193,19 @@ div.gradebook-wrapper { @extend .top-header; } } + + .student-table tr:hover td, + .grade-table tr:hover td, + .student-table tr.highlight td, + .grade-table tr.highlight td { + border-color: #74b7d6; + @include linear-gradient(#8ed6f7, #76cbf4); + color: #333; + + a { + color: #333; + } + } } diff --git a/lms/templates/courseware/gradebook.html b/lms/templates/courseware/gradebook.html index fb750aed19..12cc491982 100644 --- a/lms/templates/courseware/gradebook.html +++ b/lms/templates/courseware/gradebook.html @@ -53,6 +53,82 @@ %endfor + + + + Name + + + + + Name + + + + + Name + + + + + Name + + + + + Name + + + + + Name + + + + + Name + + + + + Name + + + + + Name + + + + + Name + + + + + Name + + + + + Name + + + + + Name + + + + + Name + + + + + Name + + @@ -97,6 +173,24 @@ ${percent_data( student['grade_summary']['percent'])} %endfor + + + + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 From 2783ff7e634932405b49e3c4d66bcbe642f161cb Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Thu, 23 Aug 2012 16:57:03 -0400 Subject: [PATCH 18/42] added simple table filtering to gradebook --- lms/static/js/jquery.gradebook.js | 11 ++++++--- lms/templates/courseware/gradebook.html | 30 ++++++++++++------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/lms/static/js/jquery.gradebook.js b/lms/static/js/jquery.gradebook.js index c2c3553006..7a5ce66081 100644 --- a/lms/static/js/jquery.gradebook.js +++ b/lms/static/js/jquery.gradebook.js @@ -80,11 +80,16 @@ var Gradebook = function($element) { var filter = function(e) { var term = $(this).val(); if(term.length > 0) { - $studentTable.find('tr').hide(); - $studentTable.find('tbody tr').not(':contains(' + term + '), :nth-child(1)').hide(); + $studentTable.find('tbody tr').hide(); + $gradeTable.find('tbody tr').hide(); + $studentTable.find('tbody tr:contains(' + term + ')').each(function(i) { + $(this).show(); + $gradeTable.find('tr').eq($(this).index() + 1).show(); + }); } else { $studentTable.find('tbody tr').show(); - } + $gradeTable.find('tbody tr').show(); + } } $leftShadow.css('height', tableHeight + 'px'); diff --git a/lms/templates/courseware/gradebook.html b/lms/templates/courseware/gradebook.html index 12cc491982..c4d3d125d4 100644 --- a/lms/templates/courseware/gradebook.html +++ b/lms/templates/courseware/gradebook.html @@ -56,77 +56,77 @@ - Name + Charles Darwin - Name + George Washington - Name + George Clooney - Name + Aldous Huxley - Name + George Orwell - Name + Ayn Rand - Name + Friedrich Nietzsche - Name + Steve Jobs - Name + Bill Gates - Name + Jimmy Johns - Name + Susan B. Anthony - Name + Random McRandomson - Name + Fakey Fakerson - Name + Bob - Name + Jim From ca09c7427bd3733df8eac045288b3a5b9112fc74 Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Thu, 23 Aug 2012 11:43:05 -0700 Subject: [PATCH 19/42] some tiny bug fixes --- lms/static/coffee/src/discussion/content.coffee | 6 +++++- lms/static/coffee/src/discussion/utils.coffee | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee index f7e7bcc5dc..8ae22ccf73 100644 --- a/lms/static/coffee/src/discussion/content.coffee +++ b/lms/static/coffee/src/discussion/content.coffee @@ -191,6 +191,8 @@ if Backbone? comment = @model.addComment response.content commentView = new CommentView el: $comment[0], model: comment comment.updateInfo response.annotated_content_info + if autowatch + @model.get('thread').set('subscribed', true) @cancelReply() cancelReply: -> @@ -330,9 +332,11 @@ if Backbone? DiscussionUtil.safeAjax $elem: $elem url: url + type: "POST" success: (response, textStatus) => @$el.remove() - @model.get('thread').removeComment(@model) + if @model.get('type') == 'comment' + @model.get('thread').removeComment(@model) events: "click .discussion-follow-thread": "toggleFollow" diff --git a/lms/static/coffee/src/discussion/utils.coffee b/lms/static/coffee/src/discussion/utils.coffee index e156b09a63..727b43b348 100644 --- a/lms/static/coffee/src/discussion/utils.coffee +++ b/lms/static/coffee/src/discussion/utils.coffee @@ -72,12 +72,10 @@ class @DiscussionUtil params["beforeSend"] = -> $elem.attr("disabled", "disabled") if params["$loading"] - console.log "loading" params["$loading"].loading() $.ajax(params).always -> $elem.removeAttr("disabled") if params["$loading"] - console.log "loaded" params["$loading"].loaded() @get: ($elem, url, data, success) -> From 3761b02ce8e69e4552ce763316d1e091458a5298 Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Thu, 23 Aug 2012 15:12:02 -0700 Subject: [PATCH 20/42] hide reply button after closing thread --- .../django_comment_client/base/views.py | 6 +++++- lms/djangoapps/django_comment_client/utils.py | 19 +++++++++++-------- .../coffee/src/discussion/content.coffee | 5 ++++- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lms/djangoapps/django_comment_client/base/views.py b/lms/djangoapps/django_comment_client/base/views.py index dd9da857c6..d1e428ef9c 100644 --- a/lms/djangoapps/django_comment_client/base/views.py +++ b/lms/djangoapps/django_comment_client/base/views.py @@ -156,7 +156,11 @@ def openclose_thread(request, course_id, thread_id): thread = cc.Thread.find(thread_id) thread.closed = request.POST.get('closed', 'false').lower() == 'true' thread.save() - return JsonResponse(thread.to_dict()) + thread = thread.to_dict() + return JsonResponse({ + 'content': thread, + 'ability': utils.get_ability(course_id, thread, request.user), + }) @require_POST @login_required diff --git a/lms/djangoapps/django_comment_client/utils.py b/lms/djangoapps/django_comment_client/utils.py index fded387462..4908ee91b5 100644 --- a/lms/djangoapps/django_comment_client/utils.py +++ b/lms/djangoapps/django_comment_client/utils.py @@ -164,6 +164,16 @@ class QueryCountDebugMiddleware(object): logging.info('%s queries run, total %s seconds' % (len(connection.queries), total_time)) return response +def get_ability(course_id, content, user): + return { + 'editable': check_permissions_by_view(user, course_id, content, "update_thread" if content['type'] == 'thread' else "update_comment"), + 'can_reply': check_permissions_by_view(user, course_id, content, "create_comment" if content['type'] == 'thread' else "create_sub_comment"), + 'can_endorse': check_permissions_by_view(user, course_id, content, "endorse_comment") if content['type'] == 'comment' else False, + 'can_delete': check_permissions_by_view(user, course_id, content, "delete_thread" if content['type'] == 'thread' else "delete_comment"), + 'can_openclose': check_permissions_by_view(user, course_id, content, "openclose_thread") if content['type'] == 'thread' else False, + 'can_vote': check_permissions_by_view(user, course_id, content, "vote_for_thread" if content['type'] == 'thread' else "vote_for_comment"), + } + def get_annotated_content_info(course_id, content, user, user_info): voted = '' if content['id'] in user_info['upvoted_ids']: @@ -173,14 +183,7 @@ def get_annotated_content_info(course_id, content, user, user_info): return { 'voted': voted, 'subscribed': content['id'] in user_info['subscribed_thread_ids'], - 'ability': { - 'editable': check_permissions_by_view(user, course_id, content, "update_thread" if content['type'] == 'thread' else "update_comment"), - 'can_reply': check_permissions_by_view(user, course_id, content, "create_comment" if content['type'] == 'thread' else "create_sub_comment"), - 'can_endorse': check_permissions_by_view(user, course_id, content, "endorse_comment") if content['type'] == 'comment' else False, - 'can_delete': check_permissions_by_view(user, course_id, content, "delete_thread" if content['type'] == 'thread' else "delete_comment"), - 'can_openclose': check_permissions_by_view(user, course_id, content, "openclose_thread") if content['type'] == 'thread' else False, - 'can_vote': check_permissions_by_view(user, course_id, content, "vote_for_thread" if content['type'] == 'thread' else "vote_for_comment"), - }, + 'ability': get_ability(course_id, content, user), } def get_annotated_content_infos(course_id, thread, user, user_info): diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee index 8ae22ccf73..d94f79ac25 100644 --- a/lms/static/coffee/src/discussion/content.coffee +++ b/lms/static/coffee/src/discussion/content.coffee @@ -90,7 +90,9 @@ if Backbone? ability: (ability) -> for action, elemSelector of @model.actions if not ability[action] - @$(elemSelector).parent().remove() + @$(elemSelector).parent().hide() + else + @$(elemSelector).parent().show() $discussionContent: -> @_discussionContent ||= @$el.children(".discussion-content") @@ -271,6 +273,7 @@ if Backbone? data: data success: (response, textStatus) => @model.set('closed', not closed) + @model.set('ability', response.ability) edit: (event) -> @$(".discussion-content-wrapper").hide() From 7e6656f2df174eb369b571acbe9f274a59301d86 Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Thu, 23 Aug 2012 15:21:27 -0700 Subject: [PATCH 21/42] fixed profiled user id (toggle moderator status) --- lms/templates/discussion/user_profile.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/templates/discussion/user_profile.html b/lms/templates/discussion/user_profile.html index 98d7ddedfd..a0a64deeb7 100644 --- a/lms/templates/discussion/user_profile.html +++ b/lms/templates/discussion/user_profile.html @@ -36,6 +36,6 @@ From 88a3e828f691b4ae0143239a0657c519d24e4028 Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Fri, 24 Aug 2012 01:04:11 -0700 Subject: [PATCH 22/42] don't use raw html when rendering --- lms/static/coffee/src/discussion/content.coffee | 8 ++++---- lms/templates/discussion/mustache/_content.mustache | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee index d94f79ac25..95eef8cda8 100644 --- a/lms/static/coffee/src/discussion/content.coffee +++ b/lms/static/coffee/src/discussion/content.coffee @@ -284,11 +284,11 @@ if Backbone? view = {} view.id = @model.id if @model.get('type') == 'thread' - view.title = @$(".thread-raw-title").html() - view.body = @$(".thread-raw-body").html() - view.tags = @$(".thread-raw-tags").html() + view.title = @model.get('title') + view.body = @model.get('body') + view.tags = @model.get('tags') else - view.body = @$(".comment-raw-body").html() + view.body = @model.get('body') @$discussionContent().append Mustache.render DiscussionUtil.getTemplate("_edit_#{@model.get('type')}"), view DiscussionUtil.makeWmdEditor @$el, $.proxy(@$, @), "#{@model.get('type')}-body-edit" @$(".thread-tags-edit").tagsInput DiscussionUtil.tagsInputOptions() diff --git a/lms/templates/discussion/mustache/_content.mustache b/lms/templates/discussion/mustache/_content.mustache index 91330dd0d8..a5089793e7 100644 --- a/lms/templates/discussion/mustache/_content.mustache +++ b/lms/templates/discussion/mustache/_content.mustache @@ -16,19 +16,16 @@ {{#thread}} {{content.displayed_title}} - {{/thread}}
{{content.displayed_body}}
- {{#thread}}
{{#content.tags}} {{.}} {{/content.tags}}
- {{/thread}}
From 80195176e99893e9f045476f3f71f22ac179af2b Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Fri, 24 Aug 2012 01:45:55 -0700 Subject: [PATCH 23/42] use escape html everywhere and only send discussion data relavent --- .../django_comment_client/base/views.py | 26 +++++++++---------- .../django_comment_client/forum/views.py | 12 ++++----- lms/djangoapps/django_comment_client/utils.py | 14 ++++++++++ .../coffee/src/discussion/discussion.coffee | 2 +- .../discussion/_content_renderer.html | 2 +- .../discussion/_discussion_module.html | 2 +- lms/templates/discussion/_forum.html | 2 +- lms/templates/discussion/_inline.html | 2 +- lms/templates/discussion/_paginator.html | 4 +-- .../discussion/_recent_active_posts.html | 2 +- lms/templates/discussion/_search_bar.html | 4 +-- lms/templates/discussion/_similar_posts.html | 2 +- lms/templates/discussion/_single_thread.html | 2 +- lms/templates/discussion/_sort.html | 2 +- lms/templates/discussion/_trending_tags.html | 2 +- .../discussion/_user_active_threads.html | 2 +- lms/templates/discussion/_user_profile.html | 6 ++--- lms/templates/discussion/index.html | 2 +- lms/templates/discussion/user_profile.html | 2 +- 19 files changed, 53 insertions(+), 39 deletions(-) diff --git a/lms/djangoapps/django_comment_client/base/views.py b/lms/djangoapps/django_comment_client/base/views.py index d1e428ef9c..68250a035e 100644 --- a/lms/djangoapps/django_comment_client/base/views.py +++ b/lms/djangoapps/django_comment_client/base/views.py @@ -55,7 +55,7 @@ def ajax_content_response(request, course_id, content, template_name): annotated_content_info = utils.get_annotated_content_info(course_id, content, request.user, user_info) return JsonResponse({ 'html': html, - 'content': content, + 'content': utils.safe_content(content), 'annotated_content_info': annotated_content_info, }) @@ -78,7 +78,7 @@ def create_thread(request, course_id, commentable_id): if request.is_ajax(): return ajax_content_response(request, course_id, thread.to_dict(), 'discussion/ajax_create_thread.html') else: - return JsonResponse(thread.to_dict()) + return JsonResponse(utils.safe_content(thread.to_dict())) @require_POST @login_required @@ -90,7 +90,7 @@ def update_thread(request, course_id, thread_id): if request.is_ajax(): return ajax_content_response(request, course_id, thread.to_dict(), 'discussion/ajax_update_thread.html') else: - return JsonResponse(thread.to_dict()) + return JsonResponse(utils.safe_content(thread.to_dict())) def _create_comment(request, course_id, thread_id=None, parent_id=None): post = request.POST @@ -109,7 +109,7 @@ def _create_comment(request, course_id, thread_id=None, parent_id=None): if request.is_ajax(): return ajax_content_response(request, course_id, comment.to_dict(), 'discussion/ajax_create_comment.html') else: - return JsonResponse(comment.to_dict()) + return JsonResponse(utils.safe_content(comment.to_dict())) @require_POST @login_required @@ -126,7 +126,7 @@ def create_comment(request, course_id, thread_id): def delete_thread(request, course_id, thread_id): thread = cc.Thread.find(thread_id) thread.delete() - return JsonResponse(thread.to_dict()) + return JsonResponse(utils.safe_content(thread.to_dict())) @require_POST @login_required @@ -138,7 +138,7 @@ def update_comment(request, course_id, comment_id): if request.is_ajax(): return ajax_content_response(request, course_id, comment.to_dict(), 'discussion/ajax_update_comment.html') else: - return JsonResponse(comment.to_dict()), + return JsonResponse(utils.safe_content(comment.to_dict())) @require_POST @login_required @@ -147,7 +147,7 @@ def endorse_comment(request, course_id, comment_id): comment = cc.Comment.find(comment_id) comment.endorsed = request.POST.get('endorsed', 'false').lower() == 'true' comment.save() - return JsonResponse(comment.to_dict()) + return JsonResponse(utils.safe_content(comment.to_dict())) @require_POST @login_required @@ -158,7 +158,7 @@ def openclose_thread(request, course_id, thread_id): thread.save() thread = thread.to_dict() return JsonResponse({ - 'content': thread, + 'content': utils.safe_content(thread), 'ability': utils.get_ability(course_id, thread, request.user), }) @@ -177,7 +177,7 @@ def create_sub_comment(request, course_id, comment_id): def delete_comment(request, course_id, comment_id): comment = cc.Comment.find(comment_id) comment.delete() - return JsonResponse(comment.to_dict()) + return JsonResponse(utils.safe_content(comment.to_dict())) @require_POST @login_required @@ -186,7 +186,7 @@ def vote_for_comment(request, course_id, comment_id, value): user = cc.User.from_django_user(request.user) comment = cc.Comment.find(comment_id) user.vote(comment, value) - return JsonResponse(comment.to_dict()) + return JsonResponse(utils.safe_content(comment.to_dict())) @require_POST @login_required @@ -195,7 +195,7 @@ def undo_vote_for_comment(request, course_id, comment_id): user = cc.User.from_django_user(request.user) comment = cc.Comment.find(comment_id) user.unvote(comment) - return JsonResponse(comment.to_dict()) + return JsonResponse(utils.safe_content(comment.to_dict())) @require_POST @login_required @@ -204,7 +204,7 @@ def vote_for_thread(request, course_id, thread_id, value): user = cc.User.from_django_user(request.user) thread = cc.Thread.find(thread_id) user.vote(thread, value) - return JsonResponse(thread.to_dict()) + return JsonResponse(utils.safe_content(thread.to_dict())) @require_POST @login_required @@ -213,7 +213,7 @@ def undo_vote_for_thread(request, course_id, thread_id): user = cc.User.from_django_user(request.user) thread = cc.Thread.find(thread_id) user.unvote(thread) - return JsonResponse(thread.to_dict()) + return JsonResponse(utils.safe_content(thread.to_dict())) @require_POST diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index eda574cb6e..62055f0ab7 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -83,7 +83,7 @@ def render_discussion(request, course_id, threads, *args, **kwargs): 'base_url': base_url, 'query_params': strip_blank(strip_none(extract(query_params, ['page', 'sort_key', 'sort_order', 'tags', 'text']))), 'annotated_content_info': json.dumps(annotated_content_info), - 'discussion_data': json.dumps({ (discussion_id or user_id): threads }) + 'discussion_data': json.dumps({ (discussion_id or user_id): map(utils.safe_content, threads) }) } context = dict(context.items() + query_params.items()) return render_to_string(template, context) @@ -128,7 +128,7 @@ def inline_discussion(request, course_id, discussion_id): return utils.JsonResponse({ 'html': html, - 'discussionData': threads, + 'discussion_data': map(utils.safe_content, threads), }) def render_search_bar(request, course_id, discussion_id=None, text=''): @@ -149,7 +149,7 @@ def forum_form_discussion(request, course_id): if request.is_ajax(): return utils.JsonResponse({ 'html': content, - 'discussionData': threads, + 'discussion_data': map(utils.safe_content, threads), }) else: recent_active_threads = cc.search_recent_active_threads( @@ -186,7 +186,7 @@ def render_single_thread(request, discussion_id, course_id, thread_id): 'annotated_content_info': json.dumps(annotated_content_info), 'course_id': course_id, 'request': request, - 'discussion_data': json.dumps({ discussion_id: [thread] }), + 'discussion_data': json.dumps({ discussion_id: [utils.safe_content(thread)] }), } return render_to_string('discussion/_single_thread.html', context) @@ -202,7 +202,7 @@ def single_thread(request, course_id, discussion_id, thread_id): return utils.JsonResponse({ 'html': html, - 'content': thread.to_dict(), + 'content': utils.safe_content(thread.to_dict()), 'annotated_content_info': annotated_content_info, }) @@ -252,7 +252,7 @@ def user_profile(request, course_id, user_id): if request.is_ajax(): return utils.JsonResponse({ 'html': content, - 'discussionData': threads, + 'discussion_data': map(utils.safe_content, threads), }) else: context = { diff --git a/lms/djangoapps/django_comment_client/utils.py b/lms/djangoapps/django_comment_client/utils.py index 4908ee91b5..45e0fc196c 100644 --- a/lms/djangoapps/django_comment_client/utils.py +++ b/lms/djangoapps/django_comment_client/utils.py @@ -219,3 +219,17 @@ def extend_content(content): 'permalink': permalink(content), } return merge_dict(content, content_info) + +def safe_content(content): + fields = [ + 'id', 'body', 'course_id', 'anonymous', 'endorsed', + 'parent_id', 'thread_id', 'votes', 'closed', + 'created_at', 'updated_at', 'depth', 'type', + 'commentable_id', 'comments_count', 'at_position_list', + 'children', 'highlighted_title', 'highlighted_body', + ] + + if content.get('anonymous') is False: + fields += ['username', 'user_id'] + + return strip_none(extract(content, fields)) diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index 8f9639d344..d30fdc70a8 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -48,7 +48,7 @@ if Backbone? $parent = @$el.parent() @$el.replaceWith(response.html) $discussion = $parent.find("section.discussion") - @model.reset(response.discussionData, { silent: false }) + @model.reset(response.discussion_data, { silent: false }) view = new DiscussionView el: $discussion[0], model: @model DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info) $("html, body").animate({ scrollTop: 0 }, 0) diff --git a/lms/templates/discussion/_content_renderer.html b/lms/templates/discussion/_content_renderer.html index 54371bf4dd..ac2b0b4897 100644 --- a/lms/templates/discussion/_content_renderer.html +++ b/lms/templates/discussion/_content_renderer.html @@ -5,7 +5,7 @@ <%def name="render_content_with_comments(content, *args, **kwargs)"> -
+
${render_content(content, *args, **kwargs)} ${render_comments(content.get('children', []), *args, **kwargs)}
diff --git a/lms/templates/discussion/_discussion_module.html b/lms/templates/discussion/_discussion_module.html index 3d92f242a1..b9e69cc0ad 100644 --- a/lms/templates/discussion/_discussion_module.html +++ b/lms/templates/discussion/_discussion_module.html @@ -1,3 +1,3 @@ diff --git a/lms/templates/discussion/_forum.html b/lms/templates/discussion/_forum.html index 0a812fd1bb..b43efae666 100644 --- a/lms/templates/discussion/_forum.html +++ b/lms/templates/discussion/_forum.html @@ -1,6 +1,6 @@ <%namespace name="renderer" file="_content_renderer.html"/> -
+
diff --git a/lms/templates/discussion/_inline.html b/lms/templates/discussion/_inline.html index aa90e4e6ba..abef7f39e8 100644 --- a/lms/templates/discussion/_inline.html +++ b/lms/templates/discussion/_inline.html @@ -1,6 +1,6 @@ <%namespace name="renderer" file="_content_renderer.html"/> -
+
diff --git a/lms/templates/discussion/_paginator.html b/lms/templates/discussion/_paginator.html index dd9bd2d43d..bb94b64289 100644 --- a/lms/templates/discussion/_paginator.html +++ b/lms/templates/discussion/_paginator.html @@ -9,7 +9,7 @@ %> <%def name="link_to_page(_page, text)"> - ${text} + ${text} <%def name="div_page(_page)"> @@ -36,7 +36,7 @@ % endfor -
+
% if page > 1: ${link_to_page(page - 1, "< Previous page")} diff --git a/lms/templates/discussion/_recent_active_posts.html b/lms/templates/discussion/_recent_active_posts.html index baf505838d..b787df2fcf 100644 --- a/lms/templates/discussion/_recent_active_posts.html +++ b/lms/templates/discussion/_recent_active_posts.html @@ -8,7 +8,7 @@
    % for thread in recent_active_threads: -
  1. ${thread['title']} ${thread['votes']['point']}
  2. +
  3. ${thread['title'] | h} ${thread['votes']['point'] | h}
  4. % endfor
      diff --git a/lms/templates/discussion/_search_bar.html b/lms/templates/discussion/_search_bar.html index e93b46efb2..1f46a8e3c8 100644 --- a/lms/templates/discussion/_search_bar.html +++ b/lms/templates/discussion/_search_bar.html @@ -10,9 +10,9 @@ def base_url_for_search():
      % if query_params.get('tags', None): - + % else: - + % endif
      diff --git a/lms/templates/discussion/_similar_posts.html b/lms/templates/discussion/_similar_posts.html index f68964acec..ef6eedee2e 100644 --- a/lms/templates/discussion/_similar_posts.html +++ b/lms/templates/discussion/_similar_posts.html @@ -3,7 +3,7 @@ Hide
      % for thread in threads: - ${thread['title']} + ${thread['title'] | h} % endfor
      % endif diff --git a/lms/templates/discussion/_single_thread.html b/lms/templates/discussion/_single_thread.html index 395eb72e46..bfbf7b069e 100644 --- a/lms/templates/discussion/_single_thread.html +++ b/lms/templates/discussion/_single_thread.html @@ -1,6 +1,6 @@ <%namespace name="renderer" file="_content_renderer.html"/> -
      +
      Discussion
      ${renderer.render_content_with_comments(thread)} diff --git a/lms/templates/discussion/_sort.html b/lms/templates/discussion/_sort.html index 934973a966..7ba10eab7b 100644 --- a/lms/templates/discussion/_sort.html +++ b/lms/templates/discussion/_sort.html @@ -26,7 +26,7 @@ else: return base_url + '?' + urlencode(merge(query_params, {'page': 1, 'sort_key': key, 'sort_order': order})) %> - ${title} + ${title}
      diff --git a/lms/templates/discussion/_trending_tags.html b/lms/templates/discussion/_trending_tags.html index fea18c02dc..509516c2d5 100644 --- a/lms/templates/discussion/_trending_tags.html +++ b/lms/templates/discussion/_trending_tags.html @@ -7,7 +7,7 @@
        % for tag, count in trending_tags: -
      1. ${tag}×${count}
      2. +
      3. ${tag | h}×${count | h}
      4. % endfor
          diff --git a/lms/templates/discussion/_user_active_threads.html b/lms/templates/discussion/_user_active_threads.html index ad72ccdd5e..1844009466 100644 --- a/lms/templates/discussion/_user_active_threads.html +++ b/lms/templates/discussion/_user_active_threads.html @@ -1,6 +1,6 @@ <%namespace name="renderer" file="_content_renderer.html"/> -
          +
          diff --git a/lms/templates/discussion/_user_profile.html b/lms/templates/discussion/_user_profile.html index a092a97951..8660d8035f 100644 --- a/lms/templates/discussion/_user_profile.html +++ b/lms/templates/discussion/_user_profile.html @@ -7,12 +7,12 @@ <% role_names = sorted(map(attrgetter('name'), django_user.roles.all())) %> - + - - + + % if check_permissions_by_view(user, course.id, content=None, name='update_moderator_status'): % if "Moderator" in role_names: Revoke Moderator provileges diff --git a/lms/templates/discussion/index.html b/lms/templates/discussion/index.html index 43291b6f9b..1160a14d90 100644 --- a/lms/templates/discussion/index.html +++ b/lms/templates/discussion/index.html @@ -1,7 +1,7 @@ <%inherit file="../main.html" /> <%namespace name='static' file='../static_content.html'/> <%block name="bodyclass">discussion -<%block name="title">Discussion – ${course.number} +<%block name="title">Discussion – ${course.number | h} <%block name="headextra"> <%static:css group='course'/> diff --git a/lms/templates/discussion/user_profile.html b/lms/templates/discussion/user_profile.html index a0a64deeb7..4c067db710 100644 --- a/lms/templates/discussion/user_profile.html +++ b/lms/templates/discussion/user_profile.html @@ -3,7 +3,7 @@ <%inherit file="../main.html" /> <%namespace name='static' file='../static_content.html'/> <%block name="bodyclass">discussion -<%block name="title">Discussion – ${course.number} +<%block name="title">Discussion – ${course.number | h} <%block name="headextra"> <%static:css group='course'/> From cc5fbdd5a78f8beaffb44e84366dac8f7596113c Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Fri, 24 Aug 2012 02:05:11 -0700 Subject: [PATCH 24/42] fixed appearance of loading icon near sort bar --- lms/static/coffee/src/discussion/discussion.coffee | 5 +++++ lms/static/coffee/src/discussion/utils.coffee | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index d30fdc70a8..272a4949e0 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -42,9 +42,14 @@ if Backbone? DiscussionUtil.safeAjax $elem: $elem $loading: $elem + loadingCallback: -> + $(this).parent().append("") + loadedCallback: -> + $(this).parent().children(".discussion-loading").remove() url: url type: "GET" success: (response, textStatus) => + return $parent = @$el.parent() @$el.replaceWith(response.html) $discussion = $parent.find("section.discussion") diff --git a/lms/static/coffee/src/discussion/utils.coffee b/lms/static/coffee/src/discussion/utils.coffee index 727b43b348..94807654c9 100644 --- a/lms/static/coffee/src/discussion/utils.coffee +++ b/lms/static/coffee/src/discussion/utils.coffee @@ -72,11 +72,17 @@ class @DiscussionUtil params["beforeSend"] = -> $elem.attr("disabled", "disabled") if params["$loading"] - params["$loading"].loading() + if params["loadingCallback"]? + params["loadingCallback"].apply(params["$loading"]) + else + params["$loading"].loading() $.ajax(params).always -> $elem.removeAttr("disabled") if params["$loading"] - params["$loading"].loaded() + if params["loadedCallback"]? + params["loadedCallback"].apply(params["$loading"]) + else + params["$loading"].loaded() @get: ($elem, url, data, success) -> @safeAjax From fe74f9bc85e6a7b458d2ecdfdc526b69ef1de9f0 Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Fri, 24 Aug 2012 02:17:58 -0700 Subject: [PATCH 25/42] reconstruct after editing --- lms/djangoapps/django_comment_client/utils.py | 2 +- lms/static/coffee/src/discussion/content.coffee | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/django_comment_client/utils.py b/lms/djangoapps/django_comment_client/utils.py index 45e0fc196c..516344d79b 100644 --- a/lms/djangoapps/django_comment_client/utils.py +++ b/lms/djangoapps/django_comment_client/utils.py @@ -222,7 +222,7 @@ def extend_content(content): def safe_content(content): fields = [ - 'id', 'body', 'course_id', 'anonymous', 'endorsed', + 'id', 'title', 'body', 'course_id', 'anonymous', 'endorsed', 'parent_id', 'thread_id', 'votes', 'closed', 'created_at', 'updated_at', 'depth', 'type', 'commentable_id', 'comments_count', 'at_position_list', diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee index 95eef8cda8..13d6f1094f 100644 --- a/lms/static/coffee/src/discussion/content.coffee +++ b/lms/static/coffee/src/discussion/content.coffee @@ -316,8 +316,12 @@ if Backbone? success: (response, textStatus) => DiscussionUtil.clearFormErrors @$(".discussion-update-errors") @$discussionContent().replaceWith(response.html) - @model.set response.content - @model.updateInfo response.annotated_content_info + if @model.get('type') == 'thread' + @model = new Thread response.content + else + @model = new Comment $.extend {}, response.content, { thread: @model.get('thread') } + @reconstruct() + @model.updateInfo response.annotated_content_info, { forceUpdate: true } cancelEdit: (event) -> @$(".discussion-content-edit").hide() @@ -388,6 +392,14 @@ if Backbone? @initTitle() @initBody() @initCommentViews() + + reconstruct: -> + @initBindings() + @initLocal() + @initTimeago() + @initTitle() + @initBody() + @delegateEvents() class @Thread extends @Content urlMappers: From eb65b95232034305de3cf4b54d698aa7c0d48455 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Fri, 24 Aug 2012 03:06:44 -0700 Subject: [PATCH 26/42] Removing stray return statement --- lms/static/coffee/src/discussion/discussion.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/lms/static/coffee/src/discussion/discussion.coffee b/lms/static/coffee/src/discussion/discussion.coffee index 272a4949e0..d8df704d1c 100644 --- a/lms/static/coffee/src/discussion/discussion.coffee +++ b/lms/static/coffee/src/discussion/discussion.coffee @@ -49,7 +49,6 @@ if Backbone? url: url type: "GET" success: (response, textStatus) => - return $parent = @$el.parent() @$el.replaceWith(response.html) $discussion = $parent.find("section.discussion") From eb4068cc11a396e2e8a20f1d0e9fe0abc72d5b04 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Fri, 24 Aug 2012 03:14:42 -0700 Subject: [PATCH 27/42] Make profile page look reasonable --- lms/static/sass/_discussion.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lms/static/sass/_discussion.scss b/lms/static/sass/_discussion.scss index 717770f459..bcda46a0bb 100644 --- a/lms/static/sass/_discussion.scss +++ b/lms/static/sass/_discussion.scss @@ -150,6 +150,10 @@ $tag-text-color: #5b614f; //user profile + .user-profile{ + margin-top: 24px; + } + .sidebar-username { font-size: 1.5em; font-weight: bold; From b72ec72528a07acfe46a94ae2370a000eb6c6160 Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Fri, 24 Aug 2012 13:32:16 -0400 Subject: [PATCH 28/42] pulled temp data from gradebook --- lms/templates/courseware/gradebook.html | 94 ------------------------- 1 file changed, 94 deletions(-) diff --git a/lms/templates/courseware/gradebook.html b/lms/templates/courseware/gradebook.html index c4d3d125d4..fb750aed19 100644 --- a/lms/templates/courseware/gradebook.html +++ b/lms/templates/courseware/gradebook.html @@ -53,82 +53,6 @@ %endfor - - - - Charles Darwin - - - - - George Washington - - - - - George Clooney - - - - - Aldous Huxley - - - - - George Orwell - - - - - Ayn Rand - - - - - Friedrich Nietzsche - - - - - Steve Jobs - - - - - Bill Gates - - - - - Jimmy Johns - - - - - Susan B. Anthony - - - - - Random McRandomson - - - - - Fakey Fakerson - - - - - Bob - - - - - Jim - - @@ -173,24 +97,6 @@ ${percent_data( student['grade_summary']['percent'])} %endfor - - - - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
      From 8e9211d2dd816c56d85a4c9b85c1afab2025f746 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Fri, 24 Aug 2012 14:04:17 -0400 Subject: [PATCH 29/42] Re-enabling link to wiki 'see all children'. The bug for which this was disabled is already fixed. --- lms/templates/wiki/article.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/lms/templates/wiki/article.html b/lms/templates/wiki/article.html index 0e2a997290..5b12942af5 100644 --- a/lms/templates/wiki/article.html +++ b/lms/templates/wiki/article.html @@ -33,11 +33,9 @@
      {% if urlpath %} - {% endif %}
From 2bc9d08b25181bb31c629809b483a4878c7b213a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Fri, 24 Aug 2012 14:11:35 -0400 Subject: [PATCH 30/42] Fixed WikiPath regexp. It was incorrectly matching [Title](Link) on the same line. --- repo-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo-requirements.txt b/repo-requirements.txt index fc6ae1b301..7119106d8b 100644 --- a/repo-requirements.txt +++ b/repo-requirements.txt @@ -1,6 +1,6 @@ -e git://github.com/MITx/django-staticfiles.git@6d2504e5c8#egg=django-staticfiles -e git://github.com/MITx/django-pipeline.git#egg=django-pipeline --e git://github.com/benjaoming/django-wiki.git@7e42bce#egg=django-wiki +-e git://github.com/benjaoming/django-wiki.git@cd1c23e1#egg=django-wiki -e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev -e common/lib/capa -e common/lib/xmodule From f7e299d04ea190d86f6fcefc0ffdf064ad11d39e Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Fri, 24 Aug 2012 11:15:06 -0700 Subject: [PATCH 31/42] fix inline discussion --- lms/static/coffee/src/discussion/discussion_module.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/static/coffee/src/discussion/discussion_module.coffee b/lms/static/coffee/src/discussion/discussion_module.coffee index 4bcacc1474..8306d4d2ae 100644 --- a/lms/static/coffee/src/discussion/discussion_module.coffee +++ b/lms/static/coffee/src/discussion/discussion_module.coffee @@ -27,7 +27,7 @@ if Backbone? $discussion = @$el.find("section.discussion") $(event.target).html("Hide Discussion") discussion = new Discussion() - discussion.reset(response.discussionData, {silent: false}) + discussion.reset(response.discussion_data, {silent: false}) view = new DiscussionView(el: $discussion[0], model: discussion) DiscussionUtil.bulkUpdateContentInfo(window.$$annotated_content_info) @retrieved = true From 268a87442e85ff6553486c2c6a7dd679c4460ed2 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Fri, 24 Aug 2012 14:18:49 -0400 Subject: [PATCH 32/42] Added SECURE_PROXY_SSL_HEADER to env/aws. --- lms/envs/aws.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lms/envs/aws.py b/lms/envs/aws.py index 75ae712b88..a035d291e8 100644 --- a/lms/envs/aws.py +++ b/lms/envs/aws.py @@ -23,6 +23,12 @@ DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' MITX_FEATURES['ENABLE_DISCUSSION'] = False MITX_FEATURES['ENABLE_DISCUSSION_SERVICE'] = True +# IMPORTANT: With this enabled, the server must always be behind a proxy that +# strips the header HTTP_X_FORWARDED_PROTO from client requests. Otherwise, +# a user can fool our server into thinking it was an https connection. +# See https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header +# for other warnings. +SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') ########################### NON-SECURE ENV CONFIG ############################## # Things like server locations, ports, etc. From 4c21abc433433e7dce3392cd988f8cfbb470c194 Mon Sep 17 00:00:00 2001 From: John Jarvis Date: Fri, 24 Aug 2012 15:05:18 -0400 Subject: [PATCH 33/42] empty default for comments_service_key and comments_url because it was breaking configs that do not have it in env.json --- lms/envs/aws.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lms/envs/aws.py b/lms/envs/aws.py index a035d291e8..fbcb9b8e4c 100644 --- a/lms/envs/aws.py +++ b/lms/envs/aws.py @@ -32,6 +32,7 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') ########################### NON-SECURE ENV CONFIG ############################## # Things like server locations, ports, etc. + with open(ENV_ROOT / "env.json") as env_file: ENV_TOKENS = json.load(env_file) @@ -55,6 +56,8 @@ LOGGING = get_logger_config(LOG_DIR, COURSE_LISTINGS = ENV_TOKENS.get('COURSE_LISTINGS', {}) SUBDOMAIN_BRANDING = ENV_TOKENS.get('SUBDOMAIN_BRANDING', {}) +COMMENTS_SERVICE_URL = ENV_TOKENS.get("COMMENTS_SERVICE_URL",'') +COMMENTS_SERVICE_KEY = ENV_TOKENS.get("COMMENTS_SERVICE_KEY",'') ############################## SECURE AUTH ITEMS ############################### # Secret things: passwords, access keys, etc. @@ -73,5 +76,3 @@ XQUEUE_INTERFACE = AUTH_TOKENS['XQUEUE_INTERFACE'] if 'COURSE_ID' in ENV_TOKENS: ASKBOT_URL = "courses/{0}/discussions/".format(ENV_TOKENS['COURSE_ID']) -COMMENTS_SERVICE_URL = ENV_TOKENS["COMMENTS_SERVICE_URL"] -COMMENTS_SERVICE_KEY = ENV_TOKENS["COMMENTS_SERVICE_KEY"] From 31216ccd9ff2eb51b398003b7ba4622a884af331 Mon Sep 17 00:00:00 2001 From: kimth Date: Fri, 24 Aug 2012 16:16:26 -0400 Subject: [PATCH 34/42] Do URL replacement in course info page --- lms/djangoapps/courseware/courses.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index e5ef915e25..5c24d033d5 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -133,8 +133,13 @@ def get_course_info_section(course, section_key): if section_key in ['handouts', 'guest_handouts', 'updates', 'guest_updates']: try: with course.system.resources_fs.open(path("info") / section_key + ".html") as htmlFile: - return replace_urls(htmlFile.read().decode('utf-8'), - course.metadata['data_dir']) + # Replace '/static/' urls + info_html = replace_urls(htmlFile.read().decode('utf-8'), course.metadata['data_dir']) + + # Replace '/course/' urls + course_root = '/courses/' + course.location.course_id + info_html = replace_urls(info_html, course_root, '/course/') + return info_html except ResourceNotFoundError: log.exception("Missing info section {key} in course {url}".format( key=section_key, url=course.location.url())) From b8659e084ce7edb845bc2f52e7c11597e8773d0e Mon Sep 17 00:00:00 2001 From: Victor Shnayder Date: Fri, 24 Aug 2012 16:30:16 -0400 Subject: [PATCH 35/42] Set request.user = student when impersonating a student * currently only in the staff view of student progress page [Fix #34379687] --- lms/djangoapps/courseware/views.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index 83957c17d7..50b7a2d645 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -325,14 +325,17 @@ def progress(request, course_id, student_id=None): raise Http404 student = User.objects.get(id=int(student_id)) + # NOTE: To make sure impersonation by instructor works, use + # student instead of request.user in the rest of the function. + student_module_cache = StudentModuleCache.cache_for_descriptor_descendents( - course_id, request.user, course) - course_module = get_module(request.user, request, course.location, + course_id, student, course) + course_module = get_module(student, request, course.location, student_module_cache, course_id) courseware_summary = grades.progress_summary(student, course_module, course.grader, student_module_cache) - grade_summary = grades.grade(request.user, request, course, student_module_cache) + grade_summary = grades.grade(student, request, course, student_module_cache) context = {'course': course, 'courseware_summary': courseware_summary, From 7d14632c3a5d3966b140b3491276eb2bf280c282 Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Fri, 24 Aug 2012 16:41:12 -0400 Subject: [PATCH 36/42] added log in button to unauthenticated courseware --- .../course/layout/_courseware_header.scss | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/lms/static/sass/course/layout/_courseware_header.scss b/lms/static/sass/course/layout/_courseware_header.scss index 3122281271..86b5fe58c5 100644 --- a/lms/static/sass/course/layout/_courseware_header.scss +++ b/lms/static/sass/course/layout/_courseware_header.scss @@ -67,7 +67,39 @@ header.global.slim { @include linear-gradient(top, #fff, #eee); .guest .secondary { + margin-right: 0; + } + + .guest .secondary a { display: none; + + &#login { + display: block; + @include background-image(linear-gradient(-90deg, lighten($blue, 8%), lighten($blue, 5%) 50%, $blue 50%, darken($blue, 10%) 100%)); + border: 1px solid transparent; + border-color: darken($blue, 10%); + @include border-radius(3px); + @include box-sizing(border-box); + @include box-shadow(0 1px 0 0 rgba(255,255,255, 0.6)); + color: #fff; + display: inline-block; + font-family: $sans-serif; + font-size: 14px; + font-weight: bold; + @include inline-block; + letter-spacing: 0; + line-height: 1em; + margin: 4px; + padding: 6px 12px 8px; + text-decoration: none; + text-transform: none; + text-shadow: 0 -1px rgba(0, 0, 0, 0.6); + vertical-align: middle; + + &:hover, &.active { + @include background-image(linear-gradient(-90deg, $blue, $blue 50%, $blue 50%, $blue 100%)); + } + } } nav { @@ -129,7 +161,7 @@ header.global.slim { a#signup { position: relative; - margin-top: 4px; + margin-top: 3px; padding: 6px 12px 8px; text-transform: none; font-size: 14px; From 115eebdb76003661de63e76ada9c02145ea43f4f Mon Sep 17 00:00:00 2001 From: kimth Date: Fri, 24 Aug 2012 16:47:50 -0400 Subject: [PATCH 37/42] Use reverse for course_info url replacement --- lms/djangoapps/courseware/courses.py | 3 ++- lms/urls.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index 5c24d033d5..1c0b2203ca 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -5,6 +5,7 @@ import logging from path import path from django.conf import settings +from django.core.urlresolvers import reverse from django.http import Http404 from xmodule.course_module import CourseDescriptor @@ -137,7 +138,7 @@ def get_course_info_section(course, section_key): info_html = replace_urls(htmlFile.read().decode('utf-8'), course.metadata['data_dir']) # Replace '/course/' urls - course_root = '/courses/' + course.location.course_id + course_root = reverse('course_root', args=[course.id])[:-1] # Remove trailing slash info_html = replace_urls(info_html, course_root, '/course/') return info_html except ResourceNotFoundError: diff --git a/lms/urls.py b/lms/urls.py index 4e4eee4f43..86d654eb40 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -124,6 +124,8 @@ if settings.COURSEWARE_ENABLED: 'courseware.views.course_about', name="about_course"), #Inside the course + url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/$', + 'courseware.views.course_info', name="course_root"), url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/info$', 'courseware.views.course_info', name="info"), url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/syllabus$', From 7a6fa1dd41a5f6b8b10a5b65f76edbaa59457da9 Mon Sep 17 00:00:00 2001 From: Victor Shnayder Date: Fri, 24 Aug 2012 16:52:19 -0400 Subject: [PATCH 38/42] Fix access control for impersonation case - only relevant in tests due to start dates - still irritatingly intricate logic... --- lms/djangoapps/courseware/module_render.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index da9828fb12..8a96d2533f 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -143,8 +143,9 @@ def get_module(user, request, location, student_module_cache, course_id, positio exists. Arguments: - - user : current django User - - request : current django HTTPrequest + - user : User for whom we're getting the module + - request : current django HTTPrequest -- used in particular for auth + (This is important e.g. for prof impersonation of students in progress view) - location : A Location-like object identifying the module to load - student_module_cache : a StudentModuleCache - course_id : the course_id in the context of which to load module @@ -170,7 +171,9 @@ def _get_module(user, request, location, student_module_cache, course_id, positi descriptor = modulestore().get_instance(course_id, location) # Short circuit--if the user shouldn't have access, bail without doing any work - if not has_access(user, descriptor, 'load'): + # NOTE: Do access check on request.user -- that's who actually needs access (e.g. could be prof + # impersonating a user) + if not has_access(request.user, descriptor, 'load'): return None #TODO Only check the cache if this module can possibly have state From 1f7a1065ef41a1f0efe7fc006f4262002494031e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andre=CC=81s=20Rocha?= Date: Sat, 25 Aug 2012 10:53:20 -0400 Subject: [PATCH 39/42] Important fix! Remove HTML tags from Markdown code. --- repo-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo-requirements.txt b/repo-requirements.txt index 7119106d8b..da38cee406 100644 --- a/repo-requirements.txt +++ b/repo-requirements.txt @@ -1,6 +1,6 @@ -e git://github.com/MITx/django-staticfiles.git@6d2504e5c8#egg=django-staticfiles -e git://github.com/MITx/django-pipeline.git#egg=django-pipeline --e git://github.com/benjaoming/django-wiki.git@cd1c23e1#egg=django-wiki +-e git://github.com/rocha/django-wiki.git@33e9a24b9a20#egg=django-wiki -e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev -e common/lib/capa -e common/lib/xmodule From e7b67d7225d526e2c5f6ef671326c7b9adf38ef8 Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Sat, 25 Aug 2012 12:15:03 -0400 Subject: [PATCH 40/42] Switch django-wiki dependency to a clone in our org so that local changes are easier to make --- repo-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo-requirements.txt b/repo-requirements.txt index da38cee406..f98d05ffc9 100644 --- a/repo-requirements.txt +++ b/repo-requirements.txt @@ -1,6 +1,6 @@ -e git://github.com/MITx/django-staticfiles.git@6d2504e5c8#egg=django-staticfiles -e git://github.com/MITx/django-pipeline.git#egg=django-pipeline --e git://github.com/rocha/django-wiki.git@33e9a24b9a20#egg=django-wiki +-e git://github.com/MITx/django-wiki.git@e2e84558#egg=django-wiki -e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev -e common/lib/capa -e common/lib/xmodule From eb8ee01d21fe50a50454242bc35477eebf0a5153 Mon Sep 17 00:00:00 2001 From: Rocky Duan Date: Sat, 25 Aug 2012 12:12:57 -0700 Subject: [PATCH 41/42] fixed a tiny bug that loading icon sometimes doesn't appear at the right place / doesn't go away --- lms/static/coffee/src/discussion/content.coffee | 2 +- lms/static/coffee/src/discussion/utils.coffee | 5 +++-- lms/templates/discussion/mustache/_content.mustache | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee index 13d6f1094f..73c3688c2a 100644 --- a/lms/static/coffee/src/discussion/content.coffee +++ b/lms/static/coffee/src/discussion/content.coffee @@ -124,7 +124,7 @@ if Backbone? url = @model.urlFor('retrieve') DiscussionUtil.safeAjax $elem: $elem - $loading: $(event.target) if event + $loading: @$(".discussion-show-comments") type: "GET" url: url success: (response, textStatus) => diff --git a/lms/static/coffee/src/discussion/utils.coffee b/lms/static/coffee/src/discussion/utils.coffee index 94807654c9..0990f0c87c 100644 --- a/lms/static/coffee/src/discussion/utils.coffee +++ b/lms/static/coffee/src/discussion/utils.coffee @@ -1,9 +1,10 @@ $ -> $.fn.extend loading: -> - $(this).after("") + @$_loading = $("") + $(this).after(@$_loading) loaded: -> - $(this).parent().children(".discussion-loading").remove() + @$_loading.remove() class @DiscussionUtil diff --git a/lms/templates/discussion/mustache/_content.mustache b/lms/templates/discussion/mustache/_content.mustache index a5089793e7..b4f3176931 100644 --- a/lms/templates/discussion/mustache/_content.mustache +++ b/lms/templates/discussion/mustache/_content.mustache @@ -7,11 +7,11 @@
{{#thread}} From 25c83bbb5d447cee8b3617193f014106a17699dc Mon Sep 17 00:00:00 2001 From: Victor Shnayder Date: Sun, 26 Aug 2012 21:55:55 -0400 Subject: [PATCH 42/42] xml format docs! --- doc/xml-format.md | 370 +++++++++++++++++++++++++++++++++------------- 1 file changed, 264 insertions(+), 106 deletions(-) diff --git a/doc/xml-format.md b/doc/xml-format.md index 2a9e379ccc..55bcda4480 100644 --- a/doc/xml-format.md +++ b/doc/xml-format.md @@ -1,147 +1,305 @@ -This doc is a rough spec of our xml format +# edX xml format tutorial -Every content element (within a course) should have a unique id. This id is formed as {category}/{url_name}. Categories are the different tag types ('chapter', 'problem', 'html', 'sequential', etc). Url_name is a string containing a-z, A-Z, dot (.) and _. This is what appears in urls that point to this object. +## Goals of this document -File layout: +* This was written assuming the reader has no prior programming/CS knowledge and has jumped cold turkey into the edX platform. +* To educate the reader on how to build and maintain the back end structure of the course content. This is important for debugging and standardization. +* After reading this, you should be able to add content to a course and make sure it shows up in the courseware and does not break the code. +* __Prerequisites:__ it would be helpful to know a little bit about xml. Here is a [simple example](http://www.ultraslavonic.info/intro-to-xml/) if you've never seen it before. -- Xml files have content -- "policy", which is also called metadata in various places, should live in a policy file. +## Outline -- each module (except customtag and course, which are special, see below) should live in a file, located at {category}/{url_name].xml -To include this module in another one (e.g. to put a problem in a vertical), put in a "pointer tag": <{category} url_name="{url_name}"/>. When we read that, we'll load the actual contents. +* First, we will show a sample course structure as a case study/model of how xml and files in a course are organized to introductory understanding. -Customtag is already a pointer, you can just use it in place: +* More technical details are below, including discussion of some special cases. -Course tags: - - the top level course pointer tag lives in course.xml - - have 2 extra required attributes: "org" and "course" -- organization name, and course name. Note that the course name is referring to the platonic ideal of this course, not to any particular run of this course. The url_name should be particular run of this course. E.g. -If course.xml contains: - +## Introduction -we would load the actual course definition from course/2012.xml +* The course is organized hierarchically. We start by describing course-wide parameters, then break the course into chapters, and then go deeper and deeper until we reach a specific pset, video, etc. -To support multiple different runs of the course, you could have a different course.xml, containing +* You could make an analogy to finding a green shirt in your house - front door -> bedroom -> closet -> drawer -> shirts -> green shirt - -which would load the Harvard-internal version from course/2012H.xml +## Case Study -If there is only one run of the course for now, just have a single course.xml with the right url_name. +Let's jump right in by looking at the directory structure of a very simple toy course: -If there is more than one run of the course, the different course root pointer files should live in -roots/url_name.xml, and course.xml should be a symbolic link to the one you want to run in your dev instance. + toy/ + course + course.xml + problem + policies + roots -If you want to run both versions, you need to checkout the repo twice, and have course.xml point to different root/{url_name}.xml files. +The only top level file is `course.xml`, which should contain one line, looking something like this: -Policies: - - the policy for a course url_name lives in policies/{url_name}.json + -The format is called "json", and is best shown by example (though also feel free to google :) +This gives all the information to uniquely identify a particular run of any course--which organization is producing the course, what the course name is, and what "run" this is, specified via the `url_name` attribute. -the file is a dictionary (mapping from keys to values, syntax "{ key : value, key2 : value2, etc}" +Obviously, this doesn't actually specify any of the course content, so we need to find that next. To know where to look, you need to know the standard organizational structure of our system: _course elements are uniquely identified by the combination `(category, url_name)`_. In this case, we are looking for a `course` element with the `url_name` "2012_Fall". The definition of this element will be in `course/2012_Fall.xml`. Let's look there next: -Keys are in the form "{category}/{url_name}", which should uniquely id a content element. -Values are dictionaries of the form {"metadata-key" : "metadata-value"}. +`course/2012_Fall.xml` -metadata can also live in the xml files, but anything defined in the policy file overrides anything in the xml. This is primarily for backwards compatibility, and you should probably not use both. If you do leave some metadata tags in the xml, please be consistent (e.g. if display_names stay in xml, they should all stay in xml). - - note, some xml attributes are not metadata. e.g. in