diff --git a/.pylintrc b/.pylintrc index 2f2be69eb0..49fcf80eb9 100644 --- a/.pylintrc +++ b/.pylintrc @@ -35,6 +35,7 @@ load-plugins= # it should appear only once). disable= # C0301: Line too long +# C0302: Too many lines in module # W0141: Used builtin function 'map' # W0142: Used * or ** magic # R0201: Method could be a function @@ -42,8 +43,11 @@ disable= # R0902: Too many instance attributes # R0903: Too few public methods (1/2) # R0904: Too many public methods +# R0911: Too many return statements +# R0912: Too many branches # R0913: Too many arguments - C0301,W0141,W0142,R0201,R0901,R0902,R0903,R0904,R0913 +# R0914: Too many local variables + C0301,C0302,W0141,W0142,R0201,R0901,R0902,R0903,R0904,R0911,R0912,R0913,R0914 [REPORTS] @@ -92,7 +96,7 @@ zope=no # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E0201 when accessed. Python regular # expressions are accepted. -generated-members=REQUEST,acl_users,aq_parent,objects,DoesNotExist,can_read,can_write,get_url,size +generated-members=REQUEST,acl_users,aq_parent,objects,DoesNotExist,can_read,can_write,get_url,size,content [BASIC] diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index 1791d845cd..451ab96ca6 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -628,6 +628,113 @@ class ContentStoreTest(ModuleStoreTestCase): self.assertIn('markdown', context, "markdown is missing from context") self.assertNotIn('markdown', problem.editable_metadata_fields, "Markdown slipped into the editable metadata fields") + def test_cms_imported_course_walkthrough(self): + """ + Import and walk through some common URL endpoints. This just verifies non-500 and no other + correct behavior, so it is not a deep test + """ + import_from_xml(modulestore(), 'common/test/data/', ['simple']) + loc = Location(['i4x', 'edX', 'simple', 'course', '2012_Fall', None]) + resp = self.client.get(reverse('course_index', + kwargs={'org': loc.org, + 'course': loc.course, + 'name': loc.name})) + + self.assertEqual(200, resp.status_code) + self.assertContains(resp, 'Chapter 2') + + # go to various pages + + # import page + resp = self.client.get(reverse('import_course', + kwargs={'org': loc.org, + 'course': loc.course, + 'name': loc.name})) + self.assertEqual(200, resp.status_code) + + # export page + resp = self.client.get(reverse('export_course', + kwargs={'org': loc.org, + 'course': loc.course, + 'name': loc.name})) + self.assertEqual(200, resp.status_code) + + # manage users + resp = self.client.get(reverse('manage_users', + kwargs={'location': loc.url()})) + self.assertEqual(200, resp.status_code) + + # course info + resp = self.client.get(reverse('course_info', + kwargs={'org': loc.org, + 'course': loc.course, + 'name': loc.name})) + self.assertEqual(200, resp.status_code) + + # settings_details + resp = self.client.get(reverse('settings_details', + kwargs={'org': loc.org, + 'course': loc.course, + 'name': loc.name})) + self.assertEqual(200, resp.status_code) + + # settings_details + resp = self.client.get(reverse('settings_grading', + kwargs={'org': loc.org, + 'course': loc.course, + 'name': loc.name})) + self.assertEqual(200, resp.status_code) + + # static_pages + resp = self.client.get(reverse('static_pages', + kwargs={'org': loc.org, + 'course': loc.course, + 'coursename': loc.name})) + self.assertEqual(200, resp.status_code) + + # static_pages + resp = self.client.get(reverse('asset_index', + kwargs={'org': loc.org, + 'course': loc.course, + 'name': loc.name})) + self.assertEqual(200, resp.status_code) + + # go look at a subsection page + subsection_location = loc._replace(category='sequential', name='test_sequence') + resp = self.client.get(reverse('edit_subsection', + kwargs={'location': subsection_location.url()})) + self.assertEqual(200, resp.status_code) + + # go look at the Edit page + unit_location = loc._replace(category='vertical', name='test_vertical') + resp = self.client.get(reverse('edit_unit', + kwargs={'location': unit_location.url()})) + self.assertEqual(200, resp.status_code) + + # delete a component + del_loc = loc._replace(category='html', name='test_html') + resp = self.client.post(reverse('delete_item'), + json.dumps({'id': del_loc.url()}), "application/json") + self.assertEqual(200, resp.status_code) + + # delete a unit + del_loc = loc._replace(category='vertical', name='test_vertical') + resp = self.client.post(reverse('delete_item'), + json.dumps({'id': del_loc.url()}), "application/json") + self.assertEqual(200, resp.status_code) + + # delete a unit + del_loc = loc._replace(category='sequential', name='test_sequence') + resp = self.client.post(reverse('delete_item'), + json.dumps({'id': del_loc.url()}), "application/json") + self.assertEqual(200, resp.status_code) + + # delete a chapter + del_loc = loc._replace(category='chapter', name='chapter_2') + resp = self.client.post(reverse('delete_item'), + json.dumps({'id': del_loc.url()}), "application/json") + self.assertEqual(200, resp.status_code) + def test_import_metadata_with_attempts_empty_string(self): import_from_xml(modulestore(), 'common/test/data/', ['simple']) module_store = modulestore('direct') diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 8850f230eb..227379979e 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -120,6 +120,12 @@ def howitworks(request): else: return render_to_response('howitworks.html', {}) + +# static/proof-of-concept views +def ux_alerts(request): + return render_to_response('ux-alerts.html', {}) + + # ==== Views for any logged-in user ================================== diff --git a/cms/static/js/base.js b/cms/static/js/base.js index 6a582a45a6..4140beb2da 100644 --- a/cms/static/js/base.js +++ b/cms/static/js/base.js @@ -1,3 +1,5 @@ +if (!window.CmsUtils) window.CmsUtils = {}; + var $body; var $modal; var $modalCover; @@ -48,6 +50,10 @@ $(document).ready(function () { (e).preventDefault(); }); + // alerts/notifications - manual close + $('.action-alert-close, .alert.has-actions .nav-actions a').bind('click', hideAlert); + $('.action-notification-close').bind('click', hideNotification); + // nav - dropdown related $body.click(function (e) { $('.nav-dropdown .nav-item .wrapper-nav-sub').removeClass('is-shown'); @@ -87,7 +93,7 @@ $(document).ready(function () { $('a[rel*="view"][href^="#"]').bind('click', smoothScrollLink); // tender feedback window scrolling - $('a.show-tender').bind('click', smoothScrollTop); + $('a.show-tender').bind('click', window.CmsUtils.smoothScrollTop); // toggling footer additional support $('.cta-show-sock').bind('click', toggleSock); @@ -159,21 +165,24 @@ $(document).ready(function () { function smoothScrollLink(e) { (e).preventDefault(); - $.smoothScroll({ - offset: -200, - easing: 'swing', + $.smoothScroll({ + offset: -200, + easing: 'swing', speed: 1000, scrollElement: null, scrollTarget: $(this).attr('href') }); } -function smoothScrollTop(e) { +// On AWS instances, this base.js gets wrapped in a separate scope as part of Django static +// pipelining (note, this doesn't happen on local runtimes). So if we set it on window, +// when we can access it from other scopes (namely Course Advanced Settings). +window.CmsUtils.smoothScrollTop = function (e) { (e).preventDefault(); - $.smoothScroll({ - offset: -200, - easing: 'swing', + $.smoothScroll({ + offset: -200, + easing: 'swing', speed: 1000, scrollElement: null, scrollTarget: $('#view-top') @@ -483,9 +492,9 @@ function toggleSock(e) { $sock.toggleClass('is-shown'); $sockContent.toggle('fast'); - $.smoothScroll({ - offset: -200, - easing: 'swing', + $.smoothScroll({ + offset: -200, + easing: 'swing', speed: 1000, scrollElement: null, scrollTarget: $sock @@ -538,6 +547,17 @@ function removeDateSetter(e) { $block.find('.time').val(''); } + +function hideNotification(e) { + (e).preventDefault(); + $(this).closest('.wrapper-notification').removeClass('is-shown').addClass('is-hiding').attr('aria-hidden','true'); +} + +function hideAlert(e) { + (e).preventDefault(); + $(this).closest('.wrapper-alert').removeClass('is-shown'); +} + function showToastMessage(message, $button, lifespan) { var $toast = $('
'); var $closeBtn = $('×'); @@ -839,4 +859,4 @@ function saveSetSectionScheduleDate(e) { hideModal(); }); -} \ No newline at end of file +} diff --git a/cms/static/js/views/settings/advanced_view.js b/cms/static/js/views/settings/advanced_view.js index 52c5ed78d0..c1392831b8 100644 --- a/cms/static/js/views/settings/advanced_view.js +++ b/cms/static/js/views/settings/advanced_view.js @@ -32,7 +32,7 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ var listEle$ = this.$el.find('.course-advanced-policy-list'); listEle$.empty(); - + // b/c we've deleted all old fields, clear the map and repopulate this.fieldToSelectorMap = {}; this.selectorToField = {}; @@ -101,13 +101,13 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ }); }, showMessage: function (type) { - this.$el.find(".message-status").removeClass("is-shown"); + $(".wrapper-alert").removeClass("is-shown"); if (type) { if (type === this.error_saving) { - this.$el.find(".message-status.error").addClass("is-shown"); + $(".wrapper-alert-error").addClass("is-shown").attr('aria-hidden','false'); } else if (type === this.successful_changes) { - this.$el.find(".message-status.confirm").addClass("is-shown"); + $(".wrapper-alert-confirmation").addClass("is-shown").attr('aria-hidden','false'); this.hideSaveCancelButtons(); } } @@ -117,17 +117,20 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ } }, showSaveCancelButtons: function(event) { - if (!this.buttonsVisible) { + if (!this.notificationBarShowing) { this.$el.find(".message-status").removeClass("is-shown"); - $('.wrapper-notification').addClass('is-shown'); - this.buttonsVisible = true; + $('.wrapper-notification').removeClass('is-hiding').addClass('is-shown').attr('aria-hidden','false'); + this.notificationBarShowing = true; } }, hideSaveCancelButtons: function() { - $('.wrapper-notification').removeClass('is-shown'); - this.buttonsVisible = false; + if (this.notificationBarShowing) { + $('.wrapper-notification').removeClass('is-shown').addClass('is-hiding').attr('aria-hidden','true'); + this.notificationBarShowing = false; + } }, saveView : function(event) { + window.CmsUtils.smoothScrollTop(event); // TODO one last verification scan: // call validateKey on each to ensure proper format // check for dupes @@ -146,6 +149,7 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ }); }, revertView : function(event) { + event.preventDefault(); var self = event.data; self.model.deleteKeys = []; self.model.clear({silent : true}); @@ -158,7 +162,7 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ var newKeyId = _.uniqueId('policy_key_'), newEle = this.template({ key : key, value : JSON.stringify(value, null, 4), keyUniqueId: newKeyId, valueUniqueId: _.uniqueId('policy_value_')}); - + this.fieldToSelectorMap[key] = newKeyId; this.selectorToField[newKeyId] = key; return newEle; @@ -169,4 +173,4 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ blurInput : function(event) { $(event.target).prev().removeClass("is-focused"); } -}); \ No newline at end of file +}); diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index cda99b676c..73519b812d 100644 --- a/cms/static/sass/_base.scss +++ b/cms/static/sass/_base.scss @@ -25,7 +25,7 @@ a { @include transition(color 0.25s ease-in-out); &:hover { - color: #cb9c40; + color: $orange-d1; } } @@ -50,11 +50,72 @@ h1 { // ==================== +// typography - basic +.title-1, .title-2, .title-3, .title-4, .title-5, .title-6 { + font-weight: 600; + color: $gray-d3; + margin: 0; + padding: 0; +} + +.title-1 { + @include font-size(32); + margin-bottom: ($baseline*1.5); +} + +.title-2 { + @include font-size(24); + margin-bottom: $baseline; +} + +.title-3 { + @include font-size(18); + margin-bottom: ($baseline/2); +} + +.title-4 { + @include font-size(14); + margin-bottom: $baseline; + font-weight: 500 +} + +.title-5 { + @include font-size(14); + color: $gray-l1; + margin-bottom: $baseline; + font-weight: 500 +} + +.title-6 { + @include font-size(14); + color: $gray-l2; + margin-bottom: $baseline; + font-weight: 500 +} + +p, ul, ol, dl { + margin-bottom: ($baseline/2); + + &:last-child { + margin-bottom: 0; + } +} + +// ==================== + +// layout - basic +.wrapper-view { + +} + +// ==================== + // layout - basic page header .wrapper-mast { + margin: ($baseline*1.5) 0 0 0; padding: 0 $baseline; position: relative; - + .mast, .metadata { @include clearfix(); @include font-size(16); @@ -62,7 +123,7 @@ h1 { max-width: $fg-max-width; min-width: $fg-min-width; width: flex-grid(12); - margin: ($baseline*1.5) auto $baseline auto; + margin: 0 auto $baseline auto; color: $gray-d2; } @@ -284,18 +345,33 @@ h1 { margin: 0 0 ($baseline/2) 0; } - .title-4 { + header { + @include clearfix(); - } - - .title-5 { + .title-2 { + width: flex-grid(5, 12); + margin: 0 flex-gutter() 0 0; + float: left; + } + .tip { + @include font-size(13); + width: flex-grid(7, 12); + float: right; + margin-top: ($baseline/2); + text-align: right; + color: $gray-l2; + } } } // layout - supplemental content .content-supplementary { + > section { + margin: 0 0 $baseline 0; + } + .bit { @include font-size(13); margin: 0 0 $baseline 0; @@ -761,10 +837,10 @@ body.js { // ==================== -// works in progress +// works in progress & testing body.hide-wip { .wip-box { display: none; } -} \ No newline at end of file +} diff --git a/cms/static/sass/_cms_mixins.scss b/cms/static/sass/_cms_mixins.scss index a25a07cb73..c11f81f79e 100644 --- a/cms/static/sass/_cms_mixins.scss +++ b/cms/static/sass/_cms_mixins.scss @@ -15,17 +15,17 @@ // mixins - grandfathered @mixin button { display: inline-block; - padding: 4px 20px 6px; - font-size: 14px; + padding: ($baseline/5) $baseline ($baseline/4); + @include font-size(14); font-weight: 700; @include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset, 0 0 0 rgba(0, 0, 0, 0)); @include transition(background-color .15s, box-shadow .15s); &.disabled { - border: 1px solid $lightGrey !important; + border: 1px solid $gray-l1 !important; border-radius: 3px !important; - background: $lightGrey !important; - color: $darkGrey !important; + background: $gray-l1 !important; + color: $gray-d1 !important; pointer-events: none; cursor: none; &:hover { @@ -38,32 +38,111 @@ } } -@mixin blue-button { +@mixin green-button { @include button; - border: 1px solid #437fbf; + border: 1px solid $green-d1; border-radius: 3px; @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); - background-color: $blue; - color: #fff; + background-color: $green; + @include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset); + color: $white; - &:hover, &.active { - background-color: #62aaf5; - color: #fff; + &:hover { + background-color: $green-s1; + color: $white; + } + + &.disabled { + border: 1px solid $green-l3 !important; + background: $green-l3 !important; + color: $white !important; + @include box-shadow(none); } } -@mixin green-button { - @include button; - border: 1px solid #0d7011; - border-radius: 3px; - @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); - background-color: $green; - color: #fff; +@mixin blue-button { + @include button; + border: 1px solid $blue-d1; + border-radius: 3px; + @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); + background-color: $blue; + color: $white; - &:hover { - background-color: #129416; - color: #fff; - } + &:hover, &.active { + background-color: $blue-s2; + color: $white; + } + + &.disabled { + border: 1px solid $blue-l3 !important; + background: $blue-l3 !important; + color: $white !important; + @include box-shadow(none); + } +} + +@mixin red-button { + @include button; + border: 1px solid $red-d1; + border-radius: 3px; + @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); + background-color: $red; + color: $white; + + &:hover, &.active { + background-color: $red-s1; + color: $white; + } + + &.disabled { + border: 1px solid $red-l3 !important; + background: $red-l3 !important; + color: $white !important; + @include box-shadow(none); + } +} + +@mixin pink-button { + @include button; + border: 1px solid $pink-d1; + border-radius: 3px; + @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); + background-color: $pink; + color: $white; + + &:hover, &.active { + background-color: $pink-s1; + color: $white; + } + + &.disabled { + border: 1px solid $pink-l3 !important; + background: $pink-l3 !important; + color: $white !important; + @include box-shadow(none); + } +} + +@mixin orange-button { + @include button; + border: 1px solid $orange-d1; + border-radius: 3px; + @include linear-gradient(top, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0) 60%); + background-color: $orange; + @include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset); + color: $gray-d2; + + &:hover { + background-color: $orange-s2; + color: $gray-d2; + } + + &.disabled { + border: 1px solid $orange-l3 !important; + background: $orange-l2 !important; + color: $gray-l1 !important; + @include box-shadow(none); + } } @mixin white-button { @@ -82,24 +161,9 @@ } } -@mixin orange-button { - @include button; - border: 1px solid #bda046; - border-radius: 3px; - @include linear-gradient(top, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0) 60%); - background-color: #edbd3c; - @include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset); - color: #3c3c3c; - - &:hover { - background-color: #ffcd46; - color: #3c3c3c; - } -} - @mixin grey-button { @include button; - border: 1px solid $darkGrey; + border: 1px solid $gray-d2; border-radius: 3px; @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); background-color: #d1dae3; @@ -127,39 +191,17 @@ } } -@mixin green-button { - @include button; - border: 1px solid $darkGreen; - border-radius: 3px; - @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); - background-color: $green; - @include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset); - color: #fff; - - &:hover { - background-color: $brightGreen; - color: #fff; - } - - &.disabled { - border: 1px solid $disabledGreen !important; - background: $disabledGreen !important; - color: #fff !important; - @include box-shadow(none); - } -} - @mixin dark-grey-button { @include button; - border: 1px solid #1c1e20; + border: 1px solid $gray-d2; border-radius: 3px; - background: -webkit-linear-gradient(top, rgba(255, 255, 255, .2), rgba(255, 255, 255, 0)) $extraDarkGrey; + background: -webkit-linear-gradient(top, rgba(255, 255, 255, .2), rgba(255, 255, 255, 0)) $gray-d1; box-shadow: 0 1px 0 rgba(255, 255, 255, .2) inset; - color: #fff; + color: $white; &:hover { - background-color: #595f64; - color: #fff; + background-color: $gray-d4; + color: $white; } } @@ -180,7 +222,7 @@ } textarea { - min-height: 80px; + min-height: 80px; } h5 { @@ -225,7 +267,7 @@ .section-item { position: relative; display: block; - padding: 6px 8px 8px 16px; + padding: 6px 8px 8px 16px; background: #edf1f5; font-size: 13px; @@ -296,6 +338,9 @@ } } +// ==================== + +// sunsetted mixins @mixin active { @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); background-color: rgba(255, 255, 255, .3); @@ -389,4 +434,4 @@ .depth2 { z-index: 100; } .depth3 { z-index: 1000; } .depth4 { z-index: 10000; } -.depth5 { z-index: 100000; } \ No newline at end of file +.depth5 { z-index: 100000; } diff --git a/cms/static/sass/_variables.scss b/cms/static/sass/_variables.scss index 41c9e57d54..4c35bfad32 100644 --- a/cms/static/sass/_variables.scss +++ b/cms/static/sass/_variables.scss @@ -113,7 +113,7 @@ $green-u1: desaturate($green,15%); $green-u2: desaturate($green,30%); $green-u3: desaturate($green,45%); -$yellow: rgb(231, 214, 143); +$yellow: rgb(237, 189, 60); $yellow-l1: tint($yellow,20%); $yellow-l2: tint($yellow,40%); $yellow-l3: tint($yellow,60%); @@ -149,8 +149,13 @@ $orange-u3: desaturate($orange,45%); $shadow: rgba(0,0,0,0.2); $shadow-l1: rgba(0,0,0,0.1); +$shadow-l2: rgba(0,0,0,0.05); $shadow-d1: rgba(0,0,0,0.4); + +// specific UI +$notification-height: ($baseline*10); + // colors - inherited $baseFontColor: $gray-d2; $offBlack: #3c3c3c; @@ -167,4 +172,4 @@ $disabledGreen: rgb(124, 206, 153); $darkGreen: rgb(52, 133, 76); $lightBluishGrey: rgb(197, 207, 223); $lightBluishGrey2: rgb(213, 220, 228); -$error-red: rgb(253, 87, 87); \ No newline at end of file +$error-red: rgb(253, 87, 87); diff --git a/cms/static/sass/assets/_keyframes.scss b/cms/static/sass/assets/_keyframes.scss index 7661f18980..a756f66b2e 100644 --- a/cms/static/sass/assets/_keyframes.scss +++ b/cms/static/sass/assets/_keyframes.scss @@ -1,7 +1,133 @@ -@mixin bounce-in { +// studio animations & keyframes +// ==================== + +// rotate clockwise +@mixin rotateClockwise { + 0% { + @include transform(rotate(0deg)); + } + + 100% { + @include transform(rotate(360deg)); + } +} + +@-moz-keyframes rotateClockwise { @include rotateClockwise(); } +@-webkit-keyframes rotateClockwise { @include rotateClockwise(); } +@-o-keyframes rotateClockwise { @include rotateClockwise(); } +@keyframes rotateClockwise { @include rotateClockwise();} + +@mixin anim-rotateClockwise($duration, $timing: ease-in-out, $count: 1, $delay: 0) { + @include animation-name(rotateClockwise); + @include animation-duration($duration); + @include animation-delay($delay); + @include animation-timing-function($timing); + @include animation-iteration-count($count); + @include animation-fill-mode(both); + +} + +// ==================== + +// notifications slide up +@mixin notificationsSlideUp { + 0% { + @include transform(translateY(0)); + } + + 90% { + @include transform(translateY(-($notification-height))); + } + + 100% { + @include transform(translateY(-($notification-height*0.99))); + } +} + +@-moz-keyframes notificationsSlideUp { @include notificationsSlideUp(); } +@-webkit-keyframes notificationsSlideUp { @include notificationsSlideUp(); } +@-o-keyframes notificationsSlideUp { @include notificationsSlideUp(); } +@keyframes notificationsSlideUp { @include notificationsSlideUp();} + +@mixin anim-notificationsSlideUp($duration, $timing: ease-in-out, $count: 1, $delay: 0) { + @include animation-name(notificationsSlideUp); + @include animation-duration($duration); + @include animation-delay($delay); + @include animation-timing-function($timing); + @include animation-iteration-count($count); + @include animation-fill-mode(both); + +} + +// ==================== + +// notifications slide down +@mixin notificationsSlideDown { + 0% { + @include transform(translateY(-($notification-height*0.99))); + } + + 10% { + @include transform(translateY(-($notification-height))); + } + + 100% { + @include transform(translateY(0)); + } +} + +@-moz-keyframes notificationsSlideDown { @include notificationsSlideDown(); } +@-webkit-keyframes notificationsSlideDown { @include notificationsSlideDown(); } +@-o-keyframes notificationsSlideDown { @include notificationsSlideDown(); } +@keyframes notificationsSlideDown { @include notificationsSlideDown();} + +@mixin anim-notificationsSlideDown($duration, $timing: ease-in-out, $count: 1, $delay: 0) { + @include animation-name(notificationsSlideDown); + @include animation-duration($duration); + @include animation-delay($delay); + @include animation-timing-function($timing); + @include animation-iteration-count($count); + @include animation-fill-mode(both); +} + +// ==================== + +// notifications slide up then down +@mixin notificationsSlideUpDown { + 0%, 100% { + @include transform(translateY(0)); + } + + 15%, 85% { + @include transform(translateY(-($notification-height))); + } + + 20%, 80% { + @include transform(translateY(-($notification-height*0.99))); + } +} + +@-moz-keyframes notificationsSlideUpDown { @include notificationsSlideUpDown(); } +@-webkit-keyframes notificationsSlideUpDown { @include notificationsSlideUpDown(); } +@-o-keyframes notificationsSlideUpDown { @include notificationsSlideUpDown(); } +@keyframes notificationsSlideUpDown { @include notificationsSlideUpDown();} + +@mixin anim-notificationsSlideUpDown($duration, $timing: ease-in-out, $count: 1, $delay: 0) { + @include animation-name(notificationsSlideUpDown); + @include animation-duration($duration); + @include animation-delay($delay); + @include animation-timing-function($timing); + @include animation-iteration-count($count); + @include animation-fill-mode(both); +} + +// ==================== + +// bounce in +@mixin bounceIn { 0% { opacity: 0; - @include transform(scale(.3)); + @include transform(scale(0.3)); } 50% { @@ -14,14 +140,63 @@ } } -@-moz-keyframes bounce-in { @include bounce-in(); } -@-webkit-keyframes bounce-in { @include bounce-in(); } -@-o-keyframes bounce-in { @include bounce-in(); } -@keyframes bounce-in { @include bounce-in();} +@-moz-keyframes bounceIn { @include bounceIn(); } +@-webkit-keyframes bounceIn { @include bounceIn(); } +@-o-keyframes bounceIn { @include bounceIn(); } +@keyframes bounceIn { @include bounceIn();} -@mixin bounce-in-animation($duration, $timing: ease-in-out) { - @include animation-name(bounce-in); +@mixin anim-bounceIn($duration, $timing: ease-in-out, $count: 1, $delay: 0) { + @include animation-name(bounceIn); @include animation-duration($duration); + @include animation-delay($delay); @include animation-timing-function($timing); + @include animation-iteration-count($count); @include animation-fill-mode(both); } + +// ==================== + +// bounce in +@mixin bounceOut { + 0% { + opacity: 0; + @include transform(scale(0.3)); + } + + 50% { + opacity: 1; + @include transform(scale(1.05)); + } + + 100% { + @include transform(scale(1)); + } + + 0% { + @include transform(scale(1)); + } + + 50% { + opacity: 1; + @include transform(scale(1.05)); + } + + 100% { + opacity: 0; + @include transform(scale(0.3)); + } +} + +@-moz-keyframes bounceOut { @include bounceOut(); } +@-webkit-keyframes bounceOut { @include bounceOut(); } +@-o-keyframes bounceOut { @include bounceOut(); } +@keyframes bounceOut { @include bounceOut();} + +@mixin anim-bounceOut($duration, $timing: ease-in-out, $count: 1, $delay: 0) { + @include animation-name(bounceOut); + @include animation-duration($duration); + @include animation-delay($delay); + @include animation-timing-function($timing); + @include animation-iteration-count($count); + @include animation-fill-mode(both); +} \ No newline at end of file diff --git a/cms/static/sass/base-style.scss b/cms/static/sass/base-style.scss index 1bf9329b36..a355b3a03f 100644 --- a/cms/static/sass/base-style.scss +++ b/cms/static/sass/base-style.scss @@ -4,6 +4,7 @@ // bourbon libs and resets @import 'bourbon/bourbon'; @import 'bourbon/addons/button'; +@import "variables"; @import 'vendor/normalize'; @import 'reset'; diff --git a/cms/static/sass/elements/_alerts.scss b/cms/static/sass/elements/_alerts.scss index 9c15f811e0..49aa015313 100644 --- a/cms/static/sass/elements/_alerts.scss +++ b/cms/static/sass/elements/_alerts.scss @@ -1,135 +1,761 @@ -// studio - elements - alerts, notifications, prompts +// studio alerts, prompts and notifications +// ==================== + +// shared +.wrapper-notification, .wrapper-alert, .prompt { + @include box-sizing(border-box); + + .copy { + @include font-size(13); + } +} + +.wrapper-notification, .wrapper-alert, .prompt { + background: $gray-d3; + + .copy { + color: $gray-l2; + + .title { + color: $white; + } + + .nav-actions { + + .action-primary { + color: $gray-d4; + } + } + } +} + +.alert, .notification, .prompt { + + // types - confirm + &.confirm { + + .nav-actions .action-primary { + @include blue-button(); + border-color: $blue-d2; + } + + a { + color: $blue; + + &:hover { + color: $blue-s2; + } + } + } + + // types - warning + &.warning { + + .nav-actions .action-primary { + @include orange-button(); + border-color: $orange-d2; + color: $gray-d4; + } + + a { + color: $orange; + + &:hover { + color: $orange-s2; + } + } + } + + // types - error + &.error { + + .nav-actions .action-primary { + @include red-button(); + border-color: $red-d2; + } + + a { + color: $red-l1; + + &:hover { + color: $red; + } + } + } + + // types - announcement + &.announcement { + + .nav-actions .action-primary { + @include blue-button(); + border-color: $blue-d2; + } + + a { + color: $blue; + + &:hover { + color: $blue-s2; + } + } + } + + // types - confirmation + &.confirmation { + + .nav-actions .action-primary { + @include green-button(); + border-color: $green-d2; + } + + a { + color: $green; + + &:hover { + color: $green-s2; + } + } + } + + // types - step required + &.step-required { + + .nav-actions .action-primary { + border-color: $pink-d2; + @include pink-button(); + } + + a { + color: $pink; + + &:hover { + color: $pink-s1; + } + } + } +} + +// prompts +.wrapper-prompt { + @include transition(all 0.05s ease-in-out); + position: fixed; + top: 0; + background: $black-t0; + width: 100%; + height: 100%; + text-align: center; + z-index: 10000; + + &:before { + content: ''; + display: inline-block; + height: 100%; + vertical-align: middle; + margin-right: -0.25em; /* Adjusts for spacing */ + } + + .prompt { + @include border-radius(($baseline/5)); + @include box-shadow(0 0 3px $shadow-d1); + display: inline-block; + vertical-align: middle; + width: $baseline*17.5; + border: 4px solid $black; + text-align: left; + + .copy { + border-top: 4px solid $blue; + padding: $baseline; + } + + .nav-actions { + @include box-shadow(inset 0 1px 2px $shadow-d1); + border-top: 1px solid $black-t1; + padding: ($baseline*0.75) $baseline; + background: $gray-d4; + + .nav-item { + display: inline-block; + margin-right: ($baseline*0.75); + + &:last-child { + margin-right: 0; + } + } + + .action-primary { + @include font-size(13); + font-weight: 600; + } + + .action-secondary { + @include font-size(13); + } + } + } + + // types of prompts - error + .prompt.error { + + .icon-error { + color: $red-l1; + } + + .copy { + border-top-color: $red-l1; + } + } + + // types of prompts - confirmation + .prompt.confirmation { + + .icon-error { + color: $green; + } + + .copy { + border-top-color: $green; + } + } + + // types of prompts - error + .prompt.warning { + + .icon-warning { + color: $orange; + } + + .copy { + border-top-color: $orange; + } + } +} + // ==================== // notifications .wrapper-notification { - @include clearfix(); - @include box-sizing(border-box); - @include transition (bottom 2.0s ease-in-out 5s); - @include box-shadow(0 -1px 2px rgba(0,0,0,0.1)); - position: fixed; - bottom: -100px; - z-index: 1000; - width: 100%; - overflow: hidden; - opacity: 0; - border-top: 1px solid $darkGrey; - padding: 20px 40px; + @include clearfix(); + @include box-shadow(0 -1px 3px $shadow, inset 0 3px 1px $blue); + position: fixed; + bottom: 0; + z-index: 1000; + width: 100%; + padding: $baseline ($baseline*2); - &.is-shown { - bottom: 0; - opacity: 1.0; - } + &.wrapper-notification-warning { + @include box-shadow(0 -1px 3px $shadow, inset 0 3px 1px $orange); - &.wrapper-notification-warning { - border-color: shade($yellow, 25%); - background: tint($yellow, 25%); - } + .icon-warning { + color: $orange; + } + } - &.wrapper-notification-error { - border-color: shade($red, 50%); - background: tint($red, 20%); - color: $white; - } + &.wrapper-notification-error { + @include box-shadow(0 -1px 3px $shadow, inset 0 3px 1px $red-l1); - &.wrapper-notification-confirm { - border-color: shade($green, 30%); - background: tint($green, 40%); - color: shade($green, 30%); - } + .icon-error { + color: $red-l1; + } + } + + &.wrapper-notification-confirmation { + @include box-shadow(0 -1px 3px $shadow, inset 0 3px 1px $green); + + .icon-confirmation { + color: $green; + } + } + + &.wrapper-notification-saving { + @include box-shadow(0 -1px 3px $shadow, inset 0 3px 1px $pink); + } + + // shorter/status notifications + &.wrapper-notification-status { + @include border-top-radius(3px); + width: ($baseline*8); + right: ($baseline); + border: 4px solid $black; + border-bottom: none; + padding: ($baseline/2) $baseline; + + .notification { + @include box-sizing(border-box); + @include clearfix(); + width: 100%; + max-width: none; + min-width: none; + + .icon, .copy { + float: none; + display: inline-block; + vertical-align: middle; + } + + .icon { + width: $baseline; + height: ($baseline*1.25); + margin-right: ($baseline*0.75); + line-height: 3rem; + } + + .copy { + + } + } + } + + // help notifications + &.wrapper-notification-help { + @include border-top-radius(3px); + width: ($baseline*14); + right: ($baseline); + border: 4px solid $black; + border-bottom: none; + padding: $baseline; + + .notification { + @include box-sizing(border-box); + @include clearfix(); + width: 100%; + max-width: none; + min-width: none; + + .icon-help { + width: $baseline; + margin-right: ($baseline*0.75); + } + + .action-notification-close { + right: 0; + } + + .copy { + width: ($baseline*10); + } + } + } } .notification { - @include box-sizing(border-box); - margin: 0 auto; - width: flex-grid(12); - max-width: $fg-max-width; - min-width: $fg-min-width; + @include box-sizing(border-box); + @include clearfix(); + margin: 0 auto; + width: flex-grid(12); + max-width: $fg-max-width; + min-width: $fg-min-width; - .copy { - float: left; - width: flex-grid(9, 12); - margin-right: flex-gutter(); - margin-top: 5px; - font-size: 14px; + strong { + font-weight: 700; + } - .icon { - display: inline-block; - vertical-align: top; - margin-right: 5px; - font-size: 20px; - } + .icon, .copy { + float: left; + display: inline-block; + vertical-align: middle; + } - p { - width: flex-grid(8, 9); - display: inline-block; - vertical-align: top; - } - } + .icon { + @include transition (color 0.5s ease-in-out); + @include font-size(22); + width: flex-grid(1, 12); + height: ($baseline*1.25); + margin-right: flex-gutter(); + text-align: right; + color: $white; + } - .actions { - float: right; - width: flex-grid(3, 12); - margin-top: ($baseline/2); - text-align: right; + .copy { + @include font-size(13); + width: flex-grid(10, 12); + color: $gray-l2; - li { - display: inline-block; - vertical-align: middle; - margin-right: 10px; + .title { + @include font-size(14); + margin-bottom: 0; + color: $white; + } + } - &:last-child { - margin-right: 0; - } - } + // with actions + &.has-actions { - .save-button { - @include blue-button; - } + .icon { + width: flex-grid(1, 12); + } - .cancel-button { - @include white-button; - } - } + .copy { + width: flex-grid(7, 12); + margin-right: flex-gutter(); + } - strong { - font-weight: 700; - } + .nav-actions { + width: flex-grid(4, 12); + float: right; + margin-top: ($baseline/4); + text-align: right; + + .nav-item { + display: inline-block; + vertical-align: middle; + margin-right: ($baseline/2); + + &:last-child { + margin-right: 0; + } + } + } + + .action-primary { + @include blue-button(); + @include font-size(13); + border-color: $blue-d2; + font-weight: 600; + } + + .action-secondary { + @include font-size(13); + } + } + + &.confirmation { + + .copy { + margin-top: ($baseline/5); + } + } + + &.saving { + + .icon-saving { + @include anim-rotateClockwise(3s, linear, infinite); + width: 22px; + } + + .copy p { + @include text-sr(); + } + } +} + +// ==================== + +// alerts +.wrapper-alert { + @include box-sizing(border-box); + @include box-shadow(0 1px 1px $white, inset 0 2px 2px $shadow-d1, inset 0 -4px 1px $blue); + position: relative; + z-index: 100; + overflow: hidden; + width: 100%; + border-top: 1px solid $black; + padding: $baseline ($baseline*2) ($baseline*1.5) ($baseline*2); + background: $gray-d3; + + // needed since page load is very slow + display: none; + + // needed since page load is very slow + &.is-shown { + display: block; + } + + &.wrapper-alert-warning { + @include box-shadow(0 1px 1px $white, inset 0 2px 2px $shadow-d1, inset 0 -4px 1px $orange); + + .icon-warning { + color: $orange; + } + } + + &.wrapper-alert-error { + @include box-shadow(0 1px 1px $white, inset 0 2px 2px $shadow-d1, inset 0 -4px 1px $red-l1); + + .icon-error { + color: $red-l1; + } + } + + &.wrapper-alert-confirmation { + @include box-shadow(0 1px 1px $white, inset 0 2px 2px $shadow-d1, inset 0 -4px 1px $green); + + .icon-confirmation { + color: $green; + } + } + + &.wrapper-alert-announcement { + @include box-shadow(0 1px 1px $white, inset 0 2px 2px $shadow-d1, inset 0 -4px 1px $blue); + + .icon-announcement { + color: $blue; + } + } + + &.wrapper-alert-step-required { + @include box-shadow(0 1px 1px $white, inset 0 2px 2px $shadow-d1, inset 0 -4px 1px $pink); + + .icon-step-required { + color: $pink; + } + } } // adopted alerts .alert { - padding: 15px 20px; - margin-bottom: 30px; - border-radius: 3px; - border: 1px solid #edbd3c; - border-radius: 3px; - background: #fbf6e1; - // background: #edbd3c; - font-size: 14px; - @include clearfix; + @include font-size(14); + @include box-sizing(border-box); + @include clearfix(); + margin: 0 auto; + width: flex-grid(12); + max-width: $fg-max-width; + min-width: $fg-min-width; + color: $white; - .alert-message { - float: left; - margin-top: 4px; - } + strong { + font-weight: 700; + } - strong { - font-weight: 700; - } + .icon, .copy { + float: left; + } - .alert-action { - float: right; + .icon { + @include transition (color 0.5s ease-in-out); + @include font-size(22); + width: flex-grid(1, 12); + margin: ($baseline/4) flex-gutter() 0 0; + text-align: right; + } - &.secondary { - @include orange-button; - } - } + .copy { + @include font-size(13); + width: flex-grid(10, 12); + color: $gray-l2; + + .title { + margin-bottom: 0; + color: $white; + } + } + + // with actions + &.has-actions { + + .icon { + width: flex-grid(1, 12); + } + + .copy { + width: flex-grid(7, 12); + margin-right: flex-gutter(); + } + + .nav-actions { + width: flex-grid(4, 12); + float: right; + margin-top: ($baseline/2); + text-align: right; + + .nav-item { + display: inline-block; + vertical-align: middle; + margin-right: ($baseline/2); + + &:last-child { + margin-right: 0; + } + + .action-primary { + @include font-size(13); + font-weight: 600; + } + + .action-secondary { + @include font-size(13); + } + } + } + } + + // with cancel + .action-alert-close { + @include border-bottom-radius(($baseline/5)); + position: absolute; + top: -($baseline/10); + right: $baseline; + padding: ($baseline/4) ($baseline/2) 0 ($baseline/2); + background: $gray-d4; + text-align: center; + + .label { + @include text-sr(); + } + + .icon { + @include font-size(14); + color: $white; + width: auto; + margin: 0; + padding: 2px; + } + + &:hover { + background: $gray-d1; + } + } +} + +// ==================== + +// js enabled +.js { + + // prompt set-up + .wrapper-prompt { + visibility: hidden; + pointer-events: none; + + .prompt { + opacity: 0; + } + } + + // prompt showing/hiding + &.prompt-is-shown { + + .wrapper-view { + -webkit-filter: blur(2px) grayscale(25%); + filter: blur(2px) grayscale(25%); + } + + .wrapper-prompt.is-shown { + visibility: visible; + pointer-events: auto; + + .prompt { + @include anim-bounceIn(0.5s); + opacity: 1.0; + } + } + } + + // alert showing/hiding + .wrapper-alert { + display: none; + + &.is-shown { + display: block; + } + } + + // notification showing/hiding + .wrapper-notification { + bottom: -($notification-height); + + // varying animations + &.is-shown { + @include anim-notificationsSlideUp(1s); + } + + &.is-hiding { + @include anim-notificationsSlideDown(0.25s); + } + + &.is-fleeting { + @include anim-notificationsSlideUpDown(2s); + } + } +} + +// ==================== + +// temporary +body.uxdesign.alerts { + + .content-primary, .content-supplementary { + @include box-sizing(border-box); + float: left; + } + + .content-primary { + @extend .window; + width: flex-grid(12, 12); + margin-right: flex-gutter(); + padding: $baseline ($baseline*1.5); + + > section { + margin-bottom: ($baseline*2); + + &:last-child { + margin-bottom: 0; + } + } + + ul { + + li { + @include clearfix(); + width: flex-grid(12, 12); + margin-bottom: ($baseline/4); + border-bottom: 1px solid $gray-l4; + padding-bottom: ($baseline/4); + + &:last-child { + margin-bottom: 0; + border-bottom: none; + padding-bottom: 0; + } + + a { + float: left; + width: flex-grid(5, 12); + margin-right: flex-gutter(); + } + } + } + } +} + +// ==================== + +// artifact styles +.main-wrapper { + .alert { + padding: 15px 20px; + margin-bottom: 30px; + border-radius: 3px; + border: 1px solid #edbd3c; + border-radius: 3px; + background: #fbf6e1; + // background: #edbd3c; + font-size: 14px; + @include clearfix; + + .alert-message { + float: left; + margin: 4px 0 0 0; + color: $gray-d3; + } + + strong { + font-weight: 700; + } + + .alert-action { + float: right; + + &.secondary { + @include orange-button; + } + } + } } body.error { - background: $darkGrey; - color: #3c3c3c; + background: $gray-d4; + color: $gray-d3; .primary-header { display: none; @@ -140,7 +766,7 @@ body.error { margin: 150px auto; padding: 60px 50px 90px; border-radius: 3px; - background: #fff; + background: $white; text-align: center; } @@ -148,8 +774,8 @@ body.error { float: none; margin: 0; font-size: 60px; - font-weight: 300; - color: #3c3c3c; + font-weight: 300; + color: $gray-d3; } .description { @@ -162,4 +788,4 @@ body.error { padding: 14px 40px 18px; font-size: 18px; } -} \ No newline at end of file +} diff --git a/cms/static/sass/elements/_header.scss b/cms/static/sass/elements/_header.scss index 00e2963630..6628d10c4c 100644 --- a/cms/static/sass/elements/_header.scss +++ b/cms/static/sass/elements/_header.scss @@ -5,12 +5,12 @@ margin: 0; padding: $baseline; border-bottom: 1px solid $gray; - @include box-shadow(0 1px 5px 0 rgba(0,0,0, 0.1)); + @include box-shadow(0 1px 5px 0 rgba(0,0,0, 0.2)); background: $white; height: 76px; position: relative; width: 100%; - z-index: 10; + z-index: 1000; a { color: $baseFontColor; diff --git a/cms/static/sass/views/_account.scss b/cms/static/sass/views/_account.scss index 2be94a81ea..9b38492921 100644 --- a/cms/static/sass/views/_account.scss +++ b/cms/static/sass/views/_account.scss @@ -4,7 +4,7 @@ body.signup, body.signin { .wrapper-content { - margin: 0; + margin: ($baseline*1.5) 0 0 0; padding: 0 $baseline; position: relative; width: 100%; @@ -18,7 +18,7 @@ body.signup, body.signin { width: flex-grid(12); margin: 0 auto; color: $gray-d2; - + header { position: relative; margin-bottom: $baseline; @@ -121,7 +121,7 @@ body.signup, body.signin { @include font-size(16); height: 100%; width: 100%; - padding: ($baseline/2); + padding: ($baseline/2); &.long { width: 100%; @@ -136,15 +136,15 @@ body.signup, body.signin { } :-moz-placeholder { - color: $gray-l3; + color: $gray-l3; } ::-moz-placeholder { - color: $gray-l3; + color: $gray-l3; } - :-ms-input-placeholder { - color: $gray-l3; + :-ms-input-placeholder { + color: $gray-l3; } &:focus { diff --git a/cms/templates/base.html b/cms/templates/base.html index 0809795f70..4517790622 100644 --- a/cms/templates/base.html +++ b/cms/templates/base.html @@ -49,20 +49,30 @@ - <%include file="widgets/header.html" /> - - <%block name="content"> - - % if user.is_authenticated(): - <%include file="widgets/sock.html" /> - % endif - - <%include file="widgets/footer.html" /> - <%include file="widgets/tender.html" /> + +
+ <%include file="widgets/header.html" /> + + <%block name="view_alerts"> + <%block name="view_banners"> + + <%block name="content"> + + % if user.is_authenticated(): + <%include file="widgets/sock.html" /> + % endif + + <%include file="widgets/footer.html" /> + <%include file="widgets/tender.html" /> + + <%block name="view_notifications"> +
+ + <%block name="view_prompts"> <%block name="jsextra"> diff --git a/cms/templates/index.html b/cms/templates/index.html index 8b14ea179a..916720f4e7 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -69,7 +69,7 @@
% if user.is_active: - +

edX Studio Help

- +

Studio Support

- +
-

Need help with Studio? Creating a course is complex, so we're here to help? Take advantage of our documentation, help center, as well as our edX101 introduction course for course authors.

+

Need help with Studio? Creating a course is complex, so we're here to help. Take advantage of our documentation, help center, as well as our edX101 introduction course for course authors.

- +
- +
-
- \ No newline at end of file + + diff --git a/cms/urls.py b/cms/urls.py index e1eae3352a..06569e4178 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -116,6 +116,8 @@ urlpatterns += ( url(r'^logout$', 'student.views.logout_user', name='logout'), + # static/proof-of-concept views + url(r'^ux-alerts$', 'contentstore.views.ux_alerts', name='ux-alerts') ) if settings.ENABLE_JASMINE: diff --git a/common/lib/xmodule/xmodule/modulestore/mongo.py b/common/lib/xmodule/xmodule/modulestore/mongo.py index dbc8b4506c..0ed93c9768 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo.py @@ -258,11 +258,11 @@ class MongoModuleStore(ModuleStoreBase): # get all collections in the course, this query should not return any leaf nodes # note this is a bit ugly as when we add new categories of containers, we have to add it here - query = { - '_id.org': location.org, - '_id.course': location.course, - '_id.category': {'$in': ['course', 'chapter', 'sequential', 'vertical']} - } + query = {'_id.org': location.org, + '_id.course': location.course, + '_id.category': {'$in': ['course', 'chapter', 'sequential', 'vertical', + 'wrapper', 'problemset', 'conditional']} + } # we just want the Location, children, and inheritable metadata record_filter = {'_id': 1, 'definition.children': 1} diff --git a/common/lib/xmodule/xmodule/modulestore/xml_importer.py b/common/lib/xmodule/xmodule/modulestore/xml_importer.py index 53eaebf850..97b3396baa 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml_importer.py +++ b/common/lib/xmodule/xmodule/modulestore/xml_importer.py @@ -284,7 +284,7 @@ def import_from_xml(store, data_dir, course_dirs=None, except KeyError: # Ignore any missing keys in _model_data pass - + if 'data' in content: module_data = content['data'] @@ -301,16 +301,18 @@ def import_from_xml(store, data_dir, course_dirs=None, # Note the dropped element closing tag. This causes the LMS to fail when rendering modules - that's # no good, so we have to do this kludge if isinstance(module_data, str) or isinstance(module_data, unicode): # some module 'data' fields are non strings which blows up the link traversal code - lxml_rewrite_links(module_data, lambda link: verify_content_links(module, course_data_path, - static_content_store, link, remap_dict)) + lxml_rewrite_links(module_data, + lambda link: verify_content_links(module, course_data_path, static_content_store, link, remap_dict)) for key in remap_dict.keys(): module_data = module_data.replace(key, remap_dict[key]) - except Exception, e: + except Exception: logging.exception("failed to rewrite links on {0}. Continuing...".format(module.location)) + else: + module_data = content - store.update_item(module.location, content) + store.update_item(module.location, module_data) if hasattr(module, 'children') and module.children != []: store.update_children(module.location, module.children) diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py index 49b854e1d7..5075507bce 100644 --- a/common/lib/xmodule/xmodule/peer_grading_module.py +++ b/common/lib/xmodule/xmodule/peer_grading_module.py @@ -199,8 +199,8 @@ class PeerGradingModule(PeerGradingFields, XModule): self.student_data_for_location = response score_dict = { - 'score': int(count_graded >= count_required), - 'total': self.max_grade, + 'score': int(count_graded >= count_required and count_graded>0) * int(self.weight), + 'total': self.max_grade * int(self.weight), } return score_dict diff --git a/common/lib/xmodule/xmodule/tests/test_export.py b/common/lib/xmodule/xmodule/tests/test_export.py index 443014f9ef..170a89d783 100644 --- a/common/lib/xmodule/xmodule/tests/test_export.py +++ b/common/lib/xmodule/xmodule/tests/test_export.py @@ -24,6 +24,11 @@ def strip_filenames(descriptor): """ print "strip filename from {desc}".format(desc=descriptor.location.url()) descriptor._model_data.pop('filename', None) + + if hasattr(descriptor, 'xml_attributes'): + if 'filename' in descriptor.xml_attributes: + del descriptor.xml_attributes['filename'] + for d in descriptor.get_children(): strip_filenames(d) diff --git a/common/static/sass/_mixins.scss b/common/static/sass/_mixins.scss index c1dd5b7f2d..493555b879 100644 --- a/common/static/sass/_mixins.scss +++ b/common/static/sass/_mixins.scss @@ -11,11 +11,15 @@ font-size: ($sizeValue/10) + rem; } +// ==================== + // line-height @function lh($amount: 1) { @return $body-line-height * $amount; } +// ==================== + // image-replacement hidden text @mixin text-hide() { text-indent: 100%; @@ -35,6 +39,8 @@ width: 1px; } +// ==================== + // vertical and horizontal centering @mixin vertically-and-horizontally-centered ($height, $width) { left: 50%; @@ -46,6 +52,8 @@ top: 150px; } +// ==================== + // sizing @mixin size($width: $baseline, $height: $baseline) { height: $height; @@ -56,6 +64,8 @@ @include size($size); } +// ==================== + // placeholder styling @mixin placeholder($color) { :-moz-placeholder { diff --git a/common/test/data/simple/course.xml b/common/test/data/simple/course.xml index 532a55792c..b86a1898f9 100644 --- a/common/test/data/simple/course.xml +++ b/common/test/data/simple/course.xml @@ -12,15 +12,15 @@ - +