Commit d9fa723e by Chris Dodge

Merge branch 'master' of github.com:MITx/mitx into fix/cdodge/export-draft-modules

Conflicts:
	common/lib/xmodule/xmodule/modulestore/xml_importer.py
parents 4cbb9253 276b86a7
...@@ -35,6 +35,7 @@ load-plugins= ...@@ -35,6 +35,7 @@ load-plugins=
# it should appear only once). # it should appear only once).
disable= disable=
# C0301: Line too long # C0301: Line too long
# C0302: Too many lines in module
# W0141: Used builtin function 'map' # W0141: Used builtin function 'map'
# W0142: Used * or ** magic # W0142: Used * or ** magic
# R0201: Method could be a function # R0201: Method could be a function
...@@ -42,8 +43,11 @@ disable= ...@@ -42,8 +43,11 @@ disable=
# R0902: Too many instance attributes # R0902: Too many instance attributes
# R0903: Too few public methods (1/2) # R0903: Too few public methods (1/2)
# R0904: Too many public methods # R0904: Too many public methods
# R0911: Too many return statements
# R0912: Too many branches
# R0913: Too many arguments # 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] [REPORTS]
...@@ -92,7 +96,7 @@ zope=no ...@@ -92,7 +96,7 @@ zope=no
# List of members which are set dynamically and missed by pylint inference # List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E0201 when accessed. Python regular # system, and so shouldn't trigger E0201 when accessed. Python regular
# expressions are accepted. # 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] [BASIC]
......
...@@ -120,6 +120,12 @@ def howitworks(request): ...@@ -120,6 +120,12 @@ def howitworks(request):
else: else:
return render_to_response('howitworks.html', {}) 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 ================================== # ==== Views for any logged-in user ==================================
......
if (!window.CmsUtils) window.CmsUtils = {};
var $body; var $body;
var $modal; var $modal;
var $modalCover; var $modalCover;
...@@ -48,6 +50,10 @@ $(document).ready(function () { ...@@ -48,6 +50,10 @@ $(document).ready(function () {
(e).preventDefault(); (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 // nav - dropdown related
$body.click(function (e) { $body.click(function (e) {
$('.nav-dropdown .nav-item .wrapper-nav-sub').removeClass('is-shown'); $('.nav-dropdown .nav-item .wrapper-nav-sub').removeClass('is-shown');
...@@ -87,7 +93,7 @@ $(document).ready(function () { ...@@ -87,7 +93,7 @@ $(document).ready(function () {
$('a[rel*="view"][href^="#"]').bind('click', smoothScrollLink); $('a[rel*="view"][href^="#"]').bind('click', smoothScrollLink);
// tender feedback window scrolling // tender feedback window scrolling
$('a.show-tender').bind('click', smoothScrollTop); $('a.show-tender').bind('click', window.CmsUtils.smoothScrollTop);
// toggling footer additional support // toggling footer additional support
$('.cta-show-sock').bind('click', toggleSock); $('.cta-show-sock').bind('click', toggleSock);
...@@ -168,7 +174,10 @@ function smoothScrollLink(e) { ...@@ -168,7 +174,10 @@ function smoothScrollLink(e) {
}); });
} }
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(); (e).preventDefault();
$.smoothScroll({ $.smoothScroll({
...@@ -538,6 +547,17 @@ function removeDateSetter(e) { ...@@ -538,6 +547,17 @@ function removeDateSetter(e) {
$block.find('.time').val(''); $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) { function showToastMessage(message, $button, lifespan) {
var $toast = $('<div class="toast-notification"></div>'); var $toast = $('<div class="toast-notification"></div>');
var $closeBtn = $('<a href="#" class="close-button">×</a>'); var $closeBtn = $('<a href="#" class="close-button">×</a>');
......
...@@ -101,13 +101,13 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -101,13 +101,13 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
}); });
}, },
showMessage: function (type) { showMessage: function (type) {
this.$el.find(".message-status").removeClass("is-shown"); $(".wrapper-alert").removeClass("is-shown");
if (type) { if (type) {
if (type === this.error_saving) { 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) { 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(); this.hideSaveCancelButtons();
} }
} }
...@@ -117,17 +117,20 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -117,17 +117,20 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
} }
}, },
showSaveCancelButtons: function(event) { showSaveCancelButtons: function(event) {
if (!this.buttonsVisible) { if (!this.notificationBarShowing) {
this.$el.find(".message-status").removeClass("is-shown"); this.$el.find(".message-status").removeClass("is-shown");
$('.wrapper-notification').addClass('is-shown'); $('.wrapper-notification').removeClass('is-hiding').addClass('is-shown').attr('aria-hidden','false');
this.buttonsVisible = true; this.notificationBarShowing = true;
} }
}, },
hideSaveCancelButtons: function() { hideSaveCancelButtons: function() {
$('.wrapper-notification').removeClass('is-shown'); if (this.notificationBarShowing) {
this.buttonsVisible = false; $('.wrapper-notification').removeClass('is-shown').addClass('is-hiding').attr('aria-hidden','true');
this.notificationBarShowing = false;
}
}, },
saveView : function(event) { saveView : function(event) {
window.CmsUtils.smoothScrollTop(event);
// TODO one last verification scan: // TODO one last verification scan:
// call validateKey on each to ensure proper format // call validateKey on each to ensure proper format
// check for dupes // check for dupes
...@@ -146,6 +149,7 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -146,6 +149,7 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
}); });
}, },
revertView : function(event) { revertView : function(event) {
event.preventDefault();
var self = event.data; var self = event.data;
self.model.deleteKeys = []; self.model.deleteKeys = [];
self.model.clear({silent : true}); self.model.clear({silent : true});
......
...@@ -25,7 +25,7 @@ a { ...@@ -25,7 +25,7 @@ a {
@include transition(color 0.25s ease-in-out); @include transition(color 0.25s ease-in-out);
&:hover { &:hover {
color: #cb9c40; color: $orange-d1;
} }
} }
...@@ -50,8 +50,69 @@ h1 { ...@@ -50,8 +50,69 @@ 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 // layout - basic page header
.wrapper-mast { .wrapper-mast {
margin: ($baseline*1.5) 0 0 0;
padding: 0 $baseline; padding: 0 $baseline;
position: relative; position: relative;
...@@ -62,7 +123,7 @@ h1 { ...@@ -62,7 +123,7 @@ h1 {
max-width: $fg-max-width; max-width: $fg-max-width;
min-width: $fg-min-width; min-width: $fg-min-width;
width: flex-grid(12); width: flex-grid(12);
margin: ($baseline*1.5) auto $baseline auto; margin: 0 auto $baseline auto;
color: $gray-d2; color: $gray-d2;
} }
...@@ -284,18 +345,33 @@ h1 { ...@@ -284,18 +345,33 @@ h1 {
margin: 0 0 ($baseline/2) 0; margin: 0 0 ($baseline/2) 0;
} }
.title-4 { header {
@include clearfix();
.title-2 {
width: flex-grid(5, 12);
margin: 0 flex-gutter() 0 0;
float: left;
} }
.title-5 { .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 // layout - supplemental content
.content-supplementary { .content-supplementary {
> section {
margin: 0 0 $baseline 0;
}
.bit { .bit {
@include font-size(13); @include font-size(13);
margin: 0 0 $baseline 0; margin: 0 0 $baseline 0;
...@@ -761,7 +837,7 @@ body.js { ...@@ -761,7 +837,7 @@ body.js {
// ==================== // ====================
// works in progress // works in progress & testing
body.hide-wip { body.hide-wip {
.wip-box { .wip-box {
......
...@@ -15,17 +15,17 @@ ...@@ -15,17 +15,17 @@
// mixins - grandfathered // mixins - grandfathered
@mixin button { @mixin button {
display: inline-block; display: inline-block;
padding: 4px 20px 6px; padding: ($baseline/5) $baseline ($baseline/4);
font-size: 14px; @include font-size(14);
font-weight: 700; font-weight: 700;
@include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset, 0 0 0 rgba(0, 0, 0, 0)); @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); @include transition(background-color .15s, box-shadow .15s);
&.disabled { &.disabled {
border: 1px solid $lightGrey !important; border: 1px solid $gray-l1 !important;
border-radius: 3px !important; border-radius: 3px !important;
background: $lightGrey !important; background: $gray-l1 !important;
color: $darkGrey !important; color: $gray-d1 !important;
pointer-events: none; pointer-events: none;
cursor: none; cursor: none;
&:hover { &:hover {
...@@ -38,31 +38,110 @@ ...@@ -38,31 +38,110 @@
} }
} }
@mixin green-button {
@include button;
border: 1px solid $green-d1;
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: $white;
&: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 blue-button { @mixin blue-button {
@include button; @include button;
border: 1px solid #437fbf; border: 1px solid $blue-d1;
border-radius: 3px; border-radius: 3px;
@include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0));
background-color: $blue; background-color: $blue;
color: #fff; color: $white;
&:hover, &.active { &:hover, &.active {
background-color: #62aaf5; background-color: $blue-s2;
color: #fff; color: $white;
}
&.disabled {
border: 1px solid $blue-l3 !important;
background: $blue-l3 !important;
color: $white !important;
@include box-shadow(none);
} }
} }
@mixin green-button { @mixin red-button {
@include button; @include button;
border: 1px solid #0d7011; border: 1px solid $red-d1;
border-radius: 3px; border-radius: 3px;
@include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0));
background-color: $green; background-color: $red;
color: #fff; 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 { &:hover {
background-color: #129416; background-color: $orange-s2;
color: #fff; color: $gray-d2;
}
&.disabled {
border: 1px solid $orange-l3 !important;
background: $orange-l2 !important;
color: $gray-l1 !important;
@include box-shadow(none);
} }
} }
...@@ -82,24 +161,9 @@ ...@@ -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 { @mixin grey-button {
@include button; @include button;
border: 1px solid $darkGrey; border: 1px solid $gray-d2;
border-radius: 3px; border-radius: 3px;
@include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0));
background-color: #d1dae3; background-color: #d1dae3;
...@@ -127,39 +191,17 @@ ...@@ -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 { @mixin dark-grey-button {
@include button; @include button;
border: 1px solid #1c1e20; border: 1px solid $gray-d2;
border-radius: 3px; 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; box-shadow: 0 1px 0 rgba(255, 255, 255, .2) inset;
color: #fff; color: $white;
&:hover { &:hover {
background-color: #595f64; background-color: $gray-d4;
color: #fff; color: $white;
} }
} }
...@@ -296,6 +338,9 @@ ...@@ -296,6 +338,9 @@
} }
} }
// ====================
// sunsetted mixins
@mixin active { @mixin active {
@include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0));
background-color: rgba(255, 255, 255, .3); background-color: rgba(255, 255, 255, .3);
......
...@@ -113,7 +113,7 @@ $green-u1: desaturate($green,15%); ...@@ -113,7 +113,7 @@ $green-u1: desaturate($green,15%);
$green-u2: desaturate($green,30%); $green-u2: desaturate($green,30%);
$green-u3: desaturate($green,45%); $green-u3: desaturate($green,45%);
$yellow: rgb(231, 214, 143); $yellow: rgb(237, 189, 60);
$yellow-l1: tint($yellow,20%); $yellow-l1: tint($yellow,20%);
$yellow-l2: tint($yellow,40%); $yellow-l2: tint($yellow,40%);
$yellow-l3: tint($yellow,60%); $yellow-l3: tint($yellow,60%);
...@@ -149,8 +149,13 @@ $orange-u3: desaturate($orange,45%); ...@@ -149,8 +149,13 @@ $orange-u3: desaturate($orange,45%);
$shadow: rgba(0,0,0,0.2); $shadow: rgba(0,0,0,0.2);
$shadow-l1: rgba(0,0,0,0.1); $shadow-l1: rgba(0,0,0,0.1);
$shadow-l2: rgba(0,0,0,0.05);
$shadow-d1: rgba(0,0,0,0.4); $shadow-d1: rgba(0,0,0,0.4);
// specific UI
$notification-height: ($baseline*10);
// colors - inherited // colors - inherited
$baseFontColor: $gray-d2; $baseFontColor: $gray-d2;
$offBlack: #3c3c3c; $offBlack: #3c3c3c;
......
@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(0.3));
}
50% {
opacity: 1;
@include transform(scale(1.05));
}
100% {
@include transform(scale(1));
}
}
@-moz-keyframes bounceIn { @include bounceIn(); }
@-webkit-keyframes bounceIn { @include bounceIn(); }
@-o-keyframes bounceIn { @include bounceIn(); }
@keyframes bounceIn { @include bounceIn();}
@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% { 0% {
opacity: 0; opacity: 0;
@include transform(scale(.3)); @include transform(scale(0.3));
} }
50% { 50% {
...@@ -12,16 +171,32 @@ ...@@ -12,16 +171,32 @@
100% { 100% {
@include transform(scale(1)); @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 bounce-in { @include bounce-in(); } @-moz-keyframes bounceOut { @include bounceOut(); }
@-webkit-keyframes bounce-in { @include bounce-in(); } @-webkit-keyframes bounceOut { @include bounceOut(); }
@-o-keyframes bounce-in { @include bounce-in(); } @-o-keyframes bounceOut { @include bounceOut(); }
@keyframes bounce-in { @include bounce-in();} @keyframes bounceOut { @include bounceOut();}
@mixin bounce-in-animation($duration, $timing: ease-in-out) { @mixin anim-bounceOut($duration, $timing: ease-in-out, $count: 1, $delay: 0) {
@include animation-name(bounce-in); @include animation-name(bounceOut);
@include animation-duration($duration); @include animation-duration($duration);
@include animation-delay($delay);
@include animation-timing-function($timing); @include animation-timing-function($timing);
@include animation-iteration-count($count);
@include animation-fill-mode(both); @include animation-fill-mode(both);
} }
\ No newline at end of file
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
// bourbon libs and resets // bourbon libs and resets
@import 'bourbon/bourbon'; @import 'bourbon/bourbon';
@import 'bourbon/addons/button'; @import 'bourbon/addons/button';
@import "variables";
@import 'vendor/normalize'; @import 'vendor/normalize';
@import 'reset'; @import 'reset';
......
...@@ -97,7 +97,7 @@ ...@@ -97,7 +97,7 @@
color: $blue; color: $blue;
&:hover, &:active { &:hover, &:active {
background: $blue-l3; background: $blue-l4;
color: $blue-s2; color: $blue-s2;
} }
......
...@@ -8,11 +8,11 @@ input[type="password"], ...@@ -8,11 +8,11 @@ input[type="password"],
textarea.text { textarea.text {
padding: 6px 8px 8px; padding: 6px 8px 8px;
@include box-sizing(border-box); @include box-sizing(border-box);
border: 1px solid $mediumGrey; border: 1px solid $gray-l2;
border-radius: 2px; border-radius: 2px;
@include linear-gradient($lightGrey, tint($lightGrey, 90%)); @include linear-gradient($gray-l5, $white);
background-color: $lightGrey; background-color: $gray-l5;
@include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset); @include box-shadow(inset 0 1px 2px $shadow-l1);
font-family: 'Open Sans', sans-serif; font-family: 'Open Sans', sans-serif;
font-size: 11px; font-size: 11px;
color: $baseFontColor; color: $baseFontColor;
...@@ -21,7 +21,7 @@ textarea.text { ...@@ -21,7 +21,7 @@ textarea.text {
&::-webkit-input-placeholder, &::-webkit-input-placeholder,
&:-moz-placeholder, &:-moz-placeholder,
&:-ms-input-placeholder { &:-ms-input-placeholder {
color: #979faf; color: $gray-l2;
} }
&:focus { &:focus {
...@@ -30,7 +30,72 @@ textarea.text { ...@@ -30,7 +30,72 @@ textarea.text {
} }
} }
// forms - specific // ====================
// forms - fields - not editable
.field.is-not-editable {
& label.is-focused {
color: $gray-d2;
}
label, input, textarea {
pointer-events: none;
}
}
// ====================
// field with error
.field.error {
input, textarea {
border-color: $red;
}
}
// ====================
// forms - additional UI
form {
.note {
@include box-sizing(border-box);
.title {
}
.copy {
}
// note with actions
&.has-actions {
@include clearfix();
.title {
}
.copy {
}
.list-actions {
}
}
}
.note-promotion {
}
}
// ====================
// forms - grandfathered
input.search { input.search {
padding: 6px 15px 8px 30px; padding: 6px 15px 8px 30px;
@include box-sizing(border-box); @include box-sizing(border-box);
......
...@@ -5,12 +5,12 @@ ...@@ -5,12 +5,12 @@
margin: 0; margin: 0;
padding: $baseline; padding: $baseline;
border-bottom: 1px solid $gray; 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; background: $white;
height: 76px; height: 76px;
position: relative; position: relative;
width: 100%; width: 100%;
z-index: 10; z-index: 1000;
a { a {
color: $baseFontColor; color: $baseFontColor;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
body.signup, body.signin { body.signup, body.signin {
.wrapper-content { .wrapper-content {
margin: 0; margin: ($baseline*1.5) 0 0 0;
padding: 0 $baseline; padding: 0 $baseline;
position: relative; position: relative;
width: 100%; width: 100%;
......
...@@ -147,7 +147,7 @@ body.course.settings { ...@@ -147,7 +147,7 @@ body.course.settings {
} }
label { label {
@include font-size(14); @extend .t-copy-sub1;
@include transition(color, 0.15s, ease-in-out); @include transition(color, 0.15s, ease-in-out);
margin: 0 0 ($baseline/4) 0; margin: 0 0 ($baseline/4) 0;
font-weight: 400; font-weight: 400;
...@@ -238,33 +238,36 @@ body.course.settings { ...@@ -238,33 +238,36 @@ body.course.settings {
} }
} }
// not editable fields
.field.is-not-editable {
& label.is-focused {
color: $gray-d2;
}
}
// field with error
.field.error {
input, textarea {
border-color: $red;
}
}
// specific fields - basic // specific fields - basic
&.basic { &.basic {
.list-input { .list-input {
@include clearfix(); @include clearfix();
padding: 0 ($baseline/2);
.field { .field {
margin-bottom: 0; margin-bottom: 0;
} }
} }
// course details that should appear more like content than elements to change
.field.is-not-editable {
label {
}
input, textarea {
@extend .t-copy-lead1;
@include box-shadow(none);
border: none;
background: none;
padding: 0;
margin: 0;
font-weight: 600;
}
}
#field-course-organization { #field-course-organization {
float: left; float: left;
width: flex-grid(2, 9); width: flex-grid(2, 9);
...@@ -281,6 +284,58 @@ body.course.settings { ...@@ -281,6 +284,58 @@ body.course.settings {
float: left; float: left;
width: flex-grid(5, 9); width: flex-grid(5, 9);
} }
// course link note
.note-promotion-courseURL {
@include box-shadow(0 2px 1px $shadow-l1);
@include border-radius(($baseline/5));
margin-top: ($baseline*1.5);
border: 1px solid $gray-l2;
padding: ($baseline/2) 0 0 0;
.title {
@extend .t-copy-sub1;
margin: 0 0 ($baseline/10) 0;
padding: 0 ($baseline/2);
.tip {
display: inline;
margin-left: ($baseline/4);
}
}
.copy {
padding: 0 ($baseline/2) ($baseline/2) ($baseline/2);
.link-courseURL {
@extend .t-copy-lead1;
&:hover {
}
}
}
.list-actions {
@include box-shadow(inset 0 1px 1px $shadow-l1);
border-top: 1px solid $gray-l2;
padding: ($baseline/2);
background: $gray-l5;
.action-primary {
@include blue-button();
@include font-size(13);
font-weight: 600;
.icon {
@extend .t-icon;
@include font-size(16);
display: inline-block;
vertical-align: middle;
}
}
}
}
} }
// specific fields - schedule // specific fields - schedule
......
...@@ -53,8 +53,13 @@ ...@@ -53,8 +53,13 @@
document.location.protocol + '//www.youtube.com/player_api">\x3C/script>'); document.location.protocol + '//www.youtube.com/player_api">\x3C/script>');
</script> </script>
<!-- view -->
<div class="wrapper wrapper-view">
<%include file="widgets/header.html" /> <%include file="widgets/header.html" />
<%block name="view_alerts"></%block>
<%block name="view_banners"></%block>
<%block name="content"></%block> <%block name="content"></%block>
% if user.is_authenticated(): % if user.is_authenticated():
...@@ -63,6 +68,11 @@ ...@@ -63,6 +68,11 @@
<%include file="widgets/footer.html" /> <%include file="widgets/footer.html" />
<%include file="widgets/tender.html" /> <%include file="widgets/tender.html" />
<%block name="view_notifications"></%block>
</div>
<%block name="view_prompts"></%block>
<%block name="jsextra"></%block> <%block name="jsextra"></%block>
</body> </body>
......
...@@ -83,7 +83,19 @@ from contentstore import utils ...@@ -83,7 +83,19 @@ from contentstore import utils
<input title="This field is disabled: this information cannot be changed." type="text" class="long" id="course-name" value="[Course Name]" readonly /> <input title="This field is disabled: this information cannot be changed." type="text" class="long" id="course-name" value="[Course Name]" readonly />
</li> </li>
</ol> </ol>
<span class="tip tip-stacked">These are used in <a rel="external" href="${utils.get_lms_link_for_about_page(course_location)}" />your course URL</a>, and cannot be changed</span>
<div class="note note-promotion note-promotion-courseURL has-actions">
<h3 class="title">Course Summary Page <span class="tip">(for student enrollment and access)</span></h3>
<div class="copy">
<p><a class="link-courseURL" rel="external" href="${utils.get_lms_link_for_about_page(course_location)}" />${utils.get_lms_link_for_about_page(course_location)}</a></p>
</div>
<ul class="list-actions">
<li class="action-item">
<a title="Send a note to students via email" href="mailto:john.doe@gmail.com?Subject=Enroll%20in%20COURSENAME&body=Hi,%20COURSENAME,%20provided%20by%20edX,%20is%20almost%20ready%20to%20begin.%20Please%20enroll%20for%20this%20course%20at%20${utils.get_lms_link_for_about_page(course_location)}." class="action action-primary"><i class="ss-icon icon ss-symbolicons-standard icon icon-inline icon-announcement">&#x2709;</i> Send an invitation to your students</a>
</li>
</ul>
</div>
</section> </section>
<hr class="divide" /> <hr class="divide" />
...@@ -167,7 +179,7 @@ from contentstore import utils ...@@ -167,7 +179,7 @@ from contentstore import utils
<li class="field text" id="field-course-overview"> <li class="field text" id="field-course-overview">
<label for="course-overview">Course Overview</label> <label for="course-overview">Course Overview</label>
<textarea class="tinymce text-editor" id="course-overview"></textarea> <textarea class="tinymce text-editor" id="course-overview"></textarea>
<span class="tip tip-stacked">Introductions, prerequisites, FAQs that are used on <a href="${utils.get_lms_link_for_about_page(course_location)}">your course summary page</a></span> <span class="tip tip-stacked">Introductions, prerequisites, FAQs that are used on <a class="link-courseURL" rel="external" href="${utils.get_lms_link_for_about_page(course_location)}">your course summary page</a></span>
</li> </li>
<li class="field video" id="field-course-introduction-video"> <li class="field video" id="field-course-introduction-video">
...@@ -175,7 +187,6 @@ from contentstore import utils ...@@ -175,7 +187,6 @@ from contentstore import utils
<div class="input input-existing"> <div class="input input-existing">
<div class="current current-course-introduction-video"> <div class="current current-course-introduction-video">
<iframe width="618" height="350" src="" frameborder="0" allowfullscreen></iframe> <iframe width="618" height="350" src="" frameborder="0" allowfullscreen></iframe>
</div> </div>
<div class="actions"> <div class="actions">
<a href="#" class="remove-item remove-course-introduction-video remove-video-data"><span class="delete-icon"></span> Delete Current Video</a> <a href="#" class="remove-item remove-course-introduction-video remove-video-data"><span class="delete-icon"></span> Delete Current Video</a>
......
...@@ -42,13 +42,17 @@ editor.render(); ...@@ -42,13 +42,17 @@ editor.render();
</%block> </%block>
<%block name="content"> <%block name="content">
<div class="wrapper-content wrapper"> <div class="wrapper-mast wrapper">
<section class="content"> <header class="mast has-subtitle">
<header class="page"> <div class="title">
<span class="title-sub">Settings</span> <span class="title-sub">Settings</span>
<h1 class="title-1">Advanced Settings</h1> <h1 class="title-1">Advanced Settings</h1>
</div>
</header> </header>
</div>
<div class="wrapper-content wrapper">
<section class="content">
<article class="content-primary" role="main"> <article class="content-primary" role="main">
<form id="settings_advanced" class="settings-advanced" method="post" action=""> <form id="settings_advanced" class="settings-advanced" method="post" action="">
...@@ -100,22 +104,60 @@ editor.render(); ...@@ -100,22 +104,60 @@ editor.render();
</aside> </aside>
</section> </section>
</div> </div>
</%block>
<%block name="view_notifications">
<!-- notification: change has been made and a save is needed --> <!-- notification: change has been made and a save is needed -->
<div class="wrapper wrapper-notification wrapper-notification-warning"> <div class="wrapper wrapper-notification wrapper-notification-warning" aria-hidden="true" role="dialog" aria-labelledby="notification-changesMade-title" aria-describedby="notification-changesMade-description">
<div class="notification warning"> <div class="notification warning has-actions">
<div class="copy">
<i class="ss-icon ss-symbolicons-block icon icon-warning">&#x26A0;</i> <i class="ss-icon ss-symbolicons-block icon icon-warning">&#x26A0;</i>
<p><strong>Note: </strong>Your changes will not take effect until you <strong>save your <div class="copy">
progress</strong>. Take care with policy value formatting, as validation is <strong>not implemented</strong>.</p> <h2 class="title title-3" id="notification-changesMade-title">You've Made Some Changes</h2>
<p id="notification-changesMade-description">Your changes will not take effect until you <strong>save your progress</strong>. Take care with key and value formatting, as validation is <strong>not implemented</strong>.</p>
</div> </div>
<div class="actions"> <nav class="nav-actions">
<h3 class="sr">Notification Actions</h3>
<ul> <ul>
<li><a href="#" class="save-button">Save</a></li> <li class="nav-item">
<li><a href="#" class="cancel-button">Cancel</a></li> <a href="" class="action-primary save-button">Save Changes</a>
</li>
<li class="nav-item">
<a href="" class="action-secondary cancel-button">Cancel</a>
</li>
</ul> </ul>
</nav>
</div>
</div>
</%block>
<%block name="view_alerts">
<!-- alert: save confirmed with close -->
<div class="wrapper wrapper-alert wrapper-alert-confirmation" role="status">
<div class="alert confirmation">
<i class="ss-icon ss-symbolicons-standard icon icon-confirmation">&#x2713;</i>
<div class="copy">
<h2 class="title title-3">Your policy changes have been saved.</h2>
<p>Please note that validation of your policy key and value pairs is not currently in place yet. If you are having difficulties, please review your policy pairs.</p>
</div>
<a href="" rel="view" class="action action-alert-close">
<i class="ss-icon ss-symbolicons-block icon icon-close">&#x2421;</i>
<span class="label">close alert</span>
</a>
</div>
</div>
<!-- alert: error -->
<div class="wrapper wrapper-alert wrapper-alert-error" role="status">
<div class="alert error">
<i class="ss-icon ss-symbolicons-block icon icon-error">&#x26A0;</i>
<div class="copy">
<h2 class="title title-3">There was an error saving your information</h2>
<p>Please see the error below and correct it to ensure there are no problems in rendering your course.</p>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
}); });
$(document).ready(function() { $(document).ready(function() {
$('body').addClass('js');
// tabs // tabs
$('.tab-group').tabs(); $('.tab-group').tabs();
......
...@@ -116,6 +116,8 @@ urlpatterns += ( ...@@ -116,6 +116,8 @@ urlpatterns += (
url(r'^logout$', 'student.views.logout_user', name='logout'), 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: if settings.ENABLE_JASMINE:
......
...@@ -7,12 +7,10 @@ from path import path ...@@ -7,12 +7,10 @@ from path import path
from xblock.core import Scope from xblock.core import Scope
from .xml import XMLModuleStore, ImportSystem, ParentTracker from .xml import XMLModuleStore, ImportSystem, ParentTracker
from .exceptions import DuplicateItemError
from xmodule.modulestore import Location from xmodule.modulestore import Location
from xmodule.contentstore.content import StaticContent, XASSET_SRCREF_PREFIX from xmodule.contentstore.content import StaticContent
from .inheritance import own_metadata from .inheritance import own_metadata
from xmodule.errortracker import make_error_tracker from xmodule.errortracker import make_error_tracker
from collections import defaultdict
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -141,8 +139,7 @@ def import_module_from_xml(modulestore, static_content_store, course_data_path, ...@@ -141,8 +139,7 @@ def import_module_from_xml(modulestore, static_content_store, course_data_path,
# Note the dropped element closing tag. This causes the LMS to fail when rendering modules - that's # 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 # 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 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, lxml_rewrite_links(module.data, lambda link: verify_content_links(module, course_data_path, static_content_store, link, remap_dict))
static_content_store, link, remap_dict))
for key in remap_dict.keys(): for key in remap_dict.keys():
module.data = module.data.replace(key, remap_dict[key]) module.data = module.data.replace(key, remap_dict[key])
...@@ -250,7 +247,6 @@ def import_from_xml(store, data_dir, course_dirs=None, ...@@ -250,7 +247,6 @@ def import_from_xml(store, data_dir, course_dirs=None,
course_items.append(module) course_items.append(module)
# then import all the static content # then import all the static content
if static_content_store is not None: if static_content_store is not None:
_namespace_rename = target_location_namespace if target_location_namespace is not None else course_location _namespace_rename = target_location_namespace if target_location_namespace is not None else course_location
...@@ -291,6 +287,7 @@ def import_from_xml(store, data_dir, course_dirs=None, ...@@ -291,6 +287,7 @@ def import_from_xml(store, data_dir, course_dirs=None,
return xml_module_store, course_items return xml_module_store, course_items
def import_module(module, store, course_data_path, static_content_store, allow_not_found=False): def import_module(module, store, course_data_path, static_content_store, allow_not_found=False):
content = {} content = {}
for field in module.fields: for field in module.fields:
...@@ -319,8 +316,7 @@ def import_module(module, store, course_data_path, static_content_store, allow_n ...@@ -319,8 +316,7 @@ def import_module(module, store, course_data_path, static_content_store, allow_n
# Note the dropped element closing tag. This causes the LMS to fail when rendering modules - that's # 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 # 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 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, lxml_rewrite_links(module_data, lambda link: verify_content_links(module, course_data_path, static_content_store, link, remap_dict))
static_content_store, link, remap_dict))
for key in remap_dict.keys(): for key in remap_dict.keys():
module_data = module_data.replace(key, remap_dict[key]) module_data = module_data.replace(key, remap_dict[key])
...@@ -469,7 +465,6 @@ def check_module_metadata_editability(module): ...@@ -469,7 +465,6 @@ def check_module_metadata_editability(module):
allowed = allowed + ['xml_attributes', 'display_name'] allowed = allowed + ['xml_attributes', 'display_name']
err_cnt = 0 err_cnt = 0
my_metadata = dict(own_metadata(module))
illegal_keys = set(own_metadata(module).keys()) - set(allowed) illegal_keys = set(own_metadata(module).keys()) - set(allowed)
if len(illegal_keys) > 0: if len(illegal_keys) > 0:
...@@ -586,7 +581,6 @@ def perform_xlint(data_dir, course_dirs, ...@@ -586,7 +581,6 @@ def perform_xlint(data_dir, course_dirs,
print "WARN: Missing course marketing video. It is recommended that every course have a marketing video." print "WARN: Missing course marketing video. It is recommended that every course have a marketing video."
warn_cnt += 1 warn_cnt += 1
print "\n\n------------------------------------------\nVALIDATION SUMMARY: {0} Errors {1} Warnings\n".format(err_cnt, warn_cnt) print "\n\n------------------------------------------\nVALIDATION SUMMARY: {0} Errors {1} Warnings\n".format(err_cnt, warn_cnt)
if err_cnt > 0: if err_cnt > 0:
......
...@@ -199,8 +199,8 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -199,8 +199,8 @@ class PeerGradingModule(PeerGradingFields, XModule):
self.student_data_for_location = response self.student_data_for_location = response
score_dict = { score_dict = {
'score': int(count_graded >= count_required), 'score': int(count_graded >= count_required and count_graded>0) * int(self.weight),
'total': self.max_grade, 'total': self.max_grade * int(self.weight),
} }
return score_dict return score_dict
......
...@@ -24,6 +24,11 @@ def strip_filenames(descriptor): ...@@ -24,6 +24,11 @@ def strip_filenames(descriptor):
""" """
print "strip filename from {desc}".format(desc=descriptor.location.url()) print "strip filename from {desc}".format(desc=descriptor.location.url())
descriptor._model_data.pop('filename', None) 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(): for d in descriptor.get_children():
strip_filenames(d) strip_filenames(d)
......
...@@ -11,11 +11,15 @@ ...@@ -11,11 +11,15 @@
font-size: ($sizeValue/10) + rem; font-size: ($sizeValue/10) + rem;
} }
// ====================
// line-height // line-height
@function lh($amount: 1) { @function lh($amount: 1) {
@return $body-line-height * $amount; @return $body-line-height * $amount;
} }
// ====================
// image-replacement hidden text // image-replacement hidden text
@mixin text-hide() { @mixin text-hide() {
text-indent: 100%; text-indent: 100%;
...@@ -35,6 +39,8 @@ ...@@ -35,6 +39,8 @@
width: 1px; width: 1px;
} }
// ====================
// vertical and horizontal centering // vertical and horizontal centering
@mixin vertically-and-horizontally-centered ($height, $width) { @mixin vertically-and-horizontally-centered ($height, $width) {
left: 50%; left: 50%;
...@@ -46,6 +52,8 @@ ...@@ -46,6 +52,8 @@
top: 150px; top: 150px;
} }
// ====================
// sizing // sizing
@mixin size($width: $baseline, $height: $baseline) { @mixin size($width: $baseline, $height: $baseline) {
height: $height; height: $height;
...@@ -56,6 +64,8 @@ ...@@ -56,6 +64,8 @@
@include size($size); @include size($size);
} }
// ====================
// placeholder styling // placeholder styling
@mixin placeholder($color) { @mixin placeholder($color) {
:-moz-placeholder { :-moz-placeholder {
......
...@@ -3,17 +3,12 @@ ...@@ -3,17 +3,12 @@
# django management command: dump grades to csv files # django management command: dump grades to csv files
# for use by batch processes # for use by batch processes
import os import csv
import sys
import string
import datetime
import json
from instructor.views import * from instructor.views import get_student_grade_summary_data
from courseware.courses import get_course_by_id from courseware.courses import get_course_by_id
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from django.conf import settings
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
...@@ -45,7 +40,7 @@ class Command(BaseCommand): ...@@ -45,7 +40,7 @@ class Command(BaseCommand):
request = self.DummyRequest() request = self.DummyRequest()
try: try:
course = get_course_by_id(course_id) course = get_course_by_id(course_id)
except Exception as err: except Exception:
if course_id in modulestore().courses: if course_id in modulestore().courses:
course = modulestore().courses[course_id] course = modulestore().courses[course_id]
else: else:
......
...@@ -11,7 +11,6 @@ import requests ...@@ -11,7 +11,6 @@ import requests
from requests.status_codes import codes from requests.status_codes import codes
import urllib import urllib
from collections import OrderedDict from collections import OrderedDict
import json
from StringIO import StringIO from StringIO import StringIO
...@@ -21,7 +20,6 @@ from django.http import HttpResponse ...@@ -21,7 +20,6 @@ from django.http import HttpResponse
from django_future.csrf import ensure_csrf_cookie from django_future.csrf import ensure_csrf_cookie
from django.views.decorators.cache import cache_control from django.views.decorators.cache import cache_control
from mitxmako.shortcuts import render_to_response from mitxmako.shortcuts import render_to_response
import requests
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from courseware import grades from courseware import grades
...@@ -36,11 +34,7 @@ from django_comment_client.models import (Role, ...@@ -36,11 +34,7 @@ from django_comment_client.models import (Role,
from django_comment_client.utils import has_forum_access from django_comment_client.utils import has_forum_access
from psychometrics import psychoanalyze from psychometrics import psychoanalyze
from student.models import CourseEnrollment, CourseEnrollmentAllowed from student.models import CourseEnrollment, CourseEnrollmentAllowed
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import InvalidLocationError, ItemNotFoundError, NoPathToItem
from xmodule.modulestore.search import path_to_location
import xmodule.graders as xmgraders import xmodule.graders as xmgraders
import track.views import track.views
...@@ -48,14 +42,15 @@ from .offline_gradecalc import student_grades, offline_grades_available ...@@ -48,14 +42,15 @@ from .offline_gradecalc import student_grades, offline_grades_available
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
template_imports = {'urllib': urllib}
# internal commands for managing forum roles: # internal commands for managing forum roles:
FORUM_ROLE_ADD = 'add' FORUM_ROLE_ADD = 'add'
FORUM_ROLE_REMOVE = 'remove' FORUM_ROLE_REMOVE = 'remove'
def split_by_comma_and_whitespace(s): def split_by_comma_and_whitespace(s):
"""
Return string s, split by , or whitespace
"""
return re.split(r'[\s,]', s) return re.split(r'[\s,]', s)
...@@ -141,7 +136,7 @@ def instructor_dashboard(request, course_id): ...@@ -141,7 +136,7 @@ def instructor_dashboard(request, course_id):
# 'beta', so adding it to get_access_group_name doesn't really make # 'beta', so adding it to get_access_group_name doesn't really make
# sense. # sense.
name = course_beta_test_group_name(course.location) name = course_beta_test_group_name(course.location)
(group, created) = Group.objects.get_or_create(name=name) (group, _) = Group.objects.get_or_create(name=name)
return group return group
# process actions from form POST # process actions from form POST
...@@ -237,13 +232,13 @@ def instructor_dashboard(request, course_id): ...@@ -237,13 +232,13 @@ def instructor_dashboard(request, course_id):
if '/' not in problem_to_reset: # allow state of modules other than problem to be reset if '/' not in problem_to_reset: # allow state of modules other than problem to be reset
problem_to_reset = "problem/" + problem_to_reset # but problem is the default problem_to_reset = "problem/" + problem_to_reset # but problem is the default
try: try:
(org, course_name, run) = course_id.split("/") (org, course_name, _) = course_id.split("/")
module_state_key = "i4x://" + org + "/" + course_name + "/" + problem_to_reset module_state_key = "i4x://" + org + "/" + course_name + "/" + problem_to_reset
module_to_reset = StudentModule.objects.get(student_id=student_to_reset.id, module_to_reset = StudentModule.objects.get(student_id=student_to_reset.id,
course_id=course_id, course_id=course_id,
module_state_key=module_state_key) module_state_key=module_state_key)
msg += "Found module to reset. " msg += "Found module to reset. "
except Exception as e: except Exception:
msg += "<font color='red'>Couldn't find module with that urlname. </font>" msg += "<font color='red'>Couldn't find module with that urlname. </font>"
if "Delete student state for problem" in action: if "Delete student state for problem" in action:
...@@ -352,7 +347,7 @@ def instructor_dashboard(request, course_id): ...@@ -352,7 +347,7 @@ def instructor_dashboard(request, course_id):
return_csv('', datatable, fp=fp) return_csv('', datatable, fp=fp)
fp.seek(0) fp.seek(0)
files = {'datafile': fp} files = {'datafile': fp}
msg2, dataset = _do_remote_gradebook(request.user, course, 'post-grades', files=files) msg2, _ = _do_remote_gradebook(request.user, course, 'post-grades', files=files)
msg += msg2 msg += msg2
...@@ -423,7 +418,7 @@ def instructor_dashboard(request, course_id): ...@@ -423,7 +418,7 @@ def instructor_dashboard(request, course_id):
datatable = {'header': ['username', 'email'] + profkeys} datatable = {'header': ['username', 'email'] + profkeys}
def getdat(u): def getdat(u):
p = u.profile p = u.profile
return [u.username, u.email] + [getattr(p,x,'') for x in profkeys] return [u.username, u.email] + [getattr(p, x, '') for x in profkeys]
datatable['data'] = [getdat(u) for u in enrolled_students] datatable['data'] = [getdat(u) for u in enrolled_students]
datatable['title'] = 'Student profile data for course %s' % course_id datatable['title'] = 'Student profile data for course %s' % course_id
...@@ -433,17 +428,17 @@ def instructor_dashboard(request, course_id): ...@@ -433,17 +428,17 @@ def instructor_dashboard(request, course_id):
elif 'Download CSV of all responses to problem' in action: elif 'Download CSV of all responses to problem' in action:
problem_to_dump = request.POST.get('problem_to_dump','') problem_to_dump = request.POST.get('problem_to_dump','')
if problem_to_dump[-4:]==".xml": if problem_to_dump[-4:] == ".xml":
problem_to_dump=problem_to_dump[:-4] problem_to_dump = problem_to_dump[:-4]
try: try:
(org, course_name, run)=course_id.split("/") (org, course_name, run) = course_id.split("/")
module_state_key="i4x://"+org+"/"+course_name+"/problem/"+problem_to_dump module_state_key = "i4x://" + org + "/" + course_name + "/problem/" + problem_to_dump
smdat = StudentModule.objects.filter(course_id=course_id, smdat = StudentModule.objects.filter(course_id=course_id,
module_state_key=module_state_key) module_state_key=module_state_key)
smdat = smdat.order_by('student') smdat = smdat.order_by('student')
msg += "Found %d records to dump " % len(smdat) msg += "Found %d records to dump " % len(smdat)
except Exception as err: except Exception as err:
msg+="<font color='red'>Couldn't find module with that urlname. </font>" msg += "<font color='red'>Couldn't find module with that urlname. </font>"
msg += "<pre>%s</pre>" % escape(err) msg += "<pre>%s</pre>" % escape(err)
smdat = [] smdat = []
...@@ -741,7 +736,7 @@ def _list_course_forum_members(course_id, rolename, datatable): ...@@ -741,7 +736,7 @@ def _list_course_forum_members(course_id, rolename, datatable):
# make sure datatable is set up properly for display first, before checking for errors # make sure datatable is set up properly for display first, before checking for errors
datatable['header'] = ['Username', 'Full name', 'Roles'] datatable['header'] = ['Username', 'Full name', 'Roles']
datatable['title'] = 'List of Forum {0}s in course {1}'.format(rolename, course_id) datatable['title'] = 'List of Forum {0}s in course {1}'.format(rolename, course_id)
datatable['data'] = []; datatable['data'] = []
try: try:
role = Role.objects.get(name=rolename, course_id=course_id) role = Role.objects.get(name=rolename, course_id=course_id)
except Role.DoesNotExist: except Role.DoesNotExist:
...@@ -1040,7 +1035,8 @@ def _do_enroll_students(course, course_id, students, overload=False): ...@@ -1040,7 +1035,8 @@ def _do_enroll_students(course, course_id, students, overload=False):
datatable['data'] = [[x, status[x]] for x in status] datatable['data'] = [[x, status[x]] for x in status]
datatable['title'] = 'Enrollment of students' datatable['title'] = 'Enrollment of students'
def sf(stat): return [x for x in status if status[x] == stat] def sf(stat):
return [x for x in status if status[x] == stat]
data = dict(added=sf('added'), rejected=sf('rejected') + sf('exists'), data = dict(added=sf('added'), rejected=sf('rejected') + sf('exists'),
deleted=sf('deleted'), datatable=datatable) deleted=sf('deleted'), datatable=datatable)
......
...@@ -27,8 +27,6 @@ from mitxmako.shortcuts import render_to_string ...@@ -27,8 +27,6 @@ from mitxmako.shortcuts import render_to_string
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
template_imports = {'urllib': urllib}
system = ModuleSystem( system = ModuleSystem(
ajax_url=None, ajax_url=None,
track_function=None, track_function=None,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment