From 3b2c970248947758fc45882d9a7762f397aa8bdc Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Fri, 8 Mar 2013 11:47:11 -0500 Subject: [PATCH 001/135] studio - reorganized and documented used Sass for studio --- cms/static/sass/_account.scss | 294 --------- cms/static/sass/_alerts.scss | 162 ----- cms/static/sass/_assets.scss | 186 ------ cms/static/sass/_base.scss | 4 +- cms/static/sass/_calendar.scss | 367 ----------- cms/static/sass/_cms_mixins.scss | 3 + cms/static/sass/_content-types.scss | 69 --- cms/static/sass/_course-info.scss | 218 ------- cms/static/sass/_courseware.scss | 689 --------------------- cms/static/sass/_dashboard.scss | 114 ---- cms/static/sass/_export.scss | 123 ---- cms/static/sass/_extends.scss | 78 --- cms/static/sass/_fonts.scss | 36 -- cms/static/sass/_footer.scss | 48 -- cms/static/sass/_graphics.scss | 336 ---------- cms/static/sass/_header.scss | 562 ----------------- cms/static/sass/_import.scss | 102 ---- cms/static/sass/_index.scss | 353 ----------- cms/static/sass/_jquery-ui-calendar.scss | 53 -- cms/static/sass/_keyframes.scss | 27 - cms/static/sass/_landing.scss | 126 ---- cms/static/sass/_layout.scss | 125 ---- cms/static/sass/_lms.scss | 69 --- cms/static/sass/_login.scss | 139 ----- cms/static/sass/_modal.scss | 69 --- cms/static/sass/_module-header.scss | 128 ---- cms/static/sass/_problem.scss | 24 - cms/static/sass/_reset.scss | 3 + cms/static/sass/_section.scss | 239 -------- cms/static/sass/_settings.scss | 740 ----------------------- cms/static/sass/_static-pages.scss | 153 ----- cms/static/sass/_subsection.scss | 295 --------- cms/static/sass/_unit.scss | 667 -------------------- cms/static/sass/_users.scss | 78 --- cms/static/sass/_variables.scss | 3 + cms/static/sass/_video.scss | 33 - cms/static/sass/_week.scss | 256 -------- cms/static/sass/base-style.scss | 68 ++- common/static/sass/_mixins.scss | 89 ++- 39 files changed, 138 insertions(+), 6990 deletions(-) delete mode 100644 cms/static/sass/_account.scss delete mode 100644 cms/static/sass/_alerts.scss delete mode 100644 cms/static/sass/_assets.scss delete mode 100644 cms/static/sass/_calendar.scss delete mode 100644 cms/static/sass/_content-types.scss delete mode 100644 cms/static/sass/_course-info.scss delete mode 100644 cms/static/sass/_courseware.scss delete mode 100644 cms/static/sass/_dashboard.scss delete mode 100644 cms/static/sass/_export.scss delete mode 100644 cms/static/sass/_extends.scss delete mode 100644 cms/static/sass/_fonts.scss delete mode 100644 cms/static/sass/_footer.scss delete mode 100644 cms/static/sass/_graphics.scss delete mode 100644 cms/static/sass/_header.scss delete mode 100644 cms/static/sass/_import.scss delete mode 100644 cms/static/sass/_index.scss delete mode 100644 cms/static/sass/_jquery-ui-calendar.scss delete mode 100644 cms/static/sass/_keyframes.scss delete mode 100644 cms/static/sass/_landing.scss delete mode 100644 cms/static/sass/_layout.scss delete mode 100644 cms/static/sass/_lms.scss delete mode 100644 cms/static/sass/_login.scss delete mode 100644 cms/static/sass/_modal.scss delete mode 100644 cms/static/sass/_module-header.scss delete mode 100644 cms/static/sass/_problem.scss delete mode 100644 cms/static/sass/_section.scss delete mode 100644 cms/static/sass/_settings.scss delete mode 100644 cms/static/sass/_static-pages.scss delete mode 100644 cms/static/sass/_subsection.scss delete mode 100644 cms/static/sass/_unit.scss delete mode 100644 cms/static/sass/_users.scss delete mode 100644 cms/static/sass/_video.scss delete mode 100644 cms/static/sass/_week.scss diff --git a/cms/static/sass/_account.scss b/cms/static/sass/_account.scss deleted file mode 100644 index 650743979f..0000000000 --- a/cms/static/sass/_account.scss +++ /dev/null @@ -1,294 +0,0 @@ -// Studio - Sign In/Up -// ==================== -body.signup, body.signin { - - .wrapper-content { - margin: 0; - padding: 0 $baseline; - position: relative; - width: 100%; - } - - .content { - @include clearfix(); - @include font-size(16); - max-width: $fg-max-width; - min-width: $fg-min-width; - width: flex-grid(12); - margin: 0 auto; - color: $gray-d2; - - header { - position: relative; - margin-bottom: $baseline; - border-bottom: 1px solid $gray-l4; - padding-bottom: ($baseline/2); - - h1 { - @include font-size(32); - margin: 0; - padding: 0; - font-weight: 600; - } - - .action { - @include font-size(13); - position: absolute; - right: 0; - top: 40%; - } - } - - .introduction { - @include font-size(14); - margin: 0 0 $baseline 0; - } - } - - .content-primary, .content-supplementary { - @include box-sizing(border-box); - float: left; - } - - .content-primary { - width: flex-grid(8, 12); - margin-right: flex-gutter(); - - form { - @include box-sizing(border-box); - @include box-shadow(0 1px 2px $shadow-l1); - @include border-radius(2px); - width: 100%; - border: 1px solid $gray-l2; - padding: $baseline ($baseline*1.5); - background: $white; - - .form-actions { - margin-top: $baseline; - - .action-primary { - @include blue-button; - @include transition(all .15s); - @include font-size(15); - display:block; - width: 100%; - padding: ($baseline*0.75) ($baseline/2); - font-weight: 600; - text-transform: uppercase; - } - } - - .list-input { - margin: 0; - padding: 0; - list-style: none; - - .field { - margin: 0 0 ($baseline*0.75) 0; - - &:last-child { - margin-bottom: 0; - } - - &.required { - - label { - font-weight: 600; - } - - label:after { - margin-left: ($baseline/4); - content: "*"; - } - } - - label, input, textarea { - display: block; - } - - label { - @include font-size(14); - @include transition(color, 0.15s, ease-in-out); - margin: 0 0 ($baseline/4) 0; - - &.is-focused { - color: $blue; - } - } - - input, textarea { - @include font-size(16); - height: 100%; - width: 100%; - padding: ($baseline/2); - - &.long { - width: 100%; - } - - &.short { - width: 25%; - } - - ::-webkit-input-placeholder { - color: $gray-l4; - } - - :-moz-placeholder { - color: $gray-l3; - } - - ::-moz-placeholder { - color: $gray-l3; - } - - :-ms-input-placeholder { - color: $gray-l3; - } - - &:focus { - - + .tip { - color: $gray; - } - } - } - - textarea.long { - height: ($baseline*5); - } - - input[type="checkbox"] { - display: inline-block; - margin-right: ($baseline/4); - width: auto; - height: auto; - - & + label { - display: inline-block; - } - } - - .tip { - @include transition(color, 0.15s, ease-in-out); - @include font-size(13); - display: block; - margin-top: ($baseline/4); - color: $gray-l3; - } - } - - .field-group { - @include clearfix(); - margin: 0 0 ($baseline/2) 0; - - .field { - display: block; - width: 47%; - border-bottom: none; - margin: 0 $baseline 0 0; - padding-bottom: 0; - - &:nth-child(odd) { - float: left; - } - - &:nth-child(even) { - float: right; - margin-right: 0; - } - - input, textarea { - width: 100%; - } - } - } - } - } - } - - .content-supplementary { - width: flex-grid(4, 12); - - .bit { - @include font-size(13); - margin: 0 0 $baseline 0; - border-bottom: 1px solid $gray-l4; - padding: 0 0 $baseline 0; - color: $gray-l1; - - &:last-child { - margin-bottom: 0; - border: none; - padding-bottom: 0; - } - - h3 { - @include font-size(14); - margin: 0 0 ($baseline/4) 0; - color: $gray-d2; - font-weight: 600; - } - - } - } -} - -.signup { - -} - -.signin { - - #field-password { - position: relative; - - .action-forgotpassword { - @include font-size(13); - position: absolute; - top: 0; - right: 0; - } - } -} - -// ==================== - -// messages -.message { - @include font-size(14); - display: block; -} - -.message-status { - display: none; - @include border-top-radius(2px); - @include box-sizing(border-box); - border-bottom: 2px solid $yellow-d2; - margin: 0 0 $baseline 0; - padding: ($baseline/2) $baseline; - font-weight: 500; - background: $yellow-d1; - color: $white; - - .ss-icon { - position: relative; - top: 3px; - @include font-size(16); - display: inline-block; - margin-right: ($baseline/2); - } - - .text { - display: inline-block; - } - - &.error { - border-color: shade($red, 50%); - background: tint($red, 20%); - } - - &.is-shown { - display: block; - } -} diff --git a/cms/static/sass/_alerts.scss b/cms/static/sass/_alerts.scss deleted file mode 100644 index bd7f687f67..0000000000 --- a/cms/static/sass/_alerts.scss +++ /dev/null @@ -1,162 +0,0 @@ -// 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; - - &.is-shown { - bottom: 0; - opacity: 1.0; - } - - &.wrapper-notification-warning { - border-color: shade($yellow, 25%); - background: tint($yellow, 25%); - } - - &.wrapper-notification-error { - border-color: shade($red, 50%); - background: tint($red, 20%); - color: $white; - } - - &.wrapper-notification-confirm { - border-color: shade($green, 30%); - background: tint($green, 40%); - color: shade($green, 30%); - } -} - -.notification { - @include box-sizing(border-box); - 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; - - .icon { - display: inline-block; - vertical-align: top; - margin-right: 5px; - font-size: 20px; - } - - p { - width: flex-grid(8, 9); - display: inline-block; - vertical-align: top; - } - } - - .actions { - float: right; - width: flex-grid(3, 12); - margin-top: ($baseline/2); - text-align: right; - - li { - display: inline-block; - vertical-align: middle; - margin-right: 10px; - - &:last-child { - margin-right: 0; - } - } - - .save-button { - @include blue-button; - } - - .cancel-button { - @include white-button; - } - } - - strong { - font-weight: 700; - } -} - -// 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; - - .alert-message { - float: left; - margin-top: 4px; - } - - strong { - font-weight: 700; - } - - .alert-action { - float: right; - - &.secondary { - @include orange-button; - } - } -} - -body.error { - background: $darkGrey; - color: #3c3c3c; - - .primary-header { - display: none; - } - - .error-prompt { - width: 700px; - margin: 150px auto; - padding: 60px 50px 90px; - border-radius: 3px; - background: #fff; - text-align: center; - } - - h1 { - float: none; - margin: 0; - font-size: 60px; - font-weight: 300; - color: #3c3c3c; - } - - .description { - margin-bottom: 50px; - font-size: 21px; - } - - .back-button { - @include blue-button; - padding: 14px 40px 18px; - font-size: 18px; - } -} \ No newline at end of file diff --git a/cms/static/sass/_assets.scss b/cms/static/sass/_assets.scss deleted file mode 100644 index d9b215faec..0000000000 --- a/cms/static/sass/_assets.scss +++ /dev/null @@ -1,186 +0,0 @@ -.uploads { - input.asset-search-input { - float: left; - width: 260px; - background-color: #fff; - } - - .asset-library { - @include clearfix; - - table { - width: 100%; - border-radius: 3px 3px 0 0; - border: 1px solid #c5cad4; - - td, - th { - padding: 10px 20px; - text-align: left; - vertical-align: middle; - } - - thead th { - @include linear-gradient(top, transparent, rgba(0, 0, 0, .1)); - background-color: #ced2db; - font-size: 12px; - font-weight: 700; - text-shadow: 0 1px 0 rgba(255, 255, 255, .5); - } - - tbody { - background: #fff; - - tr { - border-top: 1px solid #c5cad4; - - &:first-child { - border-top: none; - } - } - - .name-col { - font-size: 14px; - } - - .date-col { - font-size: 12px; - } - } - - .thumb-col { - width: 100px; - } - - .date-col { - width: 220px; - } - - .embed-col { - width: 250px; - } - - .embeddable-xml-input { - @include box-shadow(none); - width: 100%; - } - - .thumb { - width: 100px; - max-height: 80px; - - img { - width: 100%; - } - } - } - - .pagination { - float: right; - margin: 15px 10px; - - ol, li { - display: inline; - } - - a { - display: inline-block; - height: 25px; - padding: 0 4px; - text-align: center; - line-height: 25px; - } - } - } - .show-xml { - @include blue-button; - } -} - -.upload-modal { - display: none; - width: 640px !important; - margin-left: -320px !important; - - .modal-body { - height: auto !important; - overflow-y: auto !important; - text-align: center; - } - - .file-input { - display: none; - } - - .choose-file-button { - @include blue-button; - padding: 10px 82px 12px; - font-size: 17px; - } - - .progress-bar { - display: none; - width: 350px; - height: 50px; - margin: 30px auto 10px; - border: 1px solid $blue; - - &.loaded { - border-color: #66b93d; - - .progress-fill { - background: #66b93d; - } - } - } - - .progress-fill { - width: 0%; - height: 50px; - background: $blue; - color: #fff; - line-height: 48px; - } - - h1 { - float: none; - margin: 40px 0 30px; - font-size: 34px; - font-weight: 300; - } - - .close-button { - @include white-button; - position: absolute; - top: 0; - right: 15px; - width: 29px; - height: 29px; - padding: 0 !important; - border-radius: 17px !important; - line-height: 29px; - text-align: center; - } - - .embeddable { - display: none; - margin: 30px 0 130px; - - label { - display: block; - margin-bottom: 10px; - font-weight: 700; - } - } - - .embeddable-xml-input { - @include box-shadow(none); - width: 400px; - } - - .copy-button { - @include white-button; - display: none; - margin-bottom: 100px; - } -} \ No newline at end of file diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index 5d4bc7c773..1bf9119654 100644 --- a/cms/static/sass/_base.scss +++ b/cms/static/sass/_base.scss @@ -1,4 +1,4 @@ -// studio base styling +// studio - base styling // ==================== // basic reset @@ -9,7 +9,7 @@ html { body { @include font-size(16); - min-width: 980px; + min-width: $fg-min-width; background: $gray-l5; line-height: 1.6; color: $baseFontColor; diff --git a/cms/static/sass/_calendar.scss b/cms/static/sass/_calendar.scss deleted file mode 100644 index 4c007bb561..0000000000 --- a/cms/static/sass/_calendar.scss +++ /dev/null @@ -1,367 +0,0 @@ -section.cal { - @include box-sizing(border-box); - @include clearfix; - padding: 20px; - - > header { - display: none; - @include clearfix; - margin-bottom: 10px; - opacity: .4; - @include transition; - text-shadow: 0 1px 0 #fff; - - &:hover { - opacity: 1; - } - - h2 { - @include inline-block(); - text-transform: uppercase; - letter-spacing: 1px; - font-size: 14px; - padding: 6px 6px 6px 0; - font-size: 12px; - margin: 0; - } - - ul { - @include inline-block; - float: right; - margin: 0; - padding: 0; - - &.actions { - float: left; - } - - li { - @include inline-block; - margin-right: 6px; - border-right: 1px solid #ddd; - padding: 0 6px 0 0; - - &:last-child { - border-right: 0; - margin-right: 0; - padding-right: 0; - } - - a { - @include inline-block(); - font-size: 12px; - @include inline-block; - margin: 0 6px; - font-style: italic; - } - - ul { - @include inline-block(); - margin: 0; - - li { - @include inline-block(); - padding: 0; - border-left: 0; - } - } - } - } - } - - ol { - list-style: none; - @include clearfix; - border: 1px solid lighten( $dark-blue , 30% ); - background: #FFF; - width: 100%; - @include box-sizing(border-box); - margin: 0; - padding: 0; - @include box-shadow(0 0 5px lighten($dark-blue, 45%)); - @include border-radius(3px); - overflow: hidden; - - > li { - border-right: 1px solid lighten($dark-blue, 40%); - border-bottom: 1px solid lighten($dark-blue, 40%); - @include box-sizing(border-box); - float: left; - width: flex-grid(3) + ((flex-gutter() * 3) / 4); - background-color: $light-blue; - @include box-shadow(inset 0 0 0 1px lighten($light-blue, 8%)); - - &:hover { - li.create-module { - opacity: 1; - } - } - - &:nth-child(4n) { - border-right: 0; - } - - header { - border-bottom: 1px solid lighten($dark-blue, 40%); - @include box-shadow(0 2px 2px $light-blue); - display: block; - margin-bottom: 2px; - background: #FFF; - - h1 { - font-size: 14px; - text-transform: uppercase; - border-bottom: 1px solid lighten($dark-blue, 60%); - padding: 6px; - color: $bright-blue; - margin: 0; - - a { - color: $bright-blue; - display: block; - padding: 6px; - margin: -6px; - - &:hover { - color: darken($bright-blue, 10%); - background: lighten($yellow, 10%); - } - } - } - - ul { - margin: 0; - padding: 0; - - li { - background: #fff; - color: #888; - border-bottom: 0; - font-size: 12px; - @include box-shadow(none); - } - } - } - - ul { - list-style: none; - margin: 0 0 1px 0; - padding: 0; - - li { - border-bottom: 1px solid darken($light-blue, 6%); - // @include box-shadow(0 1px 0 lighten($light-blue, 4%)); - overflow: hidden; - position: relative; - text-shadow: 0 1px 0 #fff; - - &:hover { - background-color: lighten($yellow, 14%); - - a.draggable { - background-color: lighten($yellow, 14%); - opacity: 1; - } - } - - &.editable { - padding: 3px 6px; - } - - a { - color: lighten($dark-blue, 10%); - display: block; - padding: 6px 35px 6px 6px; - - &:hover { - background-color: lighten($yellow, 10%); - } - - &.draggable { - background-color: $light-blue; - opacity: .3; - padding: 0; - - &:hover { - background-color: lighten($yellow, 10%); - } - } - } - - &.create-module { - position: relative; - opacity: 0; - @include transition(all 3s ease-in-out); - background: darken($light-blue, 2%); - - > div { - background: $dark-blue; - @include box-shadow(0 0 5px darken($light-blue, 60%)); - @include box-sizing(border-box); - display: none; - margin-left: 3%; - padding: 10px; - @include position(absolute, 30px 0 0 0); - width: 90%; - z-index: 99; - - ul { - li { - border-bottom: 0; - background: none; - - input { - @include box-sizing(border-box); - width: 100%; - } - - select { - @include box-sizing(border-box); - width: 100%; - - option { - font-size: 14px; - } - } - - div { - margin-top: 10px; - } - - a { - color: $light-blue; - float: right; - - &:first-child { - float: left; - } - - &:hover { - color: #fff; - } - } - } - } - } - } - } - } - } - } - - section.new-section { - margin: 10px 0 40px; - @include inline-block(); - position: relative; - - > a { - @extend .button; - display: block; - } - - section { - display: none; - @include position(absolute, 30px 0 0 0); - background: rgba(#000, .8); - min-width: 300px; - padding: 10px; - @include box-sizing(border-box); - @include border-radius(3px); - z-index: 99; - - &:before { - content: " "; - display: block; - background: rgba(#000, .8); - width: 10px; - height: 10px; - @include position(absolute, -5px 0 0 20%); - @include transform(rotate(45deg)); - } - - form { - - ul { - list-style: none; - - li { - border-bottom: 0; - background: none; - margin-bottom: 6px; - - input { - width: 100%; - @include box-sizing(border-box); - border-color: #000; - padding: 6px; - } - - select { - width: 100%; - @include box-sizing(border-box); - - option { - font-size: 14px; - } - } - - a { - float: right; - - &:first-child { - float: left; - } - } - } - } - } - } - } -} - -body.content -section.cal { - width: flex-grid(3); - float: left; - overflow: scroll; - @include box-sizing(border-box); - opacity: .4; - @include transition(); - - &:hover { - opacity: 1; - } - - > header { - @include transition; - overflow: hidden; - - > a { - display: none; - } - - ul { - float: none; - display: block; - - li { - - ul { - display: inline; - } - } - } - } - - ol { - li { - @include box-sizing(border-box); - width: 100%; - border-right: 0; - - &.create-module { - display: none; - } - } - } -} diff --git a/cms/static/sass/_cms_mixins.scss b/cms/static/sass/_cms_mixins.scss index b8d9a8ae2e..015a94b762 100644 --- a/cms/static/sass/_cms_mixins.scss +++ b/cms/static/sass/_cms_mixins.scss @@ -1,3 +1,6 @@ +// studio - utilities - mixins and extends +// ==================== + @mixin clearfix { &:after { content: ''; diff --git a/cms/static/sass/_content-types.scss b/cms/static/sass/_content-types.scss deleted file mode 100644 index 7cca469350..0000000000 --- a/cms/static/sass/_content-types.scss +++ /dev/null @@ -1,69 +0,0 @@ -.content-type { - display: inline-block; - width: 14px; - height: 16px; - padding-left: 14px; - background-position: 8px center; - background-repeat: no-repeat; - vertical-align: middle; -} - -.videosequence-icon { - @extend .content-type; - background-image: url('../img/content-types/videosequence.png'); -} - -.video-icon { - @extend .content-type; - background-image: url('../img/content-types/video.png'); -} - -.problemset-icon { - @extend .content-type; - background-image: url('../img/content-types/problemset.png'); -} - -.problem-icon { - @extend .content-type; - background-image: url('../img/content-types/problem.png'); -} - -.lab-icon { - @extend .content-type; - background-image: url('../img/content-types/lab.png'); -} - -.tab-icon { - @extend .content-type; - background-image: url('../img/content-types/lab.png'); -} - -.html-icon { - @extend .content-type; - background-image: url('../img/content-types/html.png'); -} - -.vertical-icon { - @extend .content-type; - background-image: url('../img/content-types/vertical.png'); -} - -.sequential-icon { - @extend .content-type; - background-image: url('../img/content-types/sequential.png'); -} - -.chapter-icon { - @extend .content-type; - background-image: url('../img/content-types/chapter.png'); -} - -.module-icon { - @extend .content-type; - background-image: url('../img/content-types/module.png'); -} - -.module-icon { - @extend .content-type; - background-image: url('../img/content-types/module.png'); -} diff --git a/cms/static/sass/_course-info.scss b/cms/static/sass/_course-info.scss deleted file mode 100644 index 5a2a5a9432..0000000000 --- a/cms/static/sass/_course-info.scss +++ /dev/null @@ -1,218 +0,0 @@ -.course-info { - h2 { - margin-bottom: 24px; - font-size: 22px; - font-weight: 300; - } - - .course-info-wrapper { - display: table; - width: 100%; - clear: both; - } - - .main-column, - .course-handouts { - float: none; - display: table-cell; - } - - .main-column { - border-radius: 3px 0 0 3px; - border-right-color: $mediumGrey; - } - - .CodeMirror { - border: 1px solid #3c3c3c; - background: #fff; - color: #3c3c3c; - } -} - -.course-updates { - padding: 30px 40px; - margin: 0; - - .update-list > li { - padding: 34px 0 42px; - border-top: 1px solid #cbd1db; - - &:first-child { - padding-top: 0; - border: none; - } - - &.editing { - position: relative; - z-index: 1001; - padding: 0; - border-top: none; - border-radius: 3px; - background: #fff; - - .post-preview { - display: none; - } - } - - h1 { - float: none; - font-size: 24px; - font-weight: 300; - } - - h2 { - margin-bottom: 18px; - font-size: 14px; - font-weight: 700; - line-height: 30px; - color: #646464; - letter-spacing: 1px; - text-transform: uppercase; - } - - h3 { - margin: 34px 0 11px; - font-size: 16px; - font-weight: 700; - } - } - - .update-contents { - p { - font-size: 16px; - line-height: 25px; - } - - p + p { - margin-top: 25px; - } - - .primary { - border: 1px solid #ddd; - background: #f6f6f6; - padding: 20px; - } - - ol, ul { - margin: 1em 0; - padding: 0 0 0 1em; - color: $baseFontColor; - - li { - margin-bottom: 0.708em; - } - } - - ol { - list-style: decimal outside none; - } - - ul { - list-style: disc outside none; - } - - pre { - margin: 1em 0; - color: $baseFontColor; - font-family: monospace, serif; - font-size: 1em; - white-space: pre-wrap; - word-wrap: break-word; - } - - code { - color: $baseFontColor; - font-family: monospace, serif; - background: none; - padding: 0; - } - } - - .new-update-button { - @include blue-button; - display: block; - text-align: center; - padding: 18px 0; - margin-bottom: 28px; - } - - .new-update-form { - @include edit-box; - margin-bottom: 24px; - padding: 30px; - border: none; - - textarea { - height: 180px; - } - } - - .post-actions { - float: right; - - .edit-button, - .delete-button{ - float: left; - @include white-button; - padding: 3px 10px 4px; - margin-left: 7px; - font-size: 12px; - font-weight: 400; - - .edit-icon, - .delete-icon { - margin-right: 4px; - } - } - } -} - -.course-handouts { - width: 30%; - padding: 20px 30px; - margin: 0; - border-radius: 0 3px 3px 0; - border-left: none; - background: $lightGrey; - - h2 { - font-size: 18px; - font-weight: 700; - } - - .edit-button { - float: right; - @include white-button; - padding: 3px 10px 4px; - margin-left: 7px; - font-size: 12px; - font-weight: 400; - - .edit-icon, - .delete-icon { - margin-right: 4px; - } - } - - .handouts-content { - font-size: 14px; - } - - .treeview-handoutsnav li { - margin-bottom: 12px; - } -} - -.edit-handouts-form { - @include edit-box; - position: absolute; - right: 0; - z-index: 10001; - width: 800px; - padding: 30px; - - textarea { - height: 300px; - } -} \ No newline at end of file diff --git a/cms/static/sass/_courseware.scss b/cms/static/sass/_courseware.scss deleted file mode 100644 index 45ea111b6f..0000000000 --- a/cms/static/sass/_courseware.scss +++ /dev/null @@ -1,689 +0,0 @@ - -input.courseware-unit-search-input { - float: left; - width: 260px; - background-color: #fff; -} - -.branch { - - .section-item { - @include clearfix(); - - .details { - display: block; - float: left; - margin-bottom: 0; - width: 650px; - } - - .gradable-status { - float: right; - position: relative; - top: -4px; - right: 50px; - width: 145px; - - .status-label { - position: absolute; - top: 2px; - right: -5px; - display: none; - width: 110px; - padding: 5px 40px 5px 10px; - @include border-radius(3px); - color: $lightGrey; - text-align: right; - font-size: 12px; - font-weight: bold; - line-height: 16px; - } - - .menu-toggle { - z-index: 10; - position: absolute; - top: 0; - right: 5px; - padding: 5px; - color: $mediumGrey; - - &:hover, &.is-active { - color: $blue; - } - } - - .menu { - z-index: 1; - display: none; - opacity: 0.0; - position: absolute; - top: -1px; - left: 5px; - margin: 0; - padding: 8px 12px; - background: $white; - border: 1px solid $mediumGrey; - font-size: 12px; - @include border-radius(4px); - @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); - @include transition(opacity .15s); - - - li { - width: 115px; - margin-bottom: 3px; - padding-bottom: 3px; - border-bottom: 1px solid $lightGrey; - - &:last-child { - margin-bottom: 0; - padding-bottom: 0; - border: none; - - a { - color: $darkGrey; - } - } - } - - a { - color: $blue; - - &.is-selected { - font-weight: bold; - } - } - } - - // dropdown state - &.is-active { - - .menu { - z-index: 1000; - display: block; - opacity: 1.0; - } - - .menu-toggle { - z-index: 10000; - } - } - - // set state - &.is-set { - - .menu-toggle { - color: $blue; - } - - .status-label { - display: block; - color: $blue; - } - } - } - } - } - - -.courseware-section { - position: relative; - background: #fff; - border-radius: 3px; - border: 1px solid $mediumGrey; - margin-top: 15px; - padding-bottom: 12px; - @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.1)); - - &:first-child { - margin-top: 0; - } - - &.collapsed { - padding-bottom: 0; - } - - label { - float: left; - line-height: 29px; - } - - .datepair { - float: left; - margin-left: 10px; - } - - .section-published-date { - position: absolute; - top: 19px; - right: 90px; - padding: 4px 10px; - border-radius: 3px; - background: $lightGrey; - text-align: right; - - .published-status { - font-size: 12px; - margin-right: 15px; - - strong { - font-weight: bold; - } - } - - .schedule-button { - @include blue-button; - } - - .edit-button { - @include blue-button; - } - - .schedule-button, - .edit-button { - font-size: 11px; - padding: 3px 15px 5px; - } - } - - .datepair .date, - .datepair .time { - padding-left: 0; - padding-right: 0; - border: none; - background: none; - @include box-shadow(none); - font-size: 13px; - font-weight: bold; - color: $blue; - cursor: pointer; - } - - .datepair .date { - width: 80px; - } - - .datepair .time { - width: 65px; - } - - &.collapsed .subsection-list, - .collapsed .subsection-list, - .collapsed > ol { - display: none !important; - } - - header { - min-height: 75px; - @include clearfix(); - - .item-details, .section-published-date { - - } - - .item-details { - display: inline-block; - padding: 20px 0 10px 0; - @include clearfix(); - - .section-name { - float: left; - margin-right: 10px; - width: 350px; - font-size: 19px; - font-weight: bold; - color: $blue; - } - - .section-name-span { - cursor: pointer; - @include transition(color .15s); - - &:hover { - color: $orange; - } - } - - .section-name-edit { - position: relative; - width: 400px; - background: $white; - - input { - font-size: 16px; - } - - .save-button { - @include blue-button; - padding: 7px 20px 7px; - margin-right: 5px; - } - - .cancel-button { - @include white-button; - padding: 7px 20px 7px; - } - } - - .section-published-date { - float: right; - width: 265px; - margin-right: 220px; - @include border-radius(3px); - background: $lightGrey; - - .published-status { - font-size: 12px; - margin-right: 15px; - - strong { - font-weight: bold; - } - } - - .schedule-button { - @include blue-button; - } - - .edit-button { - @include blue-button; - } - - .schedule-button, - .edit-button { - font-size: 11px; - padding: 3px 15px 5px; - - } - } - - .gradable-status { - position: absolute; - top: 20px; - right: 70px; - width: 145px; - - .status-label { - position: absolute; - top: 0; - right: 2px; - display: none; - width: 100px; - padding: 10px 35px 10px 10px; - @include border-radius(3px); - background: $lightGrey; - color: $lightGrey; - text-align: right; - font-size: 12px; - font-weight: bold; - line-height: 16px; - } - - .menu-toggle { - z-index: 10; - position: absolute; - top: 2px; - right: 5px; - padding: 5px; - color: $lightGrey; - - &:hover, &.is-active { - color: $blue; - } - } - - .menu { - z-index: 1; - display: none; - opacity: 0.0; - position: absolute; - top: -1px; - left: 2px; - margin: 0; - padding: 8px 12px; - background: $white; - border: 1px solid $mediumGrey; - font-size: 12px; - @include border-radius(4px); - @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); - @include transition(opacity .15s); - @include transition(display .15s); - - - li { - width: 115px; - margin-bottom: 3px; - padding-bottom: 3px; - border-bottom: 1px solid $lightGrey; - - &:last-child { - margin-bottom: 0; - padding-bottom: 0; - border: none; - - a { - color: $darkGrey; - } - } - } - - a { - - &.is-selected { - font-weight: bold; - } - } - } - - // dropdown state - &.is-active { - - .menu { - z-index: 1000; - display: block; - opacity: 1.0; - } - - - .menu-toggle { - z-index: 10000; - } - } - - // set state - &.is-set { - - .menu-toggle { - color: $blue; - } - - .status-label { - display: block; - color: $blue; - } - } - - float: left; - padding: 21px 0 0; - } - } - - .item-actions { - margin-top: 21px; - margin-right: 12px; - - .edit-button, - .delete-button { - margin-top: -3px; - } - } - - .expand-collapse-icon { - float: left; - margin: 29px 6px 16px 16px; - @include transition(none); - - &.expand { - background-position: 0 0; - } - - &.collapsed { - - } - } - - .drag-handle { - margin-left: 11px; - } - } - - h3 { - font-size: 19px; - font-weight: 700; - color: $blue; - } - - .section-name-span { - cursor: pointer; - @include transition(color .15s); - - &:hover { - color: $orange; - } - } - - .section-name-form { - margin-bottom: 15px; - } - - .section-name-edit { - input { - font-size: 16px; - } - - .save-button { - @include blue-button; - padding: 7px 20px 7px; - margin-right: 5px; - } - - .cancel-button { - @include white-button; - padding: 7px 20px 7px; - } - } - - h4 { - font-size: 12px; - color: #878e9d; - - strong { - font-weight: bold; - } - } - - .list-header { - @include linear-gradient(top, transparent, rgba(0, 0, 0, .1)); - background-color: #ced2db; - border-radius: 3px 3px 0 0; - } - - .subsection-list { - margin: 0 12px; - - > ol { - @include tree-view; - border-top-width: 0; - } - } - - &.new-section { - - header { - height: auto; - @include clearfix(); - } - - .expand-collapse-icon { - visibility: hidden; - } - - .item-details { - padding: 25px 0 0 0; - - .section-name { - float: none; - width: 100%; - } - } - } -} - -.toggle-button-sections { - display: none; - position: relative; - float: right; - margin-top: 10px; - - font-size: 13px; - color: $darkGrey; - - &.is-shown { - display: block; - } - - .ss-icon { - @include border-radius(20px); - position: relative; - top: -1px; - display: inline-block; - margin-right: 2px; - line-height: 5px; - font-size: 11px; - } - - .label { - display: inline-block; - } -} - -.new-section-name, -.new-subsection-name-input { - width: 515px; -} - -.new-section-name-save, -.new-subsection-name-save { - @include blue-button; - padding: 4px 20px 7px; - margin: 0 5px; - color: #fff !important; -} - -.new-section-name-cancel, -.new-subsection-name-cancel { - @include white-button; - padding: 4px 20px 7px; - color: #8891a1 !important; -} - -.dummy-calendar { - display: none; - position: absolute; - top: 55px; - left: 110px; - z-index: 9999; - border: 1px solid #3C3C3C; - @include box-shadow(0 1px 15px rgba(0, 0, 0, .2)); -} - -.unit-name-input { - padding: 20px 40px; - - label { - display: block; - } - - input { - width: 100%; - font-size: 20px; - } -} - -.preview { - background: url(../img/preview.jpg) center top no-repeat; -} - -.edit-subsection-publish-settings { - display: none; - position: fixed; - top: 100px; - left: 50%; - z-index: 99999; - width: 600px; - margin-left: -300px; - background: #fff; - text-align: center; - - .settings { - padding: 40px; - } - - h3 { - font-size: 34px; - font-weight: 300; - } - - .picker { - margin: 30px 0 65px; - } - - .description { - margin-top: 30px; - font-size: 14px; - line-height: 20px; - } - - strong { - font-weight: 700; - } - - .start-date, - .start-time { - font-size: 19px; - } - - .save-button { - @include blue-button; - margin-right: 10px; - } - - .cancel-button { - @include white-button; - } - - .save-button, - .cancel-button { - font-size: 16px; - } -} - -.collapse-all-button { - float: right; - margin-top: 10px; - font-size: 13px; - color: $darkGrey; -} - -// sort/drag and drop -.ui-droppable { - @include transition (padding 0.5s ease-in-out 0s); - min-height: 20px; - padding: 0; - - &.dropover { - padding: 15px 0; - } -} - -.ui-draggable-dragging { - @include box-shadow(0 1px 2px rgba(0, 0, 0, .3)); - border: 1px solid $darkGrey; - opacity : 0.2; - &:hover { - opacity : 1.0; - .section-item { - background: $yellow !important; - } - } - - // hiding unit button - temporary fix until this semantically corrected - .new-unit-item { - display: none; - } -} - -ol.ui-droppable .branch:first-child .section-item { - border-top: none; -} - diff --git a/cms/static/sass/_dashboard.scss b/cms/static/sass/_dashboard.scss deleted file mode 100644 index 0d4d046e57..0000000000 --- a/cms/static/sass/_dashboard.scss +++ /dev/null @@ -1,114 +0,0 @@ -.class-list { - margin-top: 20px; - border-radius: 3px; - border: 1px solid $darkGrey; - background: #fff; - @include box-shadow(0 1px 2px rgba(0, 0, 0, .1)); - - li { - position: relative; - border-bottom: 1px solid $mediumGrey; - - &:last-child { - border-bottom: none; - } - - .class-link { - z-index: 100; - display: block; - padding: 20px 25px; - line-height: 1.3; - - &:hover { - background: $paleYellow; - - + .view-live-button { - opacity: 1.0; - pointer-events: auto; - } - } - } - } - - .class-name { - display: block; - font-size: 19px; - font-weight: 300; - } - - .detail { - font-size: 14px; - font-weight: 400; - margin-right: 20px; - color: #3c3c3c; - } - - // view live button - .view-live-button { - z-index: 10000; - position: absolute; - top: 15px; - right: $baseline; - padding: ($baseline/4) ($baseline/2); - opacity: 0; - pointer-events: none; - - &:hover { - opacity: 1.0; - pointer-events: auto; - } - } -} - -.new-course { - padding: 15px 25px; - margin-top: 20px; - border-radius: 3px; - border: 1px solid $darkGrey; - background: #fff; - box-shadow: 0 1px 2px rgba(0, 0, 0, .1); - @include clearfix; - - .row { - margin-bottom: 15px; - @include clearfix; - } - - .column { - float: left; - width: 48%; - } - - .column:first-child { - margin-right: 4%; - } - - .course-info { - width: 600px; - } - - label { - display: block; - font-size: 13px; - font-weight: 700; - } - - .new-course-org, - .new-course-number, - .new-course-name { - width: 100%; - } - - .new-course-name { - font-size: 19px; - font-weight: 300; - } - - .new-course-save { - @include blue-button; - } - - .new-course-cancel { - @include white-button; - } -} \ No newline at end of file diff --git a/cms/static/sass/_export.scss b/cms/static/sass/_export.scss deleted file mode 100644 index e1ab7eb605..0000000000 --- a/cms/static/sass/_export.scss +++ /dev/null @@ -1,123 +0,0 @@ -.export { - .export-overview { - @extend .window; - @include clearfix; - padding: 30px 40px; - } - - .description { - float: left; - width: 62%; - margin-right: 3%; - font-size: 14px; - - h2 { - font-weight: 700; - font-size: 19px; - margin-bottom: 20px; - } - - strong { - font-weight: 700; - } - - p + p { - margin-top: 20px; - } - - ul { - margin: 20px 0; - list-style: disc inside; - - li { - margin: 0 0 5px 0; - } - } - } - - .export-form-wrapper { - - .export-form { - float: left; - width: 35%; - padding: 25px 30px 35px; - @include box-sizing(border-box); - border: 1px solid $mediumGrey; - border-radius: 3px; - background: $lightGrey; - text-align: center; - - h2 { - margin-bottom: 30px; - font-size: 26px; - font-weight: 300; - } - - .error-block { - display: none; - margin-bottom: 15px; - font-size: 13px; - } - - .error-block { - color: $error-red; - } - - .button-export { - @include green-button; - padding: 10px 50px 11px; - font-size: 17px; - } - - .message-status { - margin-top: 10px; - font-size: 12px; - } - - .progress-bar { - display: none; - width: 350px; - height: 30px; - margin: 30px auto 10px; - border: 1px solid $blue; - - &.loaded { - border-color: #66b93d; - - .progress-fill { - background: #66b93d; - } - } - } - - .progress-fill { - width: 0%; - height: 30px; - background: $blue; - color: #fff; - line-height: 48px; - } - } - - // downloading state - &.is-downloading { - - .progress-bar { - display: block; - } - - .button-export { - padding: 10px 50px 11px; - font-size: 17px; - - &.disabled { - - pointer-events: none; - cursor: default; - } - } - } - } - - -} \ No newline at end of file diff --git a/cms/static/sass/_extends.scss b/cms/static/sass/_extends.scss deleted file mode 100644 index 5907481bd1..0000000000 --- a/cms/static/sass/_extends.scss +++ /dev/null @@ -1,78 +0,0 @@ -.faded-hr-divider { - @include background-image(linear-gradient(180deg, rgba(200,200,200, 0) 0%, - rgba(200,200,200, 1) 50%, - rgba(200,200,200, 0))); - height: 1px; - width: 100%; -} - -.faded-hr-divider-medium { - @include background-image(linear-gradient(180deg, rgba(240,240,240, 0) 0%, - rgba(240,240,240, 1) 50%, - rgba(240,240,240, 0))); - height: 1px; - width: 100%; -} - -.faded-hr-divider-light { - @include background-image(linear-gradient(180deg, rgba(255,255,255, 0) 0%, - rgba(255,255,255, 0.8) 50%, - rgba(255,255,255, 0))); - height: 1px; - width: 100%; -} - -.faded-vertical-divider { - @include background-image(linear-gradient(90deg, rgba(200,200,200, 0) 0%, - rgba(200,200,200, 1) 50%, - rgba(200,200,200, 0))); - height: 100%; - width: 1px; -} - -.faded-vertical-divider-light { - @include background-image(linear-gradient(90deg, rgba(255,255,255, 0) 0%, - rgba(255,255,255, 0.6) 50%, - rgba(255,255,255, 0))); - height: 100%; - width: 1px; -} - -.vertical-divider { - @extend .faded-vertical-divider; - position: relative; - - &::after { - @extend .faded-vertical-divider-light; - content: ""; - display: block; - position: absolute; - left: 1px; - } -} - -.horizontal-divider { - border: none; - @extend .faded-hr-divider; - position: relative; - - &::after { - @extend .faded-hr-divider-light; - content: ""; - display: block; - position: absolute; - top: 1px; - } -} - -.fade-right-hr-divider { - @include background-image(linear-gradient(180deg, rgba(200,200,200, 0) 0%, - rgba(200,200,200, 1))); - border: none; -} - -.fade-left-hr-divider { - @include background-image(linear-gradient(180deg, rgba(200,200,200, 1) 0%, - rgba(200,200,200, 0))); - border: none; -} \ No newline at end of file diff --git a/cms/static/sass/_fonts.scss b/cms/static/sass/_fonts.scss deleted file mode 100644 index 92a2e185b7..0000000000 --- a/cms/static/sass/_fonts.scss +++ /dev/null @@ -1,36 +0,0 @@ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - src: local('Open Sans Bold'), local('OpenSans-Bold'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/k3k702ZOKiLJc3WVjuplzKRDOzjiPcYnFooOUGCOsRk.woff) format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - src: local('Open Sans Light'), local('OpenSans-Light'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/DXI1ORHCpsQm3Vp6mXoaTaRDOzjiPcYnFooOUGCOsRk.woff) format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/PRmiXeptR36kaC0GEAetxhbnBKKEOwRKgsHDreGcocg.woff) format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - src: local('Open Sans Light Italic'), local('OpenSansLight-Italic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/PRmiXeptR36kaC0GEAetxvR_54zmj3SbGZQh3vCOwvY.woff) format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - src: local('Open Sans Italic'), local('OpenSans-Italic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/xjAJXh38I15wypJXxuGMBrrIa-7acMAeDBVuclsi6Gc.woff) format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - src: local('Open Sans'), local('OpenSans'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/cJZKeOuBrn4kERxqtaUH3bO3LdcAZYWl9Si6vvxL-qU.woff) format('woff'); -} diff --git a/cms/static/sass/_footer.scss b/cms/static/sass/_footer.scss deleted file mode 100644 index 66a9ce0e95..0000000000 --- a/cms/static/sass/_footer.scss +++ /dev/null @@ -1,48 +0,0 @@ -//studio global footer -.wrapper-footer { - margin: ($baseline*1.5) 0 $baseline 0; - padding: $baseline; - position: relative; - width: 100%; - - footer.primary { - @include clearfix(); - @include font-size(13); - max-width: $fg-max-width; - min-width: $fg-min-width; - width: flex-grid(12); - margin: 0 auto; - padding-top: $baseline; - border-top: 1px solid $gray-l4; - color: $gray-l2; - - .colophon { - width: flex-grid(4, 12); - float: left; - margin-right: flex-gutter(2); - } - - .nav-peripheral { - width: flex-grid(6, 12); - float: right; - text-align: right; - - .nav-item { - display: inline-block; - margin-right: ($baseline/2); - - &:last-child { - margin-right: 0; - } - } - } - - a { - color: $gray-l1; - - &:hover, &:active { - color: $blue; - } - } - } -} \ No newline at end of file diff --git a/cms/static/sass/_graphics.scss b/cms/static/sass/_graphics.scss deleted file mode 100644 index 300cf3b692..0000000000 --- a/cms/static/sass/_graphics.scss +++ /dev/null @@ -1,336 +0,0 @@ -.expand-collapse-icon { - position: relative; - display: inline-block; - width: 9px; - height: 11px; - margin-right: 10px; - background: url(../img/expand-collapse-icons.png) no-repeat; - @include transition(none); - - &.expand { - top: 1px; - background-position: 0 0; - } - - &.collapse { - top: -1px; - background-position: 0 -11px; - } -} - -.sequence-icon { - display: inline-block; - width: 15px; - height: 9px; - margin-right: 5px; - background: url(../img/sequence-icon.png) no-repeat; -} - -.video-icon { - display: inline-block; - width: 14px; - height: 12px; - margin-right: 5px; - background: url(../img/video-icon.png) no-repeat; -} - -.upload-icon { - display: inline-block; - width: 22px; - height: 13px; - margin-right: 5px; - background: url(../img/upload-icon.png) no-repeat; -} - -.list-icon { - display: inline-block; - width: 14px; - height: 10px; - margin-right: 5px; - background: url(../img/list-icon.png) no-repeat; -} - -.close-icon { - display: inline-block; - width: 13px; - height: 12px; - background: url(../img/close-icon.png) no-repeat; -} - -.home-icon { - display: inline-block; - width: 19px; - height: 16px; - background: url(../img/home-icon.png) no-repeat; -} - -.small-home-icon { - display: inline-block; - width: 16px; - height: 14px; - background: url(../img/small-home-icon.png) no-repeat; -} - -.log-out-icon { - display: inline-block; - width: 15px; - height: 13px; - background: url(../img/log-out-icon.png) no-repeat; -} - -.collapse-all-icon { - display: inline-block; - width: 15px; - height: 9px; - background: url(../img/collapse-all-icon.png) no-repeat; -} - -.calendar-icon { - display: inline-block; - width: 12px; - height: 11px; - margin-right: 5px; - background: url(../img/calendar-icon.png) no-repeat; -} - -.edit-icon { - display: inline-block; - width: 12px; - height: 12px; - margin-right: 2px; - background: url(../img/edit-icon.png) no-repeat; - - &.white { - background: url(../img/edit-icon-white.png) no-repeat; - } -} - -.visibility-toggle { - .toggle-icon { - display: inline-block; - width: 27px; - height: 20px; - background: url(../img/small-toggle-icons.png) no-repeat; - background-position: 0 -34px; - } - - &.hidden .toggle-icon { - background-position: 0 -4px; - } - - &.both .toggle-icon { - background-position: 0 -64px; - } -} - - -.delete-icon { - display: inline-block; - width: 10px; - height: 11px; - margin-right: 2px; - background: url(../img/delete-icon.png) no-repeat; - - &.white { - background: url(../img/delete-icon-white.png) no-repeat; - } -} - -.drag-handle { - display: inline-block; - float: right; - width: 7px; - height: 22px; - margin-left: 10px; - background: url(../img/drag-handles.png) no-repeat; - cursor: move; -} - -.draft-tag, -.public-tag, -.private-tag { - margin-left: 3px; - font-size: 9px; - font-weight: 600; - text-transform: uppercase; - color: #a4aab7; -} - -.draft-tag { - color: #9f7d10; -} - -.plus-icon { - display: inline-block; - width: 11px; - height: 11px; - margin-right: 8px; - background: url(../img/plus-icon.png) no-repeat; - - &.white { - background: url(../img/plus-icon-white.png) no-repeat; - } -} - -.plus-icon-small { - display: inline-block; - width: 6px; - height: 6px; - margin-right: 8px; - background: url(../img/plus-icon-small.png) no-repeat center; -} - -.folder-icon { - display: inline-block; - width: 15px; - height: 11px; - margin-right: 4px; - background: url(../img/folder-icon.png) no-repeat; -} - -.new-folder-icon { - display: inline-block; - width: 23px; - height: 11px; - margin-right: 8px; - background: url(../img/new-folder-icon.png) no-repeat; -} - -.file-icon { - display: inline-block; - width: 10px; - height: 11px; - margin-right: 8px; - background: url(../img/file-icon.png) no-repeat; -} - -.new-unit-icon { - display: inline-block; - width: 23px; - height: 12px; - margin-right: 8px; - background: url(../img/new-unit-icon.png) right no-repeat; -} - -.new-policy-icon { - display: inline-block; - width: 23px; - height: 12px; - margin-right: 8px; - background: url(../img/new-unit-icon.png) right no-repeat; -} - -.textbook-icon { - display: inline-block; - width: 32px; - height: 32px; - margin-right: 8px; - vertical-align: middle; - background: url(../img/textbook-icon.png) no-repeat; -} - -.slides-icon { - display: inline-block; - width: 32px; - height: 32px; - margin-right: 8px; - vertical-align: middle; - background: url(../img/slides-icon.png) no-repeat; -} - -.large-slide-icon { - display: inline-block; - width: 100px; - height: 60px; - margin-right: 5px; - background: url(../img/large-slide-icon.png) center no-repeat; -} - -.large-html-icon { - display: inline-block; - width: 100px; - height: 60px; - margin-right: 5px; - background: url(../img/html-icon.png) center no-repeat; -} - -.large-openended-icon { - display: inline-block; - width: 100px; - height: 60px; - margin-right: 5px; - background: url(../img/large-openended-icon.png) center no-repeat; -} - -.large-annotations-icon { - display: inline-block; - width: 100px; - height: 60px; - margin-right: 5px; - background: url(../img/large-annotations-icon.png) center no-repeat; -} - -.large-advanced-icon { - display: inline-block; - width: 100px; - height: 60px; - margin-right: 5px; - background: url(../img/large-advanced-icon.png) center no-repeat; -} - -.large-textbook-icon { - display: inline-block; - width: 100px; - height: 60px; - margin-right: 5px; - background: url(../img/large-textbook-icon.png) center no-repeat; -} - -.large-discussion-icon { - display: inline-block; - width: 100px; - height: 60px; - margin-right: 5px; - background: url(../img/large-discussion-icon.png) center no-repeat; -} - -.large-freeform-icon { - display: inline-block; - width: 100px; - height: 60px; - margin-right: 5px; - background: url(../img/large-freeform-icon.png) center no-repeat; -} - -.large-problem-icon { - display: inline-block; - width: 100px; - height: 60px; - margin-right: 5px; - background: url(../img/large-problem-icon.png) center no-repeat; -} - -.large-video-icon { - display: inline-block; - width: 100px; - height: 60px; - margin-right: 5px; - background: url(../img/large-video-icon.png) center no-repeat; -} - -.spinner-icon { - display: inline-block; - width: 20px; - height: 20px; - margin-left: 10px; - vertical-align: middle; - background: url(../img/blue-spinner.gif) no-repeat; -} - -.spinner-in-field-icon { - display: inline-block; - width: 14px; - height: 14px; - vertical-align: middle; - background: url(../img/spinner-in-field.gif) no-repeat; -} diff --git a/cms/static/sass/_header.scss b/cms/static/sass/_header.scss deleted file mode 100644 index ca1092f44b..0000000000 --- a/cms/static/sass/_header.scss +++ /dev/null @@ -1,562 +0,0 @@ -// studio global header and navigation -// ==================== - -.wrapper-header { - margin: 0 0 ($baseline*1.5) 0; - padding: $baseline; - border-bottom: 1px solid $gray; - @include box-shadow(0 1px 5px 0 rgba(0,0,0, 0.1)); - background: $white; - height: 76px; - position: relative; - width: 100%; - z-index: 10; - - a { - color: $baseFontColor; - display: block; - - &:hover, &:active { - color: $blue; - } - } - - header.primary { - @include clearfix(); - max-width: $fg-max-width; - min-width: $fg-min-width; - width: flex-grid(12); - margin: 0 auto; - color: $gray-l1; - } - - nav .nav-item { - display: inline-block; - } -} - -// ==================== - -// basic layout -.wrapper-left, .wrapper-right { - @include box-sizing(border-box); -} - -.wrapper-left { - width: flex-grid(10, 12); - float: left; - margin-right: flex-gutter(); -} - -.wrapper-right { - width: flex-grid(2, 12); - float: right; -} - -// ==================== - -// specific elements - branding -.branding, .info-course, .nav-course, .nav-account, .nav-unauth, .nav-pitch { - display: inline-block; - vertical-align: top; -} - -.branding { - position: relative; - margin: 0 ($baseline/2) 0 0; - padding-right: ($baseline*0.75); - - a { - @include text-hide(); - display: block; - width: 164px; - height: 32px; - background: transparent url('../img/logo-edx-studio.png') 0 0 no-repeat; - } -} - -// ==================== - -// specific elements - course name/info -.info-course { - @include font-size(14); - position: relative; - margin: -3px ($baseline/2) 0 0; - padding-right: ($baseline*0.75); - - &:before { - @extend .faded-vertical-divider; - content: ""; - display: block; - height: 50px; - position: absolute; - right: 1px; - top: -8px; - width: 1px; - } - - &:after { - @extend .faded-vertical-divider-light; - content: ""; - display: block; - height: 50px; - position: absolute; - right: 0px; - top: -12px; - width: 1px; - } - - .course-org { - margin-right: ($baseline/4); - } - - .course-number, .course-org { - @include font-size(12); - display: inline-block; - } - - .course-title { - display: block; - width: 100%; - max-width: 220px; - overflow: hidden; - margin-top: -4px; - white-space: nowrap; - text-overflow: ellipsis; - @include font-size(16); - font-weight: 600; - } -} - -// ==================== - -// specific elements - course nav -.nav-course { - width: 335px; - margin-top: -($baseline/4); - @include font-size(14); - - > ol > .nav-item { - vertical-align: bottom; - margin: 0 ($baseline/2) 0 0; - - &:last-child { - margin-right: 0; - } - - .title { - display: block; - padding: 5px; - text-transform: uppercase; - font-weight: 600; - color: $gray-d3; - - .label-prefix { - display: block; - @include font-size(11); - font-weight: 400; - } - } - - // specific nav items - &.nav-course-courseware { - } - - &.nav-course-settings { - } - - &.nav-course-tools { - } - } -} - -// ==================== - -// specific elements - account-based nav -.nav-account { - width: 100%; - margin-top: ($baseline*0.75); - @include font-size(14); - text-align: right; - - .nav-account-username { - width: 100%; - - .icon-user { - display: inline-block; - vertical-align: middle; - margin-right: 3px; - @include font-size(12); - } - - .account-username { - display: inline-block; - vertical-align: middle; - width: 80%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - .icon-expand { - display: inline-block; - vertical-align: middle; - } - } -} - -// ==================== - -// UI - dropdown -.nav-dropdown { - - .nav-item { - position: relative; - - .icon-expand { - @include font-size(14); - @include transition (color 0.5s ease-in-out, opacity 0.5s ease-in-out); - display: inline-block; - margin-left: 2px; - opacity: 0.5; - color: $gray-l2; - } - - &:hover { - - .icon-expand { - color: $blue; - opacity: 1.0; - } - } - } - - .wrapper-nav-sub { - position: absolute; - left: -7px; - top: 47px; - width: 140px; - opacity: 0; - pointer-events: none; - } - - .nav-sub { - @include border-radius(2px); - @include box-sizing(border-box); - @include box-shadow(0 1px 5px 0 rgba(0,0,0, 0.1)); - position: relative; - width: 100%; - border: 1px solid $gray-l2; - padding: ($baseline/4) ($baseline/2); - background: $white; - - &:after, &:before { - bottom: 100%; - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; - } - - &:after { - border-color: rgba(255, 255, 255, 0); - border-bottom-color: #fff; - border-width: 5px; - right: 3px; - margin-left: -5px; - } - - &:before { - border-color: rgba(178, 178, 178, 0); - border-bottom-color: $gray-l2; - border-width: 6px; - right: 3px; - margin-left: -6px; - } - - .nav-item { - display: block; - margin: 0 0 ($baseline/4) 0; - border-bottom: 1px solid $gray-l5; - padding: 0 0($baseline/4) 0; - @include font-size(13); - - &:last-child { - margin-bottom: 0; - border-bottom: none; - padding-bottom: 0; - } - - a { - display: block; - } - } - } - - // UI - dropdown - specific navs - &.nav-account { - - .wrapper-nav-sub { - top: 27px; - left: auto; - right: -13px; - width: 110px; - } - - .nav-sub { - text-align: left; - - .icon-expand { - top: -2px; - } - } - - .nav-sub:after { - left: auto; - right: 11px; - } - - .nav-sub:before { - left: auto; - right: 10px; - } - } - - &.nav-course { - - .nav-course-courseware { - - .nav-sub:after { - left: 88px; - } - - .nav-sub:before { - left: 88px; - } - } - - .nav-course-settings { - - .nav-sub:after { - left: 88px; - } - - .nav-sub:before { - left: 88px; - } - } - - .nav-course-tools { - - .wrapper-nav-sub { - top: ($baseline*1.5); - width: 100px; - } - - .nav-sub:after { - left: 68px; - } - - .nav-sub:before { - left: 68px; - } - } - } -} - -// ==================== - -// STATE: is-signed in -.is-signedin { - - &.course .branding { - - &:before { - @extend .faded-vertical-divider; - content: ""; - display: block; - height: 50px; - position: absolute; - right: 1px; - top: -8px; - width: 1px; - } - - &:after { - @extend .faded-vertical-divider-light; - content: ""; - display: block; - height: 50px; - position: absolute; - right: 0px; - top: -12px; - width: 1px; - } - } -} - -// ==================== - -// STATE: not signed in -.not-signedin { - - .wrapper-left { - width: flex-grid(4, 12); - } - - .wrapper-right { - width: flex-grid(8, 12); - } - - // STATE: not signed in - unauthenticated nav - .nav-not-signedin { - float: right; - margin-top: ($baseline/4); - - .nav-item { - @include font-size(16); - vertical-align: middle; - margin: 0 $baseline 0 0; - - &:last-child { - margin-right: 0; - } - - .action { - margin-top: -($baseline/4); - display: inline-block; - padding: ($baseline/4) ($baseline/2); - } - } - - // STATE: not signed in - specific items - .nav-not-signedin-help { - - } - - .nav-not-signedin-signup { - margin-right: ($baseline/2); - - .action-signup { - @include blue-button; - @include transition(all .15s); - @include font-size(14); - padding: ($baseline/4) ($baseline/2); - text-transform: uppercase; - font-weight: 600; - } - } - - .nav-not-signedin-signin { - - .action-signin { - @include white-button; - @include transition(all .15s); - @include font-size(14); - padding: ($baseline/4) ($baseline/2); - text-transform: uppercase; - font-weight: 600; - } - } - } -} - -// ==================== - -// STATE: active/current nav states - -.nav-item.is-current, -body.howitworks .nav-not-signedin-hiw, - -// dashboard -body.dashboard .nav-account-dashboard, - -// course content -body.course.outline .nav-course-courseware .title, -body.course.updates .nav-course-courseware .title, -body.course.pages .nav-course-courseware .title, -body.course.uploads .nav-course-courseware .title, - -body.course.outline .nav-course-courseware-outline, -body.course.updates .nav-course-courseware-updates, -body.course.pages .nav-course-courseware-pages, -body.course.uploads .nav-course-courseware-uploads, - -// course settings -body.course.schedule .nav-course-settings .title, -body.course.grading .nav-course-settings .title, -body.course.team .nav-course-settings .title, -body.course.advanced .nav-course-settings .title, - -body.course.schedule .nav-course-settings-schedule, -body.course.grading .nav-course-settings-grading, -body.course.team .nav-course-settings-team, -body.course.advanced .nav-course-settings-advanced, - -// course tools -body.course.import .nav-course-tools .title, -body.course.export .nav-course-tools .title, - -body.course.import .nav-course-tools-import, -body.course.export .nav-course-tools-export, - -{ - - color: $blue; - - a { - color: $blue; - pointer-events: none; - } -} - -body.signup .nav-not-signedin-signin { - - a { - background-color: #d9e3ee; - color: #6d788b; - } -} - -body.signin .nav-not-signedin-signup { - - a { - background-color: #62aaf5; - color: #fff; - } -} - -// ==================== - -// STATE: js enabled -.js { - - .nav-dropdown { - - .nav-item .title { - outline: 0; - cursor: pointer; - - &:hover, &:active, &.is-selected { - color: $blue; - - .icon-expand { - color: $blue; - } - } - } - } - - .wrapper-nav-sub { - @include transition (opacity 1.0s ease-in-out 0s); - opacity: 0; - pointer-events: none; - - &.is-shown { - opacity: 1.0; - pointer-events: auto; - } - } -} \ No newline at end of file diff --git a/cms/static/sass/_import.scss b/cms/static/sass/_import.scss deleted file mode 100644 index a0a1f5e512..0000000000 --- a/cms/static/sass/_import.scss +++ /dev/null @@ -1,102 +0,0 @@ -.import { - .import-overview { - @extend .window; - @include clearfix; - padding: 30px 40px; - } - - .description { - float: left; - width: 62%; - margin-right: 3%; - font-size: 14px; - - h2 { - font-weight: 700; - font-size: 19px; - margin-bottom: 20px; - } - - strong { - font-weight: 700; - } - - p + p { - margin-top: 20px; - } - } - - .import-form { - float: left; - width: 35%; - padding: 25px 30px 35px; - @include box-sizing(border-box); - border: 1px solid $mediumGrey; - border-radius: 3px; - background: $lightGrey; - text-align: center; - - h2 { - margin-bottom: 30px; - font-size: 26px; - font-weight: 300; - } - - .file-name-block, - .error-block { - display: none; - margin-bottom: 15px; - font-size: 13px; - } - - .error-block { - color: $error-red; - } - - .choose-file-button { - @include blue-button; - padding: 10px 50px 11px; - font-size: 17px; - } - - .choose-file-button-inline { - display: block; - } - - .file-input { - display: none; - } - - .submit-button { - @include orange-button; - display: none; - max-width: 100%; - padding: 8px 20px 10px; - white-space: normal; - } - } - - .progress-bar { - display: none; - width: 350px; - height: 30px; - margin: 30px auto 10px; - border: 1px solid $blue; - - &.loaded { - border-color: #66b93d; - - .progress-fill { - background: #66b93d; - } - } - } - - .progress-fill { - width: 0%; - height: 30px; - background: $blue; - color: #fff; - line-height: 48px; - } -} \ No newline at end of file diff --git a/cms/static/sass/_index.scss b/cms/static/sass/_index.scss deleted file mode 100644 index e0f6d0f2b7..0000000000 --- a/cms/static/sass/_index.scss +++ /dev/null @@ -1,353 +0,0 @@ -// how it works/not signed in index -.index { - - &.not-signedin { - - .wrapper-header { - margin-bottom: 0; - } - - .wrapper-footer { - margin: 0; - border-top: 2px solid $gray-l3; - - footer.primary { - border: none; - margin-top: 0; - padding-top: 0; - } - } - - .wrapper-content-header, .wrapper-content-features, .wrapper-content-cta { - @include box-sizing(border-box); - margin: 0; - padding: 0 $baseline; - position: relative; - width: 100%; - } - - .content { - @include clearfix(); - @include font-size(16); - max-width: $fg-max-width; - min-width: $fg-min-width; - width: flex-grid(12); - margin: 0 auto; - color: $gray-d2; - - header { - border: none; - padding-bottom: 0; - margin-bottom: 0; - } - - h1, h2, h3, h4, h5, h6 { - color: $gray-d3; - } - - h2 { - - } - - h3 { - - } - - h4 { - - } - } - - // welcome content - .wrapper-content-header { - @include linear-gradient($blue-l1,$blue,$blue-d1); - padding-bottom: ($baseline*4); - padding-top: ($baseline*4); - } - - .content-header { - position: relative; - text-align: center; - color: $white; - - h1 { - @include font-size(52); - float: none; - margin: 0 0 ($baseline/2) 0; - border-bottom: 1px solid $blue-l1; - padding: 0; - font-weight: 500; - color: $white; - } - - .logo { - @include text-hide(); - position: relative; - top: 3px; - display: inline-block; - vertical-align: baseline; - width: 282px; - height: 57px; - background: transparent url('../img/logo-edx-studio-white.png') 0 0 no-repeat; - } - - .tagline { - @include font-size(24); - margin: 0; - color: $blue-l3; - } - } - - .arrow_box { - position: relative; - background: #fff; - border: 4px solid #000; - } - .arrow_box:after, .arrow_box:before { - top: 100%; - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; - } - - .arrow_box:after { - border-color: rgba(255, 255, 255, 0); - border-top-color: #fff; - border-width: 30px; - left: 50%; - margin-left: -30px; - } - .arrow_box:before { - border-color: rgba(0, 0, 0, 0); - border-top-color: #000; - border-width: 36px; - left: 50%; - margin-left: -36px; - } - - // feature content - .wrapper-content-features { - @include box-shadow(0 -1px ($baseline/4) $shadow); - padding-bottom: ($baseline*2); - padding-top: ($baseline*3); - background: $white; - } - - .content-features { - - .list-features { - - } - - // indiv features - .feature { - @include clearfix(); - margin: 0 0 ($baseline*2) 0; - border-bottom: 1px solid $gray-l4; - padding: 0 0 ($baseline*2) 0; - - .img { - @include box-sizing(border-box); - float: left; - width: flex-grid(3, 12); - margin-right: flex-gutter(); - - a { - @include box-sizing(border-box); - @include box-shadow(0 1px ($baseline/10) $shadow-l1); - position: relative; - top: 0; - display: block; - overflow: hidden; - border: 1px solid $gray-l3; - padding: ($baseline/4); - background: $white; - - .action-zoom { - @include transition(bottom .50s ease-in-out); - position: absolute; - bottom: -30px; - right: ($baseline/2); - opacity: 0; - - .ss-icon { - @include font-size(18); - @include border-top-radius(3px); - display: inline-block; - padding: ($baseline/4) ($baseline/2); - background: $blue; - color: $white; - text-align: center; - } - } - - &:hover { - border-color: $blue; - - .action-zoom { - opacity: 1.0; - bottom: -2px; - } - } - } - - img { - display: block; - width: 100%; - height: 100%; - } - } - - .copy { - float: left; - width: flex-grid(9, 12); - margin-top: -($baseline/4); - - h3 { - margin: 0 0 ($baseline/2) 0; - @include font-size(24); - font-weight: 600; - } - - > p { - @include font-size(18); - color: $gray-d1; - } - - strong { - color: $gray-d2; - font-weight: 500; - } - - .list-proofpoints { - @include clearfix(); - @include font-size(14); - width: flex-grid(9, 9); - margin: ($baseline*1.5) 0 0 0; - - .proofpoint { - @include box-sizing(border-box); - @include border-radius(($baseline/4)); - @include transition(color .50s ease-in-out); - position: relative; - top: 0; - float: left; - width: flex-grid(3, 9); - min-height: ($baseline*8); - margin-right: flex-gutter(); - padding: ($baseline*0.75) $baseline; - color: $gray-l1; - - .title { - @include font-size(16); - margin: 0 0 ($baseline/4) 0; - font-weight: 500; - color: $gray-d3; - } - - &:hover { - @include box-shadow(0 1px ($baseline/10) $shadow-l1); - background: $blue-l5; - top: -($baseline/5); - - .title { - color: $blue; - } - } - - &:last-child { - margin-right: 0; - } - } - } - } - - - &:last-child { - margin-bottom: 0; - border: none; - padding-bottom: 0; - } - - &:nth-child(even) { - - .img { - float: right; - margin-right: 0; - margin-left: flex-gutter(); - } - - .copy { - float: right; - text-align: right; - } - - .list-proofpoints { - - .proofpoint { - float: right; - width: flex-grid(3, 9); - margin-left: flex-gutter(); - margin-right: 0; - - &:last-child { - margin-left: 0; - } - } - } - } - } - } - - // call to action content - .wrapper-content-cta { - padding-bottom: ($baseline*2); - padding-top: ($baseline*2); - background: $white; - } - - .content-cta { - border-top: 1px solid $gray-l4; - - header { - border: none; - margin: 0; - padding: 0; - } - - .list-actions { - position: relative; - margin-top: -($baseline*1.5); - - li { - width: flex-grid(6, 12); - margin: 0 auto; - } - - .action { - display: block; - width: 100%; - text-align: center; - } - - .action-primary { - @include blue-button; - @include transition(all .15s); - @include font-size(18); - padding: ($baseline*0.75) ($baseline/2); - font-weight: 600; - text-align: center; - text-transform: uppercase; - } - - .action-secondary { - @include font-size(14); - margin-top: ($baseline/2); - } - } - } - } -} \ No newline at end of file diff --git a/cms/static/sass/_jquery-ui-calendar.scss b/cms/static/sass/_jquery-ui-calendar.scss deleted file mode 100644 index 96cffc059f..0000000000 --- a/cms/static/sass/_jquery-ui-calendar.scss +++ /dev/null @@ -1,53 +0,0 @@ -.ui-datepicker { - border-color: $darkGrey; - border-radius: 2px; - background: #fff; - font-family: $sans-serif; - font-size: 12px; - @include box-shadow(0 5px 10px rgba(0, 0, 0, 0.1)); - - .ui-widget-header { - background: $darkGrey; - border: none; - border-radius: 2px; - } - - .ui-datepicker-next, - .ui-datepicker-prev { - @include transition(none); - - &.ui-state-hover { - border-color: transparent; - background: $mediumGrey; - - .ui-icon-circle-triangle-e, - .ui-icon-circle-triangle-w { - background-image: url(../css/vendor/ui-lightness/images/ui-icons_ffffff_256x240.png); - } - } - } - - .ui-state-default { - border-color: $mediumGrey; - color: $blue; - @include transition(none); - - &.ui-state-hover { - background: $orange; - border-color: $orange; - color: #fff; - } - } - - .ui-state-highlight { - background: $blue; - border-color: $blue; - color: #fff; - } - - .ui-state-active { - background: $orange; - border-color: $orange; - color: #fff; - } -} \ No newline at end of file diff --git a/cms/static/sass/_keyframes.scss b/cms/static/sass/_keyframes.scss deleted file mode 100644 index 7661f18980..0000000000 --- a/cms/static/sass/_keyframes.scss +++ /dev/null @@ -1,27 +0,0 @@ -@mixin bounce-in { - 0% { - opacity: 0; - @include transform(scale(.3)); - } - - 50% { - opacity: 1; - @include transform(scale(1.05)); - } - - 100% { - @include transform(scale(1)); - } -} - -@-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();} - -@mixin bounce-in-animation($duration, $timing: ease-in-out) { - @include animation-name(bounce-in); - @include animation-duration($duration); - @include animation-timing-function($timing); - @include animation-fill-mode(both); -} diff --git a/cms/static/sass/_landing.scss b/cms/static/sass/_landing.scss deleted file mode 100644 index 16f1b5b5a7..0000000000 --- a/cms/static/sass/_landing.scss +++ /dev/null @@ -1,126 +0,0 @@ -// This is a temporary page, which will be replaced once we have a more extensive course catalog and marketing site for edX labs. - -.class-landing { - - .main-wrapper { - width: 700px !important; - margin: 100px auto; - } - - .class-info { - padding: 30px 40px 40px; - @extend .window; - - hgroup { - padding-bottom: 26px; - border-bottom: 1px solid $mediumGrey; - } - - h1 { - float: none; - font-size: 30px; - font-weight: 300; - margin: 0; - } - - h2 { - color: #5d6779; - } - - .class-actions { - @include clearfix; - padding: 15px 0; - margin-bottom: 18px; - border-bottom: 1px solid $mediumGrey; - } - - .log-in-form { - @include clearfix; - padding: 15px 0 20px; - margin-bottom: 18px; - border-bottom: 1px solid $mediumGrey; - - .log-in-submit-button { - @include blue-button; - padding: 6px 20px 8px; - margin: 24px 0 0; - } - - .column { - float: left; - width: 41%; - margin-right: 1%; - - &.submit { - width: 16%; - margin-right: 0; - } - - label { - float: left; - } - } - - input { - width: 100%; - font-family: $sans-serif; - font-size: 13px; - } - - .forgot-button { - float: right; - margin-bottom: 6px; - font-size: 12px; - } - } - - .sign-up-button { - @include blue-button; - display: block; - width: 250px; - margin: auto; - } - - .log-in-button { - @include white-button; - float: right; - } - - .sign-up-button, - .log-in-button { - padding: 8px 0 12px; - font-size: 18px; - font-weight: 300; - text-align: center; - } - - .class-description { - margin-top: 30px; - font-size: 14px; - } - - p + p { - margin-top: 22px; - } - } - - .edx-labs-logo-small { - display: block; - width: 124px; - height: 30px; - margin: auto; - background: url(../img/edx-labs-logo-small.png) no-repeat; - text-indent: -9999px; - overflow: hidden; - } - - .edge-logo { - display: block; - width: 143px; - height: 39px; - margin: auto; - background: url(../images/edge-logo-small.png) no-repeat; - text-indent: -9999px; - overflow: hidden; - } -} \ No newline at end of file diff --git a/cms/static/sass/_layout.scss b/cms/static/sass/_layout.scss deleted file mode 100644 index 43308a973c..0000000000 --- a/cms/static/sass/_layout.scss +++ /dev/null @@ -1,125 +0,0 @@ -body { - @include clearfix(); - height: 100%; - font: 14px $body-font-family; - background-color: lighten($dark-blue, 62%); - background-image: url('/static/img/noise.png'); - - > section { - display: table; - table-layout: fixed; - width: 100%; - } - - > header { - background: $dark-blue; - @include background-image(url('/static/img/noise.png'), linear-gradient(lighten($dark-blue, 10%), $dark-blue)); - border-bottom: 1px solid darken($dark-blue, 15%); - @include box-shadow(inset 0 -1px 0 lighten($dark-blue, 10%)); - @include box-sizing(border-box); - color: #fff; - display: block; - float: none; - padding: 0 20px; - text-shadow: 0 -1px 0 darken($dark-blue, 15%); - width: 100%; - - nav { - @include clearfix; - - > a { - @include hide-text; - background: url('/static/img/menu.png') 0 center no-repeat; - border-right: 1px solid darken($dark-blue, 10%); - @include box-shadow(1px 0 0 lighten($dark-blue, 10%)); - display: block; - float: left; - height: 19px; - padding: 8px 10px 8px 0; - width: 14px; - - &:hover, &:focus { - opacity: .7; - } - } - - h2 { - border-right: 1px solid darken($dark-blue, 10%); - @include box-shadow(1px 0 0 lighten($dark-blue, 10%)); - float: left; - font-size: 14px; - margin: 0; - text-transform: uppercase; - -webkit-font-smoothing: antialiased; - - a { - color: #fff; - padding: 8px 20px; - display: block; - - &:hover { - background-color: rgba(darken($dark-blue, 15%), .5); - color: $yellow; - } - } - } - - a { - color: rgba(#fff, .8); - - &:hover { - color: rgba(#fff, .6); - } - } - - ul { - float: left; - margin: 0; - padding: 0; - @include clearfix; - - &.user-nav { - float: right; - border-left: 1px solid darken($dark-blue, 10%); - } - - li { - border-right: 1px solid darken($dark-blue, 10%); - float: left; - @include box-shadow(1px 0 0 lighten($dark-blue, 10%)); - - a { - padding: 8px 20px; - display: block; - - &:hover { - background-color: rgba(darken($dark-blue, 15%), .5); - color: $yellow; - } - - &.new-module { - &:before { - @include inline-block; - content: "+"; - font-weight: bold; - margin-right: 10px; - } - } - } - } - } - } - } - - &.content { - section.main-content { - border-left: 2px solid $dark-blue; - @include box-sizing(border-box); - width: flex-grid(9) + flex-gutter(); - float: left; - @include box-shadow( -2px 0 0 lighten($dark-blue, 55%)); - @include transition(); - background: #FFF; - } - } -} diff --git a/cms/static/sass/_lms.scss b/cms/static/sass/_lms.scss deleted file mode 100644 index 1ddc48edaf..0000000000 --- a/cms/static/sass/_lms.scss +++ /dev/null @@ -1,69 +0,0 @@ -.component { - font-family: 'Open Sans', Verdana, Arial, Helvetica, sans-serif; - font-size: 16px; - line-height: 1.6; - color: #3c3c3c; - - a { - color: #1d9dd9; - text-decoration: none; - } - - p { - font-size: 16px; - line-height: 1.6; - } - - h1 { - float: none; - } - - h2 { - color: #646464; - font-size: 19px; - font-weight: 300; - letter-spacing: 1px; - margin-bottom: 15px; - margin-left: 0; - text-transform: uppercase; - } - - h3 { - font-size: 19px; - font-weight: 400; - } - - h4 { - background: none; - padding: 0; - border: none; - @include box-shadow(none); - font-size: 16px; - font-weight: 400; - } - - code { - margin: 0 2px; - padding: 0px 5px; - border-radius: 3px; - border: 1px solid #eaeaea; - white-space: nowrap; - font-family: Monaco, monospace; - font-size: 14px; - background-color: #f8f8f8; - } - - p + h2, ul + h2, ol + h2, p + h3 { - margin-top: 40px; - } - - p + p, ul + p, ol + p { - margin-top: 20px; - } - - p { - color: #3c3c3c; - font: normal 1em/1.6em; - margin: 0px; - } -} \ No newline at end of file diff --git a/cms/static/sass/_login.scss b/cms/static/sass/_login.scss deleted file mode 100644 index c2bff74638..0000000000 --- a/cms/static/sass/_login.scss +++ /dev/null @@ -1,139 +0,0 @@ -.edx-studio-logo-large { - display: block; - width: 224px; - height: 45px; - margin: 100px auto 30px; - background: url(../img/edx-studio-large.png) no-repeat; -} - -.sign-up-box, -.log-in-box { - width: 500px; - margin: auto; - border-radius: 3px; - - header { - height: 36px; - border-radius: 3px 3px 0 0; - border: 1px solid #2c2e33; - @include linear-gradient(top, #686b76, #54565e); - color: #fff; - @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.2), 0 -1px 0px rgba(255, 255, 255, 0.05) inset, 0 1px 0 rgba(255, 255, 255, .25) inset); - - h1 { - float: none; - margin: 5px 0; - font-size: 15px; - font-weight: 300; - text-align: center; - } - } - - form { - padding: 40px; - border: 1px solid $darkGrey; - border-top-width: 0; - border-radius: 0 0 3px 3px; - background: #fff; - @include box-shadow(0 1px 2px rgba(0, 0, 0, .1)); - } - - label { - display: block; - margin-bottom: 5px; - font-size: 13px; - font-weight: 700; - } - - input[type="text"], - input[type="email"], - input[type="password"] { - width: 100%; - font-size: 20px; - font-weight: 300; - } - - .row { - @include clearfix; - margin-bottom: 24px; - - .split { - float: left; - width: 48%; - - &:first-child { - margin-right: 4%; - } - } - } - - .form-actions { - @include clearfix; - margin-top: 32px; - margin-bottom: 5px; - text-align: center; - } - - .log-in-button, - .create-account-button { - @include blue-button; - padding: 8px 0 10px; - font-family: $sans-serif; - @include transition(all .15s); - } - - .create-account-button { - padding: 10px 40px 12px; - margin-bottom: 10px; - } - - .enrolled { - font-size: 14px; - } - - .sign-up-button { - @include white-button; - padding: 7px 0 9px; - } - - .log-in-button, - .sign-up-button { - @include box-sizing(border-box); - float: left; - width: 45%; - } - - .or { - float: left; - display: inline-block; - width: 10%; - font-size: 15px; - line-height: 36px; - color: $darkGrey; - text-align: center; - } - - .forgot-button { - float: right; - font-size: 11px; - font-weight: 400; - line-height: 21px; - } - - .log-in-extra { - margin-top: 10px; - text-align: right; - font-size: 13px; - } - - #login_error, - #register_error { - display: none; - margin-bottom: 30px; - padding: 5px 10px; - border-radius: 3px; - background: $error-red; - font-size: 14px; - color: #fff; - } -} \ No newline at end of file diff --git a/cms/static/sass/_modal.scss b/cms/static/sass/_modal.scss deleted file mode 100644 index f9fbf81a8f..0000000000 --- a/cms/static/sass/_modal.scss +++ /dev/null @@ -1,69 +0,0 @@ -.modal-cover { - display: none; - position: fixed; - top: 0; - left: 0; - z-index: 1000; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, .8); -} - -.modal { - display: none; - position: fixed; - top: 60px; - left: 50%; - z-index: 1001; - width: 930px; - height: 540px; - margin-left: -465px; - background: #fff; - - .modal-body { - height: 400px; - padding: 40px; - overflow-y: scroll; - } - - .modal-actions { - height: 60px; - @include linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0)); - background-color: #d1dae3; - } - - h2 { - margin: 0 10px 30px; - color: #646464; - font-size: 19px; - font-weight: 300; - letter-spacing: 1px; - text-transform: uppercase; - } - - p { - margin: 20px; - } - - .revert-button { - @include blue-button; - margin: 13px 6px 0 13px; - } - - .close-button { - @include white-button; - margin-top: 13px; - } -} - -// lean modal alternative -#lean_overlay { - position: fixed; - z-index: 10000; - top: 0px; - left: 0px; - display: none; - height: 100%; - width: 100%; - background: $black; -} \ No newline at end of file diff --git a/cms/static/sass/_module-header.scss b/cms/static/sass/_module-header.scss deleted file mode 100644 index e2af263618..0000000000 --- a/cms/static/sass/_module-header.scss +++ /dev/null @@ -1,128 +0,0 @@ -section.video-new, section.video-edit, section.problem-new, section.problem-edit { - position: absolute; - top: 72px; - right: 0; - background: #fff; - width: flex-grid(6); - @include box-shadow(0 0 6px #666); - border: 1px solid #333; - border-right: 0; - z-index: 4; - - > header { - background: #666; - @include clearfix; - color: #fff; - padding: 6px; - border-bottom: 1px solid #333; - -webkit-font-smoothing: antialiased; - - h2 { - float: left; - font-size: 14px; - } - - a { - color: #fff; - - &.save-update { - float: right; - } - - &.cancel { - float: left; - } - } - - } - - > section { - padding: 20px; - - > header { - h1 { - font-size: 24px; - margin: 12px 0; - } - - section { - &.status-settings { - ul { - list-style: none; - @include border-radius(2px); - border: 1px solid #999; - @include inline-block(); - - li { - @include inline-block(); - border-right: 1px solid #999; - padding: 6px; - - &:last-child { - border-right: 0; - } - - &.current { - background: #eee; - } - } - } - - a.settings { - @include inline-block(); - margin: 0 20px; - border: 1px solid #999; - padding: 6px; - } - - select { - float: right; - } - } - - &.meta { - background: #eee; - padding: 10px; - margin: 20px 0; - @include clearfix(); - - div { - float: left; - margin-right: 20px; - - h2 { - font-size: 14px; - @include inline-block(); - } - - p { - @include inline-block(); - } - } - } - } - } - - section.notes { - margin-top: 20px; - padding: 6px; - background: #eee; - border: 1px solid #ccc; - - textarea { - @include box-sizing(border-box); - display: block; - width: 100%; - } - - h2 { - font-size: 14px; - margin-bottom: 6px; - } - - input[type="submit"]{ - margin-top: 10px; - } - } - } -} diff --git a/cms/static/sass/_problem.scss b/cms/static/sass/_problem.scss deleted file mode 100644 index 66acacf65c..0000000000 --- a/cms/static/sass/_problem.scss +++ /dev/null @@ -1,24 +0,0 @@ -section.problem-new, section.problem-edit { - > section { - textarea { - @include box-sizing(border-box); - display: block; - width: 100%; - } - - div.preview { - background: #eee; - @include box-sizing(border-box); - height: 40px; - padding: 10px; - width: 100%; - } - - a.save { - @extend .button; - @include inline-block(); - margin-top: 20px; - } - } -} - diff --git a/cms/static/sass/_reset.scss b/cms/static/sass/_reset.scss index ee03a0fca3..8a7e7e9835 100644 --- a/cms/static/sass/_reset.scss +++ b/cms/static/sass/_reset.scss @@ -1,3 +1,6 @@ +// studio - utilities - reset +// ==================== + html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, diff --git a/cms/static/sass/_section.scss b/cms/static/sass/_section.scss deleted file mode 100644 index 97818326be..0000000000 --- a/cms/static/sass/_section.scss +++ /dev/null @@ -1,239 +0,0 @@ -section#unit-wrapper { - section.filters { - @include clearfix; - display: none; - opacity: .4; - margin-bottom: 10px; - @include transition; - - &:hover { - opacity: 1; - } - - h2 { - @include inline-block(); - text-transform: uppercase; - letter-spacing: 1px; - font-size: 14px; - padding: 6px 6px 6px 0; - font-size: 12px; - margin: 0; - } - - ul { - @include clearfix(); - list-style: none; - margin: 0; - padding: 0; - - li { - @include inline-block; - margin-right: 6px; - border-right: 1px solid #ddd; - padding-right: 6px; - - &.search { - float: right; - border: 0; - } - - a { - &.more { - font-size: 12px; - @include inline-block; - margin: 0 6px; - font-style: italic; - } - } - } - } - } - - div.content { - display: table; - border: 1px solid lighten($dark-blue, 40%); - width: 100%; - @include border-radius(3px); - @include box-shadow(0 0 4px lighten($dark-blue, 50%)); - - section { - header { - background: #fff; - padding: 6px; - border-bottom: 1px solid lighten($dark-blue, 60%); - @include clearfix; - - h2 { - color: $bright-blue; - // float: left; - font-size: 14px; - letter-spacing: 1px; - // line-height: 20px; - text-transform: uppercase; - margin: 0; - } - } - - &.modules { - @include box-sizing(border-box); - display: table-cell; - width: flex-grid(6, 9); - border-right: 1px solid lighten($dark-blue, 40%); - - &.empty { - text-align: center; - vertical-align: middle; - - a { - @extend .button; - @include inline-block(); - margin-top: 10px; - } - } - - ol { - list-style: none; - margin: 0; - padding: 0; - - li { - border-bottom: 1px solid lighten($dark-blue, 60%); - - a { - color: #000; - } - - ol { - list-style: none; - margin: 0; - padding: 0; - - li { - padding: 6px; - position: relative; - - &:last-child { - border-bottom: 0; - } - - &:hover { - background-color: lighten($yellow, 10%); - - a.draggable { - opacity: 1; - } - } - - a.draggable { - float: right; - opacity: .4; - } - - &.group { - padding: 0; - - header { - padding: 6px; - background: none; - - h3 { - font-size: 14px; - margin: 0; - } - } - - ol { - border-left: 4px solid #999; - border-bottom: 0; - margin: 0; - padding: 0; - - li { - &:last-child { - border-bottom: 0; - } - } - } - } - } - } - } - } - } - - &.scratch-pad { - @include box-sizing(border-box); - display: table-cell; - width: flex-grid(3, 9) + flex-gutter(9); - vertical-align: top; - - ol { - list-style: none; - margin: 0; - padding: 0; - - li { - background: $light-blue; - - &:last-child { - border-bottom: 0; - } - - &.new-module a { - background-color: darken($light-blue, 2%); - border-bottom: 1px solid darken($light-blue, 8%); - - &:hover { - background-color: lighten($yellow, 10%); - } - } - - a { - color: $dark-blue; - } - - ul { - list-style: none; - margin: 0; - padding: 0; - - li { - padding: 6px; - border-collapse: collapse; - border-bottom: 1px solid darken($light-blue, 8%); - position: relative; - - &:last-child { - border-bottom: 1px solid darken($light-blue, 8%); - } - - &:hover { - background-color: lighten($yellow, 10%); - - a.draggable { - opacity: 1; - } - } - - - &.empty { - padding: 12px; - - a { - @extend .button; - display: block; - text-align: center; - } - } - - a.draggable { - opacity: .3; - } - } - } - } - } - } - } - } -} diff --git a/cms/static/sass/_settings.scss b/cms/static/sass/_settings.scss deleted file mode 100644 index d8011dd651..0000000000 --- a/cms/static/sass/_settings.scss +++ /dev/null @@ -1,740 +0,0 @@ -// Studio - Course Settings -// ==================== -body.course.settings { - - .content-primary, .content-supplementary { - @include box-sizing(border-box); - float: left; - } - - .content-primary { - @extend .window; - width: flex-grid(9, 12); - margin-right: flex-gutter(); - padding: $baseline ($baseline*1.5); - } - - // messages - should be synced up with global messages in the future - .message { - display: block; - font-size: 14px; - } - - .message-status { - display: none; - @include border-top-radius(2px); - @include box-sizing(border-box); - border-bottom: 2px solid $yellow; - margin: 0 0 20px 0; - padding: 10px 20px; - font-weight: 500; - background: $paleYellow; - - .text { - display: inline-block; - } - - &.error { - border-color: shade($red, 50%); - background: tint($red, 20%); - color: $white; - } - - &.confirm { - border-color: shade($green, 50%); - background: tint($green, 20%); - color: $white; - } - - &.is-shown { - display: block; - } - } - - // in form - elements - .group-settings { - margin: 0 0 ($baseline*2) 0; - - header { - @include clearfix(); - - .title-2 { - width: flex-grid(4, 9); - margin: 0 flex-gutter() 0 0; - float: left; - } - - .tip { - @include font-size(13); - width: flex-grid(5, 9); - float: right; - margin-top: ($baseline/2); - text-align: right; - color: $gray-l2; - } - } - - // basic layout/elements - .title-2 { - - } - - .title-3 { - - } - - // in form -UI hints/tips/messages - .instructions { - @include font-size(14); - margin: 0 0 $baseline 0; - } - - .tip { - @include transition(color, 0.15s, ease-in-out); - @include font-size(13); - display: block; - margin-top: ($baseline/4); - color: $gray-l3; - } - - .message-error { - @include font-size(13); - display: block; - margin-top: ($baseline/4); - margin-bottom: ($baseline/2); - color: $red; - } - - // buttons - .remove-item { - @include white-button; - @include font-size(13); - font-weight: 400; - } - - .new-button { - @include font-size(13); - } - - // form basics - .list-input { - margin: 0; - padding: 0; - list-style: none; - - .field { - margin: 0 0 $baseline 0; - - &:last-child { - margin-bottom: 0; - } - - &.required { - - label { - font-weight: 600; - } - - label:after { - margin-left: ($baseline/4); - content: "*"; - } - } - - label, input, textarea { - display: block; - } - - label { - @include font-size(14); - @include transition(color, 0.15s, ease-in-out); - margin: 0 0 ($baseline/4) 0; - font-weight: 400; - - &.is-focused { - color: $blue; - } - } - - input, textarea { - @include placeholder($gray-l4); - @include font-size(16); - @include size(100%,100%); - padding: ($baseline/2); - - &.long { - } - - &.short { - } - - &.error { - border-color: $red; - } - - &:focus { - - + .tip { - color: $gray; - } - } - } - - textarea.long { - height: ($baseline*5); - } - - input[type="checkbox"] { - display: inline-block; - margin-right: ($baseline/4); - width: auto; - height: auto; - - & + label { - display: inline-block; - } - } - } - - .field-group { - @include clearfix(); - margin: 0 0 ($baseline/2) 0; - } - - // enumerated/grouped lists - &.enum { - - .field-group { - @include box-sizing(border-box); - @include border-radius(3px); - background: $gray-l5; - padding: $baseline; - - &:last-child { - padding-bottom: $baseline; - } - - .actions { - @include clearfix(); - margin-top: ($baseline/2); - border-top: 1px solid $gray-l4; - padding-top: ($baseline/2); - - .remove-item { - float: right; - } - } - } - } - } - - // existing inputs - .input-existing { - margin: 0 0 $baseline 0; - - .actions { - margin: ($baseline/4) 0 0 0; - } - } - - // not editable fields - .field.is-not-editable { - - label, .label { - color: $gray-l3; - } - - input { - opacity: 0.25; - } - } - - // field with error - .field.error { - - input, textarea { - border-color: $red; - } - } - - // specific fields - basic - &.basic { - - .list-input { - @include clearfix(); - - .field { - margin-bottom: 0; - } - } - - #field-course-organization { - float: left; - width: flex-grid(2, 9); - margin-right: flex-gutter(); - } - - #field-course-number { - float: left; - width: flex-grid(2, 9); - margin-right: flex-gutter(); - } - - #field-course-name { - float: left; - width: flex-grid(5, 9); - } - } - - // specific fields - schedule - &.schedule { - - .list-input { - margin-bottom: ($baseline*1.5); - - &:last-child { - margin-bottom: 0; - } - } - - .field-group { - @include clearfix(); - border-bottom: 1px solid $gray-l5; - padding-bottom: ($baseline/2); - - &:last-child { - border: none; - padding-bottom: 0; - } - - .field { - float: left; - width: flex-grid(3, 9); - margin-bottom: ($baseline/4); - margin-right: flex-gutter(); - } - - .field.time { - position: relative; - - .tip { - position: absolute; - top: 0; - right: 0; - } - } - } - } - - // specific fields - overview - #field-course-overview { - - #course-overview { - height: ($baseline*20); - } - } - - // specific fields - video - #field-course-introduction-video { - - .input-existing { - @include box-sizing(border-box); - @include border-radius(3px); - background: $gray-l5; - padding: ($baseline/2); - - .actions { - @include clearfix(); - margin-top: ($baseline/2); - border-top: 1px solid $gray-l4; - padding-top: ($baseline/2); - - .remove-item { - float: right; - } - } - } - - .actions { - margin-top: ($baseline/2); - border-top: 1px solid $gray-l5; - padding-top: ($baseline/2); - } - } - - // specific fields - requirements - &.requirements { - - #field-course-effort { - width: flex-grid(3, 9); - } - } - - // specific fields - grading range (artifact styling) - &.grade-range { - margin-bottom: ($baseline*3); - - .grade-controls { - @include clearfix; - width: flex-grid(9,9); - } - - .new-grade-button { - @include box-sizing(border-box); - @include linear-gradient(top, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0)); - @include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset); - width: flex-grid(1,9); - height: ($baseline*2); - position: relative; - display: inline-block; - vertical-align: middle; - margin-right: flex-gutter(); - border-radius: 20px; - border: 1px solid $darkGrey; - background-color: #d1dae3; - color: #6d788b; - - .plus-icon { - position: absolute; - top: 50%; - left: 50%; - margin-left: -6px; - margin-top: -6px; - } - } - - .grade-slider { - @include box-sizing(border-box); - width: flex-grid(8,9); - display: inline-block; - vertical-align: middle; - - .grade-bar { - position: relative; - width: 100%; - height: ($baseline*2.5); - background: $lightGrey; - - .increments { - position: relative; - - li { - position: absolute; - top: 52px; - width: 30px; - margin-left: -15px; - font-size: 9px; - text-align: center; - - &.increment-0 { - left: 0; - } - - &.increment-10 { - left: 10%; - } - - &.increment-20 { - left: 20%; - } - - &.increment-30 { - left: 30%; - } - - &.increment-40 { - left: 40%; - } - - &.increment-50 { - left: 50%; - } - - &.increment-60 { - left: 60%; - } - - &.increment-70 { - left: 70%; - } - - &.increment-80 { - left: 80%; - } - - &.increment-90 { - left: 90%; - } - - &.increment-100 { - left: 100%; - } - } - } - - .grade-specific-bar { - height: 50px !important; - } - - .grades { - position: relative; - - li { - position: absolute; - top: 0; - height: 50px; - text-align: right; - @include border-radius(2px); - - &:hover, - &.is-dragging { - .remove-button { - display: block; - } - } - - &.is-dragging { - - } - - .remove-button { - display: none; - position: absolute; - top: -17px; - right: 1px; - height: 17px; - font-size: 10px; - } - - &:nth-child(1) { - background: #4fe696; - } - - &:nth-child(2) { - background: #ffdf7e; - } - - &:nth-child(3) { - background: #ffb657; - } - - &:nth-child(4) { - background: #ef54a1; - } - - &:nth-child(5), - &.bar-fail { - background: #fb336c; - } - - .letter-grade { - display: block; - margin: 10px 15px 0 0; - font-size: 16px; - font-weight: 700; - line-height: 14px; - } - - .range { - display: block; - margin-right: 15px; - font-size: 10px; - line-height: 12px; - } - - .drag-bar { - position: absolute; - top: 0; - right: -1px; - height: 50px; - width: 2px; - background-color: #fff; - @include box-shadow(-1px 0 3px rgba(0,0,0,0.1)); - - cursor: ew-resize; - @include transition(none); - - &:hover { - width: 6px; - right: -2px; - } - } - } - } - } - } - } - - // specific fields - grading rules - &.grade-rules { - - #field-course-grading-graceperiod { - width: flex-grid(3, 9); - } - } - - &.assignment-types { - - .list-input { - - &:last-child { - margin-bottom: 0; - } - } - - .field-group { - @include clearfix(); - width: flex-grid(9, 9); - margin-bottom: ($baseline*1.5); - border-bottom: 1px solid $gray-l5; - padding-bottom: ($baseline*1.5); - - &:last-child { - border: none; - padding-bottom: 0; - } - - .field { - display: inline-block; - vertical-align: top; - width: flex-grid(3, 6); - margin-bottom: ($baseline/2); - margin-right: flex-gutter(); - } - - #field-course-grading-assignment-shortname, - #field-course-grading-assignment-totalassignments, - #field-course-grading-assignment-gradeweight, - #field-course-grading-assignment-droppable { - width: flex-grid(2, 6); - } - } - - .actions { - float: left; - width: flex-grid(9, 9); - - .delete-button { - margin: 0; - } - } - } - - // specific fields - advanced settings - &.advanced-policies { - - .field-group { - margin-bottom: ($baseline*1.5); - - &:last-child { - border: none; - padding-bottom: 0; - } - } - - .course-advanced-policy-list-item { - @include clearfix(); - position: relative; - - .field { - - input { - width: 100%; - } - - .tip { - @include transition (opacity 0.5s ease-in-out 0s); - opacity: 0; - position: absolute; - bottom: ($baseline*1.25); - } - - input:focus { - - & + .tip { - opacity: 1.0; - } - } - - input.error { - - & + .tip { - opacity: 0; - } - } - } - - .key, .value { - float: left; - margin: 0 0 ($baseline/2) 0; - } - - .key { - width: flex-grid(3, 9); - margin-right: flex-gutter(); - } - - .value { - width: flex-grid(6, 9); - } - - .actions { - float: left; - width: flex-grid(9, 9); - - .delete-button { - margin: 0; - } - } - } - - .message-error { - position: absolute; - bottom: ($baseline*0.75); - } - - // specific to code mirror instance in JSON policy editing, need to sync up with other similar code mirror UIs - .CodeMirror { - @include font-size(16); - @include box-sizing(border-box); - @include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset); - @include linear-gradient($lightGrey, tint($lightGrey, 90%)); - padding: 5px 8px; - border: 1px solid $mediumGrey; - border-radius: 2px; - background-color: $lightGrey; - font-family: 'Open Sans', sans-serif; - color: $baseFontColor; - outline: 0; - - &.CodeMirror-focused { - @include linear-gradient($paleYellow, tint($paleYellow, 90%)); - outline: 0; - } - - .CodeMirror-scroll { - overflow: hidden; - height: auto; - min-height: ($baseline*1.5); - max-height: ($baseline*10); - } - - // editor color changes just for JSON - .CodeMirror-lines { - - .cm-string { - color: #cb9c40; - } - - pre { - line-height: 2.0rem; - } - } - } - } - } - - .content-supplementary { - width: flex-grid(3, 12); - } -} \ No newline at end of file diff --git a/cms/static/sass/_static-pages.scss b/cms/static/sass/_static-pages.scss deleted file mode 100644 index 138e817769..0000000000 --- a/cms/static/sass/_static-pages.scss +++ /dev/null @@ -1,153 +0,0 @@ -.static-pages { - .new-static-page-button { - @include grey-button; - display: block; - text-align: center; - padding: 12px 0; - } - - .unit-body { - padding: 0; - - .details { - display: block !important; - - h2 { - margin: 0 0 5px 0; - } - } - } - - .component-editor { - border: none; - border-radius: 0; - } - - .components > li { - margin: 0; - border-radius: 0; - - &.new-component-item { - background: transparent; - border: none; - @include box-shadow(none); - } - } - - .component { - border: 1px solid $mediumGrey; - border-top: none; - - &:first-child { - border-top: 1px solid $mediumGrey; - } - - &:hover { - border: 1px solid $mediumGrey; - border-top: none; - - &:first-child { - border-top: 1px solid $mediumGrey; - } - - .drag-handle { - background: url(../img/drag-handles.png) center no-repeat #fff; - } - } - - .drag-handle { - top: 0; - right: 0; - z-index: 11; - width: 35px; - border: none; - background: url(../img/drag-handles.png) center no-repeat #fff; - - &:hover { - background: url(../img/drag-handles.png) center no-repeat #fff; - } - } - - .component-actions { - top: 26px; - right: 44px; - } - } - - .component.editing { - border-left: 1px solid $mediumGrey; - border-right: 1px solid $mediumGrey; - - .xmodule_display { - display: none; - } - } - - .new .xmodule_display { - background: $yellow; - } - - .xmodule_display { - padding: 20px 20px 22px; - font-size: 24px; - font-weight: 300; - background: #fff; - @include transition(background-color 3s); - } - - .static-page-item { - position: relative; - margin: 10px 0; - padding: 22px 20px; - border: 1px solid $darkGrey; - border-radius: 3px; - background: #fff; - @include box-shadow(0 1px 2px rgba(0, 0, 0, .1)); - - .page-name { - font-size: 19px; - font-weight: 700; - } - - .item-actions { - margin-top: 19px; - margin-right: 12px; - } - } -} - -.edit-static-page { - .main-wrapper { - margin-top: 40px; - } - - .static-page-details { - @extend .window; - padding: 32px 40px; - - .row { - border: none; - } - } - - .page-display-name-input { - width: 100%; - font-size: 20px; - } - - .page-contents { - @include box-sizing(border-box); - width: 100%; - height: 360px; - padding: 15px; - border: 1px solid #b0b6c2; - border-radius: 2px; - @include linear-gradient(top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .3)); - background-color: #edf1f5; - @include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset); - font-family: Monaco, monospace; - font-size: 13px; - color: #3c3c3c; - outline: 0; - } -} \ No newline at end of file diff --git a/cms/static/sass/_subsection.scss b/cms/static/sass/_subsection.scss deleted file mode 100644 index a39c0d757a..0000000000 --- a/cms/static/sass/_subsection.scss +++ /dev/null @@ -1,295 +0,0 @@ -.subsection .main-wrapper { - margin: 40px; -} - -.subsection .inner-wrapper { - @include clearfix(); -} - -.subsection-body { - padding: 32px 40px; - @include clearfix; - - > div { - margin-bottom: 40px; - } - - input { - font-size: 14px; - } - - .unit-subtitle { - display: block; - width: 100%; - } - - .sortable-unit-list { - ol { - @include tree-view; - } - } - - .policy-list { - input[disabled] { - border: none; - @include box-shadow(none); - } - - .policy-list-name { - margin-right: 5px; - margin-bottom: 10px; - } - - .policy-list-value { - width: 320px; - margin-right: 10px; - } - } - - .policy-list-element { - .save-button, - .cancel-button { - display: none; - } - - .edit-icon { - margin-right: 8px; - } - - &.editing, - &.new-policy-list-element { - .policy-list-name, - .policy-list-value { - border: 1px solid #b0b6c2; - @include linear-gradient(top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .3)); - background-color: #edf1f5; - @include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset); - } - } - } - - .new-policy-list-element { - padding: 10px 10px 0; - margin: 0 -10px 10px; - border-radius: 3px; - background: $mediumGrey; - - .save-button { - @include blue-button; - margin-bottom: 10px; - } - - .cancel-button { - @include white-button; - } - - .edit-icon { - display: none; - } - - .delete-icon { - display: none; - } - } - - .new-policy-item { - margin: 10px 0; - - .plus-icon-small { - position: relative; - top: -1px; - vertical-align: middle; - } - } -} - -.subsection-name-input { - label { - display: block; - } - - input { - width: 100%; - font-size: 20px; - } -} - -.scheduled-date-input, -.due-date-input { - @include clearfix; - - .date-input, - .time-input { - display: inline-block; - width: 100px; - } - - .inherits-check { - label { - font-size: 13px; - } - } - - .notice { - margin-top: 6px; - font-size: 11px; - color: #999; - } -} - -.due-date-input { - label { - display: inline-block !important; - margin-right: 10px; - } - - a { - font-size: 11px; - font-weight: bold; - text-transform: uppercase; - } - - .date-setter { - @include clearfix; - display: none; - } - - .remove-date { - display: block; - } -} - -.row.visibility { - label { - display: inline-block !important; - margin-right: 10px; - line-height: 21px; - } - - a { - display: inline-block; - height: 31px; - margin-right: 8px; - vertical-align: middle; - font-size: 11px; - font-weight: 700; - line-height: 31px; - text-transform: uppercase; - } - - .large-toggle { - width: 41px; - background: url(../img/large-toggles.png) no-repeat; - background-position: 0 -50px; - - .hidden { - background-position: 0 -5px; - } - } -} - -.gradable { - - label { - display: inline-block; - vertical-align: top; - } - - .gradable-status { - position: relative; - top: -4px; - display: inline-block; - margin-left: 10px; - width: 65%; - - .status-label { - margin: 0; - padding: 0; - background: transparent; - color: $blue; - border: none; - font-size: 11px; - font-weight: bold; - text-transform: uppercase; - } - - .menu-toggle { - z-index: 100; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 20px; - background: transparent; - - &:hover, &.is-active { - color: $blue; - } - } - - .menu { - z-index: 1; - position: absolute; - top: -12px; - left: -7px; - display: none; - width: 100%; - margin: 0; - padding: 8px 12px; - opacity: 0.0; - background: $white; - border: 1px solid $mediumGrey; - font-size: 12px; - @include border-radius(4px); - @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); - @include transition(opacity .15s); - - - li { - margin-bottom: 3px; - padding-bottom: 3px; - border-bottom: 1px solid $lightGrey; - - &:last-child { - margin-bottom: 0; - padding-bottom: 0; - border: none; - } - } - - a { - - &.is-selected { - font-weight: bold; - } - } - } - - // dropdown state - &.is-active { - - .menu { - z-index: 10000; - display: block; - opacity: 1.0; - } - - .menu-toggle { - z-index: 1000; - } - } - - // set state - &.is-set { - - .menu-toggle { - color: $blue; - } - - .status-label { - display: block; - color: $blue; - } - } - } -} \ No newline at end of file diff --git a/cms/static/sass/_unit.scss b/cms/static/sass/_unit.scss deleted file mode 100644 index b7600e4205..0000000000 --- a/cms/static/sass/_unit.scss +++ /dev/null @@ -1,667 +0,0 @@ -.unit .main-wrapper { - @include clearfix(); - margin: 40px; -} - -//Problem Selector tab menu requirements -.js .tabs .tab { - display: none; -} -//end problem selector reqs - -.main-column { - clear: both; - float: left; - width: 70%; -} - -.unit-body.published { - .components > li { - border: none; - - .rendered-component { - padding: 0 20px; - } - } -} - -.unit-body { - .breadcrumbs { - border-radius: 3px 3px 0 0; - border-bottom: 1px solid #cbd1db; - @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0) 70%); - background-color: #edf1f5; - @include box-shadow(0 1px 0 rgba(255, 255, 255, .7) inset); - @include clearfix; - - li { - float: left; - } - - a, - .current-page { - display: block; - padding: 15px 35px 15px 30px; - font-size: 14px; - background: url(../img/breadcrumb-arrow.png) no-repeat right center; - } - } - - h2 { - margin: 30px 40px 30px 0; - color: #646464; - font-size: 19px; - font-weight: 300; - letter-spacing: 1px; - text-transform: uppercase; - } - - .components { - - > li { - position: relative; - z-index: 10; - margin: 20px 40px; - - - - .title { - margin: 0 0 15px 0; - color: $mediumGrey; - - .value { - } - } - - &.new-component-item { - margin: 20px 0px; - border-top: 1px solid $mediumGrey; - box-shadow: 0 2px 1px rgba(182, 182, 182, 0.75) inset; - background-color: $lightGrey; - margin-bottom: 0px; - padding-bottom: 20px; - - .new-component-button { - display: block; - padding: 20px; - text-align: center; - color: #edf1f5; - } - - h5 { - margin: 20px 0px; - color: #fff; - font-weight: 600; - font-size: 18px; - } - - .rendered-component { - display: none; - background: #fff; - border-radius: 3px 3px 0 0; - } - - .new-component-type { - - a, - li { - display: inline-block; - } - - a { - border: 1px solid $mediumGrey; - width: 100px; - height: 100px; - color: #fff; - margin-right: 15px; - margin-bottom: 20px; - border-radius: 8px; - font-size: 15px; - line-height: 14px; - text-align: center; - @include box-shadow(0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset); - - .name { - position: absolute; - bottom: 5px; - left: 0; - width: 100%; - padding: 10px; - @include box-sizing(border-box); - color: #fff; - } - } - } - - .new-component-templates { - display: none; - margin: 20px 40px 20px 40px; - border-radius: 3px; - border: 1px solid $mediumGrey; - background-color: #fff; - @include box-shadow(0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset); - @include clearfix; - - .cancel-button { - margin: 20px 0px 10px 10px; - @include white-button; - } - - .problem-type-tabs { - display: none; - } - - // specific menu types - &.new-component-problem { - padding-bottom:10px; - - .ss-icon, .editor-indicator { - display: inline-block; - } - - .problem-type-tabs { - display: inline-block; - } - } - } - - .new-component-type, - .new-component-template { - @include clearfix; - - a { - position: relative; - border: 1px solid $darkGreen; - background: tint($green,20%); - color: #fff; - - &:hover { - background: $brightGreen; - } - } - } - - .problem-type-tabs { - list-style-type: none; - border-radius: 0; - width: 100%; - @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); - background-color: $lightBluishGrey; - @include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset); - - li:first-child { - margin-left: 20px; - } - - li { - float:left; - display:inline-block; - text-align:center; - width: auto; - @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); - background-color: tint($lightBluishGrey, 10%); - @include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset); - opacity:.8; - - &:hover { - opacity:1; - background-color: tint($lightBluishGrey, 20%); - } - - &.ui-state-active { - border: 0px; - @include active; - opacity:1; - } - } - - a{ - display: block; - padding: 15px 25px; - font-size: 15px; - line-height: 16px; - text-align: center; - color: #3c3c3c; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3); - } - } - - .new-component-template { - - a { - background: #fff; - border: 0px; - color: #3c3c3c; - @include transition (none); - - &:hover { - background: tint($green,30%); - color: #fff; - @include transition(background-color .15s); - } - } - - li { - border:none; - border-bottom: 1px dashed $lightGrey; - color: #fff; - } - - li:first-child { - a { - border-top: 0px; - } - } - - li:nth-child(2) { - a { - border-radius: 0px; - } - } - - a { - @include clearfix(); - display: block; - padding: 7px 20px; - border-bottom: none; - font-weight: 500; - - .name { - float: left; - - .ss-icon { - @include transition(opacity .15s); - display: inline-block; - top: 1px; - margin-right: 5px; - opacity: 0.5; - width: 17; - height: 21px; - vertical-align: middle; - } - } - - .editor-indicator { - @include transition(opacity .15s); - float: right; - position: relative; - top: 3px; - font-size: 12px; - opacity: 0.3; - } - - .ss-icon, .editor-indicator { - display: none; - } - - &:hover { - color: #fff; - - .ss-icon { - opacity: 1.0; - } - - .editor-indicator { - opacity: 1.0; - } - } - } - - // specific editor types - .empty { - - a { - line-height: 1.4; - font-weight: 400; - background: #fff; - color: #3c3c3c; - - - &:hover { - background: tint($green,30%); - color: #fff; - } - } - } - } - - .new-component { - text-align: center; - - h5 { - color: $darkGreen; - } - - } - } - } - } - - .component { - border: 1px solid $lightBluishGrey2; - border-radius: 3px; - background: #fff; - @include transition(none); - - &:hover { - border-color: #6696d7; - - .drag-handle { - background-color: $blue; - border-color: $blue; - } - } - - &.editing { - border: 1px solid $lightBluishGrey2; - z-index: auto; - - .drag-handle, - .component-actions { - display: none; - } - } - - &.component-placeholder { - border-color: #6696d7; - } - - .component-actions { - position: absolute; - top: 7px; - right: 9px; - } - - .drag-handle { - position: absolute; - display: block; - top: -1px; - right: -16px; - z-index: 10; - width: 15px; - height: 100%; - border-radius: 0 3px 3px 0; - border: 1px solid $lightBluishGrey2; - background: url(../img/white-drag-handles.png) center no-repeat $lightBluishGrey2; - cursor: move; - @include transition(none); - } - } - - .xmodule_display { - padding: 40px 20px 20px; - overflow-x: auto; - - h1 { - float: none; - margin-left: 0; - } - } - - .wrapper-component-editor { - z-index: 9999; - position: relative; - background: $lightBluishGrey2; - } - - .component-editor { - @include edit-box; - @include box-shadow(none); - display: none; - padding: 20px; - border-radius: 2px 2px 0 0; - - .metadata_edit { - margin-bottom: 20px; - font-size: 13px; - - li { - margin-bottom: 10px; - } - - label { - display: inline-block; - margin-right: 10px; - } - } - - h3 { - margin-bottom: 10px; - font-size: 18px; - font-weight: 700; - } - - h5 { - margin-bottom: 8px; - color: #fff; - font-weight: 700; - } - - .save-button { - margin-top: 10px; - margin: 15px 8px 0 0; - } - } -} - -.unit-settings { - .window-contents { - padding: 10px 20px; - } - - .unit-actions { - border-bottom: none; - padding-bottom: 0; - } - - .published-alert { - display: none; - padding: 10px; - border: 1px solid #edbd3c; - border-radius: 3px; - background: #fbf6e1; - font-size: 14px; - line-height: 1.4; - - div { - margin-top: 15px; - } - } - - input[type="radio"] { - margin-right: 7px; - } - - .status { - font-size: 12px; - - strong { - font-weight: 700; - } - } - - .preview-button, .view-button { - @include white-button; - margin-bottom: 10px; - } - - .publish-button { - @include orange-button; - } - - .delete-button { - @include blue-button; - } - - .delete-draft { - display: inline-block; - } - - .delete-button, - .preview-button, - .publish-button, - .view-button { - font-size: 11px; - margin-top: 10px; - padding: 6px 15px 8px; - } -} - -.unit-history { - &.collapsed { - h4 { - border-bottom: none; - border-radius: 3px; - } - - .window-contents { - display: none; - } - } - - ol { - border: 1px solid #ced2db; - - li { - display: block; - padding: 6px 8px 8px 10px; - background: #edf1f5; - font-size: 12px; - - &:hover { - background: #fffcf1; - - .item-actions { - display: block; - } - } - - &.checked { - background: #d1dae3; - } - - .item-actions { - display: none; - } - - input[type="radio"] { - margin-right: 7px; - } - } - } -} - -.unit-location { - .url { - width: 100%; - margin-bottom: 10px; - @include box-shadow(none); - } - - .draft-tag, - .hidden-tag, - .private-tag, - .has-new-draft-tag { - font-size: 8px; - } - - .window-contents > ol { - @include tree-view; - - .section-item { - display: inline-block; - width: 100%; - font-size: 11px; - padding: 2px 8px 4px; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - @include box-sizing(border-box); - } - - ol { - .section-item { - padding-left: 20px; - } - - .new-unit-item { - margin-left: 20px; - } - } - - ol ol { - .section-item { - padding-left: 34px; - } - - .new-unit-item { - margin: 0 0 10px 41px; - } - } - } -} - -.edit-state-draft { - .visibility, - - .edit-draft-message, - .view-button { - display: none; - } - - .published-alert { - display: block; - } -} - -.edit-state-public { - .delete-draft, - .component-actions, - .new-component-item, - .editing-draft-alert, - .publish-draft-message, - .preview-button { - display: none; - } - - .published-alert { - display: block; - } - - .drag-handle { - display: none !important; - } -} - -.edit-state-private { - .delete-draft, - .publish-draft, - .editing-draft-alert, - .create-draft, - .view-button { - display: none; - } -} - -// editing units from courseware -body.unit { - - .component { - padding-top: 30px; - - .component-actions { - @include box-sizing(border-box); - position: absolute; - width: 100%; - padding: 15px; - top: 0; - left: 0; - border-bottom: 1px solid $lightBluishGrey2; - background: $lightGrey; - } - - &.editing { - padding-top: 0; - } - } -} diff --git a/cms/static/sass/_users.scss b/cms/static/sass/_users.scss deleted file mode 100644 index e107bdbb6d..0000000000 --- a/cms/static/sass/_users.scss +++ /dev/null @@ -1,78 +0,0 @@ -.users { - .new-user-form { - display: none; - padding: 15px 20px; - background-color: $lightBluishGrey2; - - #result { - display: none; - float: left; - margin-bottom: 15px; - padding: 3px 15px; - border-radius: 3px; - background: $error-red; - font-size: 14px; - color: #fff; - } - - .form-elements { - clear: both; - } - - label { - display: inline-block; - margin-right: 10px; - } - - .email-input { - width: 350px; - padding: 8px 8px 10px; - border-color: $darkGrey; - } - - .add-button { - @include blue-button; - padding: 5px 20px 9px; - } - - .cancel-button { - @include white-button; - padding: 5px 20px 9px; - } - } - - .user-list { - border: 1px solid $mediumGrey; - background: #fff; - - li { - position: relative; - padding: 20px; - border-bottom: 1px solid $mediumGrey; - - &:last-child { - border-bottom: none; - } - - span { - display: inline-block; - } - - .user-name { - margin-right: 10px; - font-size: 24px; - font-weight: 300; - } - - .user-email { - font-size: 14px; - font-style: italic; - color: $mediumGrey; - } - - .item-actions { - top: 24px; - } - } - } -} \ No newline at end of file diff --git a/cms/static/sass/_variables.scss b/cms/static/sass/_variables.scss index 4d8e26b2f9..78b6f2b221 100644 --- a/cms/static/sass/_variables.scss +++ b/cms/static/sass/_variables.scss @@ -1,3 +1,6 @@ +// studio - utilities - variables +// ==================== + $baseline: 20px; // grid diff --git a/cms/static/sass/_video.scss b/cms/static/sass/_video.scss deleted file mode 100644 index b68176e2db..0000000000 --- a/cms/static/sass/_video.scss +++ /dev/null @@ -1,33 +0,0 @@ -section.video-new, section.video-edit { - > section { - - section.upload { - padding: 6px; - margin-bottom: 10px; - border: 1px solid #ddd; - - a.upload-button { - @extend .button; - @include inline-block(); - } - } - - section.in-use { - h2 { - font-size: 14px; - } - - div { - background: #eee; - text-align: center; - padding: 6px; - } - } - - a.save-update { - @extend .button; - @include inline-block(); - margin-top: 20px; - } - } -} diff --git a/cms/static/sass/_week.scss b/cms/static/sass/_week.scss deleted file mode 100644 index b638a36f5c..0000000000 --- a/cms/static/sass/_week.scss +++ /dev/null @@ -1,256 +0,0 @@ -section.week-edit, -section.week-new, -section.sequence-edit { - - > header { - border-bottom: 2px solid #333; - @include clearfix(); - - div { - @include clearfix(); - padding: 6px 20px; - - h1 { - font-size: 18px; - text-transform: uppercase; - letter-spacing: 1px; - float: left; - } - - p { - float: right; - } - - &.week { - background: #eee; - font-size: 12px; - border-bottom: 1px solid #ccc; - - h2 { - font-size: 12px; - @include inline-block(); - margin-right: 20px; - } - - ul { - list-style: none; - @include inline-block(); - - li { - @include inline-block(); - margin-right: 10px; - - p { - float: none; - } - } - } - } - } - - section.goals { - background: #eee; - padding: 6px 20px; - border-top: 1px solid #ccc; - - ul { - list-style: none; - color: #999; - - li { - margin-bottom: 6px; - - &:last-child { - margin-bottom: 0; - } - } - } - } - } - - > section.content { - @include box-sizing(border-box); - padding: 20px; - - section.filters { - @include clearfix; - margin-bottom: 10px; - background: #efefef; - border: 1px solid #ddd; - - ul { - @include clearfix(); - list-style: none; - padding: 6px; - - li { - @include inline-block(); - - &.advanced { - float: right; - } - } - } - } - - > div { - display: table; - border: 1px solid; - width: 100%; - - section { - header { - background: #eee; - padding: 6px; - border-bottom: 1px solid #ccc; - @include clearfix; - - h2 { - text-transform: uppercase; - letter-spacing: 1px; - font-size: 12px; - float: left; - } - } - - &.modules { - @include box-sizing(border-box); - display: table-cell; - width: flex-grid(6, 9); - border-right: 1px solid #333; - - &.empty { - text-align: center; - vertical-align: middle; - - a { - @extend .button; - @include inline-block(); - margin-top: 10px; - } - } - - ol { - list-style: none; - border-bottom: 1px solid #333; - - li { - border-bottom: 1px solid #333; - - &:last-child{ - border-bottom: 0; - } - - a { - color: #000; - } - - ol { - list-style: none; - - li { - padding: 6px; - - &:hover { - a.draggable { - opacity: 1; - } - } - - a.draggable { - float: right; - opacity: .5; - } - - &.group { - padding: 0; - - header { - padding: 6px; - background: none; - - h3 { - font-size: 14px; - } - } - - - ol { - border-left: 4px solid #999; - border-bottom: 0; - - li { - &:last-child { - border-bottom: 0; - } - } - } - } - } - } - } - } - } - - &.scratch-pad { - @include box-sizing(border-box); - display: table-cell; - width: flex-grid(3, 9) + flex-gutter(9); - vertical-align: top; - - ol { - list-style: none; - border-bottom: 1px solid #999; - - li { - border-bottom: 1px solid #999; - background: #f9f9f9; - - &:last-child { - border-bottom: 0; - } - - ul { - list-style: none; - - li { - padding: 6px; - - &:last-child { - border-bottom: 0; - } - - &:hover { - a.draggable { - opacity: 1; - } - } - - &.empty { - padding: 12px; - - a { - @extend .button; - display: block; - text-align: center; - } - } - - a.draggable { - float: right; - opacity: .3; - } - - a { - color: #000; - } - } - } - - } - } - } - } - } - } -} diff --git a/cms/static/sass/base-style.scss b/cms/static/sass/base-style.scss index dceac4233d..33b312d235 100644 --- a/cms/static/sass/base-style.scss +++ b/cms/static/sass/base-style.scss @@ -1,39 +1,49 @@ +// studio - css architecture +// ==================== + +// bourbon libs and resets @import 'bourbon/bourbon'; @import 'bourbon/addons/button'; @import 'vendor/normalize'; -@import 'keyframes'; - @import 'reset'; -@import 'mixins'; -@import "fonts"; +// utilities +@import 'mixins'; @import "variables"; @import "cms_mixins"; -@import "extends"; + +// assets +@import "assets/fonts"; +@import "assets/graphics"; +@import 'assets/keyframes'; + +// base @import "base"; -@import "header"; -@import "footer"; -@import "dashboard"; -@import "courseware"; -@import "subsection"; -@import "unit"; -@import "assets"; -@import "static-pages"; -@import "users"; -@import "import"; -@import "export"; -@import "settings"; -@import "course-info"; -@import "landing"; -@import "graphics"; -@import "modal"; -@import "alerts"; -@import "login"; -@import "account"; -@import "index"; -@import 'jquery-ui-calendar'; -@import 'content-types'; +// elements +@import "elements/header"; +@import "elements/footer"; +@import "elements/navigation"; +@import "elements/modal"; +@import "elements/alerts"; +@import 'elements/jquery-ui-calendar'; -@import 'module/module-styles.scss'; -@import 'descriptor/module-styles.scss'; +// specific views +@import "views/account"; +@import "views/assets"; +@import "views/updates"; +@import "views/dashboard"; +@import "views/export"; +@import "views/index"; +@import "views/import"; +@import "views/outline"; +@import "views/settings"; +@import "views/static-pages"; +@import "views/subsection"; +@import "views/unit"; +@import "views/users"; + +@import 'assets/content-types'; + +@import 'xmodule/module/module-styles.scss'; +@import 'xmodule/descriptor/module-styles.scss'; diff --git a/common/static/sass/_mixins.scss b/common/static/sass/_mixins.scss index 76d52ed930..3145745906 100644 --- a/common/static/sass/_mixins.scss +++ b/common/static/sass/_mixins.scss @@ -1,3 +1,6 @@ +// all - utilities - mixins and extends +// ==================== + // font-sizing @function em($pxval, $base: 16) { @return #{$pxval / $base}em; @@ -64,4 +67,88 @@ :-ms-input-placeholder { color: $color; } -} \ No newline at end of file +} + +// ==================== + +// extends - visual +.faded-hr-divider { + @include background-image(linear-gradient(180deg, rgba(200,200,200, 0) 0%, + rgba(200,200,200, 1) 50%, + rgba(200,200,200, 0))); + height: 1px; + width: 100%; +} + +.faded-hr-divider-medium { + @include background-image(linear-gradient(180deg, rgba(240,240,240, 0) 0%, + rgba(240,240,240, 1) 50%, + rgba(240,240,240, 0))); + height: 1px; + width: 100%; +} + +.faded-hr-divider-light { + @include background-image(linear-gradient(180deg, rgba(255,255,255, 0) 0%, + rgba(255,255,255, 0.8) 50%, + rgba(255,255,255, 0))); + height: 1px; + width: 100%; +} + +.faded-vertical-divider { + @include background-image(linear-gradient(90deg, rgba(200,200,200, 0) 0%, + rgba(200,200,200, 1) 50%, + rgba(200,200,200, 0))); + height: 100%; + width: 1px; +} + +.faded-vertical-divider-light { + @include background-image(linear-gradient(90deg, rgba(255,255,255, 0) 0%, + rgba(255,255,255, 0.6) 50%, + rgba(255,255,255, 0))); + height: 100%; + width: 1px; +} + +.vertical-divider { + @extend .faded-vertical-divider; + position: relative; + + &::after { + @extend .faded-vertical-divider-light; + content: ""; + display: block; + position: absolute; + left: 1px; + } +} + +.horizontal-divider { + border: none; + @extend .faded-hr-divider; + position: relative; + + &::after { + @extend .faded-hr-divider-light; + content: ""; + display: block; + position: absolute; + top: 1px; + } +} + +.fade-right-hr-divider { + @include background-image(linear-gradient(180deg, rgba(200,200,200, 0) 0%, + rgba(200,200,200, 1))); + border: none; +} + +.fade-left-hr-divider { + @include background-image(linear-gradient(180deg, rgba(200,200,200, 1) 0%, + rgba(200,200,200, 0))); + border: none; +} + +// extends - ui \ No newline at end of file From 6418d033ddfa5514baa2b1216555dbc5d2b62b1d Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Fri, 8 Mar 2013 12:20:40 -0500 Subject: [PATCH 002/135] studio - adding files I forgot to add when cleaning up Sass and originally committing --- cms/static/sass/assets/_content-types.scss | 69 ++ cms/static/sass/assets/_fonts.scss | 36 + cms/static/sass/assets/_graphics.scss | 336 ++++++++ cms/static/sass/assets/_keyframes.scss | 27 + cms/static/sass/elements/_alerts.scss | 165 ++++ cms/static/sass/elements/_footer.scss | 50 ++ cms/static/sass/elements/_header.scss | 558 +++++++++++++ .../sass/elements/_jquery-ui-calendar.scss | 56 ++ cms/static/sass/elements/_modal.scss | 72 ++ cms/static/sass/elements/_navigation.scss | 31 + cms/static/sass/views/_account.scss | 295 +++++++ cms/static/sass/views/_assets.scss | 189 +++++ cms/static/sass/views/_dashboard.scss | 117 +++ cms/static/sass/views/_export.scss | 126 +++ cms/static/sass/views/_import.scss | 105 +++ cms/static/sass/views/_index.scss | 355 +++++++++ cms/static/sass/views/_outline.scss | 691 ++++++++++++++++ cms/static/sass/views/_settings.scss | 741 ++++++++++++++++++ cms/static/sass/views/_static-pages.scss | 156 ++++ cms/static/sass/views/_subsection.scss | 298 +++++++ cms/static/sass/views/_unit.scss | 670 ++++++++++++++++ cms/static/sass/views/_updates.scss | 221 ++++++ cms/static/sass/views/_users.scss | 81 ++ 23 files changed, 5445 insertions(+) create mode 100644 cms/static/sass/assets/_content-types.scss create mode 100644 cms/static/sass/assets/_fonts.scss create mode 100644 cms/static/sass/assets/_graphics.scss create mode 100644 cms/static/sass/assets/_keyframes.scss create mode 100644 cms/static/sass/elements/_alerts.scss create mode 100644 cms/static/sass/elements/_footer.scss create mode 100644 cms/static/sass/elements/_header.scss create mode 100644 cms/static/sass/elements/_jquery-ui-calendar.scss create mode 100644 cms/static/sass/elements/_modal.scss create mode 100644 cms/static/sass/elements/_navigation.scss create mode 100644 cms/static/sass/views/_account.scss create mode 100644 cms/static/sass/views/_assets.scss create mode 100644 cms/static/sass/views/_dashboard.scss create mode 100644 cms/static/sass/views/_export.scss create mode 100644 cms/static/sass/views/_import.scss create mode 100644 cms/static/sass/views/_index.scss create mode 100644 cms/static/sass/views/_outline.scss create mode 100644 cms/static/sass/views/_settings.scss create mode 100644 cms/static/sass/views/_static-pages.scss create mode 100644 cms/static/sass/views/_subsection.scss create mode 100644 cms/static/sass/views/_unit.scss create mode 100644 cms/static/sass/views/_updates.scss create mode 100644 cms/static/sass/views/_users.scss diff --git a/cms/static/sass/assets/_content-types.scss b/cms/static/sass/assets/_content-types.scss new file mode 100644 index 0000000000..7cca469350 --- /dev/null +++ b/cms/static/sass/assets/_content-types.scss @@ -0,0 +1,69 @@ +.content-type { + display: inline-block; + width: 14px; + height: 16px; + padding-left: 14px; + background-position: 8px center; + background-repeat: no-repeat; + vertical-align: middle; +} + +.videosequence-icon { + @extend .content-type; + background-image: url('../img/content-types/videosequence.png'); +} + +.video-icon { + @extend .content-type; + background-image: url('../img/content-types/video.png'); +} + +.problemset-icon { + @extend .content-type; + background-image: url('../img/content-types/problemset.png'); +} + +.problem-icon { + @extend .content-type; + background-image: url('../img/content-types/problem.png'); +} + +.lab-icon { + @extend .content-type; + background-image: url('../img/content-types/lab.png'); +} + +.tab-icon { + @extend .content-type; + background-image: url('../img/content-types/lab.png'); +} + +.html-icon { + @extend .content-type; + background-image: url('../img/content-types/html.png'); +} + +.vertical-icon { + @extend .content-type; + background-image: url('../img/content-types/vertical.png'); +} + +.sequential-icon { + @extend .content-type; + background-image: url('../img/content-types/sequential.png'); +} + +.chapter-icon { + @extend .content-type; + background-image: url('../img/content-types/chapter.png'); +} + +.module-icon { + @extend .content-type; + background-image: url('../img/content-types/module.png'); +} + +.module-icon { + @extend .content-type; + background-image: url('../img/content-types/module.png'); +} diff --git a/cms/static/sass/assets/_fonts.scss b/cms/static/sass/assets/_fonts.scss new file mode 100644 index 0000000000..92a2e185b7 --- /dev/null +++ b/cms/static/sass/assets/_fonts.scss @@ -0,0 +1,36 @@ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/k3k702ZOKiLJc3WVjuplzKRDOzjiPcYnFooOUGCOsRk.woff) format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + src: local('Open Sans Light'), local('OpenSans-Light'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/DXI1ORHCpsQm3Vp6mXoaTaRDOzjiPcYnFooOUGCOsRk.woff) format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 700; + src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/PRmiXeptR36kaC0GEAetxhbnBKKEOwRKgsHDreGcocg.woff) format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + src: local('Open Sans Light Italic'), local('OpenSansLight-Italic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/PRmiXeptR36kaC0GEAetxvR_54zmj3SbGZQh3vCOwvY.woff) format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + src: local('Open Sans Italic'), local('OpenSans-Italic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/xjAJXh38I15wypJXxuGMBrrIa-7acMAeDBVuclsi6Gc.woff) format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans'), local('OpenSans'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/cJZKeOuBrn4kERxqtaUH3bO3LdcAZYWl9Si6vvxL-qU.woff) format('woff'); +} diff --git a/cms/static/sass/assets/_graphics.scss b/cms/static/sass/assets/_graphics.scss new file mode 100644 index 0000000000..300cf3b692 --- /dev/null +++ b/cms/static/sass/assets/_graphics.scss @@ -0,0 +1,336 @@ +.expand-collapse-icon { + position: relative; + display: inline-block; + width: 9px; + height: 11px; + margin-right: 10px; + background: url(../img/expand-collapse-icons.png) no-repeat; + @include transition(none); + + &.expand { + top: 1px; + background-position: 0 0; + } + + &.collapse { + top: -1px; + background-position: 0 -11px; + } +} + +.sequence-icon { + display: inline-block; + width: 15px; + height: 9px; + margin-right: 5px; + background: url(../img/sequence-icon.png) no-repeat; +} + +.video-icon { + display: inline-block; + width: 14px; + height: 12px; + margin-right: 5px; + background: url(../img/video-icon.png) no-repeat; +} + +.upload-icon { + display: inline-block; + width: 22px; + height: 13px; + margin-right: 5px; + background: url(../img/upload-icon.png) no-repeat; +} + +.list-icon { + display: inline-block; + width: 14px; + height: 10px; + margin-right: 5px; + background: url(../img/list-icon.png) no-repeat; +} + +.close-icon { + display: inline-block; + width: 13px; + height: 12px; + background: url(../img/close-icon.png) no-repeat; +} + +.home-icon { + display: inline-block; + width: 19px; + height: 16px; + background: url(../img/home-icon.png) no-repeat; +} + +.small-home-icon { + display: inline-block; + width: 16px; + height: 14px; + background: url(../img/small-home-icon.png) no-repeat; +} + +.log-out-icon { + display: inline-block; + width: 15px; + height: 13px; + background: url(../img/log-out-icon.png) no-repeat; +} + +.collapse-all-icon { + display: inline-block; + width: 15px; + height: 9px; + background: url(../img/collapse-all-icon.png) no-repeat; +} + +.calendar-icon { + display: inline-block; + width: 12px; + height: 11px; + margin-right: 5px; + background: url(../img/calendar-icon.png) no-repeat; +} + +.edit-icon { + display: inline-block; + width: 12px; + height: 12px; + margin-right: 2px; + background: url(../img/edit-icon.png) no-repeat; + + &.white { + background: url(../img/edit-icon-white.png) no-repeat; + } +} + +.visibility-toggle { + .toggle-icon { + display: inline-block; + width: 27px; + height: 20px; + background: url(../img/small-toggle-icons.png) no-repeat; + background-position: 0 -34px; + } + + &.hidden .toggle-icon { + background-position: 0 -4px; + } + + &.both .toggle-icon { + background-position: 0 -64px; + } +} + + +.delete-icon { + display: inline-block; + width: 10px; + height: 11px; + margin-right: 2px; + background: url(../img/delete-icon.png) no-repeat; + + &.white { + background: url(../img/delete-icon-white.png) no-repeat; + } +} + +.drag-handle { + display: inline-block; + float: right; + width: 7px; + height: 22px; + margin-left: 10px; + background: url(../img/drag-handles.png) no-repeat; + cursor: move; +} + +.draft-tag, +.public-tag, +.private-tag { + margin-left: 3px; + font-size: 9px; + font-weight: 600; + text-transform: uppercase; + color: #a4aab7; +} + +.draft-tag { + color: #9f7d10; +} + +.plus-icon { + display: inline-block; + width: 11px; + height: 11px; + margin-right: 8px; + background: url(../img/plus-icon.png) no-repeat; + + &.white { + background: url(../img/plus-icon-white.png) no-repeat; + } +} + +.plus-icon-small { + display: inline-block; + width: 6px; + height: 6px; + margin-right: 8px; + background: url(../img/plus-icon-small.png) no-repeat center; +} + +.folder-icon { + display: inline-block; + width: 15px; + height: 11px; + margin-right: 4px; + background: url(../img/folder-icon.png) no-repeat; +} + +.new-folder-icon { + display: inline-block; + width: 23px; + height: 11px; + margin-right: 8px; + background: url(../img/new-folder-icon.png) no-repeat; +} + +.file-icon { + display: inline-block; + width: 10px; + height: 11px; + margin-right: 8px; + background: url(../img/file-icon.png) no-repeat; +} + +.new-unit-icon { + display: inline-block; + width: 23px; + height: 12px; + margin-right: 8px; + background: url(../img/new-unit-icon.png) right no-repeat; +} + +.new-policy-icon { + display: inline-block; + width: 23px; + height: 12px; + margin-right: 8px; + background: url(../img/new-unit-icon.png) right no-repeat; +} + +.textbook-icon { + display: inline-block; + width: 32px; + height: 32px; + margin-right: 8px; + vertical-align: middle; + background: url(../img/textbook-icon.png) no-repeat; +} + +.slides-icon { + display: inline-block; + width: 32px; + height: 32px; + margin-right: 8px; + vertical-align: middle; + background: url(../img/slides-icon.png) no-repeat; +} + +.large-slide-icon { + display: inline-block; + width: 100px; + height: 60px; + margin-right: 5px; + background: url(../img/large-slide-icon.png) center no-repeat; +} + +.large-html-icon { + display: inline-block; + width: 100px; + height: 60px; + margin-right: 5px; + background: url(../img/html-icon.png) center no-repeat; +} + +.large-openended-icon { + display: inline-block; + width: 100px; + height: 60px; + margin-right: 5px; + background: url(../img/large-openended-icon.png) center no-repeat; +} + +.large-annotations-icon { + display: inline-block; + width: 100px; + height: 60px; + margin-right: 5px; + background: url(../img/large-annotations-icon.png) center no-repeat; +} + +.large-advanced-icon { + display: inline-block; + width: 100px; + height: 60px; + margin-right: 5px; + background: url(../img/large-advanced-icon.png) center no-repeat; +} + +.large-textbook-icon { + display: inline-block; + width: 100px; + height: 60px; + margin-right: 5px; + background: url(../img/large-textbook-icon.png) center no-repeat; +} + +.large-discussion-icon { + display: inline-block; + width: 100px; + height: 60px; + margin-right: 5px; + background: url(../img/large-discussion-icon.png) center no-repeat; +} + +.large-freeform-icon { + display: inline-block; + width: 100px; + height: 60px; + margin-right: 5px; + background: url(../img/large-freeform-icon.png) center no-repeat; +} + +.large-problem-icon { + display: inline-block; + width: 100px; + height: 60px; + margin-right: 5px; + background: url(../img/large-problem-icon.png) center no-repeat; +} + +.large-video-icon { + display: inline-block; + width: 100px; + height: 60px; + margin-right: 5px; + background: url(../img/large-video-icon.png) center no-repeat; +} + +.spinner-icon { + display: inline-block; + width: 20px; + height: 20px; + margin-left: 10px; + vertical-align: middle; + background: url(../img/blue-spinner.gif) no-repeat; +} + +.spinner-in-field-icon { + display: inline-block; + width: 14px; + height: 14px; + vertical-align: middle; + background: url(../img/spinner-in-field.gif) no-repeat; +} diff --git a/cms/static/sass/assets/_keyframes.scss b/cms/static/sass/assets/_keyframes.scss new file mode 100644 index 0000000000..7661f18980 --- /dev/null +++ b/cms/static/sass/assets/_keyframes.scss @@ -0,0 +1,27 @@ +@mixin bounce-in { + 0% { + opacity: 0; + @include transform(scale(.3)); + } + + 50% { + opacity: 1; + @include transform(scale(1.05)); + } + + 100% { + @include transform(scale(1)); + } +} + +@-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();} + +@mixin bounce-in-animation($duration, $timing: ease-in-out) { + @include animation-name(bounce-in); + @include animation-duration($duration); + @include animation-timing-function($timing); + @include animation-fill-mode(both); +} diff --git a/cms/static/sass/elements/_alerts.scss b/cms/static/sass/elements/_alerts.scss new file mode 100644 index 0000000000..9c15f811e0 --- /dev/null +++ b/cms/static/sass/elements/_alerts.scss @@ -0,0 +1,165 @@ +// studio - elements - alerts, notifications, prompts +// ==================== + +// 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; + + &.is-shown { + bottom: 0; + opacity: 1.0; + } + + &.wrapper-notification-warning { + border-color: shade($yellow, 25%); + background: tint($yellow, 25%); + } + + &.wrapper-notification-error { + border-color: shade($red, 50%); + background: tint($red, 20%); + color: $white; + } + + &.wrapper-notification-confirm { + border-color: shade($green, 30%); + background: tint($green, 40%); + color: shade($green, 30%); + } +} + +.notification { + @include box-sizing(border-box); + 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; + + .icon { + display: inline-block; + vertical-align: top; + margin-right: 5px; + font-size: 20px; + } + + p { + width: flex-grid(8, 9); + display: inline-block; + vertical-align: top; + } + } + + .actions { + float: right; + width: flex-grid(3, 12); + margin-top: ($baseline/2); + text-align: right; + + li { + display: inline-block; + vertical-align: middle; + margin-right: 10px; + + &:last-child { + margin-right: 0; + } + } + + .save-button { + @include blue-button; + } + + .cancel-button { + @include white-button; + } + } + + strong { + font-weight: 700; + } +} + +// 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; + + .alert-message { + float: left; + margin-top: 4px; + } + + strong { + font-weight: 700; + } + + .alert-action { + float: right; + + &.secondary { + @include orange-button; + } + } +} + +body.error { + background: $darkGrey; + color: #3c3c3c; + + .primary-header { + display: none; + } + + .error-prompt { + width: 700px; + margin: 150px auto; + padding: 60px 50px 90px; + border-radius: 3px; + background: #fff; + text-align: center; + } + + h1 { + float: none; + margin: 0; + font-size: 60px; + font-weight: 300; + color: #3c3c3c; + } + + .description { + margin-bottom: 50px; + font-size: 21px; + } + + .back-button { + @include blue-button; + padding: 14px 40px 18px; + font-size: 18px; + } +} \ No newline at end of file diff --git a/cms/static/sass/elements/_footer.scss b/cms/static/sass/elements/_footer.scss new file mode 100644 index 0000000000..2c32de4bb3 --- /dev/null +++ b/cms/static/sass/elements/_footer.scss @@ -0,0 +1,50 @@ +// studio - global footer +// ==================== + +.wrapper-footer { + margin: ($baseline*1.5) 0 $baseline 0; + padding: $baseline; + position: relative; + width: 100%; + + footer.primary { + @include clearfix(); + @include font-size(13); + max-width: $fg-max-width; + min-width: $fg-min-width; + width: flex-grid(12); + margin: 0 auto; + padding-top: $baseline; + border-top: 1px solid $gray-l4; + color: $gray-l2; + + .colophon { + width: flex-grid(4, 12); + float: left; + margin-right: flex-gutter(2); + } + + .nav-peripheral { + width: flex-grid(6, 12); + float: right; + text-align: right; + + .nav-item { + display: inline-block; + margin-right: ($baseline/2); + + &:last-child { + margin-right: 0; + } + } + } + + a { + color: $gray-l1; + + &:hover, &:active { + color: $blue; + } + } + } +} \ No newline at end of file diff --git a/cms/static/sass/elements/_header.scss b/cms/static/sass/elements/_header.scss new file mode 100644 index 0000000000..1e09184801 --- /dev/null +++ b/cms/static/sass/elements/_header.scss @@ -0,0 +1,558 @@ +// studio - global header +// ==================== + +.wrapper-header { + margin: 0 0 ($baseline*1.5) 0; + padding: $baseline; + border-bottom: 1px solid $gray; + @include box-shadow(0 1px 5px 0 rgba(0,0,0, 0.1)); + background: $white; + height: 76px; + position: relative; + width: 100%; + z-index: 10; + + a { + color: $baseFontColor; + display: block; + + &:hover, &:active { + color: $blue; + } + } + + header.primary { + @include clearfix(); + max-width: $fg-max-width; + min-width: $fg-min-width; + width: flex-grid(12); + margin: 0 auto; + color: $gray-l1; + } +} + +// ==================== + +// basic layout +.wrapper-left, .wrapper-right { + @include box-sizing(border-box); +} + +.wrapper-left { + width: flex-grid(10, 12); + float: left; + margin-right: flex-gutter(); +} + +.wrapper-right { + width: flex-grid(2, 12); + float: right; +} + +// ==================== + +// specific elements - branding +.branding, .info-course, .nav-course, .nav-account, .nav-unauth, .nav-pitch { + display: inline-block; + vertical-align: top; +} + +.branding { + position: relative; + margin: 0 ($baseline/2) 0 0; + padding-right: ($baseline*0.75); + + a { + @include text-hide(); + display: block; + width: 164px; + height: 32px; + background: transparent url('../img/logo-edx-studio.png') 0 0 no-repeat; + } +} + +// ==================== + +// specific elements - course name/info +.info-course { + @include font-size(14); + position: relative; + margin: -3px ($baseline/2) 0 0; + padding-right: ($baseline*0.75); + + &:before { + @extend .faded-vertical-divider; + content: ""; + display: block; + height: 50px; + position: absolute; + right: 1px; + top: -8px; + width: 1px; + } + + &:after { + @extend .faded-vertical-divider-light; + content: ""; + display: block; + height: 50px; + position: absolute; + right: 0px; + top: -12px; + width: 1px; + } + + .course-org { + margin-right: ($baseline/4); + } + + .course-number, .course-org { + @include font-size(12); + display: inline-block; + } + + .course-title { + display: block; + width: 100%; + max-width: 220px; + overflow: hidden; + margin-top: -4px; + white-space: nowrap; + text-overflow: ellipsis; + @include font-size(16); + font-weight: 600; + } +} + +// ==================== + +// specific elements - course nav +.nav-course { + width: 335px; + margin-top: -($baseline/4); + @include font-size(14); + + > ol > .nav-item { + vertical-align: bottom; + margin: 0 ($baseline/2) 0 0; + + &:last-child { + margin-right: 0; + } + + .title { + display: block; + padding: 5px; + text-transform: uppercase; + font-weight: 600; + color: $gray-d3; + + .label-prefix { + display: block; + @include font-size(11); + font-weight: 400; + } + } + + // specific nav items + &.nav-course-courseware { + } + + &.nav-course-settings { + } + + &.nav-course-tools { + } + } +} + +// ==================== + +// specific elements - account-based nav +.nav-account { + width: 100%; + margin-top: ($baseline*0.75); + @include font-size(14); + text-align: right; + + .nav-account-username { + width: 100%; + + .icon-user { + display: inline-block; + vertical-align: middle; + margin-right: 3px; + @include font-size(12); + } + + .account-username { + display: inline-block; + vertical-align: middle; + width: 80%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .icon-expand { + display: inline-block; + vertical-align: middle; + } + } +} + +// ==================== + +// UI - dropdown +.nav-dropdown { + + .nav-item { + position: relative; + + .icon-expand { + @include font-size(14); + @include transition (color 0.5s ease-in-out, opacity 0.5s ease-in-out); + display: inline-block; + margin-left: 2px; + opacity: 0.5; + color: $gray-l2; + } + + &:hover { + + .icon-expand { + color: $blue; + opacity: 1.0; + } + } + } + + .wrapper-nav-sub { + position: absolute; + left: -7px; + top: 47px; + width: 140px; + opacity: 0; + pointer-events: none; + } + + .nav-sub { + @include border-radius(2px); + @include box-sizing(border-box); + @include box-shadow(0 1px 5px 0 rgba(0,0,0, 0.1)); + position: relative; + width: 100%; + border: 1px solid $gray-l2; + padding: ($baseline/4) ($baseline/2); + background: $white; + + &:after, &:before { + bottom: 100%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + } + + &:after { + border-color: rgba(255, 255, 255, 0); + border-bottom-color: #fff; + border-width: 5px; + right: 3px; + margin-left: -5px; + } + + &:before { + border-color: rgba(178, 178, 178, 0); + border-bottom-color: $gray-l2; + border-width: 6px; + right: 3px; + margin-left: -6px; + } + + .nav-item { + display: block; + margin: 0 0 ($baseline/4) 0; + border-bottom: 1px solid $gray-l5; + padding: 0 0($baseline/4) 0; + @include font-size(13); + + &:last-child { + margin-bottom: 0; + border-bottom: none; + padding-bottom: 0; + } + + a { + display: block; + } + } + } + + // UI - dropdown - specific navs + &.nav-account { + + .wrapper-nav-sub { + top: 27px; + left: auto; + right: -13px; + width: 110px; + } + + .nav-sub { + text-align: left; + + .icon-expand { + top: -2px; + } + } + + .nav-sub:after { + left: auto; + right: 11px; + } + + .nav-sub:before { + left: auto; + right: 10px; + } + } + + &.nav-course { + + .nav-course-courseware { + + .nav-sub:after { + left: 88px; + } + + .nav-sub:before { + left: 88px; + } + } + + .nav-course-settings { + + .nav-sub:after { + left: 88px; + } + + .nav-sub:before { + left: 88px; + } + } + + .nav-course-tools { + + .wrapper-nav-sub { + top: ($baseline*1.5); + width: 100px; + } + + .nav-sub:after { + left: 68px; + } + + .nav-sub:before { + left: 68px; + } + } + } +} + +// ==================== + +// STATE: is-signed in +.is-signedin { + + &.course .branding { + + &:before { + @extend .faded-vertical-divider; + content: ""; + display: block; + height: 50px; + position: absolute; + right: 1px; + top: -8px; + width: 1px; + } + + &:after { + @extend .faded-vertical-divider-light; + content: ""; + display: block; + height: 50px; + position: absolute; + right: 0px; + top: -12px; + width: 1px; + } + } +} + +// ==================== + +// STATE: not signed in +.not-signedin { + + .wrapper-left { + width: flex-grid(4, 12); + } + + .wrapper-right { + width: flex-grid(8, 12); + } + + // STATE: not signed in - unauthenticated nav + .nav-not-signedin { + float: right; + margin-top: ($baseline/4); + + .nav-item { + @include font-size(16); + vertical-align: middle; + margin: 0 $baseline 0 0; + + &:last-child { + margin-right: 0; + } + + .action { + margin-top: -($baseline/4); + display: inline-block; + padding: ($baseline/4) ($baseline/2); + } + } + + // STATE: not signed in - specific items + .nav-not-signedin-help { + + } + + .nav-not-signedin-signup { + margin-right: ($baseline/2); + + .action-signup { + @include blue-button; + @include transition(all .15s); + @include font-size(14); + padding: ($baseline/4) ($baseline/2); + text-transform: uppercase; + font-weight: 600; + } + } + + .nav-not-signedin-signin { + + .action-signin { + @include white-button; + @include transition(all .15s); + @include font-size(14); + padding: ($baseline/4) ($baseline/2); + text-transform: uppercase; + font-weight: 600; + } + } + } +} + +// ==================== + +// STATE: active/current nav states + +.nav-item.is-current, +body.howitworks .nav-not-signedin-hiw, + +// dashboard +body.dashboard .nav-account-dashboard, + +// course content +body.course.outline .nav-course-courseware .title, +body.course.updates .nav-course-courseware .title, +body.course.pages .nav-course-courseware .title, +body.course.uploads .nav-course-courseware .title, + +body.course.outline .nav-course-courseware-outline, +body.course.updates .nav-course-courseware-updates, +body.course.pages .nav-course-courseware-pages, +body.course.uploads .nav-course-courseware-uploads, + +// course settings +body.course.schedule .nav-course-settings .title, +body.course.grading .nav-course-settings .title, +body.course.team .nav-course-settings .title, +body.course.advanced .nav-course-settings .title, + +body.course.schedule .nav-course-settings-schedule, +body.course.grading .nav-course-settings-grading, +body.course.team .nav-course-settings-team, +body.course.advanced .nav-course-settings-advanced, + +// course tools +body.course.import .nav-course-tools .title, +body.course.export .nav-course-tools .title, + +body.course.import .nav-course-tools-import, +body.course.export .nav-course-tools-export, + +{ + + color: $blue; + + a { + color: $blue; + pointer-events: none; + } +} + +body.signup .nav-not-signedin-signin { + + a { + background-color: #d9e3ee; + color: #6d788b; + } +} + +body.signin .nav-not-signedin-signup { + + a { + background-color: #62aaf5; + color: #fff; + } +} + +// ==================== + +// STATE: js enabled +.js { + + .nav-dropdown { + + .nav-item .title { + outline: 0; + cursor: pointer; + + &:hover, &:active, &.is-selected { + color: $blue; + + .icon-expand { + color: $blue; + } + } + } + } + + .wrapper-nav-sub { + @include transition (opacity 1.0s ease-in-out 0s); + opacity: 0; + pointer-events: none; + + &.is-shown { + opacity: 1.0; + pointer-events: auto; + } + } +} \ No newline at end of file diff --git a/cms/static/sass/elements/_jquery-ui-calendar.scss b/cms/static/sass/elements/_jquery-ui-calendar.scss new file mode 100644 index 0000000000..3d20bde642 --- /dev/null +++ b/cms/static/sass/elements/_jquery-ui-calendar.scss @@ -0,0 +1,56 @@ +// studio - elements - JQUI calendar +// ==================== + +.ui-datepicker { + border-color: $darkGrey; + border-radius: 2px; + background: #fff; + font-family: $sans-serif; + font-size: 12px; + @include box-shadow(0 5px 10px rgba(0, 0, 0, 0.1)); + + .ui-widget-header { + background: $darkGrey; + border: none; + border-radius: 2px; + } + + .ui-datepicker-next, + .ui-datepicker-prev { + @include transition(none); + + &.ui-state-hover { + border-color: transparent; + background: $mediumGrey; + + .ui-icon-circle-triangle-e, + .ui-icon-circle-triangle-w { + background-image: url(../css/vendor/ui-lightness/images/ui-icons_ffffff_256x240.png); + } + } + } + + .ui-state-default { + border-color: $mediumGrey; + color: $blue; + @include transition(none); + + &.ui-state-hover { + background: $orange; + border-color: $orange; + color: #fff; + } + } + + .ui-state-highlight { + background: $blue; + border-color: $blue; + color: #fff; + } + + .ui-state-active { + background: $orange; + border-color: $orange; + color: #fff; + } +} \ No newline at end of file diff --git a/cms/static/sass/elements/_modal.scss b/cms/static/sass/elements/_modal.scss new file mode 100644 index 0000000000..b81baf4565 --- /dev/null +++ b/cms/static/sass/elements/_modal.scss @@ -0,0 +1,72 @@ +// studio - elements - modal windows +// ==================== + +.modal-cover { + display: none; + position: fixed; + top: 0; + left: 0; + z-index: 1000; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, .8); +} + +.modal { + display: none; + position: fixed; + top: 60px; + left: 50%; + z-index: 1001; + width: 930px; + height: 540px; + margin-left: -465px; + background: #fff; + + .modal-body { + height: 400px; + padding: 40px; + overflow-y: scroll; + } + + .modal-actions { + height: 60px; + @include linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0)); + background-color: #d1dae3; + } + + h2 { + margin: 0 10px 30px; + color: #646464; + font-size: 19px; + font-weight: 300; + letter-spacing: 1px; + text-transform: uppercase; + } + + p { + margin: 20px; + } + + .revert-button { + @include blue-button; + margin: 13px 6px 0 13px; + } + + .close-button { + @include white-button; + margin-top: 13px; + } +} + +// lean modal alternative +#lean_overlay { + position: fixed; + z-index: 10000; + top: 0px; + left: 0px; + display: none; + height: 100%; + width: 100%; + background: $black; +} \ No newline at end of file diff --git a/cms/static/sass/elements/_navigation.scss b/cms/static/sass/elements/_navigation.scss new file mode 100644 index 0000000000..8c123db64f --- /dev/null +++ b/cms/static/sass/elements/_navigation.scss @@ -0,0 +1,31 @@ +// studio - elements - navigation +// ==================== + +// common + +// ==================== + +// primary +nav.primary { + + .nav-item { + display: inline-block; + } +} + + +// ==================== + +// right hand side + +// ==================== + +// tabs + +// ==================== + +// dropdown + +// ==================== + +// \ No newline at end of file diff --git a/cms/static/sass/views/_account.scss b/cms/static/sass/views/_account.scss new file mode 100644 index 0000000000..1206db5e76 --- /dev/null +++ b/cms/static/sass/views/_account.scss @@ -0,0 +1,295 @@ +// studio - views - sign up/in +// ==================== + +body.signup, body.signin { + + .wrapper-content { + margin: 0; + padding: 0 $baseline; + position: relative; + width: 100%; + } + + .content { + @include clearfix(); + @include font-size(16); + max-width: $fg-max-width; + min-width: $fg-min-width; + width: flex-grid(12); + margin: 0 auto; + color: $gray-d2; + + header { + position: relative; + margin-bottom: $baseline; + border-bottom: 1px solid $gray-l4; + padding-bottom: ($baseline/2); + + h1 { + @include font-size(32); + margin: 0; + padding: 0; + font-weight: 600; + } + + .action { + @include font-size(13); + position: absolute; + right: 0; + top: 40%; + } + } + + .introduction { + @include font-size(14); + margin: 0 0 $baseline 0; + } + } + + .content-primary, .content-supplementary { + @include box-sizing(border-box); + float: left; + } + + .content-primary { + width: flex-grid(8, 12); + margin-right: flex-gutter(); + + form { + @include box-sizing(border-box); + @include box-shadow(0 1px 2px $shadow-l1); + @include border-radius(2px); + width: 100%; + border: 1px solid $gray-l2; + padding: $baseline ($baseline*1.5); + background: $white; + + .form-actions { + margin-top: $baseline; + + .action-primary { + @include blue-button; + @include transition(all .15s); + @include font-size(15); + display:block; + width: 100%; + padding: ($baseline*0.75) ($baseline/2); + font-weight: 600; + text-transform: uppercase; + } + } + + .list-input { + margin: 0; + padding: 0; + list-style: none; + + .field { + margin: 0 0 ($baseline*0.75) 0; + + &:last-child { + margin-bottom: 0; + } + + &.required { + + label { + font-weight: 600; + } + + label:after { + margin-left: ($baseline/4); + content: "*"; + } + } + + label, input, textarea { + display: block; + } + + label { + @include font-size(14); + @include transition(color, 0.15s, ease-in-out); + margin: 0 0 ($baseline/4) 0; + + &.is-focused { + color: $blue; + } + } + + input, textarea { + @include font-size(16); + height: 100%; + width: 100%; + padding: ($baseline/2); + + &.long { + width: 100%; + } + + &.short { + width: 25%; + } + + ::-webkit-input-placeholder { + color: $gray-l4; + } + + :-moz-placeholder { + color: $gray-l3; + } + + ::-moz-placeholder { + color: $gray-l3; + } + + :-ms-input-placeholder { + color: $gray-l3; + } + + &:focus { + + + .tip { + color: $gray; + } + } + } + + textarea.long { + height: ($baseline*5); + } + + input[type="checkbox"] { + display: inline-block; + margin-right: ($baseline/4); + width: auto; + height: auto; + + & + label { + display: inline-block; + } + } + + .tip { + @include transition(color, 0.15s, ease-in-out); + @include font-size(13); + display: block; + margin-top: ($baseline/4); + color: $gray-l3; + } + } + + .field-group { + @include clearfix(); + margin: 0 0 ($baseline/2) 0; + + .field { + display: block; + width: 47%; + border-bottom: none; + margin: 0 $baseline 0 0; + padding-bottom: 0; + + &:nth-child(odd) { + float: left; + } + + &:nth-child(even) { + float: right; + margin-right: 0; + } + + input, textarea { + width: 100%; + } + } + } + } + } + } + + .content-supplementary { + width: flex-grid(4, 12); + + .bit { + @include font-size(13); + margin: 0 0 $baseline 0; + border-bottom: 1px solid $gray-l4; + padding: 0 0 $baseline 0; + color: $gray-l1; + + &:last-child { + margin-bottom: 0; + border: none; + padding-bottom: 0; + } + + h3 { + @include font-size(14); + margin: 0 0 ($baseline/4) 0; + color: $gray-d2; + font-weight: 600; + } + + } + } +} + +.signup { + +} + +.signin { + + #field-password { + position: relative; + + .action-forgotpassword { + @include font-size(13); + position: absolute; + top: 0; + right: 0; + } + } +} + +// ==================== + +// messages +.message { + @include font-size(14); + display: block; +} + +.message-status { + display: none; + @include border-top-radius(2px); + @include box-sizing(border-box); + border-bottom: 2px solid $yellow-d2; + margin: 0 0 $baseline 0; + padding: ($baseline/2) $baseline; + font-weight: 500; + background: $yellow-d1; + color: $white; + + .ss-icon { + position: relative; + top: 3px; + @include font-size(16); + display: inline-block; + margin-right: ($baseline/2); + } + + .text { + display: inline-block; + } + + &.error { + border-color: shade($red, 50%); + background: tint($red, 20%); + } + + &.is-shown { + display: block; + } +} diff --git a/cms/static/sass/views/_assets.scss b/cms/static/sass/views/_assets.scss new file mode 100644 index 0000000000..2c1b435c44 --- /dev/null +++ b/cms/static/sass/views/_assets.scss @@ -0,0 +1,189 @@ +// studio - views - assets +// ==================== + +.uploads { + input.asset-search-input { + float: left; + width: 260px; + background-color: #fff; + } + + .asset-library { + @include clearfix; + + table { + width: 100%; + border-radius: 3px 3px 0 0; + border: 1px solid #c5cad4; + + td, + th { + padding: 10px 20px; + text-align: left; + vertical-align: middle; + } + + thead th { + @include linear-gradient(top, transparent, rgba(0, 0, 0, .1)); + background-color: #ced2db; + font-size: 12px; + font-weight: 700; + text-shadow: 0 1px 0 rgba(255, 255, 255, .5); + } + + tbody { + background: #fff; + + tr { + border-top: 1px solid #c5cad4; + + &:first-child { + border-top: none; + } + } + + .name-col { + font-size: 14px; + } + + .date-col { + font-size: 12px; + } + } + + .thumb-col { + width: 100px; + } + + .date-col { + width: 220px; + } + + .embed-col { + width: 250px; + } + + .embeddable-xml-input { + @include box-shadow(none); + width: 100%; + } + + .thumb { + width: 100px; + max-height: 80px; + + img { + width: 100%; + } + } + } + + .pagination { + float: right; + margin: 15px 10px; + + ol, li { + display: inline; + } + + a { + display: inline-block; + height: 25px; + padding: 0 4px; + text-align: center; + line-height: 25px; + } + } + } + .show-xml { + @include blue-button; + } +} + +.upload-modal { + display: none; + width: 640px !important; + margin-left: -320px !important; + + .modal-body { + height: auto !important; + overflow-y: auto !important; + text-align: center; + } + + .file-input { + display: none; + } + + .choose-file-button { + @include blue-button; + padding: 10px 82px 12px; + font-size: 17px; + } + + .progress-bar { + display: none; + width: 350px; + height: 50px; + margin: 30px auto 10px; + border: 1px solid $blue; + + &.loaded { + border-color: #66b93d; + + .progress-fill { + background: #66b93d; + } + } + } + + .progress-fill { + width: 0%; + height: 50px; + background: $blue; + color: #fff; + line-height: 48px; + } + + h1 { + float: none; + margin: 40px 0 30px; + font-size: 34px; + font-weight: 300; + } + + .close-button { + @include white-button; + position: absolute; + top: 0; + right: 15px; + width: 29px; + height: 29px; + padding: 0 !important; + border-radius: 17px !important; + line-height: 29px; + text-align: center; + } + + .embeddable { + display: none; + margin: 30px 0 130px; + + label { + display: block; + margin-bottom: 10px; + font-weight: 700; + } + } + + .embeddable-xml-input { + @include box-shadow(none); + width: 400px; + } + + .copy-button { + @include white-button; + display: none; + margin-bottom: 100px; + } +} \ No newline at end of file diff --git a/cms/static/sass/views/_dashboard.scss b/cms/static/sass/views/_dashboard.scss new file mode 100644 index 0000000000..2a995ffdc7 --- /dev/null +++ b/cms/static/sass/views/_dashboard.scss @@ -0,0 +1,117 @@ +// studio - views - user dashboard +// ==================== + +.class-list { + margin-top: 20px; + border-radius: 3px; + border: 1px solid $darkGrey; + background: #fff; + @include box-shadow(0 1px 2px rgba(0, 0, 0, .1)); + + li { + position: relative; + border-bottom: 1px solid $mediumGrey; + + &:last-child { + border-bottom: none; + } + + .class-link { + z-index: 100; + display: block; + padding: 20px 25px; + line-height: 1.3; + + &:hover { + background: $paleYellow; + + + .view-live-button { + opacity: 1.0; + pointer-events: auto; + } + } + } + } + + .class-name { + display: block; + font-size: 19px; + font-weight: 300; + } + + .detail { + font-size: 14px; + font-weight: 400; + margin-right: 20px; + color: #3c3c3c; + } + + // view live button + .view-live-button { + z-index: 10000; + position: absolute; + top: 15px; + right: $baseline; + padding: ($baseline/4) ($baseline/2); + opacity: 0; + pointer-events: none; + + &:hover { + opacity: 1.0; + pointer-events: auto; + } + } +} + +.new-course { + padding: 15px 25px; + margin-top: 20px; + border-radius: 3px; + border: 1px solid $darkGrey; + background: #fff; + box-shadow: 0 1px 2px rgba(0, 0, 0, .1); + @include clearfix; + + .row { + margin-bottom: 15px; + @include clearfix; + } + + .column { + float: left; + width: 48%; + } + + .column:first-child { + margin-right: 4%; + } + + .course-info { + width: 600px; + } + + label { + display: block; + font-size: 13px; + font-weight: 700; + } + + .new-course-org, + .new-course-number, + .new-course-name { + width: 100%; + } + + .new-course-name { + font-size: 19px; + font-weight: 300; + } + + .new-course-save { + @include blue-button; + } + + .new-course-cancel { + @include white-button; + } +} \ No newline at end of file diff --git a/cms/static/sass/views/_export.scss b/cms/static/sass/views/_export.scss new file mode 100644 index 0000000000..27cb7f923b --- /dev/null +++ b/cms/static/sass/views/_export.scss @@ -0,0 +1,126 @@ +// studio - views - course export +// ==================== + +.export { + .export-overview { + @extend .window; + @include clearfix; + padding: 30px 40px; + } + + .description { + float: left; + width: 62%; + margin-right: 3%; + font-size: 14px; + + h2 { + font-weight: 700; + font-size: 19px; + margin-bottom: 20px; + } + + strong { + font-weight: 700; + } + + p + p { + margin-top: 20px; + } + + ul { + margin: 20px 0; + list-style: disc inside; + + li { + margin: 0 0 5px 0; + } + } + } + + .export-form-wrapper { + + .export-form { + float: left; + width: 35%; + padding: 25px 30px 35px; + @include box-sizing(border-box); + border: 1px solid $mediumGrey; + border-radius: 3px; + background: $lightGrey; + text-align: center; + + h2 { + margin-bottom: 30px; + font-size: 26px; + font-weight: 300; + } + + .error-block { + display: none; + margin-bottom: 15px; + font-size: 13px; + } + + .error-block { + color: $error-red; + } + + .button-export { + @include green-button; + padding: 10px 50px 11px; + font-size: 17px; + } + + .message-status { + margin-top: 10px; + font-size: 12px; + } + + .progress-bar { + display: none; + width: 350px; + height: 30px; + margin: 30px auto 10px; + border: 1px solid $blue; + + &.loaded { + border-color: #66b93d; + + .progress-fill { + background: #66b93d; + } + } + } + + .progress-fill { + width: 0%; + height: 30px; + background: $blue; + color: #fff; + line-height: 48px; + } + } + + // downloading state + &.is-downloading { + + .progress-bar { + display: block; + } + + .button-export { + padding: 10px 50px 11px; + font-size: 17px; + + &.disabled { + + pointer-events: none; + cursor: default; + } + } + } + } + + +} \ No newline at end of file diff --git a/cms/static/sass/views/_import.scss b/cms/static/sass/views/_import.scss new file mode 100644 index 0000000000..fe7d65626b --- /dev/null +++ b/cms/static/sass/views/_import.scss @@ -0,0 +1,105 @@ +// studio - views - course import +// ==================== + +.import { + .import-overview { + @extend .window; + @include clearfix; + padding: 30px 40px; + } + + .description { + float: left; + width: 62%; + margin-right: 3%; + font-size: 14px; + + h2 { + font-weight: 700; + font-size: 19px; + margin-bottom: 20px; + } + + strong { + font-weight: 700; + } + + p + p { + margin-top: 20px; + } + } + + .import-form { + float: left; + width: 35%; + padding: 25px 30px 35px; + @include box-sizing(border-box); + border: 1px solid $mediumGrey; + border-radius: 3px; + background: $lightGrey; + text-align: center; + + h2 { + margin-bottom: 30px; + font-size: 26px; + font-weight: 300; + } + + .file-name-block, + .error-block { + display: none; + margin-bottom: 15px; + font-size: 13px; + } + + .error-block { + color: $error-red; + } + + .choose-file-button { + @include blue-button; + padding: 10px 50px 11px; + font-size: 17px; + } + + .choose-file-button-inline { + display: block; + } + + .file-input { + display: none; + } + + .submit-button { + @include orange-button; + display: none; + max-width: 100%; + padding: 8px 20px 10px; + white-space: normal; + } + } + + .progress-bar { + display: none; + width: 350px; + height: 30px; + margin: 30px auto 10px; + border: 1px solid $blue; + + &.loaded { + border-color: #66b93d; + + .progress-fill { + background: #66b93d; + } + } + } + + .progress-fill { + width: 0%; + height: 30px; + background: $blue; + color: #fff; + line-height: 48px; + } +} \ No newline at end of file diff --git a/cms/static/sass/views/_index.scss b/cms/static/sass/views/_index.scss new file mode 100644 index 0000000000..8d81ea33fd --- /dev/null +++ b/cms/static/sass/views/_index.scss @@ -0,0 +1,355 @@ +// studio - views - how it works +// ==================== + +.index { + + &.not-signedin { + + .wrapper-header { + margin-bottom: 0; + } + + .wrapper-footer { + margin: 0; + border-top: 2px solid $gray-l3; + + footer.primary { + border: none; + margin-top: 0; + padding-top: 0; + } + } + + .wrapper-content-header, .wrapper-content-features, .wrapper-content-cta { + @include box-sizing(border-box); + margin: 0; + padding: 0 $baseline; + position: relative; + width: 100%; + } + + .content { + @include clearfix(); + @include font-size(16); + max-width: $fg-max-width; + min-width: $fg-min-width; + width: flex-grid(12); + margin: 0 auto; + color: $gray-d2; + + header { + border: none; + padding-bottom: 0; + margin-bottom: 0; + } + + h1, h2, h3, h4, h5, h6 { + color: $gray-d3; + } + + h2 { + + } + + h3 { + + } + + h4 { + + } + } + + // welcome content + .wrapper-content-header { + @include linear-gradient($blue-l1,$blue,$blue-d1); + padding-bottom: ($baseline*4); + padding-top: ($baseline*4); + } + + .content-header { + position: relative; + text-align: center; + color: $white; + + h1 { + @include font-size(52); + float: none; + margin: 0 0 ($baseline/2) 0; + border-bottom: 1px solid $blue-l1; + padding: 0; + font-weight: 500; + color: $white; + } + + .logo { + @include text-hide(); + position: relative; + top: 3px; + display: inline-block; + vertical-align: baseline; + width: 282px; + height: 57px; + background: transparent url('../img/logo-edx-studio-white.png') 0 0 no-repeat; + } + + .tagline { + @include font-size(24); + margin: 0; + color: $blue-l3; + } + } + + .arrow_box { + position: relative; + background: #fff; + border: 4px solid #000; + } + .arrow_box:after, .arrow_box:before { + top: 100%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + } + + .arrow_box:after { + border-color: rgba(255, 255, 255, 0); + border-top-color: #fff; + border-width: 30px; + left: 50%; + margin-left: -30px; + } + .arrow_box:before { + border-color: rgba(0, 0, 0, 0); + border-top-color: #000; + border-width: 36px; + left: 50%; + margin-left: -36px; + } + + // feature content + .wrapper-content-features { + @include box-shadow(0 -1px ($baseline/4) $shadow); + padding-bottom: ($baseline*2); + padding-top: ($baseline*3); + background: $white; + } + + .content-features { + + .list-features { + + } + + // indiv features + .feature { + @include clearfix(); + margin: 0 0 ($baseline*2) 0; + border-bottom: 1px solid $gray-l4; + padding: 0 0 ($baseline*2) 0; + + .img { + @include box-sizing(border-box); + float: left; + width: flex-grid(3, 12); + margin-right: flex-gutter(); + + a { + @include box-sizing(border-box); + @include box-shadow(0 1px ($baseline/10) $shadow-l1); + position: relative; + top: 0; + display: block; + overflow: hidden; + border: 1px solid $gray-l3; + padding: ($baseline/4); + background: $white; + + .action-zoom { + @include transition(bottom .50s ease-in-out); + position: absolute; + bottom: -30px; + right: ($baseline/2); + opacity: 0; + + .ss-icon { + @include font-size(18); + @include border-top-radius(3px); + display: inline-block; + padding: ($baseline/4) ($baseline/2); + background: $blue; + color: $white; + text-align: center; + } + } + + &:hover { + border-color: $blue; + + .action-zoom { + opacity: 1.0; + bottom: -2px; + } + } + } + + img { + display: block; + width: 100%; + height: 100%; + } + } + + .copy { + float: left; + width: flex-grid(9, 12); + margin-top: -($baseline/4); + + h3 { + margin: 0 0 ($baseline/2) 0; + @include font-size(24); + font-weight: 600; + } + + > p { + @include font-size(18); + color: $gray-d1; + } + + strong { + color: $gray-d2; + font-weight: 500; + } + + .list-proofpoints { + @include clearfix(); + @include font-size(14); + width: flex-grid(9, 9); + margin: ($baseline*1.5) 0 0 0; + + .proofpoint { + @include box-sizing(border-box); + @include border-radius(($baseline/4)); + @include transition(color .50s ease-in-out); + position: relative; + top: 0; + float: left; + width: flex-grid(3, 9); + min-height: ($baseline*8); + margin-right: flex-gutter(); + padding: ($baseline*0.75) $baseline; + color: $gray-l1; + + .title { + @include font-size(16); + margin: 0 0 ($baseline/4) 0; + font-weight: 500; + color: $gray-d3; + } + + &:hover { + @include box-shadow(0 1px ($baseline/10) $shadow-l1); + background: $blue-l5; + top: -($baseline/5); + + .title { + color: $blue; + } + } + + &:last-child { + margin-right: 0; + } + } + } + } + + + &:last-child { + margin-bottom: 0; + border: none; + padding-bottom: 0; + } + + &:nth-child(even) { + + .img { + float: right; + margin-right: 0; + margin-left: flex-gutter(); + } + + .copy { + float: right; + text-align: right; + } + + .list-proofpoints { + + .proofpoint { + float: right; + width: flex-grid(3, 9); + margin-left: flex-gutter(); + margin-right: 0; + + &:last-child { + margin-left: 0; + } + } + } + } + } + } + + // call to action content + .wrapper-content-cta { + padding-bottom: ($baseline*2); + padding-top: ($baseline*2); + background: $white; + } + + .content-cta { + border-top: 1px solid $gray-l4; + + header { + border: none; + margin: 0; + padding: 0; + } + + .list-actions { + position: relative; + margin-top: -($baseline*1.5); + + li { + width: flex-grid(6, 12); + margin: 0 auto; + } + + .action { + display: block; + width: 100%; + text-align: center; + } + + .action-primary { + @include blue-button; + @include transition(all .15s); + @include font-size(18); + padding: ($baseline*0.75) ($baseline/2); + font-weight: 600; + text-align: center; + text-transform: uppercase; + } + + .action-secondary { + @include font-size(14); + margin-top: ($baseline/2); + } + } + } + } +} \ No newline at end of file diff --git a/cms/static/sass/views/_outline.scss b/cms/static/sass/views/_outline.scss new file mode 100644 index 0000000000..2957b57849 --- /dev/null +++ b/cms/static/sass/views/_outline.scss @@ -0,0 +1,691 @@ +// studio - views - course outline +// ==================== + +input.courseware-unit-search-input { + float: left; + width: 260px; + background-color: #fff; +} + +.branch { + + .section-item { + @include clearfix(); + + .details { + display: block; + float: left; + margin-bottom: 0; + width: 650px; + } + + .gradable-status { + float: right; + position: relative; + top: -4px; + right: 50px; + width: 145px; + + .status-label { + position: absolute; + top: 2px; + right: -5px; + display: none; + width: 110px; + padding: 5px 40px 5px 10px; + @include border-radius(3px); + color: $lightGrey; + text-align: right; + font-size: 12px; + font-weight: bold; + line-height: 16px; + } + + .menu-toggle { + z-index: 10; + position: absolute; + top: 0; + right: 5px; + padding: 5px; + color: $mediumGrey; + + &:hover, &.is-active { + color: $blue; + } + } + + .menu { + z-index: 1; + display: none; + opacity: 0.0; + position: absolute; + top: -1px; + left: 5px; + margin: 0; + padding: 8px 12px; + background: $white; + border: 1px solid $mediumGrey; + font-size: 12px; + @include border-radius(4px); + @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); + @include transition(opacity .15s); + + + li { + width: 115px; + margin-bottom: 3px; + padding-bottom: 3px; + border-bottom: 1px solid $lightGrey; + + &:last-child { + margin-bottom: 0; + padding-bottom: 0; + border: none; + + a { + color: $darkGrey; + } + } + } + + a { + color: $blue; + + &.is-selected { + font-weight: bold; + } + } + } + + // dropdown state + &.is-active { + + .menu { + z-index: 1000; + display: block; + opacity: 1.0; + } + + .menu-toggle { + z-index: 10000; + } + } + + // set state + &.is-set { + + .menu-toggle { + color: $blue; + } + + .status-label { + display: block; + color: $blue; + } + } + } + } + } + + +.courseware-section { + position: relative; + background: #fff; + border-radius: 3px; + border: 1px solid $mediumGrey; + margin-top: 15px; + padding-bottom: 12px; + @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.1)); + + &:first-child { + margin-top: 0; + } + + &.collapsed { + padding-bottom: 0; + } + + label { + float: left; + line-height: 29px; + } + + .datepair { + float: left; + margin-left: 10px; + } + + .section-published-date { + position: absolute; + top: 19px; + right: 90px; + padding: 4px 10px; + border-radius: 3px; + background: $lightGrey; + text-align: right; + + .published-status { + font-size: 12px; + margin-right: 15px; + + strong { + font-weight: bold; + } + } + + .schedule-button { + @include blue-button; + } + + .edit-button { + @include blue-button; + } + + .schedule-button, + .edit-button { + font-size: 11px; + padding: 3px 15px 5px; + } + } + + .datepair .date, + .datepair .time { + padding-left: 0; + padding-right: 0; + border: none; + background: none; + @include box-shadow(none); + font-size: 13px; + font-weight: bold; + color: $blue; + cursor: pointer; + } + + .datepair .date { + width: 80px; + } + + .datepair .time { + width: 65px; + } + + &.collapsed .subsection-list, + .collapsed .subsection-list, + .collapsed > ol { + display: none !important; + } + + header { + min-height: 75px; + @include clearfix(); + + .item-details, .section-published-date { + + } + + .item-details { + display: inline-block; + padding: 20px 0 10px 0; + @include clearfix(); + + .section-name { + float: left; + margin-right: 10px; + width: 350px; + font-size: 19px; + font-weight: bold; + color: $blue; + } + + .section-name-span { + cursor: pointer; + @include transition(color .15s); + + &:hover { + color: $orange; + } + } + + .section-name-edit { + position: relative; + width: 400px; + background: $white; + + input { + font-size: 16px; + } + + .save-button { + @include blue-button; + padding: 7px 20px 7px; + margin-right: 5px; + } + + .cancel-button { + @include white-button; + padding: 7px 20px 7px; + } + } + + .section-published-date { + float: right; + width: 265px; + margin-right: 220px; + @include border-radius(3px); + background: $lightGrey; + + .published-status { + font-size: 12px; + margin-right: 15px; + + strong { + font-weight: bold; + } + } + + .schedule-button { + @include blue-button; + } + + .edit-button { + @include blue-button; + } + + .schedule-button, + .edit-button { + font-size: 11px; + padding: 3px 15px 5px; + + } + } + + .gradable-status { + position: absolute; + top: 20px; + right: 70px; + width: 145px; + + .status-label { + position: absolute; + top: 0; + right: 2px; + display: none; + width: 100px; + padding: 10px 35px 10px 10px; + @include border-radius(3px); + background: $lightGrey; + color: $lightGrey; + text-align: right; + font-size: 12px; + font-weight: bold; + line-height: 16px; + } + + .menu-toggle { + z-index: 10; + position: absolute; + top: 2px; + right: 5px; + padding: 5px; + color: $lightGrey; + + &:hover, &.is-active { + color: $blue; + } + } + + .menu { + z-index: 1; + display: none; + opacity: 0.0; + position: absolute; + top: -1px; + left: 2px; + margin: 0; + padding: 8px 12px; + background: $white; + border: 1px solid $mediumGrey; + font-size: 12px; + @include border-radius(4px); + @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); + @include transition(opacity .15s); + @include transition(display .15s); + + + li { + width: 115px; + margin-bottom: 3px; + padding-bottom: 3px; + border-bottom: 1px solid $lightGrey; + + &:last-child { + margin-bottom: 0; + padding-bottom: 0; + border: none; + + a { + color: $darkGrey; + } + } + } + + a { + + &.is-selected { + font-weight: bold; + } + } + } + + // dropdown state + &.is-active { + + .menu { + z-index: 1000; + display: block; + opacity: 1.0; + } + + + .menu-toggle { + z-index: 10000; + } + } + + // set state + &.is-set { + + .menu-toggle { + color: $blue; + } + + .status-label { + display: block; + color: $blue; + } + } + + float: left; + padding: 21px 0 0; + } + } + + .item-actions { + margin-top: 21px; + margin-right: 12px; + + .edit-button, + .delete-button { + margin-top: -3px; + } + } + + .expand-collapse-icon { + float: left; + margin: 29px 6px 16px 16px; + @include transition(none); + + &.expand { + background-position: 0 0; + } + + &.collapsed { + + } + } + + .drag-handle { + margin-left: 11px; + } + } + + h3 { + font-size: 19px; + font-weight: 700; + color: $blue; + } + + .section-name-span { + cursor: pointer; + @include transition(color .15s); + + &:hover { + color: $orange; + } + } + + .section-name-form { + margin-bottom: 15px; + } + + .section-name-edit { + input { + font-size: 16px; + } + + .save-button { + @include blue-button; + padding: 7px 20px 7px; + margin-right: 5px; + } + + .cancel-button { + @include white-button; + padding: 7px 20px 7px; + } + } + + h4 { + font-size: 12px; + color: #878e9d; + + strong { + font-weight: bold; + } + } + + .list-header { + @include linear-gradient(top, transparent, rgba(0, 0, 0, .1)); + background-color: #ced2db; + border-radius: 3px 3px 0 0; + } + + .subsection-list { + margin: 0 12px; + + > ol { + @include tree-view; + border-top-width: 0; + } + } + + &.new-section { + + header { + height: auto; + @include clearfix(); + } + + .expand-collapse-icon { + visibility: hidden; + } + + .item-details { + padding: 25px 0 0 0; + + .section-name { + float: none; + width: 100%; + } + } + } +} + +.toggle-button-sections { + display: none; + position: relative; + float: right; + margin-top: 10px; + + font-size: 13px; + color: $darkGrey; + + &.is-shown { + display: block; + } + + .ss-icon { + @include border-radius(20px); + position: relative; + top: -1px; + display: inline-block; + margin-right: 2px; + line-height: 5px; + font-size: 11px; + } + + .label { + display: inline-block; + } +} + +.new-section-name, +.new-subsection-name-input { + width: 515px; +} + +.new-section-name-save, +.new-subsection-name-save { + @include blue-button; + padding: 4px 20px 7px; + margin: 0 5px; + color: #fff !important; +} + +.new-section-name-cancel, +.new-subsection-name-cancel { + @include white-button; + padding: 4px 20px 7px; + color: #8891a1 !important; +} + +.dummy-calendar { + display: none; + position: absolute; + top: 55px; + left: 110px; + z-index: 9999; + border: 1px solid #3C3C3C; + @include box-shadow(0 1px 15px rgba(0, 0, 0, .2)); +} + +.unit-name-input { + padding: 20px 40px; + + label { + display: block; + } + + input { + width: 100%; + font-size: 20px; + } +} + +.preview { + background: url(../img/preview.jpg) center top no-repeat; +} + +.edit-subsection-publish-settings { + display: none; + position: fixed; + top: 100px; + left: 50%; + z-index: 99999; + width: 600px; + margin-left: -300px; + background: #fff; + text-align: center; + + .settings { + padding: 40px; + } + + h3 { + font-size: 34px; + font-weight: 300; + } + + .picker { + margin: 30px 0 65px; + } + + .description { + margin-top: 30px; + font-size: 14px; + line-height: 20px; + } + + strong { + font-weight: 700; + } + + .start-date, + .start-time { + font-size: 19px; + } + + .save-button { + @include blue-button; + margin-right: 10px; + } + + .cancel-button { + @include white-button; + } + + .save-button, + .cancel-button { + font-size: 16px; + } +} + +.collapse-all-button { + float: right; + margin-top: 10px; + font-size: 13px; + color: $darkGrey; +} + +// sort/drag and drop +.ui-droppable { + @include transition (padding 0.5s ease-in-out 0s); + min-height: 20px; + padding: 0; + + &.dropover { + padding: 15px 0; + } +} + +.ui-draggable-dragging { + @include box-shadow(0 1px 2px rgba(0, 0, 0, .3)); + border: 1px solid $darkGrey; + opacity : 0.2; + &:hover { + opacity : 1.0; + .section-item { + background: $yellow !important; + } + } + + // hiding unit button - temporary fix until this semantically corrected + .new-unit-item { + display: none; + } +} + +ol.ui-droppable .branch:first-child .section-item { + border-top: none; +} + diff --git a/cms/static/sass/views/_settings.scss b/cms/static/sass/views/_settings.scss new file mode 100644 index 0000000000..8fc9540a57 --- /dev/null +++ b/cms/static/sass/views/_settings.scss @@ -0,0 +1,741 @@ +// studio - views - course settings +// ==================== + +body.course.settings { + + .content-primary, .content-supplementary { + @include box-sizing(border-box); + float: left; + } + + .content-primary { + @extend .window; + width: flex-grid(9, 12); + margin-right: flex-gutter(); + padding: $baseline ($baseline*1.5); + } + + // messages - should be synced up with global messages in the future + .message { + display: block; + font-size: 14px; + } + + .message-status { + display: none; + @include border-top-radius(2px); + @include box-sizing(border-box); + border-bottom: 2px solid $yellow; + margin: 0 0 20px 0; + padding: 10px 20px; + font-weight: 500; + background: $paleYellow; + + .text { + display: inline-block; + } + + &.error { + border-color: shade($red, 50%); + background: tint($red, 20%); + color: $white; + } + + &.confirm { + border-color: shade($green, 50%); + background: tint($green, 20%); + color: $white; + } + + &.is-shown { + display: block; + } + } + + // in form - elements + .group-settings { + margin: 0 0 ($baseline*2) 0; + + header { + @include clearfix(); + + .title-2 { + width: flex-grid(4, 9); + margin: 0 flex-gutter() 0 0; + float: left; + } + + .tip { + @include font-size(13); + width: flex-grid(5, 9); + float: right; + margin-top: ($baseline/2); + text-align: right; + color: $gray-l2; + } + } + + // basic layout/elements + .title-2 { + + } + + .title-3 { + + } + + // in form -UI hints/tips/messages + .instructions { + @include font-size(14); + margin: 0 0 $baseline 0; + } + + .tip { + @include transition(color, 0.15s, ease-in-out); + @include font-size(13); + display: block; + margin-top: ($baseline/4); + color: $gray-l3; + } + + .message-error { + @include font-size(13); + display: block; + margin-top: ($baseline/4); + margin-bottom: ($baseline/2); + color: $red; + } + + // buttons + .remove-item { + @include white-button; + @include font-size(13); + font-weight: 400; + } + + .new-button { + @include font-size(13); + } + + // form basics + .list-input { + margin: 0; + padding: 0; + list-style: none; + + .field { + margin: 0 0 $baseline 0; + + &:last-child { + margin-bottom: 0; + } + + &.required { + + label { + font-weight: 600; + } + + label:after { + margin-left: ($baseline/4); + content: "*"; + } + } + + label, input, textarea { + display: block; + } + + label { + @include font-size(14); + @include transition(color, 0.15s, ease-in-out); + margin: 0 0 ($baseline/4) 0; + font-weight: 400; + + &.is-focused { + color: $blue; + } + } + + input, textarea { + @include placeholder($gray-l4); + @include font-size(16); + @include size(100%,100%); + padding: ($baseline/2); + + &.long { + } + + &.short { + } + + &.error { + border-color: $red; + } + + &:focus { + + + .tip { + color: $gray; + } + } + } + + textarea.long { + height: ($baseline*5); + } + + input[type="checkbox"] { + display: inline-block; + margin-right: ($baseline/4); + width: auto; + height: auto; + + & + label { + display: inline-block; + } + } + } + + .field-group { + @include clearfix(); + margin: 0 0 ($baseline/2) 0; + } + + // enumerated/grouped lists + &.enum { + + .field-group { + @include box-sizing(border-box); + @include border-radius(3px); + background: $gray-l5; + padding: $baseline; + + &:last-child { + padding-bottom: $baseline; + } + + .actions { + @include clearfix(); + margin-top: ($baseline/2); + border-top: 1px solid $gray-l4; + padding-top: ($baseline/2); + + .remove-item { + float: right; + } + } + } + } + } + + // existing inputs + .input-existing { + margin: 0 0 $baseline 0; + + .actions { + margin: ($baseline/4) 0 0 0; + } + } + + // not editable fields + .field.is-not-editable { + + label, .label { + color: $gray-l3; + } + + input { + opacity: 0.25; + } + } + + // field with error + .field.error { + + input, textarea { + border-color: $red; + } + } + + // specific fields - basic + &.basic { + + .list-input { + @include clearfix(); + + .field { + margin-bottom: 0; + } + } + + #field-course-organization { + float: left; + width: flex-grid(2, 9); + margin-right: flex-gutter(); + } + + #field-course-number { + float: left; + width: flex-grid(2, 9); + margin-right: flex-gutter(); + } + + #field-course-name { + float: left; + width: flex-grid(5, 9); + } + } + + // specific fields - schedule + &.schedule { + + .list-input { + margin-bottom: ($baseline*1.5); + + &:last-child { + margin-bottom: 0; + } + } + + .field-group { + @include clearfix(); + border-bottom: 1px solid $gray-l5; + padding-bottom: ($baseline/2); + + &:last-child { + border: none; + padding-bottom: 0; + } + + .field { + float: left; + width: flex-grid(3, 9); + margin-bottom: ($baseline/4); + margin-right: flex-gutter(); + } + + .field.time { + position: relative; + + .tip { + position: absolute; + top: 0; + right: 0; + } + } + } + } + + // specific fields - overview + #field-course-overview { + + #course-overview { + height: ($baseline*20); + } + } + + // specific fields - video + #field-course-introduction-video { + + .input-existing { + @include box-sizing(border-box); + @include border-radius(3px); + background: $gray-l5; + padding: ($baseline/2); + + .actions { + @include clearfix(); + margin-top: ($baseline/2); + border-top: 1px solid $gray-l4; + padding-top: ($baseline/2); + + .remove-item { + float: right; + } + } + } + + .actions { + margin-top: ($baseline/2); + border-top: 1px solid $gray-l5; + padding-top: ($baseline/2); + } + } + + // specific fields - requirements + &.requirements { + + #field-course-effort { + width: flex-grid(3, 9); + } + } + + // specific fields - grading range (artifact styling) + &.grade-range { + margin-bottom: ($baseline*3); + + .grade-controls { + @include clearfix; + width: flex-grid(9,9); + } + + .new-grade-button { + @include box-sizing(border-box); + @include linear-gradient(top, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0)); + @include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset); + width: flex-grid(1,9); + height: ($baseline*2); + position: relative; + display: inline-block; + vertical-align: middle; + margin-right: flex-gutter(); + border-radius: 20px; + border: 1px solid $darkGrey; + background-color: #d1dae3; + color: #6d788b; + + .plus-icon { + position: absolute; + top: 50%; + left: 50%; + margin-left: -6px; + margin-top: -6px; + } + } + + .grade-slider { + @include box-sizing(border-box); + width: flex-grid(8,9); + display: inline-block; + vertical-align: middle; + + .grade-bar { + position: relative; + width: 100%; + height: ($baseline*2.5); + background: $lightGrey; + + .increments { + position: relative; + + li { + position: absolute; + top: 52px; + width: 30px; + margin-left: -15px; + font-size: 9px; + text-align: center; + + &.increment-0 { + left: 0; + } + + &.increment-10 { + left: 10%; + } + + &.increment-20 { + left: 20%; + } + + &.increment-30 { + left: 30%; + } + + &.increment-40 { + left: 40%; + } + + &.increment-50 { + left: 50%; + } + + &.increment-60 { + left: 60%; + } + + &.increment-70 { + left: 70%; + } + + &.increment-80 { + left: 80%; + } + + &.increment-90 { + left: 90%; + } + + &.increment-100 { + left: 100%; + } + } + } + + .grade-specific-bar { + height: 50px !important; + } + + .grades { + position: relative; + + li { + position: absolute; + top: 0; + height: 50px; + text-align: right; + @include border-radius(2px); + + &:hover, + &.is-dragging { + .remove-button { + display: block; + } + } + + &.is-dragging { + + } + + .remove-button { + display: none; + position: absolute; + top: -17px; + right: 1px; + height: 17px; + font-size: 10px; + } + + &:nth-child(1) { + background: #4fe696; + } + + &:nth-child(2) { + background: #ffdf7e; + } + + &:nth-child(3) { + background: #ffb657; + } + + &:nth-child(4) { + background: #ef54a1; + } + + &:nth-child(5), + &.bar-fail { + background: #fb336c; + } + + .letter-grade { + display: block; + margin: 10px 15px 0 0; + font-size: 16px; + font-weight: 700; + line-height: 14px; + } + + .range { + display: block; + margin-right: 15px; + font-size: 10px; + line-height: 12px; + } + + .drag-bar { + position: absolute; + top: 0; + right: -1px; + height: 50px; + width: 2px; + background-color: #fff; + @include box-shadow(-1px 0 3px rgba(0,0,0,0.1)); + + cursor: ew-resize; + @include transition(none); + + &:hover { + width: 6px; + right: -2px; + } + } + } + } + } + } + } + + // specific fields - grading rules + &.grade-rules { + + #field-course-grading-graceperiod { + width: flex-grid(3, 9); + } + } + + &.assignment-types { + + .list-input { + + &:last-child { + margin-bottom: 0; + } + } + + .field-group { + @include clearfix(); + width: flex-grid(9, 9); + margin-bottom: ($baseline*1.5); + border-bottom: 1px solid $gray-l5; + padding-bottom: ($baseline*1.5); + + &:last-child { + border: none; + padding-bottom: 0; + } + + .field { + display: inline-block; + vertical-align: top; + width: flex-grid(3, 6); + margin-bottom: ($baseline/2); + margin-right: flex-gutter(); + } + + #field-course-grading-assignment-shortname, + #field-course-grading-assignment-totalassignments, + #field-course-grading-assignment-gradeweight, + #field-course-grading-assignment-droppable { + width: flex-grid(2, 6); + } + } + + .actions { + float: left; + width: flex-grid(9, 9); + + .delete-button { + margin: 0; + } + } + } + + // specific fields - advanced settings + &.advanced-policies { + + .field-group { + margin-bottom: ($baseline*1.5); + + &:last-child { + border: none; + padding-bottom: 0; + } + } + + .course-advanced-policy-list-item { + @include clearfix(); + position: relative; + + .field { + + input { + width: 100%; + } + + .tip { + @include transition (opacity 0.5s ease-in-out 0s); + opacity: 0; + position: absolute; + bottom: ($baseline*1.25); + } + + input:focus { + + & + .tip { + opacity: 1.0; + } + } + + input.error { + + & + .tip { + opacity: 0; + } + } + } + + .key, .value { + float: left; + margin: 0 0 ($baseline/2) 0; + } + + .key { + width: flex-grid(3, 9); + margin-right: flex-gutter(); + } + + .value { + width: flex-grid(6, 9); + } + + .actions { + float: left; + width: flex-grid(9, 9); + + .delete-button { + margin: 0; + } + } + } + + .message-error { + position: absolute; + bottom: ($baseline*0.75); + } + + // specific to code mirror instance in JSON policy editing, need to sync up with other similar code mirror UIs + .CodeMirror { + @include font-size(16); + @include box-sizing(border-box); + @include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset); + @include linear-gradient($lightGrey, tint($lightGrey, 90%)); + padding: 5px 8px; + border: 1px solid $mediumGrey; + border-radius: 2px; + background-color: $lightGrey; + font-family: 'Open Sans', sans-serif; + color: $baseFontColor; + outline: 0; + + &.CodeMirror-focused { + @include linear-gradient($paleYellow, tint($paleYellow, 90%)); + outline: 0; + } + + .CodeMirror-scroll { + overflow: hidden; + height: auto; + min-height: ($baseline*1.5); + max-height: ($baseline*10); + } + + // editor color changes just for JSON + .CodeMirror-lines { + + .cm-string { + color: #cb9c40; + } + + pre { + line-height: 2.0rem; + } + } + } + } + } + + .content-supplementary { + width: flex-grid(3, 12); + } +} \ No newline at end of file diff --git a/cms/static/sass/views/_static-pages.scss b/cms/static/sass/views/_static-pages.scss new file mode 100644 index 0000000000..83856c773e --- /dev/null +++ b/cms/static/sass/views/_static-pages.scss @@ -0,0 +1,156 @@ +// studio - views - course static pages +// ==================== + +.static-pages { + .new-static-page-button { + @include grey-button; + display: block; + text-align: center; + padding: 12px 0; + } + + .unit-body { + padding: 0; + + .details { + display: block !important; + + h2 { + margin: 0 0 5px 0; + } + } + } + + .component-editor { + border: none; + border-radius: 0; + } + + .components > li { + margin: 0; + border-radius: 0; + + &.new-component-item { + background: transparent; + border: none; + @include box-shadow(none); + } + } + + .component { + border: 1px solid $mediumGrey; + border-top: none; + + &:first-child { + border-top: 1px solid $mediumGrey; + } + + &:hover { + border: 1px solid $mediumGrey; + border-top: none; + + &:first-child { + border-top: 1px solid $mediumGrey; + } + + .drag-handle { + background: url(../img/drag-handles.png) center no-repeat #fff; + } + } + + .drag-handle { + top: 0; + right: 0; + z-index: 11; + width: 35px; + border: none; + background: url(../img/drag-handles.png) center no-repeat #fff; + + &:hover { + background: url(../img/drag-handles.png) center no-repeat #fff; + } + } + + .component-actions { + top: 26px; + right: 44px; + } + } + + .component.editing { + border-left: 1px solid $mediumGrey; + border-right: 1px solid $mediumGrey; + + .xmodule_display { + display: none; + } + } + + .new .xmodule_display { + background: $yellow; + } + + .xmodule_display { + padding: 20px 20px 22px; + font-size: 24px; + font-weight: 300; + background: #fff; + @include transition(background-color 3s); + } + + .static-page-item { + position: relative; + margin: 10px 0; + padding: 22px 20px; + border: 1px solid $darkGrey; + border-radius: 3px; + background: #fff; + @include box-shadow(0 1px 2px rgba(0, 0, 0, .1)); + + .page-name { + font-size: 19px; + font-weight: 700; + } + + .item-actions { + margin-top: 19px; + margin-right: 12px; + } + } +} + +.edit-static-page { + .main-wrapper { + margin-top: 40px; + } + + .static-page-details { + @extend .window; + padding: 32px 40px; + + .row { + border: none; + } + } + + .page-display-name-input { + width: 100%; + font-size: 20px; + } + + .page-contents { + @include box-sizing(border-box); + width: 100%; + height: 360px; + padding: 15px; + border: 1px solid #b0b6c2; + border-radius: 2px; + @include linear-gradient(top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .3)); + background-color: #edf1f5; + @include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset); + font-family: Monaco, monospace; + font-size: 13px; + color: #3c3c3c; + outline: 0; + } +} \ No newline at end of file diff --git a/cms/static/sass/views/_subsection.scss b/cms/static/sass/views/_subsection.scss new file mode 100644 index 0000000000..9b0789ba52 --- /dev/null +++ b/cms/static/sass/views/_subsection.scss @@ -0,0 +1,298 @@ +// studio - views - course subsection +// ==================== + +.subsection .main-wrapper { + margin: 40px; +} + +.subsection .inner-wrapper { + @include clearfix(); +} + +.subsection-body { + padding: 32px 40px; + @include clearfix; + + > div { + margin-bottom: 40px; + } + + input { + font-size: 14px; + } + + .unit-subtitle { + display: block; + width: 100%; + } + + .sortable-unit-list { + ol { + @include tree-view; + } + } + + .policy-list { + input[disabled] { + border: none; + @include box-shadow(none); + } + + .policy-list-name { + margin-right: 5px; + margin-bottom: 10px; + } + + .policy-list-value { + width: 320px; + margin-right: 10px; + } + } + + .policy-list-element { + .save-button, + .cancel-button { + display: none; + } + + .edit-icon { + margin-right: 8px; + } + + &.editing, + &.new-policy-list-element { + .policy-list-name, + .policy-list-value { + border: 1px solid #b0b6c2; + @include linear-gradient(top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .3)); + background-color: #edf1f5; + @include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset); + } + } + } + + .new-policy-list-element { + padding: 10px 10px 0; + margin: 0 -10px 10px; + border-radius: 3px; + background: $mediumGrey; + + .save-button { + @include blue-button; + margin-bottom: 10px; + } + + .cancel-button { + @include white-button; + } + + .edit-icon { + display: none; + } + + .delete-icon { + display: none; + } + } + + .new-policy-item { + margin: 10px 0; + + .plus-icon-small { + position: relative; + top: -1px; + vertical-align: middle; + } + } +} + +.subsection-name-input { + label { + display: block; + } + + input { + width: 100%; + font-size: 20px; + } +} + +.scheduled-date-input, +.due-date-input { + @include clearfix; + + .date-input, + .time-input { + display: inline-block; + width: 100px; + } + + .inherits-check { + label { + font-size: 13px; + } + } + + .notice { + margin-top: 6px; + font-size: 11px; + color: #999; + } +} + +.due-date-input { + label { + display: inline-block !important; + margin-right: 10px; + } + + a { + font-size: 11px; + font-weight: bold; + text-transform: uppercase; + } + + .date-setter { + @include clearfix; + display: none; + } + + .remove-date { + display: block; + } +} + +.row.visibility { + label { + display: inline-block !important; + margin-right: 10px; + line-height: 21px; + } + + a { + display: inline-block; + height: 31px; + margin-right: 8px; + vertical-align: middle; + font-size: 11px; + font-weight: 700; + line-height: 31px; + text-transform: uppercase; + } + + .large-toggle { + width: 41px; + background: url(../img/large-toggles.png) no-repeat; + background-position: 0 -50px; + + .hidden { + background-position: 0 -5px; + } + } +} + +.gradable { + + label { + display: inline-block; + vertical-align: top; + } + + .gradable-status { + position: relative; + top: -4px; + display: inline-block; + margin-left: 10px; + width: 65%; + + .status-label { + margin: 0; + padding: 0; + background: transparent; + color: $blue; + border: none; + font-size: 11px; + font-weight: bold; + text-transform: uppercase; + } + + .menu-toggle { + z-index: 100; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 20px; + background: transparent; + + &:hover, &.is-active { + color: $blue; + } + } + + .menu { + z-index: 1; + position: absolute; + top: -12px; + left: -7px; + display: none; + width: 100%; + margin: 0; + padding: 8px 12px; + opacity: 0.0; + background: $white; + border: 1px solid $mediumGrey; + font-size: 12px; + @include border-radius(4px); + @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); + @include transition(opacity .15s); + + + li { + margin-bottom: 3px; + padding-bottom: 3px; + border-bottom: 1px solid $lightGrey; + + &:last-child { + margin-bottom: 0; + padding-bottom: 0; + border: none; + } + } + + a { + + &.is-selected { + font-weight: bold; + } + } + } + + // dropdown state + &.is-active { + + .menu { + z-index: 10000; + display: block; + opacity: 1.0; + } + + .menu-toggle { + z-index: 1000; + } + } + + // set state + &.is-set { + + .menu-toggle { + color: $blue; + } + + .status-label { + display: block; + color: $blue; + } + } + } +} \ No newline at end of file diff --git a/cms/static/sass/views/_unit.scss b/cms/static/sass/views/_unit.scss new file mode 100644 index 0000000000..bcd3fdb912 --- /dev/null +++ b/cms/static/sass/views/_unit.scss @@ -0,0 +1,670 @@ +// studio - views - course unit +// ==================== + +.unit .main-wrapper { + @include clearfix(); + margin: 40px; +} + +//Problem Selector tab menu requirements +.js .tabs .tab { + display: none; +} +//end problem selector reqs + +.main-column { + clear: both; + float: left; + width: 70%; +} + +.unit-body.published { + .components > li { + border: none; + + .rendered-component { + padding: 0 20px; + } + } +} + +.unit-body { + .breadcrumbs { + border-radius: 3px 3px 0 0; + border-bottom: 1px solid #cbd1db; + @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0) 70%); + background-color: #edf1f5; + @include box-shadow(0 1px 0 rgba(255, 255, 255, .7) inset); + @include clearfix; + + li { + float: left; + } + + a, + .current-page { + display: block; + padding: 15px 35px 15px 30px; + font-size: 14px; + background: url(../img/breadcrumb-arrow.png) no-repeat right center; + } + } + + h2 { + margin: 30px 40px 30px 0; + color: #646464; + font-size: 19px; + font-weight: 300; + letter-spacing: 1px; + text-transform: uppercase; + } + + .components { + + > li { + position: relative; + z-index: 10; + margin: 20px 40px; + + + + .title { + margin: 0 0 15px 0; + color: $mediumGrey; + + .value { + } + } + + &.new-component-item { + margin: 20px 0px; + border-top: 1px solid $mediumGrey; + box-shadow: 0 2px 1px rgba(182, 182, 182, 0.75) inset; + background-color: $lightGrey; + margin-bottom: 0px; + padding-bottom: 20px; + + .new-component-button { + display: block; + padding: 20px; + text-align: center; + color: #edf1f5; + } + + h5 { + margin: 20px 0px; + color: #fff; + font-weight: 600; + font-size: 18px; + } + + .rendered-component { + display: none; + background: #fff; + border-radius: 3px 3px 0 0; + } + + .new-component-type { + + a, + li { + display: inline-block; + } + + a { + border: 1px solid $mediumGrey; + width: 100px; + height: 100px; + color: #fff; + margin-right: 15px; + margin-bottom: 20px; + border-radius: 8px; + font-size: 15px; + line-height: 14px; + text-align: center; + @include box-shadow(0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset); + + .name { + position: absolute; + bottom: 5px; + left: 0; + width: 100%; + padding: 10px; + @include box-sizing(border-box); + color: #fff; + } + } + } + + .new-component-templates { + display: none; + margin: 20px 40px 20px 40px; + border-radius: 3px; + border: 1px solid $mediumGrey; + background-color: #fff; + @include box-shadow(0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset); + @include clearfix; + + .cancel-button { + margin: 20px 0px 10px 10px; + @include white-button; + } + + .problem-type-tabs { + display: none; + } + + // specific menu types + &.new-component-problem { + padding-bottom:10px; + + .ss-icon, .editor-indicator { + display: inline-block; + } + + .problem-type-tabs { + display: inline-block; + } + } + } + + .new-component-type, + .new-component-template { + @include clearfix; + + a { + position: relative; + border: 1px solid $darkGreen; + background: tint($green,20%); + color: #fff; + + &:hover { + background: $brightGreen; + } + } + } + + .problem-type-tabs { + list-style-type: none; + border-radius: 0; + width: 100%; + @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); + background-color: $lightBluishGrey; + @include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset); + + li:first-child { + margin-left: 20px; + } + + li { + float:left; + display:inline-block; + text-align:center; + width: auto; + @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); + background-color: tint($lightBluishGrey, 10%); + @include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset); + opacity:.8; + + &:hover { + opacity:1; + background-color: tint($lightBluishGrey, 20%); + } + + &.ui-state-active { + border: 0px; + @include active; + opacity:1; + } + } + + a{ + display: block; + padding: 15px 25px; + font-size: 15px; + line-height: 16px; + text-align: center; + color: #3c3c3c; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3); + } + } + + .new-component-template { + + a { + background: #fff; + border: 0px; + color: #3c3c3c; + @include transition (none); + + &:hover { + background: tint($green,30%); + color: #fff; + @include transition(background-color .15s); + } + } + + li { + border:none; + border-bottom: 1px dashed $lightGrey; + color: #fff; + } + + li:first-child { + a { + border-top: 0px; + } + } + + li:nth-child(2) { + a { + border-radius: 0px; + } + } + + a { + @include clearfix(); + display: block; + padding: 7px 20px; + border-bottom: none; + font-weight: 500; + + .name { + float: left; + + .ss-icon { + @include transition(opacity .15s); + display: inline-block; + top: 1px; + margin-right: 5px; + opacity: 0.5; + width: 17; + height: 21px; + vertical-align: middle; + } + } + + .editor-indicator { + @include transition(opacity .15s); + float: right; + position: relative; + top: 3px; + font-size: 12px; + opacity: 0.3; + } + + .ss-icon, .editor-indicator { + display: none; + } + + &:hover { + color: #fff; + + .ss-icon { + opacity: 1.0; + } + + .editor-indicator { + opacity: 1.0; + } + } + } + + // specific editor types + .empty { + + a { + line-height: 1.4; + font-weight: 400; + background: #fff; + color: #3c3c3c; + + + &:hover { + background: tint($green,30%); + color: #fff; + } + } + } + } + + .new-component { + text-align: center; + + h5 { + color: $darkGreen; + } + + } + } + } + } + + .component { + border: 1px solid $lightBluishGrey2; + border-radius: 3px; + background: #fff; + @include transition(none); + + &:hover { + border-color: #6696d7; + + .drag-handle { + background-color: $blue; + border-color: $blue; + } + } + + &.editing { + border: 1px solid $lightBluishGrey2; + z-index: auto; + + .drag-handle, + .component-actions { + display: none; + } + } + + &.component-placeholder { + border-color: #6696d7; + } + + .component-actions { + position: absolute; + top: 7px; + right: 9px; + } + + .drag-handle { + position: absolute; + display: block; + top: -1px; + right: -16px; + z-index: 10; + width: 15px; + height: 100%; + border-radius: 0 3px 3px 0; + border: 1px solid $lightBluishGrey2; + background: url(../img/white-drag-handles.png) center no-repeat $lightBluishGrey2; + cursor: move; + @include transition(none); + } + } + + .xmodule_display { + padding: 40px 20px 20px; + overflow-x: auto; + + h1 { + float: none; + margin-left: 0; + } + } + + .wrapper-component-editor { + z-index: 9999; + position: relative; + background: $lightBluishGrey2; + } + + .component-editor { + @include edit-box; + @include box-shadow(none); + display: none; + padding: 20px; + border-radius: 2px 2px 0 0; + + .metadata_edit { + margin-bottom: 20px; + font-size: 13px; + + li { + margin-bottom: 10px; + } + + label { + display: inline-block; + margin-right: 10px; + } + } + + h3 { + margin-bottom: 10px; + font-size: 18px; + font-weight: 700; + } + + h5 { + margin-bottom: 8px; + color: #fff; + font-weight: 700; + } + + .save-button { + margin-top: 10px; + margin: 15px 8px 0 0; + } + } +} + +.unit-settings { + .window-contents { + padding: 10px 20px; + } + + .unit-actions { + border-bottom: none; + padding-bottom: 0; + } + + .published-alert { + display: none; + padding: 10px; + border: 1px solid #edbd3c; + border-radius: 3px; + background: #fbf6e1; + font-size: 14px; + line-height: 1.4; + + div { + margin-top: 15px; + } + } + + input[type="radio"] { + margin-right: 7px; + } + + .status { + font-size: 12px; + + strong { + font-weight: 700; + } + } + + .preview-button, .view-button { + @include white-button; + margin-bottom: 10px; + } + + .publish-button { + @include orange-button; + } + + .delete-button { + @include blue-button; + } + + .delete-draft { + display: inline-block; + } + + .delete-button, + .preview-button, + .publish-button, + .view-button { + font-size: 11px; + margin-top: 10px; + padding: 6px 15px 8px; + } +} + +.unit-history { + &.collapsed { + h4 { + border-bottom: none; + border-radius: 3px; + } + + .window-contents { + display: none; + } + } + + ol { + border: 1px solid #ced2db; + + li { + display: block; + padding: 6px 8px 8px 10px; + background: #edf1f5; + font-size: 12px; + + &:hover { + background: #fffcf1; + + .item-actions { + display: block; + } + } + + &.checked { + background: #d1dae3; + } + + .item-actions { + display: none; + } + + input[type="radio"] { + margin-right: 7px; + } + } + } +} + +.unit-location { + .url { + width: 100%; + margin-bottom: 10px; + @include box-shadow(none); + } + + .draft-tag, + .hidden-tag, + .private-tag, + .has-new-draft-tag { + font-size: 8px; + } + + .window-contents > ol { + @include tree-view; + + .section-item { + display: inline-block; + width: 100%; + font-size: 11px; + padding: 2px 8px 4px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + @include box-sizing(border-box); + } + + ol { + .section-item { + padding-left: 20px; + } + + .new-unit-item { + margin-left: 20px; + } + } + + ol ol { + .section-item { + padding-left: 34px; + } + + .new-unit-item { + margin: 0 0 10px 41px; + } + } + } +} + +.edit-state-draft { + .visibility, + + .edit-draft-message, + .view-button { + display: none; + } + + .published-alert { + display: block; + } +} + +.edit-state-public { + .delete-draft, + .component-actions, + .new-component-item, + .editing-draft-alert, + .publish-draft-message, + .preview-button { + display: none; + } + + .published-alert { + display: block; + } + + .drag-handle { + display: none !important; + } +} + +.edit-state-private { + .delete-draft, + .publish-draft, + .editing-draft-alert, + .create-draft, + .view-button { + display: none; + } +} + +// editing units from courseware +body.unit { + + .component { + padding-top: 30px; + + .component-actions { + @include box-sizing(border-box); + position: absolute; + width: 100%; + padding: 15px; + top: 0; + left: 0; + border-bottom: 1px solid $lightBluishGrey2; + background: $lightGrey; + } + + &.editing { + padding-top: 0; + } + } +} diff --git a/cms/static/sass/views/_updates.scss b/cms/static/sass/views/_updates.scss new file mode 100644 index 0000000000..1a4a54ca5e --- /dev/null +++ b/cms/static/sass/views/_updates.scss @@ -0,0 +1,221 @@ +// studio - views - course updates +// ==================== + +.course-info { + h2 { + margin-bottom: 24px; + font-size: 22px; + font-weight: 300; + } + + .course-info-wrapper { + display: table; + width: 100%; + clear: both; + } + + .main-column, + .course-handouts { + float: none; + display: table-cell; + } + + .main-column { + border-radius: 3px 0 0 3px; + border-right-color: $mediumGrey; + } + + .CodeMirror { + border: 1px solid #3c3c3c; + background: #fff; + color: #3c3c3c; + } +} + +.course-updates { + padding: 30px 40px; + margin: 0; + + .update-list > li { + padding: 34px 0 42px; + border-top: 1px solid #cbd1db; + + &:first-child { + padding-top: 0; + border: none; + } + + &.editing { + position: relative; + z-index: 1001; + padding: 0; + border-top: none; + border-radius: 3px; + background: #fff; + + .post-preview { + display: none; + } + } + + h1 { + float: none; + font-size: 24px; + font-weight: 300; + } + + h2 { + margin-bottom: 18px; + font-size: 14px; + font-weight: 700; + line-height: 30px; + color: #646464; + letter-spacing: 1px; + text-transform: uppercase; + } + + h3 { + margin: 34px 0 11px; + font-size: 16px; + font-weight: 700; + } + } + + .update-contents { + p { + font-size: 16px; + line-height: 25px; + } + + p + p { + margin-top: 25px; + } + + .primary { + border: 1px solid #ddd; + background: #f6f6f6; + padding: 20px; + } + + ol, ul { + margin: 1em 0; + padding: 0 0 0 1em; + color: $baseFontColor; + + li { + margin-bottom: 0.708em; + } + } + + ol { + list-style: decimal outside none; + } + + ul { + list-style: disc outside none; + } + + pre { + margin: 1em 0; + color: $baseFontColor; + font-family: monospace, serif; + font-size: 1em; + white-space: pre-wrap; + word-wrap: break-word; + } + + code { + color: $baseFontColor; + font-family: monospace, serif; + background: none; + padding: 0; + } + } + + .new-update-button { + @include blue-button; + display: block; + text-align: center; + padding: 18px 0; + margin-bottom: 28px; + } + + .new-update-form { + @include edit-box; + margin-bottom: 24px; + padding: 30px; + border: none; + + textarea { + height: 180px; + } + } + + .post-actions { + float: right; + + .edit-button, + .delete-button{ + float: left; + @include white-button; + padding: 3px 10px 4px; + margin-left: 7px; + font-size: 12px; + font-weight: 400; + + .edit-icon, + .delete-icon { + margin-right: 4px; + } + } + } +} + +.course-handouts { + width: 30%; + padding: 20px 30px; + margin: 0; + border-radius: 0 3px 3px 0; + border-left: none; + background: $lightGrey; + + h2 { + font-size: 18px; + font-weight: 700; + } + + .edit-button { + float: right; + @include white-button; + padding: 3px 10px 4px; + margin-left: 7px; + font-size: 12px; + font-weight: 400; + + .edit-icon, + .delete-icon { + margin-right: 4px; + } + } + + .handouts-content { + font-size: 14px; + } + + .treeview-handoutsnav li { + margin-bottom: 12px; + } +} + +.edit-handouts-form { + @include edit-box; + position: absolute; + right: 0; + z-index: 10001; + width: 800px; + padding: 30px; + + textarea { + height: 300px; + } +} \ No newline at end of file diff --git a/cms/static/sass/views/_users.scss b/cms/static/sass/views/_users.scss new file mode 100644 index 0000000000..6423bddd75 --- /dev/null +++ b/cms/static/sass/views/_users.scss @@ -0,0 +1,81 @@ +// studio - views - course users +// ==================== + +.users { + .new-user-form { + display: none; + padding: 15px 20px; + background-color: $lightBluishGrey2; + + #result { + display: none; + float: left; + margin-bottom: 15px; + padding: 3px 15px; + border-radius: 3px; + background: $error-red; + font-size: 14px; + color: #fff; + } + + .form-elements { + clear: both; + } + + label { + display: inline-block; + margin-right: 10px; + } + + .email-input { + width: 350px; + padding: 8px 8px 10px; + border-color: $darkGrey; + } + + .add-button { + @include blue-button; + padding: 5px 20px 9px; + } + + .cancel-button { + @include white-button; + padding: 5px 20px 9px; + } + } + + .user-list { + border: 1px solid $mediumGrey; + background: #fff; + + li { + position: relative; + padding: 20px; + border-bottom: 1px solid $mediumGrey; + + &:last-child { + border-bottom: none; + } + + span { + display: inline-block; + } + + .user-name { + margin-right: 10px; + font-size: 24px; + font-weight: 300; + } + + .user-email { + font-size: 14px; + font-style: italic; + color: $mediumGrey; + } + + .item-actions { + top: 24px; + } + } + } +} \ No newline at end of file From e9a2413c0483f0b15dff6882f015777ccfd9dbba Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Fri, 8 Mar 2013 12:35:28 -0500 Subject: [PATCH 003/135] studio - further small cleanup on Sass --- cms/static/sass/base-style.scss | 46 +++++++++++------------ cms/static/sass/elements/_header.scss | 4 ++ cms/static/sass/elements/_navigation.scss | 7 ---- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/cms/static/sass/base-style.scss b/cms/static/sass/base-style.scss index 33b312d235..4b9edea40c 100644 --- a/cms/static/sass/base-style.scss +++ b/cms/static/sass/base-style.scss @@ -9,39 +9,39 @@ // utilities @import 'mixins'; -@import "variables"; -@import "cms_mixins"; +@import 'variables'; +@import 'cms_mixins'; // assets -@import "assets/fonts"; -@import "assets/graphics"; +@import 'assets/fonts'; +@import 'assets/graphics'; @import 'assets/keyframes'; // base -@import "base"; +@import 'base'; // elements -@import "elements/header"; -@import "elements/footer"; -@import "elements/navigation"; -@import "elements/modal"; -@import "elements/alerts"; +@import 'elements/header'; +@import 'elements/footer'; +@import 'elements/navigation'; +@import 'elements/modal'; +@import 'elements/alerts'; @import 'elements/jquery-ui-calendar'; // specific views -@import "views/account"; -@import "views/assets"; -@import "views/updates"; -@import "views/dashboard"; -@import "views/export"; -@import "views/index"; -@import "views/import"; -@import "views/outline"; -@import "views/settings"; -@import "views/static-pages"; -@import "views/subsection"; -@import "views/unit"; -@import "views/users"; +@import 'views/account'; +@import 'views/assets'; +@import 'views/updates'; +@import 'views/dashboard'; +@import 'views/export'; +@import 'views/index'; +@import 'views/import'; +@import 'views/outline'; +@import 'views/settings'; +@import 'views/static-pages'; +@import 'views/subsection'; +@import 'views/unit'; +@import 'views/users'; @import 'assets/content-types'; diff --git a/cms/static/sass/elements/_header.scss b/cms/static/sass/elements/_header.scss index 1e09184801..432e78895a 100644 --- a/cms/static/sass/elements/_header.scss +++ b/cms/static/sass/elements/_header.scss @@ -29,6 +29,10 @@ margin: 0 auto; color: $gray-l1; } + + nav .nav-item { + display: inline-block; + } } // ==================== diff --git a/cms/static/sass/elements/_navigation.scss b/cms/static/sass/elements/_navigation.scss index 8c123db64f..066c47298b 100644 --- a/cms/static/sass/elements/_navigation.scss +++ b/cms/static/sass/elements/_navigation.scss @@ -6,13 +6,6 @@ // ==================== // primary -nav.primary { - - .nav-item { - display: inline-block; - } -} - // ==================== From 786ffd10925774f6dbd255338720325b5cc7bf81 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Fri, 8 Mar 2013 12:44:24 -0500 Subject: [PATCH 004/135] studio - further small cleanup on Sass --- cms/static/sass/_base.scss | 8 -------- common/static/sass/_mixins.scss | 22 ++++++++++++++++++++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index 1bf9119654..e5d389e6d2 100644 --- a/cms/static/sass/_base.scss +++ b/cms/static/sass/_base.scss @@ -455,14 +455,6 @@ code { // ==================== // UI - chrome -.window { - @include clearfix(); - @include border-radius(3px); - @include box-shadow(0 1px 1px $shadow-l1); - margin-bottom: $baseline; - border: 1px solid $gray-l2; - background: $white; -} // ==================== diff --git a/common/static/sass/_mixins.scss b/common/static/sass/_mixins.scss index 3145745906..64eaf20591 100644 --- a/common/static/sass/_mixins.scss +++ b/common/static/sass/_mixins.scss @@ -1,4 +1,4 @@ -// all - utilities - mixins and extends +// studio - utilities - mixins and extends // ==================== // font-sizing @@ -151,4 +151,22 @@ border: none; } -// extends - ui \ No newline at end of file +// extends - ui +.window { + @include clearfix(); + @include border-radius(3px); + @include box-shadow(0 1px 1px $shadow-l1); + margin-bottom: $baseline; + border: 1px solid $gray-l2; + background: $white; +} + +.elem-d1 { + @include clearfix(); + @include box-sizing(border-box); +} + +.elem-d2 { + @include clearfix(); + @include box-sizing(border-box); +} \ No newline at end of file From 3bc73de1b494adac6342bc1300b69c415780ba38 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Fri, 8 Mar 2013 12:56:00 -0500 Subject: [PATCH 005/135] studio - changed order of Sass import to account for variables needed in architecture --- cms/static/sass/base-style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/static/sass/base-style.scss b/cms/static/sass/base-style.scss index 4b9edea40c..750fa9071a 100644 --- a/cms/static/sass/base-style.scss +++ b/cms/static/sass/base-style.scss @@ -8,8 +8,8 @@ @import 'reset'; // utilities -@import 'mixins'; @import 'variables'; +@import 'mixins'; @import 'cms_mixins'; // assets From 7a9fdb90696688ae97d75a9bb7bfd9f8ca5b9643 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Mon, 11 Mar 2013 11:04:43 -0400 Subject: [PATCH 006/135] studio - sass clean up: in progress business with git ignore issues --- cms/static/sass/base-style.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cms/static/sass/base-style.scss b/cms/static/sass/base-style.scss index 750fa9071a..46885864ac 100644 --- a/cms/static/sass/base-style.scss +++ b/cms/static/sass/base-style.scss @@ -45,5 +45,5 @@ @import 'assets/content-types'; -@import 'xmodule/module/module-styles.scss'; -@import 'xmodule/descriptor/module-styles.scss'; +@import 'module/module-styles.scss'; +@import 'descriptor/module-styles.scss'; From a496cc0a1e64d1c8c74ccbddba7cd0f3fcf70655 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Mon, 11 Mar 2013 13:28:28 -0400 Subject: [PATCH 007/135] studio - Sass Cleanup: moved all page/view specific css to their own views and created forms specific sheet --- cms/static/sass/_base.scss | 99 +- cms/static/sass/base-style.scss | 1 + cms/static/sass/elements/_footer.scss | 2 +- cms/static/sass/elements/_forms.scss | 76 ++ cms/static/sass/elements/_header.scss | 2 +- cms/static/sass/views/_assets.scss | 3 +- cms/static/sass/views/_dashboard.scss | 203 ++-- cms/static/sass/views/_export.scss | 5 +- cms/static/sass/views/_import.scss | 3 +- cms/static/sass/views/_index.scss | 2 +- cms/static/sass/views/_outline.scss | 1211 +++++++++++----------- cms/static/sass/views/_static-pages.scss | 3 +- cms/static/sass/views/_subsection.scss | 672 +++++++----- cms/static/sass/views/_unit.scss | 1187 ++++++++++----------- cms/static/sass/views/_updates.scss | 3 +- cms/static/sass/views/_users.scss | 3 +- 16 files changed, 1819 insertions(+), 1656 deletions(-) create mode 100644 cms/static/sass/elements/_forms.scss diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index e5d389e6d2..bdafbad09a 100644 --- a/cms/static/sass/_base.scss +++ b/cms/static/sass/_base.scss @@ -350,10 +350,11 @@ h1 { // layout - grandfathered .main-wrapper { position: relative; - margin: 0 40px; + margin: 40px; } .inner-wrapper { + @include clearfix(); position: relative; max-width: 1280px; margin: auto; @@ -363,6 +364,12 @@ h1 { } } +.main-column { + clear: both; + float: left; + width: 70%; +} + .sidebar { float: right; width: 28%; @@ -378,86 +385,6 @@ h1 { // ==================== -// forms -input[type="text"], -input[type="email"], -input[type="password"], -textarea.text { - padding: 6px 8px 8px; - @include box-sizing(border-box); - border: 1px solid $mediumGrey; - border-radius: 2px; - @include linear-gradient($lightGrey, tint($lightGrey, 90%)); - background-color: $lightGrey; - @include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset); - font-family: 'Open Sans', sans-serif; - font-size: 11px; - color: $baseFontColor; - outline: 0; - - &::-webkit-input-placeholder, - &:-moz-placeholder, - &:-ms-input-placeholder { - color: #979faf; - } - - &:focus { - @include linear-gradient($paleYellow, tint($paleYellow, 90%)); - outline: 0; - } -} - -// forms - specific -input.search { - padding: 6px 15px 8px 30px; - @include box-sizing(border-box); - border: 1px solid $darkGrey; - border-radius: 20px; - background: url(../img/search-icon.png) no-repeat 8px 7px #edf1f5; - font-family: 'Open Sans', sans-serif; - color: $baseFontColor; - outline: 0; - - &::-webkit-input-placeholder { - color: #979faf; - } -} - -label { - font-size: 12px; -} - -code { - padding: 0 4px; - border-radius: 3px; - background: #eee; - font-family: Monaco, monospace; -} - -.CodeMirror { - font-size: 13px; - border: 1px solid $darkGrey; - background: #fff; -} - -.text-editor { - width: 100%; - min-height: 80px; - padding: 10px; - @include box-sizing(border-box); - border: 1px solid $mediumGrey; - @include linear-gradient(top, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.3)); - background-color: #edf1f5; - @include box-shadow(0 1px 2px rgba(0, 0, 0, 0.1) inset); - font-family: Monaco, monospace; -} - -// ==================== - -// UI - chrome - -// ==================== - // UI - actions .new-unit-item, .new-subsection-item, @@ -838,14 +765,4 @@ body.hide-wip { .wip-box { display: none; } -} - -// ==================== - -// needed fudges for now -body.dashboard { - - .my-classes { - margin-top: $baseline; - } } \ No newline at end of file diff --git a/cms/static/sass/base-style.scss b/cms/static/sass/base-style.scss index 46885864ac..b092f0054b 100644 --- a/cms/static/sass/base-style.scss +++ b/cms/static/sass/base-style.scss @@ -24,6 +24,7 @@ @import 'elements/header'; @import 'elements/footer'; @import 'elements/navigation'; +@import 'elements/forms'; @import 'elements/modal'; @import 'elements/alerts'; @import 'elements/jquery-ui-calendar'; diff --git a/cms/static/sass/elements/_footer.scss b/cms/static/sass/elements/_footer.scss index 2c32de4bb3..b1c0f57bb2 100644 --- a/cms/static/sass/elements/_footer.scss +++ b/cms/static/sass/elements/_footer.scss @@ -1,4 +1,4 @@ -// studio - global footer +// studio - elements - global footer // ==================== .wrapper-footer { diff --git a/cms/static/sass/elements/_forms.scss b/cms/static/sass/elements/_forms.scss new file mode 100644 index 0000000000..384ffc0509 --- /dev/null +++ b/cms/static/sass/elements/_forms.scss @@ -0,0 +1,76 @@ +// studio - elements - forms +// ==================== + +// forms - general +input[type="text"], +input[type="email"], +input[type="password"], +textarea.text { + padding: 6px 8px 8px; + @include box-sizing(border-box); + border: 1px solid $mediumGrey; + border-radius: 2px; + @include linear-gradient($lightGrey, tint($lightGrey, 90%)); + background-color: $lightGrey; + @include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset); + font-family: 'Open Sans', sans-serif; + font-size: 11px; + color: $baseFontColor; + outline: 0; + + &::-webkit-input-placeholder, + &:-moz-placeholder, + &:-ms-input-placeholder { + color: #979faf; + } + + &:focus { + @include linear-gradient($paleYellow, tint($paleYellow, 90%)); + outline: 0; + } +} + +// forms - specific +input.search { + padding: 6px 15px 8px 30px; + @include box-sizing(border-box); + border: 1px solid $darkGrey; + border-radius: 20px; + background: url(../img/search-icon.png) no-repeat 8px 7px #edf1f5; + font-family: 'Open Sans', sans-serif; + color: $baseFontColor; + outline: 0; + + &::-webkit-input-placeholder { + color: #979faf; + } +} + +label { + font-size: 12px; +} + +code { + padding: 0 4px; + border-radius: 3px; + background: #eee; + font-family: Monaco, monospace; +} + +.CodeMirror { + font-size: 13px; + border: 1px solid $darkGrey; + background: #fff; +} + +.text-editor { + width: 100%; + min-height: 80px; + padding: 10px; + @include box-sizing(border-box); + border: 1px solid $mediumGrey; + @include linear-gradient(top, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.3)); + background-color: #edf1f5; + @include box-shadow(0 1px 2px rgba(0, 0, 0, 0.1) inset); + font-family: Monaco, monospace; +} \ No newline at end of file diff --git a/cms/static/sass/elements/_header.scss b/cms/static/sass/elements/_header.scss index 432e78895a..e8df37f57f 100644 --- a/cms/static/sass/elements/_header.scss +++ b/cms/static/sass/elements/_header.scss @@ -1,4 +1,4 @@ -// studio - global header +// studio - elements - global header // ==================== .wrapper-header { diff --git a/cms/static/sass/views/_assets.scss b/cms/static/sass/views/_assets.scss index 2c1b435c44..779dc56684 100644 --- a/cms/static/sass/views/_assets.scss +++ b/cms/static/sass/views/_assets.scss @@ -1,7 +1,8 @@ // studio - views - assets // ==================== -.uploads { +body.course.uploads { + input.asset-search-input { float: left; width: 260px; diff --git a/cms/static/sass/views/_dashboard.scss b/cms/static/sass/views/_dashboard.scss index 2a995ffdc7..a02c4e0c29 100644 --- a/cms/static/sass/views/_dashboard.scss +++ b/cms/static/sass/views/_dashboard.scss @@ -1,117 +1,124 @@ // studio - views - user dashboard // ==================== -.class-list { - margin-top: 20px; - border-radius: 3px; - border: 1px solid $darkGrey; - background: #fff; - @include box-shadow(0 1px 2px rgba(0, 0, 0, .1)); +body.dashboard { - li { - position: relative; - border-bottom: 1px solid $mediumGrey; + .my-classes { + margin-top: $baseline; + } - &:last-child { - border-bottom: none; + .class-list { + margin-top: 20px; + border-radius: 3px; + border: 1px solid $darkGrey; + background: #fff; + @include box-shadow(0 1px 2px rgba(0, 0, 0, .1)); + + li { + position: relative; + border-bottom: 1px solid $mediumGrey; + + &:last-child { + border-bottom: none; + } + + .class-link { + z-index: 100; + display: block; + padding: 20px 25px; + line-height: 1.3; + + &:hover { + background: $paleYellow; + + + .view-live-button { + opacity: 1.0; + pointer-events: auto; + } + } + } } - .class-link { - z-index: 100; + .class-name { display: block; - padding: 20px 25px; - line-height: 1.3; - - &:hover { - background: $paleYellow; + font-size: 19px; + font-weight: 300; + } - + .view-live-button { - opacity: 1.0; - pointer-events: auto; - } + .detail { + font-size: 14px; + font-weight: 400; + margin-right: 20px; + color: #3c3c3c; + } + + // view live button + .view-live-button { + z-index: 10000; + position: absolute; + top: 15px; + right: $baseline; + padding: ($baseline/4) ($baseline/2); + opacity: 0; + pointer-events: none; + + &:hover { + opacity: 1.0; + pointer-events: auto; } } } - .class-name { - display: block; - font-size: 19px; - font-weight: 300; - } + .new-course { + padding: 15px 25px; + margin-top: 20px; + border-radius: 3px; + border: 1px solid $darkGrey; + background: #fff; + box-shadow: 0 1px 2px rgba(0, 0, 0, .1); + @include clearfix; - .detail { - font-size: 14px; - font-weight: 400; - margin-right: 20px; - color: #3c3c3c; - } + .row { + margin-bottom: 15px; + @include clearfix; + } - // view live button - .view-live-button { - z-index: 10000; - position: absolute; - top: 15px; - right: $baseline; - padding: ($baseline/4) ($baseline/2); - opacity: 0; - pointer-events: none; + .column { + float: left; + width: 48%; + } - &:hover { - opacity: 1.0; - pointer-events: auto; + .column:first-child { + margin-right: 4%; + } + + .course-info { + width: 600px; + } + + label { + display: block; + font-size: 13px; + font-weight: 700; + } + + .new-course-org, + .new-course-number, + .new-course-name { + width: 100%; + } + + .new-course-name { + font-size: 19px; + font-weight: 300; + } + + .new-course-save { + @include blue-button; + } + + .new-course-cancel { + @include white-button; } } -} - -.new-course { - padding: 15px 25px; - margin-top: 20px; - border-radius: 3px; - border: 1px solid $darkGrey; - background: #fff; - box-shadow: 0 1px 2px rgba(0, 0, 0, .1); - @include clearfix; - - .row { - margin-bottom: 15px; - @include clearfix; - } - - .column { - float: left; - width: 48%; - } - - .column:first-child { - margin-right: 4%; - } - - .course-info { - width: 600px; - } - - label { - display: block; - font-size: 13px; - font-weight: 700; - } - - .new-course-org, - .new-course-number, - .new-course-name { - width: 100%; - } - - .new-course-name { - font-size: 19px; - font-weight: 300; - } - - .new-course-save { - @include blue-button; - } - - .new-course-cancel { - @include white-button; - } } \ No newline at end of file diff --git a/cms/static/sass/views/_export.scss b/cms/static/sass/views/_export.scss index 27cb7f923b..933bb50252 100644 --- a/cms/static/sass/views/_export.scss +++ b/cms/static/sass/views/_export.scss @@ -1,7 +1,8 @@ // studio - views - course export // ==================== -.export { +body.course.export { + .export-overview { @extend .window; @include clearfix; @@ -121,6 +122,4 @@ } } } - - } \ No newline at end of file diff --git a/cms/static/sass/views/_import.scss b/cms/static/sass/views/_import.scss index fe7d65626b..e5fb955348 100644 --- a/cms/static/sass/views/_import.scss +++ b/cms/static/sass/views/_import.scss @@ -1,7 +1,8 @@ // studio - views - course import // ==================== -.import { +body.course.import { + .import-overview { @extend .window; @include clearfix; diff --git a/cms/static/sass/views/_index.scss b/cms/static/sass/views/_index.scss index 8d81ea33fd..f4087a8605 100644 --- a/cms/static/sass/views/_index.scss +++ b/cms/static/sass/views/_index.scss @@ -1,7 +1,7 @@ // studio - views - how it works // ==================== -.index { +body.index { &.not-signedin { diff --git a/cms/static/sass/views/_outline.scss b/cms/static/sass/views/_outline.scss index 2957b57849..0d72e2d2bf 100644 --- a/cms/static/sass/views/_outline.scss +++ b/cms/static/sass/views/_outline.scss @@ -1,380 +1,102 @@ // studio - views - course outline // ==================== -input.courseware-unit-search-input { - float: left; - width: 260px; - background-color: #fff; -} +body.course.outline { -.branch { - - .section-item { - @include clearfix(); - - .details { - display: block; - float: left; - margin-bottom: 0; - width: 650px; - } - - .gradable-status { - float: right; - position: relative; - top: -4px; - right: 50px; - width: 145px; - - .status-label { - position: absolute; - top: 2px; - right: -5px; - display: none; - width: 110px; - padding: 5px 40px 5px 10px; - @include border-radius(3px); - color: $lightGrey; - text-align: right; - font-size: 12px; - font-weight: bold; - line-height: 16px; - } - - .menu-toggle { - z-index: 10; - position: absolute; - top: 0; - right: 5px; - padding: 5px; - color: $mediumGrey; - - &:hover, &.is-active { - color: $blue; - } - } - - .menu { - z-index: 1; - display: none; - opacity: 0.0; - position: absolute; - top: -1px; - left: 5px; - margin: 0; - padding: 8px 12px; - background: $white; - border: 1px solid $mediumGrey; - font-size: 12px; - @include border-radius(4px); - @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); - @include transition(opacity .15s); - - - li { - width: 115px; - margin-bottom: 3px; - padding-bottom: 3px; - border-bottom: 1px solid $lightGrey; - - &:last-child { - margin-bottom: 0; - padding-bottom: 0; - border: none; - - a { - color: $darkGrey; - } - } - } - - a { - color: $blue; - - &.is-selected { - font-weight: bold; - } - } - } - - // dropdown state - &.is-active { - - .menu { - z-index: 1000; - display: block; - opacity: 1.0; - } - - .menu-toggle { - z-index: 10000; - } - } - - // set state - &.is-set { - - .menu-toggle { - color: $blue; - } - - .status-label { - display: block; - color: $blue; - } - } - } - } + input.courseware-unit-search-input { + float: left; + width: 260px; + background-color: #fff; } + .branch { -.courseware-section { - position: relative; - background: #fff; - border-radius: 3px; - border: 1px solid $mediumGrey; - margin-top: 15px; - padding-bottom: 12px; - @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.1)); - - &:first-child { - margin-top: 0; - } - - &.collapsed { - padding-bottom: 0; - } - - label { - float: left; - line-height: 29px; - } - - .datepair { - float: left; - margin-left: 10px; - } - - .section-published-date { - position: absolute; - top: 19px; - right: 90px; - padding: 4px 10px; - border-radius: 3px; - background: $lightGrey; - text-align: right; - - .published-status { - font-size: 12px; - margin-right: 15px; - - strong { - font-weight: bold; - } - } - - .schedule-button { - @include blue-button; - } - - .edit-button { - @include blue-button; - } - - .schedule-button, - .edit-button { - font-size: 11px; - padding: 3px 15px 5px; - } - } - - .datepair .date, - .datepair .time { - padding-left: 0; - padding-right: 0; - border: none; - background: none; - @include box-shadow(none); - font-size: 13px; - font-weight: bold; - color: $blue; - cursor: pointer; - } - - .datepair .date { - width: 80px; - } - - .datepair .time { - width: 65px; - } - - &.collapsed .subsection-list, - .collapsed .subsection-list, - .collapsed > ol { - display: none !important; - } - - header { - min-height: 75px; + .section-item { @include clearfix(); - .item-details, .section-published-date { - + .details { + display: block; + float: left; + margin-bottom: 0; + width: 650px; } - .item-details { - display: inline-block; - padding: 20px 0 10px 0; - @include clearfix(); - - .section-name { - float: left; - margin-right: 10px; - width: 350px; - font-size: 19px; - font-weight: bold; - color: $blue; - } - - .section-name-span { - cursor: pointer; - @include transition(color .15s); - - &:hover { - color: $orange; - } - } - - .section-name-edit { - position: relative; - width: 400px; - background: $white; - - input { - font-size: 16px; - } - - .save-button { - @include blue-button; - padding: 7px 20px 7px; - margin-right: 5px; - } - - .cancel-button { - @include white-button; - padding: 7px 20px 7px; - } - } - - .section-published-date { + .gradable-status { float: right; - width: 265px; - margin-right: 220px; - @include border-radius(3px); - background: $lightGrey; + position: relative; + top: -4px; + right: 50px; + width: 145px; - .published-status { - font-size: 12px; - margin-right: 15px; - - strong { - font-weight: bold; - } - } - - .schedule-button { - @include blue-button; - } - - .edit-button { - @include blue-button; - } - - .schedule-button, - .edit-button { - font-size: 11px; - padding: 3px 15px 5px; - - } - } - - .gradable-status { + .status-label { position: absolute; - top: 20px; - right: 70px; - width: 145px; + top: 2px; + right: -5px; + display: none; + width: 110px; + padding: 5px 40px 5px 10px; + @include border-radius(3px); + color: $lightGrey; + text-align: right; + font-size: 12px; + font-weight: bold; + line-height: 16px; + } - .status-label { - position: absolute; - top: 0; - right: 2px; - display: none; - width: 100px; - padding: 10px 35px 10px 10px; - @include border-radius(3px); - background: $lightGrey; - color: $lightGrey; - text-align: right; - font-size: 12px; - font-weight: bold; - line-height: 16px; - } + .menu-toggle { + z-index: 10; + position: absolute; + top: 0; + right: 5px; + padding: 5px; + color: $mediumGrey; - .menu-toggle { - z-index: 10; - position: absolute; - top: 2px; - right: 5px; - padding: 5px; - color: $lightGrey; - - &:hover, &.is-active { - color: $blue; + &:hover, &.is-active { + color: $blue; + } } - } - .menu { - z-index: 1; - display: none; - opacity: 0.0; - position: absolute; - top: -1px; - left: 2px; - margin: 0; - padding: 8px 12px; - background: $white; - border: 1px solid $mediumGrey; - font-size: 12px; - @include border-radius(4px); - @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); - @include transition(opacity .15s); - @include transition(display .15s); + .menu { + z-index: 1; + display: none; + opacity: 0.0; + position: absolute; + top: -1px; + left: 5px; + margin: 0; + padding: 8px 12px; + background: $white; + border: 1px solid $mediumGrey; + font-size: 12px; + @include border-radius(4px); + @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); + @include transition(opacity .15s); - li { - width: 115px; - margin-bottom: 3px; - padding-bottom: 3px; - border-bottom: 1px solid $lightGrey; + li { + width: 115px; + margin-bottom: 3px; + padding-bottom: 3px; + border-bottom: 1px solid $lightGrey; - &:last-child { - margin-bottom: 0; - padding-bottom: 0; - border: none; + &:last-child { + margin-bottom: 0; + padding-bottom: 0; + border: none; - a { - color: $darkGrey; + a { + color: $darkGrey; + } } } - } - a { + a { + color: $blue; - &.is-selected { - font-weight: bold; - } + &.is-selected { + font-weight: bold; } + } } // dropdown state @@ -386,306 +108,573 @@ input.courseware-unit-search-input { opacity: 1.0; } + .menu-toggle { + z-index: 10000; + } + } - .menu-toggle { - z-index: 10000; + // set state + &.is-set { + + .menu-toggle { + color: $blue; + } + + .status-label { + display: block; + color: $blue; + } } } - - // set state - &.is-set { - - .menu-toggle { - color: $blue; - } - - .status-label { - display: block; - color: $blue; - } - } - - float: left; - padding: 21px 0 0; } } - .item-actions { - margin-top: 21px; - margin-right: 12px; - .edit-button, - .delete-button { - margin-top: -3px; - } - } + .courseware-section { + position: relative; + background: #fff; + border-radius: 3px; + border: 1px solid $mediumGrey; + margin-top: 15px; + padding-bottom: 12px; + @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.1)); - .expand-collapse-icon { - float: left; - margin: 29px 6px 16px 16px; - @include transition(none); + &:first-child { + margin-top: 0; + } - &.expand { - background-position: 0 0; - } + &.collapsed { + padding-bottom: 0; + } - &.collapsed { - - } - } + label { + float: left; + line-height: 29px; + } - .drag-handle { - margin-left: 11px; - } - } + .datepair { + float: left; + margin-left: 10px; + } - h3 { - font-size: 19px; - font-weight: 700; - color: $blue; - } + .section-published-date { + position: absolute; + top: 19px; + right: 90px; + padding: 4px 10px; + border-radius: 3px; + background: $lightGrey; + text-align: right; - .section-name-span { - cursor: pointer; - @include transition(color .15s); + .published-status { + font-size: 12px; + margin-right: 15px; - &:hover { - color: $orange; - } - } - - .section-name-form { - margin-bottom: 15px; - } - - .section-name-edit { - input { - font-size: 16px; - } - - .save-button { - @include blue-button; - padding: 7px 20px 7px; - margin-right: 5px; - } - - .cancel-button { - @include white-button; - padding: 7px 20px 7px; - } - } - - h4 { - font-size: 12px; - color: #878e9d; - - strong { - font-weight: bold; - } - } - - .list-header { - @include linear-gradient(top, transparent, rgba(0, 0, 0, .1)); - background-color: #ced2db; - border-radius: 3px 3px 0 0; - } - - .subsection-list { - margin: 0 12px; - - > ol { - @include tree-view; - border-top-width: 0; - } - } - - &.new-section { - - header { - height: auto; - @include clearfix(); - } - - .expand-collapse-icon { - visibility: hidden; - } - - .item-details { - padding: 25px 0 0 0; - - .section-name { - float: none; - width: 100%; + strong { + font-weight: bold; + } } + + .schedule-button { + @include blue-button; + } + + .edit-button { + @include blue-button; + } + + .schedule-button, + .edit-button { + font-size: 11px; + padding: 3px 15px 5px; + } + } + + .datepair .date, + .datepair .time { + padding-left: 0; + padding-right: 0; + border: none; + background: none; + @include box-shadow(none); + font-size: 13px; + font-weight: bold; + color: $blue; + cursor: pointer; + } + + .datepair .date { + width: 80px; + } + + .datepair .time { + width: 65px; + } + + &.collapsed .subsection-list, + .collapsed .subsection-list, + .collapsed > ol { + display: none !important; + } + + header { + min-height: 75px; + @include clearfix(); + + .item-details, .section-published-date { + + } + + .item-details { + display: inline-block; + padding: 20px 0 10px 0; + @include clearfix(); + + .section-name { + float: left; + margin-right: 10px; + width: 350px; + font-size: 19px; + font-weight: bold; + color: $blue; + } + + .section-name-span { + cursor: pointer; + @include transition(color .15s); + + &:hover { + color: $orange; + } + } + + .section-name-edit { + position: relative; + width: 400px; + background: $white; + + input { + font-size: 16px; + } + + .save-button { + @include blue-button; + padding: 7px 20px 7px; + margin-right: 5px; + } + + .cancel-button { + @include white-button; + padding: 7px 20px 7px; + } + } + + .section-published-date { + float: right; + width: 265px; + margin-right: 220px; + @include border-radius(3px); + background: $lightGrey; + + .published-status { + font-size: 12px; + margin-right: 15px; + + strong { + font-weight: bold; + } + } + + .schedule-button { + @include blue-button; + } + + .edit-button { + @include blue-button; + } + + .schedule-button, + .edit-button { + font-size: 11px; + padding: 3px 15px 5px; + + } + } + + .gradable-status { + position: absolute; + top: 20px; + right: 70px; + width: 145px; + + .status-label { + position: absolute; + top: 0; + right: 2px; + display: none; + width: 100px; + padding: 10px 35px 10px 10px; + @include border-radius(3px); + background: $lightGrey; + color: $lightGrey; + text-align: right; + font-size: 12px; + font-weight: bold; + line-height: 16px; + } + + .menu-toggle { + z-index: 10; + position: absolute; + top: 2px; + right: 5px; + padding: 5px; + color: $lightGrey; + + &:hover, &.is-active { + color: $blue; + } + } + + .menu { + z-index: 1; + display: none; + opacity: 0.0; + position: absolute; + top: -1px; + left: 2px; + margin: 0; + padding: 8px 12px; + background: $white; + border: 1px solid $mediumGrey; + font-size: 12px; + @include border-radius(4px); + @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); + @include transition(opacity .15s); + @include transition(display .15s); + + + li { + width: 115px; + margin-bottom: 3px; + padding-bottom: 3px; + border-bottom: 1px solid $lightGrey; + + &:last-child { + margin-bottom: 0; + padding-bottom: 0; + border: none; + + a { + color: $darkGrey; + } + } + } + + a { + + &.is-selected { + font-weight: bold; + } + } + } + + // dropdown state + &.is-active { + + .menu { + z-index: 1000; + display: block; + opacity: 1.0; + } + + + .menu-toggle { + z-index: 10000; + } + } + + // set state + &.is-set { + + .menu-toggle { + color: $blue; + } + + .status-label { + display: block; + color: $blue; + } + } + + float: left; + padding: 21px 0 0; } - } -} + } -.toggle-button-sections { - display: none; - position: relative; - float: right; - margin-top: 10px; + .item-actions { + margin-top: 21px; + margin-right: 12px; - font-size: 13px; - color: $darkGrey; + .edit-button, + .delete-button { + margin-top: -3px; + } + } - &.is-shown { - display: block; - } + .expand-collapse-icon { + float: left; + margin: 29px 6px 16px 16px; + @include transition(none); - .ss-icon { - @include border-radius(20px); - position: relative; - top: -1px; - display: inline-block; - margin-right: 2px; - line-height: 5px; - font-size: 11px; - } + &.expand { + background-position: 0 0; + } - .label { - display: inline-block; - } -} + &.collapsed { + + } + } -.new-section-name, -.new-subsection-name-input { - width: 515px; -} + .drag-handle { + margin-left: 11px; + } + } -.new-section-name-save, -.new-subsection-name-save { - @include blue-button; - padding: 4px 20px 7px; - margin: 0 5px; - color: #fff !important; -} + h3 { + font-size: 19px; + font-weight: 700; + color: $blue; + } -.new-section-name-cancel, -.new-subsection-name-cancel { - @include white-button; - padding: 4px 20px 7px; - color: #8891a1 !important; -} + .section-name-span { + cursor: pointer; + @include transition(color .15s); -.dummy-calendar { - display: none; - position: absolute; - top: 55px; - left: 110px; - z-index: 9999; - border: 1px solid #3C3C3C; - @include box-shadow(0 1px 15px rgba(0, 0, 0, .2)); -} + &:hover { + color: $orange; + } + } -.unit-name-input { - padding: 20px 40px; + .section-name-form { + margin-bottom: 15px; + } - label { - display: block; - } + .section-name-edit { + input { + font-size: 16px; + } + + .save-button { + @include blue-button; + padding: 7px 20px 7px; + margin-right: 5px; + } - input { - width: 100%; - font-size: 20px; - } -} + .cancel-button { + @include white-button; + padding: 7px 20px 7px; + } + } -.preview { - background: url(../img/preview.jpg) center top no-repeat; -} + h4 { + font-size: 12px; + color: #878e9d; -.edit-subsection-publish-settings { - display: none; - position: fixed; - top: 100px; - left: 50%; - z-index: 99999; - width: 600px; - margin-left: -300px; - background: #fff; - text-align: center; + strong { + font-weight: bold; + } + } - .settings { - padding: 40px; - } + .list-header { + @include linear-gradient(top, transparent, rgba(0, 0, 0, .1)); + background-color: #ced2db; + border-radius: 3px 3px 0 0; + } - h3 { - font-size: 34px; - font-weight: 300; - } + .subsection-list { + margin: 0 12px; - .picker { - margin: 30px 0 65px; - } + > ol { + @include tree-view; + border-top-width: 0; + } + } - .description { - margin-top: 30px; - font-size: 14px; - line-height: 20px; - } + &.new-section { - strong { - font-weight: 700; - } + header { + height: auto; + @include clearfix(); + } - .start-date, - .start-time { - font-size: 19px; - } + .expand-collapse-icon { + visibility: hidden; + } - .save-button { - @include blue-button; - margin-right: 10px; - } + .item-details { + padding: 25px 0 0 0; - .cancel-button { - @include white-button; - } + .section-name { + float: none; + width: 100%; + } + } + } + } - .save-button, - .cancel-button { - font-size: 16px; - } -} - -.collapse-all-button { - float: right; - margin-top: 10px; - font-size: 13px; - color: $darkGrey; -} - -// sort/drag and drop -.ui-droppable { - @include transition (padding 0.5s ease-in-out 0s); - min-height: 20px; - padding: 0; - - &.dropover { - padding: 15px 0; - } -} - -.ui-draggable-dragging { - @include box-shadow(0 1px 2px rgba(0, 0, 0, .3)); - border: 1px solid $darkGrey; - opacity : 0.2; - &:hover { - opacity : 1.0; - .section-item { - background: $yellow !important; - } - } - - // hiding unit button - temporary fix until this semantically corrected - .new-unit-item { + .toggle-button-sections { display: none; - } -} + position: relative; + float: right; + margin-top: 10px; -ol.ui-droppable .branch:first-child .section-item { - border-top: none; -} + font-size: 13px; + color: $darkGrey; + &.is-shown { + display: block; + } + + .ss-icon { + @include border-radius(20px); + position: relative; + top: -1px; + display: inline-block; + margin-right: 2px; + line-height: 5px; + font-size: 11px; + } + + .label { + display: inline-block; + } + } + + .new-section-name, + .new-subsection-name-input { + width: 515px; + } + + .new-section-name-save, + .new-subsection-name-save { + @include blue-button; + padding: 4px 20px 7px; + margin: 0 5px; + color: #fff !important; + } + + .new-section-name-cancel, + .new-subsection-name-cancel { + @include white-button; + padding: 4px 20px 7px; + color: #8891a1 !important; + } + + .dummy-calendar { + display: none; + position: absolute; + top: 55px; + left: 110px; + z-index: 9999; + border: 1px solid #3C3C3C; + @include box-shadow(0 1px 15px rgba(0, 0, 0, .2)); + } + + .preview { + background: url(../img/preview.jpg) center top no-repeat; + } + + .edit-subsection-publish-settings { + display: none; + position: fixed; + top: 100px; + left: 50%; + z-index: 99999; + width: 600px; + margin-left: -300px; + background: #fff; + text-align: center; + + .settings { + padding: 40px; + } + + h3 { + font-size: 34px; + font-weight: 300; + } + + .picker { + margin: 30px 0 65px; + } + + .description { + margin-top: 30px; + font-size: 14px; + line-height: 20px; + } + + strong { + font-weight: 700; + } + + .start-date, + .start-time { + font-size: 19px; + } + + .save-button { + @include blue-button; + margin-right: 10px; + } + + .cancel-button { + @include white-button; + } + + .save-button, + .cancel-button { + font-size: 16px; + } + } + + .collapse-all-button { + float: right; + margin-top: 10px; + font-size: 13px; + color: $darkGrey; + } + + // sort/drag and drop + .ui-droppable { + @include transition (padding 0.5s ease-in-out 0s); + min-height: 20px; + padding: 0; + + &.dropover { + padding: 15px 0; + } + } + + .ui-draggable-dragging { + @include box-shadow(0 1px 2px rgba(0, 0, 0, .3)); + border: 1px solid $darkGrey; + opacity : 0.2; + &:hover { + opacity : 1.0; + .section-item { + background: $yellow !important; + } + } + + // hiding unit button - temporary fix until this semantically corrected + .new-unit-item { + display: none; + } + } + + ol.ui-droppable .branch:first-child .section-item { + border-top: none; + } +} \ No newline at end of file diff --git a/cms/static/sass/views/_static-pages.scss b/cms/static/sass/views/_static-pages.scss index 83856c773e..bc9bccf1bb 100644 --- a/cms/static/sass/views/_static-pages.scss +++ b/cms/static/sass/views/_static-pages.scss @@ -1,7 +1,8 @@ // studio - views - course static pages // ==================== -.static-pages { +body.course.static-pages { + .new-static-page-button { @include grey-button; display: block; diff --git a/cms/static/sass/views/_subsection.scss b/cms/static/sass/views/_subsection.scss index 9b0789ba52..3c6bfa9f11 100644 --- a/cms/static/sass/views/_subsection.scss +++ b/cms/static/sass/views/_subsection.scss @@ -1,297 +1,449 @@ // studio - views - course subsection // ==================== -.subsection .main-wrapper { - margin: 40px; -} +body.course.subsection { -.subsection .inner-wrapper { - @include clearfix(); -} + .unit-settings { + .window-contents { + padding: 10px 20px; + } -.subsection-body { - padding: 32px 40px; - @include clearfix; + .unit-actions { + border-bottom: none; + padding-bottom: 0; + } - > div { - margin-bottom: 40px; - } + .published-alert { + display: none; + padding: 10px; + border: 1px solid #edbd3c; + border-radius: 3px; + background: #fbf6e1; + font-size: 14px; + line-height: 1.4; - input { - font-size: 14px; - } + div { + margin-top: 15px; + } + } - .unit-subtitle { - display: block; - width: 100%; - } + input[type="radio"] { + margin-right: 7px; + } - .sortable-unit-list { - ol { - @include tree-view; - } - } + .status { + font-size: 12px; - .policy-list { - input[disabled] { - border: none; - @include box-shadow(none); - } + strong { + font-weight: 700; + } + } - .policy-list-name { - margin-right: 5px; - margin-bottom: 10px; - } + .preview-button, .view-button { + @include white-button; + margin-bottom: 10px; + } - .policy-list-value { - width: 320px; - margin-right: 10px; - } - } + .publish-button { + @include orange-button; + } - .policy-list-element { - .save-button, - .cancel-button { - display: none; - } + .delete-button { + @include blue-button; + } - .edit-icon { - margin-right: 8px; - } + .delete-draft { + display: inline-block; + } - &.editing, - &.new-policy-list-element { - .policy-list-name, - .policy-list-value { - border: 1px solid #b0b6c2; - @include linear-gradient(top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .3)); - background-color: #edf1f5; - @include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset); - } - } - } - - .new-policy-list-element { - padding: 10px 10px 0; - margin: 0 -10px 10px; - border-radius: 3px; - background: $mediumGrey; - - .save-button { - @include blue-button; - margin-bottom: 10px; - } - - .cancel-button { - @include white-button; - } - - .edit-icon { - display: none; - } - - .delete-icon { - display: none; - } - } - - .new-policy-item { - margin: 10px 0; - - .plus-icon-small { - position: relative; - top: -1px; - vertical-align: middle; - } - } -} - -.subsection-name-input { - label { - display: block; - } - - input { - width: 100%; - font-size: 20px; - } -} - -.scheduled-date-input, -.due-date-input { - @include clearfix; - - .date-input, - .time-input { - display: inline-block; - width: 100px; - } - - .inherits-check { - label { - font-size: 13px; - } - } - - .notice { - margin-top: 6px; - font-size: 11px; - color: #999; - } -} - -.due-date-input { - label { - display: inline-block !important; - margin-right: 10px; - } - - a { - font-size: 11px; - font-weight: bold; - text-transform: uppercase; - } - - .date-setter { - @include clearfix; - display: none; - } - - .remove-date { - display: block; - } -} - -.row.visibility { - label { - display: inline-block !important; - margin-right: 10px; - line-height: 21px; - } - - a { - display: inline-block; - height: 31px; - margin-right: 8px; - vertical-align: middle; - font-size: 11px; - font-weight: 700; - line-height: 31px; - text-transform: uppercase; - } - - .large-toggle { - width: 41px; - background: url(../img/large-toggles.png) no-repeat; - background-position: 0 -50px; - - .hidden { - background-position: 0 -5px; - } - } -} - -.gradable { - - label { - display: inline-block; - vertical-align: top; + .delete-button, + .preview-button, + .publish-button, + .view-button { + font-size: 11px; + margin-top: 10px; + padding: 6px 15px 8px; + } } - .gradable-status { - position: relative; - top: -4px; - display: inline-block; - margin-left: 10px; - width: 65%; + .unit-history { + &.collapsed { + h4 { + border-bottom: none; + border-radius: 3px; + } - .status-label { - margin: 0; - padding: 0; - background: transparent; - color: $blue; - border: none; + .window-contents { + display: none; + } + } + + ol { + border: 1px solid #ced2db; + + li { + display: block; + padding: 6px 8px 8px 10px; + background: #edf1f5; + font-size: 12px; + + &:hover { + background: #fffcf1; + + .item-actions { + display: block; + } + } + + &.checked { + background: #d1dae3; + } + + .item-actions { + display: none; + } + + input[type="radio"] { + margin-right: 7px; + } + } + } + } + + .unit-location { + .url { + width: 100%; + margin-bottom: 10px; + @include box-shadow(none); + } + + .draft-tag, + .hidden-tag, + .private-tag, + .has-new-draft-tag { + font-size: 8px; + } + + .window-contents > ol { + @include tree-view; + + .section-item { + display: inline-block; + width: 100%; + font-size: 11px; + padding: 2px 8px 4px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + @include box-sizing(border-box); + } + + ol { + .section-item { + padding-left: 20px; + } + + .new-unit-item { + margin-left: 20px; + } + } + + ol ol { + .section-item { + padding-left: 34px; + } + + .new-unit-item { + margin: 0 0 10px 41px; + } + } + } + } + + .subsection-body { + padding: 32px 40px; + @include clearfix; + + > div { + margin-bottom: 40px; + } + + input { + font-size: 14px; + } + + .unit-subtitle { + display: block; + width: 100%; + } + + .sortable-unit-list { + ol { + @include tree-view; + } + } + + .policy-list { + input[disabled] { + border: none; + @include box-shadow(none); + } + + .policy-list-name { + margin-right: 5px; + margin-bottom: 10px; + } + + .policy-list-value { + width: 320px; + margin-right: 10px; + } + } + + .policy-list-element { + .save-button, + .cancel-button { + display: none; + } + + .edit-icon { + margin-right: 8px; + } + + &.editing, + &.new-policy-list-element { + .policy-list-name, + .policy-list-value { + border: 1px solid #b0b6c2; + @include linear-gradient(top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .3)); + background-color: #edf1f5; + @include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset); + } + } + } + + .new-policy-list-element { + padding: 10px 10px 0; + margin: 0 -10px 10px; + border-radius: 3px; + background: $mediumGrey; + + .save-button { + @include blue-button; + margin-bottom: 10px; + } + + .cancel-button { + @include white-button; + } + + .edit-icon { + display: none; + } + + .delete-icon { + display: none; + } + } + + .new-policy-item { + margin: 10px 0; + + .plus-icon-small { + position: relative; + top: -1px; + vertical-align: middle; + } + } + } + + .subsection-name-input { + label { + display: block; + } + + input { + width: 100%; + font-size: 20px; + } + } + + .scheduled-date-input, + .due-date-input { + @include clearfix; + + .date-input, + .time-input { + display: inline-block; + width: 100px; + } + + .inherits-check { + label { + font-size: 13px; + } + } + + .notice { + margin-top: 6px; + font-size: 11px; + color: #999; + } + } + + .due-date-input { + label { + display: inline-block !important; + margin-right: 10px; + } + + a { font-size: 11px; font-weight: bold; text-transform: uppercase; } - .menu-toggle { - z-index: 100; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 20px; - background: transparent; - - &:hover, &.is-active { - color: $blue; - } - } - - .menu { - z-index: 1; - position: absolute; - top: -12px; - left: -7px; + .date-setter { + @include clearfix; display: none; - width: 100%; - margin: 0; - padding: 8px 12px; - opacity: 0.0; - background: $white; - border: 1px solid $mediumGrey; - font-size: 12px; - @include border-radius(4px); - @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); - @include transition(opacity .15s); - - - li { - margin-bottom: 3px; - padding-bottom: 3px; - border-bottom: 1px solid $lightGrey; - - &:last-child { - margin-bottom: 0; - padding-bottom: 0; - border: none; - } - } - - a { - - &.is-selected { - font-weight: bold; - } - } } - // dropdown state - &.is-active { + .remove-date { + display: block; + } + } - .menu { - z-index: 10000; - display: block; - opacity: 1.0; - } - - .menu-toggle { - z-index: 1000; - } + .row.visibility { + label { + display: inline-block !important; + margin-right: 10px; + line-height: 21px; } - // set state - &.is-set { + a { + display: inline-block; + height: 31px; + margin-right: 8px; + vertical-align: middle; + font-size: 11px; + font-weight: 700; + line-height: 31px; + text-transform: uppercase; + } - .menu-toggle { - color: $blue; + .large-toggle { + width: 41px; + background: url(../img/large-toggles.png) no-repeat; + background-position: 0 -50px; + + .hidden { + background-position: 0 -5px; } + } + } + + .gradable { + + label { + display: inline-block; + vertical-align: top; + } + + .gradable-status { + position: relative; + top: -4px; + display: inline-block; + margin-left: 10px; + width: 65%; .status-label { - display: block; + margin: 0; + padding: 0; + background: transparent; color: $blue; + border: none; + font-size: 11px; + font-weight: bold; + text-transform: uppercase; + } + + .menu-toggle { + z-index: 100; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 20px; + background: transparent; + + &:hover, &.is-active { + color: $blue; + } + } + + .menu { + z-index: 1; + position: absolute; + top: -12px; + left: -7px; + display: none; + width: 100%; + margin: 0; + padding: 8px 12px; + opacity: 0.0; + background: $white; + border: 1px solid $mediumGrey; + font-size: 12px; + @include border-radius(4px); + @include box-shadow(0 1px 2px rgba(0, 0, 0, .2)); + @include transition(opacity .15s); + + + li { + margin-bottom: 3px; + padding-bottom: 3px; + border-bottom: 1px solid $lightGrey; + + &:last-child { + margin-bottom: 0; + padding-bottom: 0; + border: none; + } + } + + a { + + &.is-selected { + font-weight: bold; + } + } + } + + // dropdown state + &.is-active { + + .menu { + z-index: 10000; + display: block; + opacity: 1.0; + } + + .menu-toggle { + z-index: 1000; + } + } + + // set state + &.is-set { + + .menu-toggle { + color: $blue; + } + + .status-label { + display: block; + color: $blue; + } } } } diff --git a/cms/static/sass/views/_unit.scss b/cms/static/sass/views/_unit.scss index bcd3fdb912..a9ce1c8c4d 100644 --- a/cms/static/sass/views/_unit.scss +++ b/cms/static/sass/views/_unit.scss @@ -1,670 +1,687 @@ -// studio - views - course unit +// studio - views - unit // ==================== -.unit .main-wrapper { - @include clearfix(); - margin: 40px; -} +body.course.unit { -//Problem Selector tab menu requirements -.js .tabs .tab { - display: none; -} -//end problem selector reqs - -.main-column { - clear: both; - float: left; - width: 70%; -} - -.unit-body.published { - .components > li { - border: none; - - .rendered-component { - padding: 0 20px; - } + .unit .main-wrapper { + @include clearfix(); + margin: 40px; } -} -.unit-body { - .breadcrumbs { - border-radius: 3px 3px 0 0; - border-bottom: 1px solid #cbd1db; - @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0) 70%); - background-color: #edf1f5; - @include box-shadow(0 1px 0 rgba(255, 255, 255, .7) inset); - @include clearfix; + //Problem Selector tab menu requirements + .js .tabs .tab { + display: none; + } + //end problem selector reqs - li { - float: left; - } + .main-column { + clear: both; + float: left; + width: 70%; + } - a, - .current-page { - display: block; - padding: 15px 35px 15px 30px; - font-size: 14px; - background: url(../img/breadcrumb-arrow.png) no-repeat right center; + .unit-body.published { + .components > li { + border: none; + + .rendered-component { + padding: 0 20px; + } } } - h2 { - margin: 30px 40px 30px 0; - color: #646464; - font-size: 19px; - font-weight: 300; - letter-spacing: 1px; - text-transform: uppercase; - } + .unit-body { + + .unit-name-input { + padding: 20px 40px; - .components { - - > li { - position: relative; - z-index: 10; - margin: 20px 40px; - - - - .title { - margin: 0 0 15px 0; - color: $mediumGrey; - - .value { - } + label { + display: block; } - &.new-component-item { - margin: 20px 0px; - border-top: 1px solid $mediumGrey; - box-shadow: 0 2px 1px rgba(182, 182, 182, 0.75) inset; - background-color: $lightGrey; - margin-bottom: 0px; - padding-bottom: 20px; + input { + width: 100%; + font-size: 20px; + } + } + + .breadcrumbs { + border-radius: 3px 3px 0 0; + border-bottom: 1px solid #cbd1db; + @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0) 70%); + background-color: #edf1f5; + @include box-shadow(0 1px 0 rgba(255, 255, 255, .7) inset); + @include clearfix; - .new-component-button { - display: block; - padding: 20px; - text-align: center; - color: #edf1f5; + li { + float: left; + } + + a, + .current-page { + display: block; + padding: 15px 35px 15px 30px; + font-size: 14px; + background: url(../img/breadcrumb-arrow.png) no-repeat right center; + } + } + + h2 { + margin: 30px 40px 30px 0; + color: #646464; + font-size: 19px; + font-weight: 300; + letter-spacing: 1px; + text-transform: uppercase; + } + + .components { + + > li { + position: relative; + z-index: 10; + margin: 20px 40px; + + + + .title { + margin: 0 0 15px 0; + color: $mediumGrey; + + .value { + } } - h5 { + &.new-component-item { margin: 20px 0px; - color: #fff; - font-weight: 600; - font-size: 18px; - } + border-top: 1px solid $mediumGrey; + box-shadow: 0 2px 1px rgba(182, 182, 182, 0.75) inset; + background-color: $lightGrey; + margin-bottom: 0px; + padding-bottom: 20px; - .rendered-component { - display: none; - background: #fff; - border-radius: 3px 3px 0 0; - } - - .new-component-type { - - a, - li { - display: inline-block; + .new-component-button { + display: block; + padding: 20px; + text-align: center; + color: #edf1f5; } - a { - border: 1px solid $mediumGrey; - width: 100px; - height: 100px; + h5 { + margin: 20px 0px; color: #fff; - margin-right: 15px; - margin-bottom: 20px; - border-radius: 8px; - font-size: 15px; - line-height: 14px; - text-align: center; - @include box-shadow(0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset); + font-weight: 600; + font-size: 18px; + } - .name { - position: absolute; - bottom: 5px; - left: 0; - width: 100%; - padding: 10px; - @include box-sizing(border-box); + .rendered-component { + display: none; + background: #fff; + border-radius: 3px 3px 0 0; + } + + .new-component-type { + + a, + li { + display: inline-block; + } + + a { + border: 1px solid $mediumGrey; + width: 100px; + height: 100px; color: #fff; + margin-right: 15px; + margin-bottom: 20px; + border-radius: 8px; + font-size: 15px; + line-height: 14px; + text-align: center; + @include box-shadow(0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset); + + .name { + position: absolute; + bottom: 5px; + left: 0; + width: 100%; + padding: 10px; + @include box-sizing(border-box); + color: #fff; + } } } - } - .new-component-templates { - display: none; - margin: 20px 40px 20px 40px; - border-radius: 3px; - border: 1px solid $mediumGrey; - background-color: #fff; - @include box-shadow(0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset); - @include clearfix; - - .cancel-button { - margin: 20px 0px 10px 10px; - @include white-button; - } - - .problem-type-tabs { + .new-component-templates { display: none; - } + margin: 20px 40px 20px 40px; + border-radius: 3px; + border: 1px solid $mediumGrey; + background-color: #fff; + @include box-shadow(0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset); + @include clearfix; - // specific menu types - &.new-component-problem { - padding-bottom:10px; - - .ss-icon, .editor-indicator { - display: inline-block; + .cancel-button { + margin: 20px 0px 10px 10px; + @include white-button; } .problem-type-tabs { - display: inline-block; - } - } - } - - .new-component-type, - .new-component-template { - @include clearfix; - - a { - position: relative; - border: 1px solid $darkGreen; - background: tint($green,20%); - color: #fff; - - &:hover { - background: $brightGreen; - } - } - } - - .problem-type-tabs { - list-style-type: none; - border-radius: 0; - width: 100%; - @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); - background-color: $lightBluishGrey; - @include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset); - - li:first-child { - margin-left: 20px; - } - - li { - float:left; - display:inline-block; - text-align:center; - width: auto; - @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); - background-color: tint($lightBluishGrey, 10%); - @include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset); - opacity:.8; - - &:hover { - opacity:1; - background-color: tint($lightBluishGrey, 20%); - } - - &.ui-state-active { - border: 0px; - @include active; - opacity:1; - } - } - - a{ - display: block; - padding: 15px 25px; - font-size: 15px; - line-height: 16px; - text-align: center; - color: #3c3c3c; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3); - } - } - - .new-component-template { - - a { - background: #fff; - border: 0px; - color: #3c3c3c; - @include transition (none); - - &:hover { - background: tint($green,30%); - color: #fff; - @include transition(background-color .15s); - } - } - - li { - border:none; - border-bottom: 1px dashed $lightGrey; - color: #fff; - } - - li:first-child { - a { - border-top: 0px; - } - } - - li:nth-child(2) { - a { - border-radius: 0px; - } - } - - a { - @include clearfix(); - display: block; - padding: 7px 20px; - border-bottom: none; - font-weight: 500; - - .name { - float: left; - - .ss-icon { - @include transition(opacity .15s); - display: inline-block; - top: 1px; - margin-right: 5px; - opacity: 0.5; - width: 17; - height: 21px; - vertical-align: middle; - } - } - - .editor-indicator { - @include transition(opacity .15s); - float: right; - position: relative; - top: 3px; - font-size: 12px; - opacity: 0.3; - } - - .ss-icon, .editor-indicator { display: none; } - &:hover { - color: #fff; + // specific menu types + &.new-component-problem { + padding-bottom:10px; - .ss-icon { - opacity: 1.0; + .ss-icon, .editor-indicator { + display: inline-block; + } + + .problem-type-tabs { + display: inline-block; + } + } + } + + .new-component-type, + .new-component-template { + @include clearfix; + + a { + position: relative; + border: 1px solid $darkGreen; + background: tint($green,20%); + color: #fff; + + &:hover { + background: $brightGreen; + } + } + } + + .problem-type-tabs { + list-style-type: none; + border-radius: 0; + width: 100%; + @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); + background-color: $lightBluishGrey; + @include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset); + + li:first-child { + margin-left: 20px; + } + + li { + float:left; + display:inline-block; + text-align:center; + width: auto; + @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); + background-color: tint($lightBluishGrey, 10%); + @include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset); + opacity:.8; + + &:hover { + opacity:1; + background-color: tint($lightBluishGrey, 20%); + } + + &.ui-state-active { + border: 0px; + @include active; + opacity:1; + } + } + + a{ + display: block; + padding: 15px 25px; + font-size: 15px; + line-height: 16px; + text-align: center; + color: #3c3c3c; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3); + } + } + + .new-component-template { + + a { + background: #fff; + border: 0px; + color: #3c3c3c; + @include transition (none); + + &:hover { + background: tint($green,30%); + color: #fff; + @include transition(background-color .15s); + } + } + + li { + border:none; + border-bottom: 1px dashed $lightGrey; + color: #fff; + } + + li:first-child { + a { + border-top: 0px; + } + } + + li:nth-child(2) { + a { + border-radius: 0px; + } + } + + a { + @include clearfix(); + display: block; + padding: 7px 20px; + border-bottom: none; + font-weight: 500; + + .name { + float: left; + + .ss-icon { + @include transition(opacity .15s); + display: inline-block; + top: 1px; + margin-right: 5px; + opacity: 0.5; + width: 17; + height: 21px; + vertical-align: middle; + } } .editor-indicator { - opacity: 1.0; + @include transition(opacity .15s); + float: right; + position: relative; + top: 3px; + font-size: 12px; + opacity: 0.3; + } + + .ss-icon, .editor-indicator { + display: none; + } + + &:hover { + color: #fff; + + .ss-icon { + opacity: 1.0; + } + + .editor-indicator { + opacity: 1.0; + } + } + } + + // specific editor types + .empty { + + a { + line-height: 1.4; + font-weight: 400; + background: #fff; + color: #3c3c3c; + + + &:hover { + background: tint($green,30%); + color: #fff; + } } } } - // specific editor types - .empty { + .new-component { + text-align: center; - a { - line-height: 1.4; - font-weight: 400; - background: #fff; - color: #3c3c3c; - - - &:hover { - background: tint($green,30%); - color: #fff; - } + h5 { + color: $darkGreen; } + } } - - .new-component { - text-align: center; - - h5 { - color: $darkGreen; - } - - } - } - } - } - - .component { - border: 1px solid $lightBluishGrey2; - border-radius: 3px; - background: #fff; - @include transition(none); - - &:hover { - border-color: #6696d7; - - .drag-handle { - background-color: $blue; - border-color: $blue; } } - &.editing { + .component { border: 1px solid $lightBluishGrey2; - z-index: auto; - - .drag-handle, - .component-actions { - display: none; - } - } - - &.component-placeholder { - border-color: #6696d7; - } - - .component-actions { - position: absolute; - top: 7px; - right: 9px; - } - - .drag-handle { - position: absolute; - display: block; - top: -1px; - right: -16px; - z-index: 10; - width: 15px; - height: 100%; - border-radius: 0 3px 3px 0; - border: 1px solid $lightBluishGrey2; - background: url(../img/white-drag-handles.png) center no-repeat $lightBluishGrey2; - cursor: move; - @include transition(none); - } - } - - .xmodule_display { - padding: 40px 20px 20px; - overflow-x: auto; - - h1 { - float: none; - margin-left: 0; - } - } - - .wrapper-component-editor { - z-index: 9999; - position: relative; - background: $lightBluishGrey2; - } - - .component-editor { - @include edit-box; - @include box-shadow(none); - display: none; - padding: 20px; - border-radius: 2px 2px 0 0; - - .metadata_edit { - margin-bottom: 20px; - font-size: 13px; - - li { - margin-bottom: 10px; - } - - label { - display: inline-block; - margin-right: 10px; - } - } - - h3 { - margin-bottom: 10px; - font-size: 18px; - font-weight: 700; - } - - h5 { - margin-bottom: 8px; - color: #fff; - font-weight: 700; - } - - .save-button { - margin-top: 10px; - margin: 15px 8px 0 0; - } - } -} - -.unit-settings { - .window-contents { - padding: 10px 20px; - } - - .unit-actions { - border-bottom: none; - padding-bottom: 0; - } - - .published-alert { - display: none; - padding: 10px; - border: 1px solid #edbd3c; - border-radius: 3px; - background: #fbf6e1; - font-size: 14px; - line-height: 1.4; - - div { - margin-top: 15px; - } - } - - input[type="radio"] { - margin-right: 7px; - } - - .status { - font-size: 12px; - - strong { - font-weight: 700; - } - } - - .preview-button, .view-button { - @include white-button; - margin-bottom: 10px; - } - - .publish-button { - @include orange-button; - } - - .delete-button { - @include blue-button; - } - - .delete-draft { - display: inline-block; - } - - .delete-button, - .preview-button, - .publish-button, - .view-button { - font-size: 11px; - margin-top: 10px; - padding: 6px 15px 8px; - } -} - -.unit-history { - &.collapsed { - h4 { - border-bottom: none; border-radius: 3px; - } - - .window-contents { - display: none; - } - } - - ol { - border: 1px solid #ced2db; - - li { - display: block; - padding: 6px 8px 8px 10px; - background: #edf1f5; - font-size: 12px; + background: #fff; + @include transition(none); &:hover { - background: #fffcf1; + border-color: #6696d7; - .item-actions { - display: block; + .drag-handle { + background-color: $blue; + border-color: $blue; } } - &.checked { - background: #d1dae3; + &.editing { + border: 1px solid $lightBluishGrey2; + z-index: auto; + + .drag-handle, + .component-actions { + display: none; + } } - .item-actions { - display: none; + &.component-placeholder { + border-color: #6696d7; } - input[type="radio"] { - margin-right: 7px; + .component-actions { + position: absolute; + top: 7px; + right: 9px; + } + + .drag-handle { + position: absolute; + display: block; + top: -1px; + right: -16px; + z-index: 10; + width: 15px; + height: 100%; + border-radius: 0 3px 3px 0; + border: 1px solid $lightBluishGrey2; + background: url(../img/white-drag-handles.png) center no-repeat $lightBluishGrey2; + cursor: move; + @include transition(none); + } + } + + .xmodule_display { + padding: 40px 20px 20px; + overflow-x: auto; + + h1 { + float: none; + margin-left: 0; + } + } + + .wrapper-component-editor { + z-index: 9999; + position: relative; + background: $lightBluishGrey2; + } + + .component-editor { + @include edit-box; + @include box-shadow(none); + display: none; + padding: 20px; + border-radius: 2px 2px 0 0; + + .metadata_edit { + margin-bottom: 20px; + font-size: 13px; + + li { + margin-bottom: 10px; + } + + label { + display: inline-block; + margin-right: 10px; + } + } + + h3 { + margin-bottom: 10px; + font-size: 18px; + font-weight: 700; + } + + h5 { + margin-bottom: 8px; + color: #fff; + font-weight: 700; + } + + .save-button { + margin-top: 10px; + margin: 15px 8px 0 0; } } } -} -.unit-location { - .url { - width: 100%; - margin-bottom: 10px; - @include box-shadow(none); - } + .unit-settings { + .window-contents { + padding: 10px 20px; + } - .draft-tag, - .hidden-tag, - .private-tag, - .has-new-draft-tag { - font-size: 8px; - } + .unit-actions { + border-bottom: none; + padding-bottom: 0; + } - .window-contents > ol { - @include tree-view; + .published-alert { + display: none; + padding: 10px; + border: 1px solid #edbd3c; + border-radius: 3px; + background: #fbf6e1; + font-size: 14px; + line-height: 1.4; - .section-item { + div { + margin-top: 15px; + } + } + + input[type="radio"] { + margin-right: 7px; + } + + .status { + font-size: 12px; + + strong { + font-weight: 700; + } + } + + .preview-button, .view-button { + @include white-button; + margin-bottom: 10px; + } + + .publish-button { + @include orange-button; + } + + .delete-button { + @include blue-button; + } + + .delete-draft { display: inline-block; - width: 100%; + } + + .delete-button, + .preview-button, + .publish-button, + .view-button { font-size: 11px; - padding: 2px 8px 4px; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - @include box-sizing(border-box); + margin-top: 10px; + padding: 6px 15px 8px; + } + } + + .unit-history { + &.collapsed { + h4 { + border-bottom: none; + border-radius: 3px; + } + + .window-contents { + display: none; + } } ol { - .section-item { - padding-left: 20px; - } + border: 1px solid #ced2db; - .new-unit-item { - margin-left: 20px; - } - } + li { + display: block; + padding: 6px 8px 8px 10px; + background: #edf1f5; + font-size: 12px; - ol ol { - .section-item { - padding-left: 34px; - } + &:hover { + background: #fffcf1; - .new-unit-item { - margin: 0 0 10px 41px; + .item-actions { + display: block; + } + } + + &.checked { + background: #d1dae3; + } + + .item-actions { + display: none; + } + + input[type="radio"] { + margin-right: 7px; + } } } } -} -.edit-state-draft { - .visibility, - - .edit-draft-message, - .view-button { - display: none; - } - - .published-alert { - display: block; - } -} - -.edit-state-public { - .delete-draft, - .component-actions, - .new-component-item, - .editing-draft-alert, - .publish-draft-message, - .preview-button { - display: none; - } - - .published-alert { - display: block; - } - - .drag-handle { - display: none !important; - } -} - -.edit-state-private { - .delete-draft, - .publish-draft, - .editing-draft-alert, - .create-draft, - .view-button { - display: none; - } -} - -// editing units from courseware -body.unit { - - .component { - padding-top: 30px; - - .component-actions { - @include box-sizing(border-box); - position: absolute; + .unit-location { + .url { width: 100%; - padding: 15px; - top: 0; - left: 0; - border-bottom: 1px solid $lightBluishGrey2; - background: $lightGrey; + margin-bottom: 10px; + @include box-shadow(none); } - &.editing { - padding-top: 0; + .draft-tag, + .hidden-tag, + .private-tag, + .has-new-draft-tag { + font-size: 8px; + } + + .window-contents > ol { + @include tree-view; + + .section-item { + display: inline-block; + width: 100%; + font-size: 11px; + padding: 2px 8px 4px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + @include box-sizing(border-box); + } + + ol { + .section-item { + padding-left: 20px; + } + + .new-unit-item { + margin-left: 20px; + } + } + + ol ol { + .section-item { + padding-left: 34px; + } + + .new-unit-item { + margin: 0 0 10px 41px; + } + } } } -} + + .edit-state-draft { + .visibility, + + .edit-draft-message, + .view-button { + display: none; + } + + .published-alert { + display: block; + } + } + + .edit-state-public { + .delete-draft, + .component-actions, + .new-component-item, + .editing-draft-alert, + .publish-draft-message, + .preview-button { + display: none; + } + + .published-alert { + display: block; + } + + .drag-handle { + display: none !important; + } + } + + .edit-state-private { + .delete-draft, + .publish-draft, + .editing-draft-alert, + .create-draft, + .view-button { + display: none; + } + } + + // editing units from courseware + body.unit { + + .component { + padding-top: 30px; + + .component-actions { + @include box-sizing(border-box); + position: absolute; + width: 100%; + padding: 15px; + top: 0; + left: 0; + border-bottom: 1px solid $lightBluishGrey2; + background: $lightGrey; + } + + &.editing { + padding-top: 0; + } + } + } +} \ No newline at end of file diff --git a/cms/static/sass/views/_updates.scss b/cms/static/sass/views/_updates.scss index 1a4a54ca5e..8d92c9d860 100644 --- a/cms/static/sass/views/_updates.scss +++ b/cms/static/sass/views/_updates.scss @@ -1,7 +1,8 @@ // studio - views - course updates // ==================== -.course-info { +body.course.updates { + h2 { margin-bottom: 24px; font-size: 22px; diff --git a/cms/static/sass/views/_users.scss b/cms/static/sass/views/_users.scss index 6423bddd75..ecaa319707 100644 --- a/cms/static/sass/views/_users.scss +++ b/cms/static/sass/views/_users.scss @@ -1,7 +1,8 @@ // studio - views - course users // ==================== -.users { +body.course.users { + .new-user-form { display: none; padding: 15px 20px; From 1b5f0400212cbd7f1d975cd4da3c523685395aad Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Tue, 12 Mar 2013 21:57:40 -0400 Subject: [PATCH 008/135] studio - checklists: roughed out initial static design, HTML, and most CSS for checklists v0 UI --- cms/djangoapps/contentstore/views.py | 3 + cms/static/sass/_base.scss | 10 +- cms/static/sass/_variables.scss | 3 + cms/static/sass/base-style.scss | 1 + cms/templates/checklists.html | 43 +++++ cms/templates/ux-checklists.html | 246 +++++++++++++++++++++++++++ cms/templates/widgets/header.html | 1 + cms/urls.py | 1 + 8 files changed, 305 insertions(+), 3 deletions(-) create mode 100644 cms/templates/checklists.html create mode 100644 cms/templates/ux-checklists.html diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index c2c80106fa..7394edb4f7 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -113,6 +113,9 @@ def howitworks(request): else: return render_to_response('howitworks.html', {}) +def ux_checklists(request): + return render_to_response('ux-checklists.html', {}) + # ==== Views for any logged-in user ================================== diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index 5d4bc7c773..23ff2b93e8 100644 --- a/cms/static/sass/_base.scss +++ b/cms/static/sass/_base.scss @@ -214,7 +214,7 @@ h1 { color: $gray-l2; } - .title, .title-1 { + .title-1 { @include font-size(32); margin: 0; padding: 0; @@ -283,8 +283,8 @@ h1 { .title-3 { @include font-size(16); - margin: 0 0 ($baseline/4) 0; - font-weight: 500; + margin: 0 0 ($baseline/2) 0; + font-weight: 600; } .title-4 { @@ -772,6 +772,10 @@ hr.divide { word-wrap: break-word; } +hr.divider { + @extend .sr; +} + // ==================== // js dependant diff --git a/cms/static/sass/_variables.scss b/cms/static/sass/_variables.scss index 4d8e26b2f9..e94ebcb7bc 100644 --- a/cms/static/sass/_variables.scss +++ b/cms/static/sass/_variables.scss @@ -77,6 +77,9 @@ $shadow: rgba(0,0,0,0.2); $shadow-l1: rgba(0,0,0,0.1); $shadow-d1: rgba(0,0,0,0.4); +// misc. +$elem-height-imaginary: 1000000px; + // colors - inherited $baseFontColor: #3c3c3c; $offBlack: #3c3c3c; diff --git a/cms/static/sass/base-style.scss b/cms/static/sass/base-style.scss index dceac4233d..e37ea22aad 100644 --- a/cms/static/sass/base-style.scss +++ b/cms/static/sass/base-style.scss @@ -31,6 +31,7 @@ @import "login"; @import "account"; @import "index"; +@import "checklists"; @import 'jquery-ui-calendar'; @import 'content-types'; diff --git a/cms/templates/checklists.html b/cms/templates/checklists.html new file mode 100644 index 0000000000..21f5c5007d --- /dev/null +++ b/cms/templates/checklists.html @@ -0,0 +1,43 @@ +<%inherit file="base.html" /> +<%block name="title">Course Checklists +<%block name="bodyclass">is-signedin course checklists + +<%block name="jsextra"> + + + +<%block name="content"> +
+
+
+ UX Design +

Alerts & Notifications

+
+
+
+ +
+
+
+
+
+

Alerts

+ persistant, static messages to the user +
+ +

In Studio, alerts are 1) general warnings/notes (e.g. drafts, published content, next steps) about the current view a user is interacting with or 2) notes about the status (e.g. saved confirmations, errors, next system steps) of any previous state that need to communicated to the user when arriving at the current view.

+
+
+
+
+ + +<%block name="view_alerts"> + + \ No newline at end of file diff --git a/cms/templates/ux-checklists.html b/cms/templates/ux-checklists.html new file mode 100644 index 0000000000..ef63561ce2 --- /dev/null +++ b/cms/templates/ux-checklists.html @@ -0,0 +1,246 @@ +<%inherit file="base.html" /> +<%block name="title">Course Checklists +<%block name="bodyclass">is-signedin course uxdesign checklists + +<%block name="content"> +
+
+
+ Tools +

Course Tasks & Checklists

+
+
+
+ +
+
+
+
+

Current Checklists

+ +
+
+

Getting Started with Studio

+ Tasks Completed: 2/5 +
+ +
    +
  • +
    +
    + + +
    +

    Grant your collaborators permission to edit your course so you can work together.

    +
    + + +
  • +
  • +
    +
    + + +
    +

    Establish a course start and end date, course enrollment start and end dates, content release and due dates, and other important dates.

    +
    + + +
  • +
  • +
    +
    + + +
    +

    Grant your collaborators permission to edit your course so you can work together.

    +
    + + +
  • +
  • +
    +
    + + +
    +

    Establish a course start and end date, course enrollment start and end dates, content release and due dates, and other important dates.

    +
    + + +
  • +
  • +
    +
    + + +
    +

    Grant your collaborators permission to edit your course so you can work together.

    +
    + + +
  • +
  • +
    +
    + + +
    +

    Establish a course start and end date, course enrollment start and end dates, content release and due dates, and other important dates.

    +
    + + +
  • +
+ + +
+ +
+ +

Completed Checklists

+ +
+
+

Getting Started with Studio

+ Tasks Completed: 5/5 +
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+ + +
+ +
+
+
+ + +
+
+ + +<%block name="view_alerts"> + +
+
+ + +
+

Your policy changes have been saved.

+

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.

+
+ + + + close alert + +
+
+ + +<%block name="jsextra"> + + \ No newline at end of file diff --git a/cms/templates/widgets/header.html b/cms/templates/widgets/header.html index a063e4b526..7648f8b6f2 100644 --- a/cms/templates/widgets/header.html +++ b/cms/templates/widgets/header.html @@ -58,6 +58,7 @@ diff --git a/cms/urls.py b/cms/urls.py index d43b9bc44c..cba19310fe 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -83,6 +83,7 @@ urlpatterns = ('', # User creation and updating views urlpatterns += ( + url(r'^ux-checklists$', 'contentstore.views.ux_checklists', name='checklists'), url(r'^howitworks$', 'contentstore.views.howitworks', name='howitworks'), url(r'^signup$', 'contentstore.views.signup', name='signup'), From 66a8735a58908b3ce1e72f5fca0e90cdc12327f9 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Wed, 13 Mar 2013 10:50:34 -0400 Subject: [PATCH 009/135] studio - Checklists: initial design and front end proofing/firming up - WIP --- cms/static/sass/_base.scss | 3 +- cms/static/sass/_checklists.scss | 273 ++++++++++++++++++++++++++++ cms/static/sass/_variables.scss | 1 + cms/templates/ux-checklists.html | 303 ++++++++++++++++++++++--------- 4 files changed, 497 insertions(+), 83 deletions(-) create mode 100644 cms/static/sass/_checklists.scss diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index 23ff2b93e8..995a0bbe9f 100644 --- a/cms/static/sass/_base.scss +++ b/cms/static/sass/_base.scss @@ -327,7 +327,8 @@ h1 { } } - .nav-related { + // navigation + .nav-related, .nav-page { .nav-item { margin-bottom: ($baseline/4); diff --git a/cms/static/sass/_checklists.scss b/cms/static/sass/_checklists.scss new file mode 100644 index 0000000000..66601fcf2a --- /dev/null +++ b/cms/static/sass/_checklists.scss @@ -0,0 +1,273 @@ +// Studio - Course Settings +// ==================== +body.course.checklists { + + .content-primary, .content-supplementary { + @include box-sizing(border-box); + float: left; + } + + .content-primary { + width: flex-grid(9, 12); + margin-right: flex-gutter(); + } + + // checklists - general + .course-checklist { + @extend .window; + margin: 0 0 ($baseline*2) 0; + + &:last-child { + margin-bottom: 0; + } + + header { + @include clearfix(); + margin-bottom: 0; + padding: $baseline ($baseline*1.5); + + .checklist-title { + @include transition(color .15s .25s ease-in-out); + width: flex-grid(7, 9); + margin: 0 flex-gutter() 0 0; + float: left; + + &.is-selectable { + cursor: pointer; + + &:hover { + color: $blue; + } + } + } + + .checklist-status { + @include font-size(13); + width: flex-grid(2, 9); + float: right; + margin-top: ($baseline/2); + text-align: right; + color: $gray-l2; + + .status-count { + @include font-size(16); + margin-left: ($baseline/4); + margin-right: ($baseline/4); + color: $gray-d3; + font-weight: 600; + } + + .status-amount { + @include font-size(16); + margin-left: ($baseline/4); + color: $gray-d3; + font-weight: 600; + } + } + } + + // checklist actions + .course-checklist-actions { + @include clearfix(); + @include box-shadow(inset 0 1px 1px $shadow-l1); + @include transition(border .15s ease-in-out .25s); + border-top: 1px solid $gray-l2; + padding: $baseline ($baseline*1.5); + background: $gray-l4; + + .action-primary { + @include green-button(); + float: left; + + .icon-add { + @include font-size(12); + display: inline-block; + vertical-align: middle; + margin-right: ($baseline/4); + } + } + + .action-secondary { + @include font-size(14); + @include grey-button(); + font-weight: 400; + float: right; + + .icon-delete { + @include font-size(12); + display: inline-block; + vertical-align: middle; + margin-right: ($baseline/4); + } + } + } + + // state - collapsed + &.is-collapsed { + + .list-tasks { + height: 0; + } + } + + // state - completed + &.is-completed { + + header { + + .checklist-title { + color: $gray-l1; + } + + .checklist-status { + + .status-count, .status-amount, .icon-confirm { + color: $green; + } + } + + .checklist-status .icon-confirm { + @include font-size(12); + display: inline-block; + vertical-align: middle; + margin-right: ($baseline/4); + } + } + } + + // state - not available + .is-not-available { + + } + } + + // list of tasks + .list-tasks { + height: auto; + overflow: hidden; + + .task { + @include transition(background .15s ease-in-out .25s, border .15s ease-in-out .25s); + @include clearfix(); + position: relative; + border-top: 1px solid $white; + border-bottom: 1px solid $gray-l5; + padding: $baseline ($baseline*1.5); + background: $white; + opacity: 1.0; + + + &:last-child { + margin-bottom: 0; + border-bottom: none; + } + + label { + float: left; + width: flex-grid(7,9); + font-weight: 500; + + .task-input { + display: inline-block; + vertical-align: middle; + margin-right: flex-gutter(); + } + + .task-details { + display: inline-block; + vertical-align: middle; + width: flex-grid(6,7); + + .task-name { + @include transition(color .15s .25s ease-in-out); + vertical-align: baseline; + cursor: pointer; + margin-bottom: 0; + } + + .task-description { + @include transition(color .15s .25s ease-in-out); + @include font-size(14); + color: $gray-l2; + } + + .task-support { + @include transition(opacity .15s .25s ease-in-out); + @include font-size(12); + opacity: 0; + pointer-events: none; + } + } + } + + .task-actions { + @include transition(opacity .15s .25s ease-in-out); + @include clearfix(); + display: inline-block; + vertical-align: middle; + float: left; + width: flex-grid(2,9); + margin: ($baseline/2) 0 0 flex-gutter(); + opacity: 0; + pointer-events: none; + text-align: right; + + .action-primary { + @include blue-button; + @include transition(all .15s); + @include font-size(12); + font-weight: 600; + text-align: center; + } + + .action-secondary { + @include font-size(13); + margin-top: ($baseline/2); + } + } + + // state - hover + &:hover { + background: $blue-l5; + border-bottom-color: $blue-l4; + border-top-color: $blue-l4; + opacity: 1.0; + + .task-details { + .task-support { + opacity: 1.0; + pointer-events: auto; + } + } + + .task-actions { + opacity: 1.0; + pointer-events: auto; + } + } + + + // state - completed + &.is-completed { + background: $gray-l6; + border-top-color: $gray-l5; + border-bottom-color: $gray-l5; + + .task-details { + opacity: 0.50; + } + + &:hover { + + .task-details { + opacity: 1.0; + } + } + } + } + } + + .content-supplementary { + width: flex-grid(3, 12); + } +} \ No newline at end of file diff --git a/cms/static/sass/_variables.scss b/cms/static/sass/_variables.scss index e94ebcb7bc..9e383af99d 100644 --- a/cms/static/sass/_variables.scss +++ b/cms/static/sass/_variables.scss @@ -24,6 +24,7 @@ $gray-l2: tint($gray,40%); $gray-l3: tint($gray,60%); $gray-l4: tint($gray,80%); $gray-l5: tint($gray,90%); +$gray-l6: tint($gray,95%); $gray-d1: shade($gray,20%); $gray-d2: shade($gray,40%); $gray-d3: shade($gray,60%); diff --git a/cms/templates/ux-checklists.html b/cms/templates/ux-checklists.html index ef63561ce2..d0a5ab8fa4 100644 --- a/cms/templates/ux-checklists.html +++ b/cms/templates/ux-checklists.html @@ -26,13 +26,14 @@
  • -
    -
    - - +
    +
    • @@ -40,29 +41,16 @@
  • +
  • -
    -
    - - -
    -

    Establish a course start and end date, course enrollment start and end dates, content release and due dates, and other important dates.

    -
    +
  • -
  • -
    -
    - - +
    +

    Add Course Team Members

    +

    Grant your collaborators permission to edit your course so you can work together.

    -

    Grant your collaborators permission to edit your course so you can work together.

    -
    +
    • @@ -70,48 +58,128 @@
  • -
  • -
    -
    - - -
    -

    Establish a course start and end date, course enrollment start and end dates, content release and due dates, and other important dates.

    -
    - -
  • -
  • -
    -
    - - -
    -

    Grant your collaborators permission to edit your course so you can work together.

    -
    - - -
  • -
    -
    - - +
    + +
  • + +
  • + + + +
  • +
+ + + + +
+
+

Getting Started with Studio

+ Tasks Completed: 2/5 +
+ +
    +
  • + + + +
  • + +
  • + + + +
  • + +
  • + + + +
  • + +
  • + + +
  • @@ -138,29 +206,68 @@ Tasks Completed: 5/5 -
      -
    • - -
    • +
      • + + +
      • -
      • +
      • + + + +
      • + +
      • + + +
      - +
    • + + Add a Task to This Checklist +
    • +
    • + Delete This Checklist +
    • +

@@ -233,12 +340,44 @@ From 16f6744aefcb53f8b1b59cdc82251288020540c6 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Wed, 13 Mar 2013 19:13:26 -0400 Subject: [PATCH 011/135] studio - checklists: revised task completion styles, added in checklist visual progress UI and demo/PoC JS and cleaned up some content/status states --- cms/static/sass/_checklists.scss | 49 ++++++++++++++++++++++++++++++-- cms/templates/ux-checklists.html | 25 ++++++++++++++-- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/cms/static/sass/_checklists.scss b/cms/static/sass/_checklists.scss index ca5424967e..7a55e07101 100644 --- a/cms/static/sass/_checklists.scss +++ b/cms/static/sass/_checklists.scss @@ -21,6 +21,32 @@ body.course.checklists { margin-bottom: 0; } + // visual status + .viz-checklist-status { + @include text-hide(); + @include size(100%,($baseline/4)); + position: relative; + display: block; + margin: 0; + background: $gray-l4; + + .viz-checklist-status-value { + @include transition(width 2s ease-in-out .25s); + position: absolute; + top: 0; + left: 0; + width: 50%; + height: ($baseline/4); + background: $green; + + .int { + @include text-sr(); + } + } + } + // 0% of checklist completed + + // header/title header { @include clearfix(); @include box-shadow(inset 0 -1px 1px $shadow-l1); @@ -127,6 +153,13 @@ body.course.checklists { // state - completed &.is-completed { + .viz-checklist-status { + + .viz-checklist-status-value { + width: 100%; + } + } + header { .checklist-title, .icon-confirm { @@ -273,10 +306,20 @@ body.course.checklists { color: $gray-l2; } + .task-actions { + + .action-primary { + @include grey-button; + @include font-size(12); + font-weight: 600; + text-align: center; + } + } + &:hover { - background: $blue-l5; - border-bottom-color: $blue-l4; - border-top-color: $blue-l4; + background: $gray-l5; + border-bottom-color: $gray-l4; + border-top-color: $gray-l4; .task-details { opacity:1.0; diff --git a/cms/templates/ux-checklists.html b/cms/templates/ux-checklists.html index 3f7a947111..833bbe000f 100644 --- a/cms/templates/ux-checklists.html +++ b/cms/templates/ux-checklists.html @@ -14,11 +14,17 @@
+
+

Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.Test Checklist Progress Visualization

+
+

Current Checklists

+ 0% of checklist completed +

Getting Started with Studio

Tasks Completed: 0/4 @@ -100,6 +106,8 @@
+ 0% of checklist completed +

Draft a Rough Course Outline

Tasks Completed: 0/7 @@ -238,6 +246,8 @@
+ 0% of checklist completed +

Explore edX's Support Tools

Tasks Completed: 0/4 @@ -319,9 +329,10 @@
+ 0% of checklist completed +

Draft your Course Introduction

- Tasks Completed: 0/4
    @@ -330,7 +341,7 @@
    -

    Drafting a Course Description/h4> +

    Drafting a Course Description

    Courses on edX each have their own introduction page, including a course video, description, and more. Draft the introduction students will read before deciding to enroll in your course.

    @@ -342,7 +353,7 @@
-
  • +
  • + + +
    <%block name="view_alerts"> - + +
    +
    + + +
    +

    Your policy changes have been saved.

    +

    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.

    +
    + + + + close alert + +
    +
    + + +<%block name="jsextra"> + \ No newline at end of file diff --git a/cms/templates/widgets/header.html b/cms/templates/widgets/header.html index 7648f8b6f2..45ff66df85 100644 --- a/cms/templates/widgets/header.html +++ b/cms/templates/widgets/header.html @@ -58,7 +58,7 @@ diff --git a/cms/urls.py b/cms/urls.py index cba19310fe..18da7c7b71 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -83,7 +83,8 @@ urlpatterns = ('', # User creation and updating views urlpatterns += ( - url(r'^ux-checklists$', 'contentstore.views.ux_checklists', name='checklists'), + url(r'^(?P[^/]+)/(?P[^/]+)/checklists/(?P[^/]+)$', 'contentstore.views.get_checklists', name='checklists'), +# url(r'^ux-checklists$', 'contentstore.views.ux_checklists', name='checklists'), url(r'^howitworks$', 'contentstore.views.howitworks', name='howitworks'), url(r'^signup$', 'contentstore.views.signup', name='signup'), diff --git a/common/lib/xmodule/xmodule/templates/course/empty.yaml b/common/lib/xmodule/xmodule/templates/course/empty.yaml index cb2f3bcec6..61cc204254 100644 --- a/common/lib/xmodule/xmodule/templates/course/empty.yaml +++ b/common/lib/xmodule/xmodule/templates/course/empty.yaml @@ -2,5 +2,31 @@ metadata: display_name: Empty start: 2020-10-10T10:00 + checklists: [ + {"short_description" : "Getting Started With Studio", + "items" : [{"short_description": "Add Course Team Members", + "long_description": "Grant your collaborators permission to edit your course so you can work together.", + "is_checked": false, + "action_url": "/manage_users/", + "action_text": "Edit Course Team"}, + {"short_description": "Drink Whiskey", + "long_description": "Team-building activity.", + "is_checked": false, + "action_url": "/drink_alcohol", + "action_text": "Drink"}], + "completed" : false}, + {"short_description" : "Launching Your Course", + "items" : [{"short_description": "Add Content", + "long_description": "Create videos and problems.", + "is_checked": false, + "action_url": "", + "action_text": ""}, + {"short_description": "View Course as a Student", + "long_description": "Create a student account and view your course.", + "is_checked": false, + "action_url": "", + "action_text": ""}], + "completed" : false} + ] data: { 'textbooks' : [ ], 'wiki_slug' : null } children: [] From 065e85044923c2b14c46246aa92d83e4696396cc Mon Sep 17 00:00:00 2001 From: cahrens Date: Thu, 14 Mar 2013 09:32:54 -0400 Subject: [PATCH 014/135] Merge with Brian's changes. --- cms/templates/checklists.html | 28 +- cms/templates/ux-checklists.html | 618 ------------------------------- 2 files changed, 24 insertions(+), 622 deletions(-) delete mode 100644 cms/templates/ux-checklists.html diff --git a/cms/templates/checklists.html b/cms/templates/checklists.html index 109ac36df5..455dbe6b04 100644 --- a/cms/templates/checklists.html +++ b/cms/templates/checklists.html @@ -14,6 +14,10 @@
    +
    +

    Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.Test Checklist Progress Visualization

    +
    +

    Current Checklists

    @@ -21,6 +25,7 @@ % for checklist in checklists:
    + 0% of checklist completed

    ${checklist['short_description']}

    Tasks Completed: 0/4 @@ -53,6 +58,8 @@ % endfor
    + 0% of checklist completed +

    Getting Started with Studio

    Tasks Completed: 0/4 @@ -134,6 +141,8 @@
    + 0% of checklist completed +

    Draft a Rough Course Outline

    Tasks Completed: 0/7 @@ -272,6 +281,8 @@
    + 0% of checklist completed +

    Explore edX's Support Tools

    Tasks Completed: 0/4 @@ -353,9 +364,10 @@
    + 0% of checklist completed +

    Draft your Course Introduction

    - Tasks Completed: 0/4
      @@ -364,8 +376,8 @@
      -

      Drafting a Course Description/h4> -

      Courses on edX each have their own introduction page, including a course video, description, and more. Draft the introduction students will read before deciding to enroll in your course.

      +

      Drafting a Course Description

      +

      Courses on edX each have their own introduction page, including a course video, description, and more. Draft the introduction students will read before deciding to enroll in your course.

      @@ -376,7 +388,7 @@
    -
  • +
  • <%block name="view_alerts">
    -
    - +
    + -
    -

    Your policy changes have been saved.

    -

    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.

    -
    - - - - close alert - +
    +

    Your policy changes have been saved.

    +

    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.

    + + + + close alert + +
    @@ -598,56 +113,55 @@ // checklists - prototype/basic js $(document).ready(function() { - $('.course-checklist .checklist-title').each(function(e){ - $(this).addClass('is-selectable').attr('title','Collapse/Expand this Checklist').bind('click', toggleChecklist); - }); + $('.course-checklist .checklist-title').each(function(e){ + $(this).addClass('is-selectable').bind('click', toggleChecklist); + }); - $('.course-checklist .task label').each(function(e){ - $(this).bind('click', toggleTask); - }); + $('.course-checklist .task label').each(function(e){ + $(this).bind('click', toggleTask); + }); - // demo/proof of concept for visual progress - $('.demo-checklistviz').click(function(e){ - (e).preventDefault(); - $('#course-checklist1 .viz-checklist-status .viz-checklist-status-value').css('width','25%'); - }); + // demo/proof of concept for visual progress + $('.demo-checklistviz').click(function(e){ + (e).preventDefault(); + $('#course-checklist1 .viz-checklist-status .viz-checklist-status-value').css('width','25%'); + }); - function toggleChecklist(e) { - (e).preventDefault(); - $(this).closest('.course-checklist').toggleClass('is-collapsed'); - } + function toggleChecklist(e) { + (e).preventDefault(); + $(this).closest('.course-checklist').toggleClass('is-collapsed'); + } - function toggleTask(e) { - (e).preventDefault(); + function toggleTask(e) { + (e).preventDefault(); - var $taskInput = $(this).find('.task-input'); + var $taskInput = $(this).find('.task-input'); - if ($taskInput.attr('checked')) { - $taskInput.removeAttr('checked'); - console.log('removing check'); - } - else { - $taskInput.attr('checked', 'checked'); - console.log('adding check'); - } + if ($taskInput.attr('checked')) { + $taskInput.removeAttr('checked'); + console.log('removing check'); + } + else { + $taskInput.attr('checked', 'checked'); + console.log('adding check'); + } - $(this).closest('.task').toggleClass('is-completed'); - } + $(this).closest('.task').toggleClass('is-completed'); + } - // in-progress update checklist progress (based on checkbox check/uncheck events) - function updateChecklistProgress() { - var $statusCount = $(this).closest('.course-checklist').find('.status-count'); - var $statusAmount = $(this).closest('.course-checklist').find('.status-amount'); + // in-progress update checklist progress (based on checkbox check/uncheck events) + function updateChecklistProgress() { + var $statusCount = $(this).closest('.course-checklist').find('.status-count'); + var $statusAmount = $(this).closest('.course-checklist').find('.status-amount'); - if ($(this).attr('checked')) { - console.log('adding'); - } - - else { - console.log('subtracting'); - } - } + if ($(this).attr('checked')) { + console.log('adding'); + } + else { + console.log('subtracting'); + } + } }); \ No newline at end of file diff --git a/common/lib/xmodule/xmodule/templates/course/empty.yaml b/common/lib/xmodule/xmodule/templates/course/empty.yaml index 61cc204254..153d7859ee 100644 --- a/common/lib/xmodule/xmodule/templates/course/empty.yaml +++ b/common/lib/xmodule/xmodule/templates/course/empty.yaml @@ -3,30 +3,104 @@ metadata: display_name: Empty start: 2020-10-10T10:00 checklists: [ - {"short_description" : "Getting Started With Studio", - "items" : [{"short_description": "Add Course Team Members", - "long_description": "Grant your collaborators permission to edit your course so you can work together.", - "is_checked": false, - "action_url": "/manage_users/", - "action_text": "Edit Course Team"}, - {"short_description": "Drink Whiskey", - "long_description": "Team-building activity.", - "is_checked": false, - "action_url": "/drink_alcohol", - "action_text": "Drink"}], - "completed" : false}, - {"short_description" : "Launching Your Course", - "items" : [{"short_description": "Add Content", - "long_description": "Create videos and problems.", - "is_checked": false, - "action_url": "", - "action_text": ""}, - {"short_description": "View Course as a Student", - "long_description": "Create a student account and view your course.", - "is_checked": false, - "action_url": "", - "action_text": ""}], - "completed" : false} + {"short_description" : "Getting Started With Studio", + "items" : [{"short_description": "Add Course Team Members", + "long_description": "Grant your collaborators permission to edit your course so you can work together.", + "is_checked": false, + "action_url": "/manage_users/", + "action_text": "Edit Course Team"}, + {"short_description": "Set Important Dates for Your Course", + "long_description": "Establish your course's student enrollment and launch dates on the Schedule and Details Settings page.", + "is_checked": false, + "action_url": "/settings-details/", + "action_text": "Edit Course Details & Schedule"}, + {"short_description": "Draft Your Course's Grading Policy", + "long_description": "Regardless of whether you have all your course assignments written, you can immediately get started setting up assignment types and a grade computation scheme.", + "is_checked": false, + "action_url": "/settings-grading/", + "action_text": "Edit Grading Settings"}, + {"short_description": "Explore the other Studio Checklists", + "long_description": "They'll help you learn the other course authoring tools available to you, and will also help you find help when you need it.", + "is_checked": false, + "action_url": "", + "action_text": ""}], + "completed" : false}, + {"short_description" : "Draft a Rough Course Outline", + "items" : [{"short_description": "Create your first Section and Subsection", + "long_description": "Walk through the mechanics of building your course's first section and subsection through your course outline to start.", + "is_checked": false, + "action_url": "/course/", + "action_text": "Edit in Course Outline"}, + {"short_description": "Set your first Section's Release Date", + "long_description": "Sections are released sequentially to students, and you have complete control over they are released to students.", + "is_checked": false, + "action_url": "/course/", + "action_text": "Edit in Course Outline"}, + {"short_description": "Designate a Subsection as Graded", + "long_description": "Assignment types are defined in your grading settings but can be quickly associated with sections using your course outline.", + "is_checked": false, + "action_url": "/course/", + "action_text": "Edit in Course Outline"}, + {"short_description": "Reordering Course Content", + "long_description": "From the Course Outline, you can easily reorder your course content based on the progression you'd like students to walk through.", + "is_checked": false, + "action_url": "/course/", + "action_text": "Edit in Course Outline"}, + {"short_description": "Renaming Course Sections", + "long_description": "Learn how to rename Sections by clicking on its name from the Course Outline; this should open the editing mode.", + "is_checked": false, + "action_url": "/course/", + "action_text": "Edit in Course Outline"}, + {"short_description": "Deleting Course Content", + "long_description": "Try out deleting on a section, subsection, or unit you don't need anymore. Be careful though, anything inside the course content you delete is also removed.", + "is_checked": false, + "action_url": "/course/", + "action_text": "Edit in Course Outline"}, + {"short_description": "Add an Instructor-Only Section to Your Outline", + "long_description": "Some course authors find creating a section for unsorted, in-progress work useful. To do this, create a section and set the release date to the distant future.", + "is_checked": false, + "action_url": "/course/", + "action_text": "Edit in Course Outline"}], + "completed" : false}, + {"short_description" : "Explore edX's Support Tools", + "items" : [{"short_description": "Explore the Studio Help Forum", + "long_description": "Access the Studio Help forum from the menu that appears when you click your user name in the top right corner of Studio.", + "is_checked": false, + "action_url": "http://help.edge.edx.org/", + "action_text": "Visit Studio Help"}, + {"short_description": "Enroll in edX101", + "long_description": "Register for edX101, edX's primer for course creation.", + "is_checked": false, + "action_url": "/", + "action_text": "Register for edX 101"}, + {"short_description": "Download the Studio Documentation", + "long_description": "View the searchable Studio documentation to find answers to your questions or information about how to do specific tasks. Once you download the PDF, you can view it offline.", + "is_checked": false, + "action_url": "/", + "action_text": "Download Documentation"}], + "completed" : false}, + {"short_description" : "Draft your Course Introduction", + "items" : [{"short_description": "Drafting a Course Description", + "long_description": "Courses on edX each have their own introduction page, including a course video, description, and more. Draft the introduction students will read before deciding to enroll in your course.", + "is_checked": false, + "action_url": "/settings-details/", + "action_text": "Edit Course Details & Schedule"}, + {"short_description": "Adding Staff Bios", + "long_description": "Showing prospective students who will be their instructor is helpful, so we recommend including staff bios in the Course introduction page.", + "is_checked": false, + "action_url": "/settings-details/", + "action_text": "Edit Course Details & Schedule"}, + {"short_description": "Add Course FAQs", + "long_description": "Students often have questions about courses, and including a short list of frequently asked questions up front often reduces the number of students who are confused.", + "is_checked": false, + "action_url": "/settings-details/", + "action_text": "Edit Course Details & Schedule"}, + {"short_description": "Add Course Prerequisites", + "long_description": "Before a student jumps into a course without the necessary preparation, we'd like them to understand the prerequisites that will make them more likely to succeed.", + "is_checked": false, + "action_url": "/settings-details/", + "action_text": "Edit Course Details & Schedule"}], + "completed" : false} ] data: { 'textbooks' : [ ], 'wiki_slug' : null } children: [] From 017fd06bfc1ff28635bb54f427c5256ac64d89ba Mon Sep 17 00:00:00 2001 From: cahrens Date: Thu, 14 Mar 2013 13:37:22 -0400 Subject: [PATCH 017/135] Pre-merge. --- cms/static/js/models/checklists.js | 1 + cms/static/js/views/checklists_view.js | 57 ++++++++++--- cms/templates/checklists.html | 113 ++++++------------------- 3 files changed, 70 insertions(+), 101 deletions(-) create mode 100644 cms/static/js/models/checklists.js diff --git a/cms/static/js/models/checklists.js b/cms/static/js/models/checklists.js new file mode 100644 index 0000000000..368db944ed --- /dev/null +++ b/cms/static/js/models/checklists.js @@ -0,0 +1 @@ +if (!CMS.Models['Checklists']) CMS.Models.Checklists = new Object(); \ No newline at end of file diff --git a/cms/static/js/views/checklists_view.js b/cms/static/js/views/checklists_view.js index b33a5d66f4..0905b392e4 100644 --- a/cms/static/js/views/checklists_view.js +++ b/cms/static/js/views/checklists_view.js @@ -1,18 +1,49 @@ +if (!CMS.Views['Checklists']) CMS.Views.Checklists = {}; + CMS.Views.Checklists = Backbone.View.extend({ - // takes CMS.Models.CourseInfo as model - tagName: 'div', + // takes CMS.Models.Checklists as model - render: function() { - // instantiate the ClassInfoUpdateView and delegate the proper dom to it - new CMS.Views.ClassInfoUpdateView({ - el: $('body.updates'), - collection: this.model.get('updates') - }); + events : { + 'click .course-checklist .checklist-title' : "toggleChecklist", + 'click .course-checklist .task label' : "toggleTask", + 'click .demo-checklistviz' : "demoUpdateProgress" + }, - new CMS.Views.ClassInfoHandoutsView({ - el: this.$('#course-handouts-view'), - model: this.model.get('handouts') - }); - return this; + initialize : function() { + // adding class and title needs to happen in HTML +// $('.course-checklist .checklist-title').each(function(e){ +// $(this).addClass('is-selectable').attr('title','Collapse/Expand this Checklist').bind('click', this.toggleChecklist); +// }); + }, + + toggleChecklist : function(e) { + (e).preventDefault(); + $(e.target).closest('.course-checklist').toggleClass('is-collapsed'); + }, + + toggleTask : function (e) { + (e).preventDefault(); + $(e.target).closest('.task').toggleClass('is-completed'); + }, + + // TODO: remove + demoUpdateProgress : function(e) { + (e).preventDefault(); + $('#course-checklist0 .viz-checklist-status .viz-checklist-status-value').css('width','25%'); + }, + + // TODO: not used. In-progress update checklist progress (based on checkbox check/uncheck events) + updateChecklistProgress : function(e) { + var $statusCount = this.$el.closest('.course-checklist').find('.status-count'); + var $statusAmount = this.$el.closest('.course-checklist').find('.status-amount'); + + if (this.$el.attr('checked')) { + console.log('adding'); + } + + else { + console.log('subtracting'); + } } + }); \ No newline at end of file diff --git a/cms/templates/checklists.html b/cms/templates/checklists.html index 455dbe6b04..a9d7255d55 100644 --- a/cms/templates/checklists.html +++ b/cms/templates/checklists.html @@ -2,6 +2,27 @@ <%block name="title">Course Checklists <%block name="bodyclass">is-signedin course uxdesign checklists +<%namespace name='static' file='static_content.html'/> +<%block name="jsextra"> + + + + + + + <%block name="content"> @@ -564,7 +578,7 @@ @@ -574,80 +588,3 @@
    -<%block name="view_alerts"> - -
    -
    - - -
    -

    Your policy changes have been saved.

    -

    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.

    -
    - - - - close alert - -
    -
    - - -<%block name="jsextra"> - - \ No newline at end of file From ac25b02ce6842babfc1ed1107f49778e8d876f6b Mon Sep 17 00:00:00 2001 From: cahrens Date: Thu, 14 Mar 2013 13:48:17 -0400 Subject: [PATCH 018/135] Minor updates. --- cms/djangoapps/contentstore/views.py | 5 ++++- cms/static/js/views/checklists_view.js | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 62c8f8da97..89360c1d42 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -1322,7 +1322,10 @@ def get_checklists(request, org, course, name): return render_to_response('checklists.html', - {'checklists' : course_module.metadata[key]}) + { + 'context_course': course_module, + 'checklists' : course_module.metadata[key] + }) @login_required diff --git a/cms/static/js/views/checklists_view.js b/cms/static/js/views/checklists_view.js index 0905b392e4..1ed79c8fac 100644 --- a/cms/static/js/views/checklists_view.js +++ b/cms/static/js/views/checklists_view.js @@ -22,7 +22,6 @@ CMS.Views.Checklists = Backbone.View.extend({ }, toggleTask : function (e) { - (e).preventDefault(); $(e.target).closest('.task').toggleClass('is-completed'); }, From 13c38fa173cd988b44bc5a2a32f9abda3102a1a4 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Thu, 14 Mar 2013 16:17:47 -0400 Subject: [PATCH 019/135] studio - checklists: revised input/label HTML and adjusted styling for each --- cms/static/sass/_checklists.scss | 87 ++++++++++++++------------------ cms/templates/checklists.html | 26 +++++----- 2 files changed, 51 insertions(+), 62 deletions(-) diff --git a/cms/static/sass/_checklists.scss b/cms/static/sass/_checklists.scss index 7a5b5213ea..b49c75f9e6 100644 --- a/cms/static/sass/_checklists.scss +++ b/cms/static/sass/_checklists.scss @@ -56,7 +56,7 @@ body.course.checklists { .checklist-title { @include transition(color .15s .25s ease-in-out); - width: flex-grid(7, 9); + width: flex-grid(6, 9); margin: 0 flex-gutter() 0 0; float: left; @@ -64,14 +64,6 @@ body.course.checklists { @include font-size(14); display: inline-block; vertical-align: middle; - margin-right: $baseline; - color: $gray-l4; - } - - .icon-confirm { - @include font-size(20); - display: inline-block; - vertical-align: middle; margin-right: ($baseline/2); color: $gray-l4; } @@ -91,11 +83,20 @@ body.course.checklists { .checklist-status { @include font-size(13); - width: flex-grid(2, 9); + width: flex-grid(3, 9); float: right; margin-top: ($baseline/2); text-align: right; color: $gray-l2; + + + .icon-confirm { + @include font-size(20); + display: inline-block; + vertical-align: middle; + margin-left: ($baseline/2); + color: $gray-l4; + } .status-count { @include font-size(16); @@ -192,13 +193,6 @@ body.course.checklists { color: $green; } } - - .checklist-status .icon-confirm { - @include font-size(12); - display: inline-block; - vertical-align: middle; - margin-right: ($baseline/4); - } } } @@ -230,41 +224,38 @@ body.course.checklists { border-bottom: none; } - label { + .task-input { + display: inline-block; + vertical-align: text-top; float: left; - width: flex-grid(7,9); + margin: ($baseline/2) flex-gutter() 0 0; + } + + .task-details { + display: inline-block; + vertical-align: text-top; + float: left; + width: flex-grid(6,9); font-weight: 500; - .task-input { - display: inline-block; - vertical-align: text-top; - margin: ($baseline/2) flex-gutter() 0 0; + .task-name { + @include transition(color .15s .25s ease-in-out); + vertical-align: baseline; + cursor: pointer; + margin-bottom: 0; + } + + .task-description { + @include transition(color .15s .25s ease-in-out); + @include font-size(14); + color: $gray-l2; } - .task-details { - display: inline-block; - vertical-align: text-top; - width: flex-grid(6,7); - - .task-name { - @include transition(color .15s .25s ease-in-out); - vertical-align: baseline; - cursor: pointer; - margin-bottom: 0; - } - - .task-description { - @include transition(color .15s .25s ease-in-out); - @include font-size(14); - color: $gray-l2; - } - - .task-support { - @include transition(opacity .15s .25s ease-in-out); - @include font-size(12); - opacity: 0; - pointer-events: none; - } + .task-support { + @include transition(opacity .15s .25s ease-in-out); + @include font-size(12); + opacity: 0; + pointer-events: none; } } @@ -273,7 +264,7 @@ body.course.checklists { @include clearfix(); display: inline-block; vertical-align: middle; - float: left; + float: right; width: flex-grid(2,9); margin: ($baseline/2) 0 0 flex-gutter(); opacity: 0; diff --git a/cms/templates/checklists.html b/cms/templates/checklists.html index 0e9be39fed..8415338466 100644 --- a/cms/templates/checklists.html +++ b/cms/templates/checklists.html @@ -48,27 +48,25 @@
    0% of checklist completed
    -

    +

    - - ${checklist['short_description']}

    - - Tasks Completed: 0/4 + ${checklist['short_description']} + + Tasks Completed: 0/4 + +
      % for item in checklist['items']:
    • -
    diff --git a/common/lib/xmodule/xmodule/js/src/capa/display.coffee b/common/lib/xmodule/xmodule/js/src/capa/display.coffee index 158c2b98d0..4173f424b6 100644 --- a/common/lib/xmodule/xmodule/js/src/capa/display.coffee +++ b/common/lib/xmodule/xmodule/js/src/capa/display.coffee @@ -70,6 +70,7 @@ class @Problem @bind() @num_queued_items = @new_queued_items.length + @updateProgress response if @num_queued_items == 0 delete window.queuePollerID else From eda6169b8b4a11dea221df37ec3cdfb93e36c819 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Fri, 15 Mar 2013 09:45:31 -0400 Subject: [PATCH 050/135] Pass along a url creator as opposed to just a url through the ModuleSystem. --- common/lib/capa/capa/responsetypes.py | 3 ++- .../open_ended_module.py | 4 ++-- .../xmodule/tests/test_combined_open_ended.py | 5 +++- lms/djangoapps/courseware/module_render.py | 23 +++++++++++++------ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 62da901656..f997829cd0 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -1413,8 +1413,9 @@ class CodeResponse(LoncapaResponse): queuekey = xqueue_interface.make_hashkey(str(self.system.seed) + qtime + anonymous_student_id + self.answer_id) + callback_url = self.system.xqueue['construct_callback']() xheader = xqueue_interface.make_xheader( - lms_callback_url=self.system.xqueue['callback_url'], + lms_callback_url=callback_url, lms_key=queuekey, queue_name=self.queue_name) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py index 1f84d2ab8c..8373700837 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py @@ -174,7 +174,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): str(len(self.child_history))) xheader = xqueue_interface.make_xheader( - lms_callback_url=system.xqueue['callback_url'], + lms_callback_url=system.xqueue['construct_callback'](), lms_key=queuekey, queue_name=self.message_queue_name ) @@ -224,7 +224,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): anonymous_student_id + str(len(self.child_history))) - xheader = xqueue_interface.make_xheader(lms_callback_url=system.xqueue['callback_url'], + xheader = xqueue_interface.make_xheader(lms_callback_url=system.xqueue['construct_callback'](), lms_key=queuekey, queue_name=self.queue_name) diff --git a/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py b/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py index 09c86baf27..aa8a077cc1 100644 --- a/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py +++ b/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py @@ -183,7 +183,10 @@ class OpenEndedModuleTest(unittest.TestCase): self.test_system.location = self.location self.mock_xqueue = MagicMock() self.mock_xqueue.send_to_queue.return_value = (None, "Message") - self.test_system.xqueue = {'interface': self.mock_xqueue, 'callback_url': '/', 'default_queuename': 'testqueue', + def constructed_callback(dispatch = "score_update"): + return dispatch + + self.test_system.xqueue = {'interface': self.mock_xqueue, 'construct_callback': constructed_callback, 'default_queuename': 'testqueue', 'waittime': 1} self.openendedmodule = OpenEndedModule(self.test_system, self.location, self.definition, self.descriptor, self.static_data, self.metadata) diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 08df7bfb8c..0954f8d28c 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -181,12 +181,21 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours host=request.get_host(), proto=request.META.get('HTTP_X_FORWARDED_PROTO', 'https' if request.is_secure() else 'http') ) - xqueue_callback_url += reverse('xqueue_callback', - kwargs=dict(course_id=course_id, - userid=str(user.id), - id=descriptor.location.url(), - dispatch='score_update'), - ) + + def make_xqueue_callback(dispatch = 'score_update'): + # Fully qualified callback URL for external queueing system + xqueue_callback_url = '{proto}://{host}'.format( + host=request.get_host(), + proto=request.META.get('HTTP_X_FORWARDED_PROTO', 'https' if request.is_secure() else 'http') + ) + + xqueue_callback_url += reverse('xqueue_callback', + kwargs=dict(course_id=course_id, + userid=str(user.id), + id=descriptor.location.url(), + dispatch=dispatch), + ) + return xqueue_callback_url # Default queuename is course-specific and is derived from the course that # contains the current module. @@ -194,7 +203,7 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours xqueue_default_queuename = descriptor.location.org + '-' + descriptor.location.course xqueue = {'interface': xqueue_interface, - 'callback_url': xqueue_callback_url, + 'construct_callback': make_xqueue_callback, 'default_queuename': xqueue_default_queuename.replace(' ', '_'), 'waittime': settings.XQUEUE_WAITTIME_BETWEEN_REQUESTS } From 45d8086e1cd5ac4fb7a583a411a9b7bdda27491b Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Fri, 15 Mar 2013 11:40:22 -0400 Subject: [PATCH 051/135] Set up ajax to submit to XQueue. Add some unit tests to make sure this is working properly --- common/lib/capa/capa/inputtypes.py | 37 +++++++++-- common/lib/capa/capa/responsetypes.py | 5 +- common/lib/capa/capa/tests/__init__.py | 9 ++- common/lib/capa/capa/tests/test_inputtypes.py | 63 +++++++++++++++++++ 4 files changed, 105 insertions(+), 9 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index f49ad5b422..08c395692f 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -48,6 +48,8 @@ import pyparsing from .registry import TagRegistry from capa.chem import chemcalc +import xqueue_interface +from datetime import datetime log = logging.getLogger(__name__) @@ -639,6 +641,7 @@ class MatlabInput(CodeInput): # Check if problem has been queued self.queue_len = 0 + self.queuename = 'matlab' # Flag indicating that the problem has been queued, 'msg' is length of # queue if self.status == 'incomplete': @@ -650,20 +653,44 @@ class MatlabInput(CodeInput): def handle_ajax(self, dispatch, get): if dispatch == 'plot': - # put the data in the queue and ship it off - pass - elif dispatch == 'display': + return self.plot_data(get) + elif dispatch == 'xqueue_response': # render the response pass def plot_data(self, get): ''' send data via xqueue to the mathworks backend''' - # only send data if xqueue exists if self.system.xqueue is not None: - pass + # pull relevant info out of get + response = get['submission'] + + # construct xqueue headers + qinterface = self.system.xqueue['interface'] + qtime = datetime.strftime(datetime.now(), xqueue_interface.dateformat) + callback_url = self.system.xqueue['construct_callback']('input_ajax') + anonymous_student_id = self.system.anonymous_student_id + queuekey = xqueue_interface.make_hashkey(str(self.system.seed) + qtime + + anonymous_student_id + + self.id) + xheader = xqueue_interface.make_xheader( + lms_callback_url = callback_url, + lms_key = queuekey, + queue_name = self.queuename) + # construct xqueue body + student_info = {'anonymous_student_id': anonymous_student_id, + 'submission_time': qtime} + contents = {'grader_payload': self.plot_payload, + 'student_info': json.dumps(student_info), + 'student_response': response} + + (error, msg) = qinterface.send_to_queue(header=xheader, + body = json.dumps(contents)) + + return json.dumps({'success': error != 0, 'message': msg}) + return json.dumps({'success': False, 'message': 'Cannot connect to the queue'}) registry.register(MatlabInput) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index f997829cd0..bb202e6d6e 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -1271,8 +1271,9 @@ class CodeResponse(LoncapaResponse): Expects 'xqueue' dict in ModuleSystem with the following keys that are needed by CodeResponse: system.xqueue = { 'interface': XqueueInterface object, - 'callback_url': Per-StudentModule callback URL - where results are posted (string), + 'construct_callback': Per-StudentModule callback URL + constructor, defaults to using 'score_update' + as the correct dispatch (function), 'default_queuename': Default queuename to submit request (string) } diff --git a/common/lib/capa/capa/tests/__init__.py b/common/lib/capa/capa/tests/__init__.py index 89cb5a5ee9..7b1bffce62 100644 --- a/common/lib/capa/capa/tests/__init__.py +++ b/common/lib/capa/capa/tests/__init__.py @@ -2,7 +2,7 @@ import fs import fs.osfs import os -from mock import Mock +from mock import Mock, MagicMock import xml.sax.saxutils as saxutils @@ -16,6 +16,11 @@ def tst_render_template(template, context): """ return '
    {0}
    '.format(saxutils.escape(repr(context))) +def calledback_url(dispatch = 'score_update'): + return dispatch + +xqueue_interface = MagicMock() +xqueue_interface.send_to_queue.return_value = (1, 'Success!') test_system = Mock( ajax_url='courses/course_id/modx/a_location', @@ -26,7 +31,7 @@ test_system = Mock( user=Mock(), filestore=fs.osfs.OSFS(os.path.join(TEST_DIR, "test_files")), debug=True, - xqueue={'interface': None, 'callback_url': '/', 'default_queuename': 'testqueue', 'waittime': 10}, + xqueue={'interface': xqueue_interface, 'construct_callback': calledback_url, 'default_queuename': 'testqueue', 'waittime': 10}, node_path=os.environ.get("NODE_PATH", "/usr/local/lib/node_modules"), anonymous_student_id='student' ) diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index 360fd9f2f6..01801ac822 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -23,6 +23,7 @@ import xml.sax.saxutils as saxutils from . import test_system from capa import inputtypes +from mock import ANY # just a handy shortcut lookup_tag = inputtypes.registry.get_class_for_tag @@ -300,6 +301,68 @@ class CodeInputTest(unittest.TestCase): self.assertEqual(context, expected) +class MatlabTest(unittest.TestCase): + ''' + Test Matlab input types + ''' + def setUp(self): + self.rows = '10' + self.cols = '80' + self.tabsize = '4' + self.mode = "" + self.payload = "payload" + self.linenumbers = 'true' + self.xml = """ + + {payload} + + """.format(r = self.rows, + c = self.cols, + tabsize = self.tabsize, + m = self.mode, + payload = self.payload, + ln = self.linenumbers) + elt = etree.fromstring(self.xml) + state = {'value': 'print "good evening"', + 'status': 'incomplete', + 'feedback': {'message': '3'}, } + + self.input_class = lookup_tag('matlabinput') + self.the_input = self.input_class(test_system, elt, state) + + + def test_rendering(self): + context = self.the_input._get_render_context() + + expected = {'id': 'prob_1_2', + 'value': 'print "good evening"', + 'status': 'queued', + 'msg': self.input_class.submitted_msg, + 'mode': self.mode, + 'rows': self.rows, + 'cols': self.cols, + 'linenumbers': 'true', + 'hidden': '', + 'tabsize': int(self.tabsize), + 'queue_len': '3', + } + + self.assertEqual(context, expected) + + def test_plot_data(self): + get = {'submission': 'x = 1234;'} + response = json.loads(self.the_input.handle_ajax("plot", get)) + + test_system.xqueue['interface'].send_to_queue.assert_called_with(header=ANY, body=ANY) + + + self.assertTrue(response['success']) + + + class SchematicTest(unittest.TestCase): ''' From 521c469a355a22d3125b9a6ae82c934b2dc5f10c Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Mon, 18 Mar 2013 17:14:16 -0400 Subject: [PATCH 052/135] Add the ability for input types to have their own state and add in a handler for ungraded responses via xqueue --- common/lib/capa/capa/capa_problem.py | 24 ++++++++ common/lib/capa/capa/inputtypes.py | 56 +++++++++++++++++-- .../lib/capa/capa/templates/matlabinput.html | 3 + common/lib/capa/capa/tests/test_inputtypes.py | 32 ++++++++++- common/lib/xmodule/xmodule/capa_module.py | 17 +++++- 5 files changed, 124 insertions(+), 8 deletions(-) diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index 42753fc90b..fbf911e500 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -111,6 +111,7 @@ class LoncapaProblem(object): if self.system is None: raise Exception() self.seed = seed + self.input_state = None if state: if 'seed' in state: @@ -121,11 +122,16 @@ class LoncapaProblem(object): self.correct_map.set_dict(state['correct_map']) if 'done' in state: self.done = state['done'] + if 'input_state' in state: + self.input_state = state['input_state'] # TODO: Does this deplete the Linux entropy pool? Is this fast enough? if not self.seed: self.seed = struct.unpack('i', os.urandom(4))[0] + if not self.input_state: + self.input_state = {} + # Convert startouttext and endouttext to proper problem_text = re.sub("startouttext\s*/", "text", problem_text) problem_text = re.sub("endouttext\s*/", "/text", problem_text) @@ -188,6 +194,7 @@ class LoncapaProblem(object): return {'seed': self.seed, 'student_answers': self.student_answers, 'correct_map': self.correct_map.get_dict(), + 'input_state': self.input_state, 'done': self.done} def get_max_score(self): @@ -237,6 +244,19 @@ class LoncapaProblem(object): self.correct_map.set_dict(cmap.get_dict()) return cmap + def ungraded_response(self, xqueue_msg, queuekey): + ''' + Handle any responses from the xqueue that are not related to grading + + Does not return any value + ''' + # check against each inputtype + for the_input in self.inputs.values(): + # if the input type has an xqueue_response function, pass in the values + if hasattr(the_input, 'ungraded_response'): + the_input.ungraded_response(xqueue_msg, queuekey) + + def is_queued(self): ''' Returns True if any part of the problem has been submitted to an external queue @@ -527,11 +547,15 @@ class LoncapaProblem(object): value = "" if self.student_answers and problemid in self.student_answers: value = self.student_answers[problemid] + + if input_id not in self.input_state: + self.input_state[input_id] = {} # do the rendering state = {'value': value, 'status': status, 'id': input_id, + 'input_state': self.input_state[input_id], 'feedback': {'message': msg, 'hint': hint, 'hintmode': hintmode, }} diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 08c395692f..303619c820 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -134,6 +134,8 @@ class InputTypeBase(object): * 'id' -- the id of this input, typically "{problem-location}_{response-num}_{input-num}" * 'status' (answered, unanswered, unsubmitted) + * 'input_state' -- dictionary containing any inputtype-specific state + that has been preserved * 'feedback' (dictionary containing keys for hints, errors, or other feedback from previous attempt. Specifically 'message', 'hint', 'hintmode'. If 'hintmode' is 'always', the hint is always displayed.) @@ -160,6 +162,7 @@ class InputTypeBase(object): self.msg = feedback.get('message', '') self.hint = feedback.get('hint', '') self.hintmode = feedback.get('hintmode', None) + self.input_state = state.get('input_state', {}) # put hint above msg if it should be displayed if self.hintmode == 'always': @@ -643,8 +646,11 @@ class MatlabInput(CodeInput): self.queue_len = 0 self.queuename = 'matlab' # Flag indicating that the problem has been queued, 'msg' is length of + self.queue_msg = None # queue if self.status == 'incomplete': + if 'queue_msg' in self.input_state: + self.queue_msg = self.input_state['queue_msg'] self.status = 'queued' self.queue_len = self.msg self.msg = self.submitted_msg @@ -652,13 +658,47 @@ class MatlabInput(CodeInput): def handle_ajax(self, dispatch, get): + ''' Handle AJAX calls directed to this input''' if dispatch == 'plot': - return self.plot_data(get) - elif dispatch == 'xqueue_response': - # render the response - pass + return self._plot_data(get) - def plot_data(self, get): + def ungraded_response(self, queue_msg, queuekey): + ''' Handle any XQueue responses that have to be saved and rendered ''' + # check the queuekey against the saved queuekey + if('queuestate' in self.input_state and self.input_state['queuestate'] == 'queued' + and self.input_state['queuekey'] == queuekey): + msg = _parse_message(queue_msg) + # save the queue message so that it can be rendered later + self.input_state['queue_msg'] = msg + self.input_state['queued'] = 'dequeued' + + def _extra_context(self): + ''' Set up additional context variables''' + extra_context = {'queue_len': self.queue_len} + if self.queue_msg is not None: + extra_context['queue_msg'] = self.queue_msg + else: + extra_context['queue_msg'] = '' + return extra_context + + def _parse_data(self, queue_msg): + ''' + takes a queue_msg returned from the queue and parses it and returns + whatever is stored in msg + returns string msg + ''' + try: + result = json.loads(queue_msg) + except (TypeError, ValueError): + log.error("External message should be a JSON serialized dict." + " Received queue_msg = %s" % queue_msg) + raise + # TODO: needs more error checking + msg = result['msg'] + return msg + + + def _plot_data(self, get): ''' send data via xqueue to the mathworks backend''' # only send data if xqueue exists if self.system.xqueue is not None: @@ -668,7 +708,7 @@ class MatlabInput(CodeInput): # construct xqueue headers qinterface = self.system.xqueue['interface'] qtime = datetime.strftime(datetime.now(), xqueue_interface.dateformat) - callback_url = self.system.xqueue['construct_callback']('input_ajax') + callback_url = self.system.xqueue['construct_callback']('ungraded_response') anonymous_student_id = self.system.anonymous_student_id queuekey = xqueue_interface.make_hashkey(str(self.system.seed) + qtime + anonymous_student_id + @@ -678,6 +718,10 @@ class MatlabInput(CodeInput): lms_key = queuekey, queue_name = self.queuename) + # save the input state + self.input_state['queuekey'] = queuekey + self.input_state['queuestate'] = 'queued' + # construct xqueue body student_info = {'anonymous_student_id': anonymous_student_id, diff --git a/common/lib/capa/capa/templates/matlabinput.html b/common/lib/capa/capa/templates/matlabinput.html index ba516be249..07433f0a3a 100644 --- a/common/lib/capa/capa/templates/matlabinput.html +++ b/common/lib/capa/capa/templates/matlabinput.html @@ -29,6 +29,9 @@
    ${msg|n}
    +
    + ${queue_msg|n} +
    diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index 01801ac822..97e27d5ffc 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -344,6 +344,35 @@ class MatlabTest(unittest.TestCase): 'mode': self.mode, 'rows': self.rows, 'cols': self.cols, + 'queue_msg': '', + 'linenumbers': 'true', + 'hidden': '', + 'tabsize': int(self.tabsize), + 'queue_len': '3', + } + + self.assertEqual(context, expected) + + + def test_rendering_with_state(self): + state = {'value': 'print "good evening"', + 'status': 'incomplete', + 'input_state': {'queue_msg': 'message'}, + 'feedback': {'message': '3'}, } + elt = etree.fromstring(self.xml) + + input_class = lookup_tag('matlabinput') + the_input = self.input_class(test_system, elt, state) + context = the_input._get_render_context() + + expected = {'id': 'prob_1_2', + 'value': 'print "good evening"', + 'status': 'queued', + 'msg': self.input_class.submitted_msg, + 'mode': self.mode, + 'rows': self.rows, + 'cols': self.cols, + 'queue_msg': 'message', 'linenumbers': 'true', 'hidden': '', 'tabsize': int(self.tabsize), @@ -358,8 +387,9 @@ class MatlabTest(unittest.TestCase): test_system.xqueue['interface'].send_to_queue.assert_called_with(header=ANY, body=ANY) - self.assertTrue(response['success']) + self.assertTrue(self.the_input.input_state['queuekey'] is not None) + self.assertEqual(self.the_input.input_state['queuestate'], 'queued') diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index e66b1d3495..1bdd62f5b7 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -93,6 +93,7 @@ class CapaFields(object): rerandomize = Randomization(help="When to rerandomize the problem", default="always", scope=Scope.settings) data = String(help="XML data for the problem", scope=Scope.content) correct_map = Object(help="Dictionary with the correctness of current student answers", scope=Scope.student_state, default={}) + input_state = Object(help="Dictionary for maintaining the state of inputtypes", scope=Scope.student_state, default={}) student_answers = Object(help="Dictionary with the current student responses", scope=Scope.student_state) done = Boolean(help="Whether the student has answered the problem", scope=Scope.student_state) display_name = String(help="Display name for this module", scope=Scope.settings) @@ -188,6 +189,7 @@ class CapaModule(CapaFields, XModule): 'done': self.done, 'correct_map': self.correct_map, 'student_answers': self.student_answers, + 'input_state': self.input_state, 'seed': self.seed, } @@ -195,6 +197,7 @@ class CapaModule(CapaFields, XModule): lcp_state = self.lcp.get_state() self.done = lcp_state['done'] self.correct_map = lcp_state['correct_map'] + self.input_state = lcp_state['input_state'] self.student_answers = lcp_state['student_answers'] self.seed = lcp_state['seed'] @@ -443,7 +446,8 @@ class CapaModule(CapaFields, XModule): 'problem_save': self.save_problem, 'problem_show': self.get_answer, 'score_update': self.update_score, - 'input_ajax': self.lcp.handle_input_ajax + 'input_ajax': self.lcp.handle_input_ajax, + 'ungraded_response': self.handle_ungraded_response } if dispatch not in handlers: @@ -537,6 +541,17 @@ class CapaModule(CapaFields, XModule): return dict() # No AJAX return is needed + def handle_ungraded_response(self, get): + ''' + Get the XQueue response + ''' + queuekey = get['queuekey'] + score_msg = get['xqueue_body'] + # pass along the xqueue message to the problem + self.lcp.ungraded_response(score_msg, queuekey) + + self.set_state_from_lcp() + def get_answer(self, get): ''' For the "show answer" button. From 8649d67b9d6fe56d807a23d64d9ec8ab2e49b61a Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Tue, 19 Mar 2013 13:11:48 -0400 Subject: [PATCH 053/135] Force the progress bar update when we get a code response answer. --- common/lib/xmodule/xmodule/js/src/capa/display.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/lib/xmodule/xmodule/js/src/capa/display.coffee b/common/lib/xmodule/xmodule/js/src/capa/display.coffee index 4173f424b6..70704ab247 100644 --- a/common/lib/xmodule/xmodule/js/src/capa/display.coffee +++ b/common/lib/xmodule/xmodule/js/src/capa/display.coffee @@ -41,6 +41,11 @@ class @Problem @el.attr progress: response.progress_status @el.trigger('progressChanged') + forceUpdate: (response) => + @el.attr progress: response.progress_status + @el.trigger('progressChanged') + + queueing: => @queued_items = @$(".xqueue") @num_queued_items = @queued_items.length @@ -70,8 +75,8 @@ class @Problem @bind() @num_queued_items = @new_queued_items.length - @updateProgress response if @num_queued_items == 0 + @forceUpdate response delete window.queuePollerID else # TODO: Some logic to dynamically adjust polling rate based on queuelen From f4d68d77f671abf3e63e8788b68d6416b18d3287 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Tue, 19 Mar 2013 15:29:40 -0400 Subject: [PATCH 054/135] Add Javascript for new button and fix Python backend issues --- common/lib/capa/capa/capa_problem.py | 5 +-- common/lib/capa/capa/inputtypes.py | 24 +++++++--- .../lib/capa/capa/templates/matlabinput.html | 45 ++++++++++++++++++- common/lib/capa/capa/tests/__init__.py | 2 +- common/lib/capa/capa/tests/test_inputtypes.py | 4 +- common/lib/xmodule/xmodule/capa_module.py | 3 +- 6 files changed, 67 insertions(+), 16 deletions(-) diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index fbf911e500..911f210812 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -548,14 +548,11 @@ class LoncapaProblem(object): if self.student_answers and problemid in self.student_answers: value = self.student_answers[problemid] - if input_id not in self.input_state: - self.input_state[input_id] = {} - # do the rendering state = {'value': value, 'status': status, 'id': input_id, - 'input_state': self.input_state[input_id], + 'input_state': self.input_state, 'feedback': {'message': msg, 'hint': hint, 'hintmode': hintmode, }} diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 303619c820..42865a01b5 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -162,7 +162,7 @@ class InputTypeBase(object): self.msg = feedback.get('message', '') self.hint = feedback.get('hint', '') self.hintmode = feedback.get('hintmode', None) - self.input_state = state.get('input_state', {}) + self.input_state_dict = state.get('input_state', {}) # put hint above msg if it should be displayed if self.hintmode == 'always': @@ -635,6 +635,11 @@ class MatlabInput(CodeInput): ''' Handle matlab-specific parsing ''' + # if we don't have state for this input type yet, make one + if self.id not in self.input_state_dict: + self.input_state_dict[self.id] = {} + + self.input_state = self.input_state_dict[self.id] xml = self.xml self.plot_payload = xml.findtext('./plot_payload') # if no student input yet, then use the default input given by the @@ -647,10 +652,13 @@ class MatlabInput(CodeInput): self.queuename = 'matlab' # Flag indicating that the problem has been queued, 'msg' is length of self.queue_msg = None + if 'queue_msg' in self.input_state: + self.queue_msg = self.input_state['queue_msg'] + if 'queued' in self.input_state and self.input_state['queuestate'] is not None: + self.status = 'queued' + self.queue_len = 1 # queue if self.status == 'incomplete': - if 'queue_msg' in self.input_state: - self.queue_msg = self.input_state['queue_msg'] self.status = 'queued' self.queue_len = self.msg self.msg = self.submitted_msg @@ -667,10 +675,11 @@ class MatlabInput(CodeInput): # check the queuekey against the saved queuekey if('queuestate' in self.input_state and self.input_state['queuestate'] == 'queued' and self.input_state['queuekey'] == queuekey): - msg = _parse_message(queue_msg) + msg = self._parse_data(queue_msg) # save the queue message so that it can be rendered later self.input_state['queue_msg'] = msg - self.input_state['queued'] = 'dequeued' + self.input_state['queuestate'] = None + self.input_state['queuekey'] = None def _extra_context(self): ''' Set up additional context variables''' @@ -733,8 +742,9 @@ class MatlabInput(CodeInput): (error, msg) = qinterface.send_to_queue(header=xheader, body = json.dumps(contents)) - return json.dumps({'success': error != 0, 'message': msg}) - return json.dumps({'success': False, 'message': 'Cannot connect to the queue'}) + + return {'success': error == 0, 'message': msg} + return {'success': False, 'message': 'Cannot connect to the queue'} registry.register(MatlabInput) diff --git a/common/lib/capa/capa/templates/matlabinput.html b/common/lib/capa/capa/templates/matlabinput.html index 07433f0a3a..cbfc4b119f 100644 --- a/common/lib/capa/capa/templates/matlabinput.html +++ b/common/lib/capa/capa/templates/matlabinput.html @@ -34,7 +34,7 @@
    - +
    diff --git a/common/lib/capa/capa/tests/__init__.py b/common/lib/capa/capa/tests/__init__.py index 7b1bffce62..72d82c683b 100644 --- a/common/lib/capa/capa/tests/__init__.py +++ b/common/lib/capa/capa/tests/__init__.py @@ -20,7 +20,7 @@ def calledback_url(dispatch = 'score_update'): return dispatch xqueue_interface = MagicMock() -xqueue_interface.send_to_queue.return_value = (1, 'Success!') +xqueue_interface.send_to_queue.return_value = (0, 'Success!') test_system = Mock( ajax_url='courses/course_id/modx/a_location', diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index 97e27d5ffc..b9da9df03f 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -357,7 +357,7 @@ class MatlabTest(unittest.TestCase): def test_rendering_with_state(self): state = {'value': 'print "good evening"', 'status': 'incomplete', - 'input_state': {'queue_msg': 'message'}, + 'input_state': {'prob_1_2': {'queue_msg': 'message'}}, 'feedback': {'message': '3'}, } elt = etree.fromstring(self.xml) @@ -383,7 +383,7 @@ class MatlabTest(unittest.TestCase): def test_plot_data(self): get = {'submission': 'x = 1234;'} - response = json.loads(self.the_input.handle_ajax("plot", get)) + response = self.the_input.handle_ajax("plot", get) test_system.xqueue['interface'].send_to_queue.assert_called_with(header=ANY, body=ANY) diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 1bdd62f5b7..a522c796bb 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -460,6 +460,7 @@ class CapaModule(CapaFields, XModule): 'progress_changed': after != before, 'progress_status': Progress.to_js_status_str(after), }) + self.set_state_from_lcp() return json.dumps(d, cls=ComplexEncoder) def is_past_due(self): @@ -549,8 +550,8 @@ class CapaModule(CapaFields, XModule): score_msg = get['xqueue_body'] # pass along the xqueue message to the problem self.lcp.ungraded_response(score_msg, queuekey) - self.set_state_from_lcp() + return dict() def get_answer(self, get): ''' From a2957cb3b72726e164824afea7be32ed453eaeba Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Tue, 19 Mar 2013 16:36:33 -0400 Subject: [PATCH 055/135] Add in some JS messages for when things go wrong. --- common/lib/capa/capa/inputtypes.py | 1 - .../lib/capa/capa/templates/matlabinput.html | 23 ++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 42865a01b5..5a47456fab 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -702,7 +702,6 @@ class MatlabInput(CodeInput): log.error("External message should be a JSON serialized dict." " Received queue_msg = %s" % queue_msg) raise - # TODO: needs more error checking msg = result['msg'] return msg diff --git a/common/lib/capa/capa/templates/matlabinput.html b/common/lib/capa/capa/templates/matlabinput.html index cbfc4b119f..e52a5297d1 100644 --- a/common/lib/capa/capa/templates/matlabinput.html +++ b/common/lib/capa/capa/templates/matlabinput.html @@ -60,9 +60,21 @@ $("#textbox_${id}").find('.CodeMirror-scroll').height(${int(13.5*eval(rows))}); + var gentle_alert = function (parent_elt, msg) { + if($(parent_elt).find('.capa_alert').length) { + $(parent_elt).find('.capa_alert').remove(); + } + var alert_elem = "
    " + msg + "
    "; + alert_elem = $(alert_elem).addClass('capa_alert'); + $(parent_elt).find('.action').after(alert_elem); + $(parent_elt).find('.capa_alert').css({opacity: 0}).animate({opacity: 1}, 700); + } + + // hook up the plot button var plot = function(event) { - url = $(this).closest('.problems-wrapper').data('url'); + var problem_elt = $(event.target).closest('.problems-wrapper'); + url = $(event.target).closest('.problems-wrapper').data('url'); input_id = "${id}"; // save the codemirror text to the textarea @@ -73,31 +85,30 @@ answer = input.serialize(); - // setup callback for + // setup callback for after we send information to plot var plot_callback = function(response) { if(response.success) { window.location.reload(); } else { - // TODO: show message + gentle_alert(problem_elt, msg); } } var save_callback = function(response) { if(response.success) { + // send information to the problem's plot functionality Problem.inputAjax(url, input_id, 'plot', {'submission': submission}, plot_callback); } else { - // TODO: show any messages + gentle_alert(problem_elt, msg); } } // save the answer $.postWithPrefix(url + '/problem_save', answer, save_callback); - - } $('#plot_${id}').click(plot); From 57f7acf86398698d1f54d15e4702092042aa7d3c Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Tue, 19 Mar 2013 17:10:38 -0400 Subject: [PATCH 056/135] Unbreak grading for capa problems Clean up some pylint errors --- common/lib/capa/capa/capa_problem.py | 8 +------- common/lib/capa/capa/inputtypes.py | 8 +++++++- .../lib/capa/capa/templates/matlabinput.html | 2 +- common/lib/xmodule/xmodule/capa_module.py | 19 ++++++++++++++++--- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index 911f210812..f1fea4d8e3 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -16,7 +16,6 @@ This is used by capa_module. from __future__ import division from datetime import datetime -import json import logging import math import numpy @@ -32,8 +31,6 @@ from xml.sax.saxutils import unescape from copy import deepcopy import chem -import chem.chemcalc -import chem.chemtools import chem.miller import verifiers import verifiers.draganddrop @@ -70,9 +67,6 @@ global_context = {'random': random, 'scipy': scipy, 'calc': calc, 'eia': eia, - 'chemcalc': chem.chemcalc, - 'chemtools': chem.chemtools, - 'miller': chem.miller, 'draganddrop': verifiers.draganddrop} # These should be removed from HTML output, including all subelements @@ -371,7 +365,7 @@ class LoncapaProblem(object): dispatch = get['dispatch'] return self.inputs[input_id].handle_ajax(dispatch, get) else: - log.warning("Could not find matching input for id: %s" % problem_id) + log.warning("Could not find matching input for id: %s" % input_id) return {} diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 5a47456fab..a62e696b20 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -37,7 +37,6 @@ graded status as'status' # makes sense, but a bunch of problems have markup that assumes block. Bigger TODO: figure out a # general css and layout strategy for capa, document it, then implement it. -from collections import namedtuple import json import logging from lxml import etree @@ -623,6 +622,13 @@ registry.register(CodeInput) class MatlabInput(CodeInput): ''' InputType for handling Matlab code input + + Example: + + + %api_key=API_KEY + + ''' template = "matlabinput.html" tags = ['matlabinput'] diff --git a/common/lib/capa/capa/templates/matlabinput.html b/common/lib/capa/capa/templates/matlabinput.html index e52a5297d1..6c02e8e68e 100644 --- a/common/lib/capa/capa/templates/matlabinput.html +++ b/common/lib/capa/capa/templates/matlabinput.html @@ -44,7 +44,7 @@ % if linenumbers == 'true': lineNumbers: true, % endif - mode: "${mode}", + mode: "matlab", matchBrackets: true, lineWrapping: true, indentUnit: "${tabsize}", diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index a522c796bb..6ce8d3a805 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -446,7 +446,7 @@ class CapaModule(CapaFields, XModule): 'problem_save': self.save_problem, 'problem_show': self.get_answer, 'score_update': self.update_score, - 'input_ajax': self.lcp.handle_input_ajax, + 'input_ajax': self.handle_input_ajax, 'ungraded_response': self.handle_ungraded_response } @@ -460,7 +460,6 @@ class CapaModule(CapaFields, XModule): 'progress_changed': after != before, 'progress_status': Progress.to_js_status_str(after), }) - self.set_state_from_lcp() return json.dumps(d, cls=ComplexEncoder) def is_past_due(self): @@ -544,7 +543,10 @@ class CapaModule(CapaFields, XModule): def handle_ungraded_response(self, get): ''' - Get the XQueue response + Delivers a response to the capa problem where the expectation where this response does + not have relevant grading information + + No ajax return is needed, so an empty dict is returned ''' queuekey = get['queuekey'] score_msg = get['xqueue_body'] @@ -553,6 +555,17 @@ class CapaModule(CapaFields, XModule): self.set_state_from_lcp() return dict() + def handle_input_ajax(self, get): + ''' + Passes information down to the capa problem so that it can handle its own ajax calls + Returns the response from the capa problem + ''' + response = self.lcp.handle_input_ajax(get) + # save any state changes that may occur + self.set_state_from_lcp() + return response + + def get_answer(self, get): ''' For the "show answer" button. From 74653ff8add08a1d63902e910736dbf41a6c0258 Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Tue, 19 Mar 2013 15:18:29 -0400 Subject: [PATCH 057/135] Factory refactor working for lms lettuce tests. --- .../contentstore/features/common.py | 12 +- .../contentstore/features/factories.py | 34 --- common/djangoapps/student/tests/__init__.py | 0 common/djangoapps/student/tests/factories.py | 198 +++++++++++++++ common/djangoapps/student/tests/tests.py | 107 ++++++++ common/djangoapps/terrain/factories.py | 229 ++++-------------- common/djangoapps/terrain/steps.py | 4 +- 7 files changed, 362 insertions(+), 222 deletions(-) delete mode 100644 cms/djangoapps/contentstore/features/factories.py create mode 100644 common/djangoapps/student/tests/__init__.py create mode 100644 common/djangoapps/student/tests/factories.py create mode 100644 common/djangoapps/student/tests/tests.py diff --git a/cms/djangoapps/contentstore/features/common.py b/cms/djangoapps/contentstore/features/common.py index 2ec0427e1d..820b60123b 100644 --- a/cms/djangoapps/contentstore/features/common.py +++ b/cms/djangoapps/contentstore/features/common.py @@ -7,8 +7,6 @@ from selenium.common.exceptions import WebDriverException, StaleElementReference from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By -from terrain.factories import UserFactory, RegistrationFactory, UserProfileFactory -from terrain.factories import CourseFactory, GroupFactory from xmodule.modulestore.django import _MODULESTORES, modulestore from xmodule.templates import update_templates from auth.authz import get_user_by_email @@ -61,7 +59,7 @@ def create_studio_user( email='robot+studio@edx.org', password='test', is_staff=False): - studio_user = UserFactory.build( + studio_user = world.UserFactory.build( username=uname, email=email, password=password, @@ -69,11 +67,11 @@ def create_studio_user( studio_user.set_password(password) studio_user.save() - registration = RegistrationFactory(user=studio_user) + registration = world.RegistrationFactory(user=studio_user) registration.register(studio_user) registration.activate() - user_profile = UserProfileFactory(user=studio_user) + user_profile = world.UserProfileFactory(user=studio_user) def flush_xmodule_store(): @@ -175,11 +173,11 @@ def log_into_studio( def create_a_course(): - c = CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course') + c = world.CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course') # Add the user to the instructor group of the course # so they will have the permissions to see it in studio - g = GroupFactory.create(name='instructor_MITx/999/Robot_Super_Course') + g = world.GroupFactory.create(name='instructor_MITx/999/Robot_Super_Course') u = get_user_by_email('robot+studio@edx.org') u.groups.add(g) u.save() diff --git a/cms/djangoapps/contentstore/features/factories.py b/cms/djangoapps/contentstore/features/factories.py deleted file mode 100644 index 087ceaaa2d..0000000000 --- a/cms/djangoapps/contentstore/features/factories.py +++ /dev/null @@ -1,34 +0,0 @@ -import factory -from student.models import User, UserProfile, Registration -from datetime import datetime -import uuid - - -class UserProfileFactory(factory.Factory): - FACTORY_FOR = UserProfile - - user = None - name = 'Robot Studio' - courseware = 'course.xml' - - -class RegistrationFactory(factory.Factory): - FACTORY_FOR = Registration - - user = None - activation_key = uuid.uuid4().hex - - -class UserFactory(factory.Factory): - FACTORY_FOR = User - - username = 'robot-studio' - email = 'robot+studio@edx.org' - password = 'test' - first_name = 'Robot' - last_name = 'Studio' - is_staff = False - is_active = True - is_superuser = False - last_login = datetime.now() - date_joined = datetime.now() diff --git a/common/djangoapps/student/tests/__init__.py b/common/djangoapps/student/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/djangoapps/student/tests/factories.py b/common/djangoapps/student/tests/factories.py new file mode 100644 index 0000000000..8dd70a7a61 --- /dev/null +++ b/common/djangoapps/student/tests/factories.py @@ -0,0 +1,198 @@ +from student.models import (User, UserProfile, Registration, + CourseEnrollmentAllowed) +from django.contrib.auth.models import Group +from datetime import datetime +from factory import Factory +from xmodule.modulestore import Location +from xmodule.modulestore.django import modulestore +from time import gmtime +from uuid import uuid4 +from xmodule.timeparse import stringify_time +from xmodule.modulestore.inheritance import own_metadata + + +class GroupFactory(Factory): + FACTORY_FOR = Group + + name = 'staff_MITx/999/Robot_Super_Course' + + +class UserProfileFactory(Factory): + FACTORY_FOR = UserProfile + + user = None + name = 'Robot Test' + level_of_education = None + gender = 'm' + mailing_address = None + goals = 'World domination' + + +class RegistrationFactory(Factory): + FACTORY_FOR = Registration + + user = None + activation_key = uuid4().hex + + +class UserFactory(Factory): + FACTORY_FOR = User + + username = 'robot' + email = 'robot+test@edx.org' + password = 'test' + first_name = 'Robot' + last_name = 'Test' + is_staff = False + is_active = True + is_superuser = False + last_login = datetime(2012, 1, 1) + date_joined = datetime(2011, 1, 1) + + +class CourseEnrollmentAllowedFactory(Factory): + FACTORY_FOR = CourseEnrollmentAllowed + + email = 'test@edx.org' + course_id = 'edX/test/2012_Fall' + + +def XMODULE_COURSE_CREATION(class_to_create, **kwargs): + return XModuleCourseFactory._create(class_to_create, **kwargs) + + +def XMODULE_ITEM_CREATION(class_to_create, **kwargs): + return XModuleItemFactory._create(class_to_create, **kwargs) + + +class XModuleCourseFactory(Factory): + """ + Factory for XModule courses. + """ + + ABSTRACT_FACTORY = True + _creation_function = (XMODULE_COURSE_CREATION,) + + @classmethod + def _create(cls, target_class, *args, **kwargs): + + template = Location('i4x', 'edx', 'templates', 'course', 'Empty') + org = kwargs.get('org') + number = kwargs.get('number') + display_name = kwargs.get('display_name') + location = Location('i4x', org, number, + 'course', Location.clean(display_name)) + + store = modulestore('direct') + + # Write the data to the mongo datastore + new_course = store.clone_item(template, location) + + # This metadata code was copied from cms/djangoapps/contentstore/views.py + if display_name is not None: + new_course.display_name = display_name + + new_course.lms.start = gmtime() + new_course.tabs = [{"type": "courseware"}, + {"type": "course_info", "name": "Course Info"}, + {"type": "discussion", "name": "Discussion"}, + {"type": "wiki", "name": "Wiki"}, + {"type": "progress", "name": "Progress"}] + + # Update the data in the mongo datastore + store.update_metadata(new_course.location.url(), own_metadata(new_course)) + + return new_course + + +class Course: + pass + + +class CourseFactory(XModuleCourseFactory): + FACTORY_FOR = Course + + template = 'i4x://edx/templates/course/Empty' + org = 'MITx' + number = '999' + display_name = 'Robot Super Course' + + +class XModuleItemFactory(Factory): + """ + Factory for XModule items. + """ + + ABSTRACT_FACTORY = True + _creation_function = (XMODULE_ITEM_CREATION,) + + @classmethod + def _create(cls, target_class, *args, **kwargs): + """ + Uses *kwargs*: + + *parent_location* (required): the location of the parent module + (e.g. the parent course or section) + + *template* (required): the template to create the item from + (e.g. i4x://templates/section/Empty) + + *data* (optional): the data for the item + (e.g. XML problem definition for a problem item) + + *display_name* (optional): the display name of the item + + *metadata* (optional): dictionary of metadata attributes + + *target_class* is ignored + """ + + DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] + + parent_location = Location(kwargs.get('parent_location')) + template = Location(kwargs.get('template')) + data = kwargs.get('data') + display_name = kwargs.get('display_name') + metadata = kwargs.get('metadata', {}) + + store = modulestore('direct') + + # This code was based off that in cms/djangoapps/contentstore/views.py + parent = store.get_item(parent_location) + + # If a display name is set, use that + dest_name = display_name.replace(" ", "_") if display_name is not None else uuid4().hex + dest_location = parent_location._replace(category=template.category, + name=dest_name) + + new_item = store.clone_item(template, dest_location) + + # replace the display name with an optional parameter passed in from the caller + if display_name is not None: + new_item.display_name = display_name + + # Add additional metadata or override current metadata + item_metadata = own_metadata(new_item) + item_metadata.update(metadata) + store.update_metadata(new_item.location.url(), item_metadata) + + # replace the data with the optional *data* parameter + if data is not None: + store.update_item(new_item.location, data) + + if new_item.location.category not in DETACHED_CATEGORIES: + store.update_children(parent_location, parent.children + [new_item.location.url()]) + + return new_item + + +class Item: + pass + + +class ItemFactory(XModuleItemFactory): + FACTORY_FOR = Item + + parent_location = 'i4x://MITx/999/course/Robot_Super_Course' + template = 'i4x://edx/templates/chapter/Empty' + display_name = 'Section One' diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py new file mode 100644 index 0000000000..6a2d75e3d8 --- /dev/null +++ b/common/djangoapps/student/tests/tests.py @@ -0,0 +1,107 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" +import logging + +from django.test import TestCase +from mock import Mock + +from .models import unique_id_for_user +from .views import process_survey_link, _cert_info + +COURSE_1 = 'edX/toy/2012_Fall' +COURSE_2 = 'edx/full/6.002_Spring_2012' + +log = logging.getLogger(__name__) + + +class CourseEndingTest(TestCase): + """Test things related to course endings: certificates, surveys, etc""" + + def test_process_survey_link(self): + username = "fred" + user = Mock(username=username) + id = unique_id_for_user(user) + link1 = "http://www.mysurvey.com" + self.assertEqual(process_survey_link(link1, user), link1) + + link2 = "http://www.mysurvey.com?unique={UNIQUE_ID}" + link2_expected = "http://www.mysurvey.com?unique={UNIQUE_ID}".format(UNIQUE_ID=id) + self.assertEqual(process_survey_link(link2, user), link2_expected) + + def test_cert_info(self): + user = Mock(username="fred") + survey_url = "http://a_survey.com" + course = Mock(end_of_course_survey_url=survey_url) + + self.assertEqual(_cert_info(user, course, None), + {'status': 'processing', + 'show_disabled_download_button': False, + 'show_download_url': False, + 'show_survey_button': False, }) + + cert_status = {'status': 'unavailable'} + self.assertEqual(_cert_info(user, course, cert_status), + {'status': 'processing', + 'show_disabled_download_button': False, + 'show_download_url': False, + 'show_survey_button': False}) + + cert_status = {'status': 'generating', 'grade': '67'} + self.assertEqual(_cert_info(user, course, cert_status), + {'status': 'generating', + 'show_disabled_download_button': True, + 'show_download_url': False, + 'show_survey_button': True, + 'survey_url': survey_url, + 'grade': '67' + }) + + cert_status = {'status': 'regenerating', 'grade': '67'} + self.assertEqual(_cert_info(user, course, cert_status), + {'status': 'generating', + 'show_disabled_download_button': True, + 'show_download_url': False, + 'show_survey_button': True, + 'survey_url': survey_url, + 'grade': '67' + }) + + download_url = 'http://s3.edx/cert' + cert_status = {'status': 'downloadable', 'grade': '67', + 'download_url': download_url} + self.assertEqual(_cert_info(user, course, cert_status), + {'status': 'ready', + 'show_disabled_download_button': False, + 'show_download_url': True, + 'download_url': download_url, + 'show_survey_button': True, + 'survey_url': survey_url, + 'grade': '67' + }) + + cert_status = {'status': 'notpassing', 'grade': '67', + 'download_url': download_url} + self.assertEqual(_cert_info(user, course, cert_status), + {'status': 'notpassing', + 'show_disabled_download_button': False, + 'show_download_url': False, + 'show_survey_button': True, + 'survey_url': survey_url, + 'grade': '67' + }) + + # Test a course that doesn't have a survey specified + course2 = Mock(end_of_course_survey_url=None) + cert_status = {'status': 'notpassing', 'grade': '67', + 'download_url': download_url} + self.assertEqual(_cert_info(user, course2, cert_status), + {'status': 'notpassing', + 'show_disabled_download_button': False, + 'show_download_url': False, + 'show_survey_button': False, + 'grade': '67' + }) diff --git a/common/djangoapps/terrain/factories.py b/common/djangoapps/terrain/factories.py index c36bf935f1..62b133217d 100644 --- a/common/djangoapps/terrain/factories.py +++ b/common/djangoapps/terrain/factories.py @@ -1,190 +1,61 @@ -from student.models import User, UserProfile, Registration -from django.contrib.auth.models import Group -from datetime import datetime -from factory import Factory -from xmodule.modulestore import Location -from xmodule.modulestore.django import modulestore -from time import gmtime -from uuid import uuid4 -from xmodule.timeparse import stringify_time -from xmodule.modulestore.inheritance import own_metadata +from student.tests.factories import (UserFactory, UserProfileFactory, + RegistrationFactory, GroupFactory, + CourseEnrollmentAllowed, + CourseFactory, ItemFactory) +from lettuce import world -class GroupFactory(Factory): - FACTORY_FOR = Group - - name = 'staff_MITx/999/Robot_Super_Course' - - -class UserProfileFactory(Factory): - FACTORY_FOR = UserProfile - - user = None - name = 'Robot Test' - level_of_education = None - gender = 'm' - mailing_address = None - goals = 'World domination' - - -class RegistrationFactory(Factory): - FACTORY_FOR = Registration - - user = None - activation_key = uuid4().hex - - -class UserFactory(Factory): - FACTORY_FOR = User - - username = 'robot' - email = 'robot+test@edx.org' - password = 'test' - first_name = 'Robot' - last_name = 'Test' - is_staff = False - is_active = True - is_superuser = False - last_login = datetime(2012, 1, 1) - date_joined = datetime(2011, 1, 1) - - -def XMODULE_COURSE_CREATION(class_to_create, **kwargs): - return XModuleCourseFactory._create(class_to_create, **kwargs) - - -def XMODULE_ITEM_CREATION(class_to_create, **kwargs): - return XModuleItemFactory._create(class_to_create, **kwargs) - - -class XModuleCourseFactory(Factory): +@world.absorb +class UserFactory(UserFactory): """ - Factory for XModule courses. - """ - - ABSTRACT_FACTORY = True - _creation_function = (XMODULE_COURSE_CREATION,) - - @classmethod - def _create(cls, target_class, *args, **kwargs): - - template = Location('i4x', 'edx', 'templates', 'course', 'Empty') - org = kwargs.get('org') - number = kwargs.get('number') - display_name = kwargs.get('display_name') - location = Location('i4x', org, number, - 'course', Location.clean(display_name)) - - store = modulestore('direct') - - # Write the data to the mongo datastore - new_course = store.clone_item(template, location) - - # This metadata code was copied from cms/djangoapps/contentstore/views.py - if display_name is not None: - new_course.display_name = display_name - - new_course.lms.start = gmtime() - new_course.tabs = [{"type": "courseware"}, - {"type": "course_info", "name": "Course Info"}, - {"type": "discussion", "name": "Discussion"}, - {"type": "wiki", "name": "Wiki"}, - {"type": "progress", "name": "Progress"}] - - # Update the data in the mongo datastore - store.update_metadata(new_course.location.url(), own_metadata(new_course)) - - return new_course - - -class Course: + User account for lms / cms + """ pass -class CourseFactory(XModuleCourseFactory): - FACTORY_FOR = Course - - template = 'i4x://edx/templates/course/Empty' - org = 'MITx' - number = '999' - display_name = 'Robot Super Course' - - -class XModuleItemFactory(Factory): +@world.absorb +class UserProfileFactory(UserProfileFactory): """ - Factory for XModule items. - """ - - ABSTRACT_FACTORY = True - _creation_function = (XMODULE_ITEM_CREATION,) - - @classmethod - def _create(cls, target_class, *args, **kwargs): - """ - Uses *kwargs*: - - *parent_location* (required): the location of the parent module - (e.g. the parent course or section) - - *template* (required): the template to create the item from - (e.g. i4x://templates/section/Empty) - - *data* (optional): the data for the item - (e.g. XML problem definition for a problem item) - - *display_name* (optional): the display name of the item - - *metadata* (optional): dictionary of metadata attributes - - *target_class* is ignored - """ - - DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] - - parent_location = Location(kwargs.get('parent_location')) - template = Location(kwargs.get('template')) - data = kwargs.get('data') - display_name = kwargs.get('display_name') - metadata = kwargs.get('metadata', {}) - - store = modulestore('direct') - - # This code was based off that in cms/djangoapps/contentstore/views.py - parent = store.get_item(parent_location) - - # If a display name is set, use that - dest_name = display_name.replace(" ", "_") if display_name is not None else uuid4().hex - dest_location = parent_location._replace(category=template.category, - name=dest_name) - - new_item = store.clone_item(template, dest_location) - - # replace the display name with an optional parameter passed in from the caller - if display_name is not None: - new_item.display_name = display_name - - # Add additional metadata or override current metadata - item_metadata = own_metadata(new_item) - item_metadata.update(metadata) - store.update_metadata(new_item.location.url(), item_metadata) - - # replace the data with the optional *data* parameter - if data is not None: - store.update_item(new_item.location, data) - - if new_item.location.category not in DETACHED_CATEGORIES: - store.update_children(parent_location, parent.children + [new_item.location.url()]) - - return new_item - - -class Item: + Demographics etc for the User + """ pass -class ItemFactory(XModuleItemFactory): - FACTORY_FOR = Item +@world.absorb +class RegistrationFactory(RegistrationFactory): + """ + Activation key for registering the user account + """ + pass - parent_location = 'i4x://MITx/999/course/Robot_Super_Course' - template = 'i4x://edx/templates/chapter/Empty' - display_name = 'Section One' + +@world.absorb +class GroupFactory(GroupFactory): + """ + Groups for user permissions for courses + """ + pass + + +@world.absorb +class CourseEnrollmentAllowed(CourseEnrollmentAllowed): + """ + Users allowed to enroll in the course outside of the usual window + """ + pass + + +@world.absorb +class CourseFactory(CourseFactory): + """ + Courseware courses + """ + pass + + +@world.absorb +class ItemFactory(ItemFactory): + """ + Everything included inside a course + """ + pass diff --git a/common/djangoapps/terrain/steps.py b/common/djangoapps/terrain/steps.py index 52eeb23c4a..e9dcd7db31 100644 --- a/common/djangoapps/terrain/steps.py +++ b/common/djangoapps/terrain/steps.py @@ -119,11 +119,11 @@ def create_user(uname): portal_user.set_password('test') portal_user.save() - registration = RegistrationFactory(user=portal_user) + registration = world.RegistrationFactory(user=portal_user) registration.register(portal_user) registration.activate() - user_profile = UserProfileFactory(user=portal_user) + user_profile = world.UserProfileFactory(user=portal_user) @world.absorb From daadaffb61fcbffb46ed555e8efb73740aa62853 Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Tue, 19 Mar 2013 15:44:01 -0400 Subject: [PATCH 058/135] Add mongo contentstore to acceptance.py for lettuce tests for lms --- lms/envs/acceptance.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lms/envs/acceptance.py b/lms/envs/acceptance.py index 3dac545367..5280c7d288 100644 --- a/lms/envs/acceptance.py +++ b/lms/envs/acceptance.py @@ -29,6 +29,14 @@ MODULESTORE = { } } +CONTENTSTORE = { + 'ENGINE': 'xmodule.contentstore.mongo.MongoContentStore', + 'OPTIONS': { + 'host': 'localhost', + 'db': 'test_xcontent', + } +} + # Set this up so that rake lms[acceptance] and running the # harvest command both use the same (test) database # which they can flush without messing up your dev db From 1c4ffcf122e53ad916cba195b8bd5328e48e8b60 Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Tue, 19 Mar 2013 16:00:15 -0400 Subject: [PATCH 059/135] Fix import statement for test.py --- common/djangoapps/student/tests.py | 107 ----------------------- common/djangoapps/student/tests/tests.py | 4 +- 2 files changed, 2 insertions(+), 109 deletions(-) delete mode 100644 common/djangoapps/student/tests.py diff --git a/common/djangoapps/student/tests.py b/common/djangoapps/student/tests.py deleted file mode 100644 index 6a2d75e3d8..0000000000 --- a/common/djangoapps/student/tests.py +++ /dev/null @@ -1,107 +0,0 @@ -""" -This file demonstrates writing tests using the unittest module. These will pass -when you run "manage.py test". - -Replace this with more appropriate tests for your application. -""" -import logging - -from django.test import TestCase -from mock import Mock - -from .models import unique_id_for_user -from .views import process_survey_link, _cert_info - -COURSE_1 = 'edX/toy/2012_Fall' -COURSE_2 = 'edx/full/6.002_Spring_2012' - -log = logging.getLogger(__name__) - - -class CourseEndingTest(TestCase): - """Test things related to course endings: certificates, surveys, etc""" - - def test_process_survey_link(self): - username = "fred" - user = Mock(username=username) - id = unique_id_for_user(user) - link1 = "http://www.mysurvey.com" - self.assertEqual(process_survey_link(link1, user), link1) - - link2 = "http://www.mysurvey.com?unique={UNIQUE_ID}" - link2_expected = "http://www.mysurvey.com?unique={UNIQUE_ID}".format(UNIQUE_ID=id) - self.assertEqual(process_survey_link(link2, user), link2_expected) - - def test_cert_info(self): - user = Mock(username="fred") - survey_url = "http://a_survey.com" - course = Mock(end_of_course_survey_url=survey_url) - - self.assertEqual(_cert_info(user, course, None), - {'status': 'processing', - 'show_disabled_download_button': False, - 'show_download_url': False, - 'show_survey_button': False, }) - - cert_status = {'status': 'unavailable'} - self.assertEqual(_cert_info(user, course, cert_status), - {'status': 'processing', - 'show_disabled_download_button': False, - 'show_download_url': False, - 'show_survey_button': False}) - - cert_status = {'status': 'generating', 'grade': '67'} - self.assertEqual(_cert_info(user, course, cert_status), - {'status': 'generating', - 'show_disabled_download_button': True, - 'show_download_url': False, - 'show_survey_button': True, - 'survey_url': survey_url, - 'grade': '67' - }) - - cert_status = {'status': 'regenerating', 'grade': '67'} - self.assertEqual(_cert_info(user, course, cert_status), - {'status': 'generating', - 'show_disabled_download_button': True, - 'show_download_url': False, - 'show_survey_button': True, - 'survey_url': survey_url, - 'grade': '67' - }) - - download_url = 'http://s3.edx/cert' - cert_status = {'status': 'downloadable', 'grade': '67', - 'download_url': download_url} - self.assertEqual(_cert_info(user, course, cert_status), - {'status': 'ready', - 'show_disabled_download_button': False, - 'show_download_url': True, - 'download_url': download_url, - 'show_survey_button': True, - 'survey_url': survey_url, - 'grade': '67' - }) - - cert_status = {'status': 'notpassing', 'grade': '67', - 'download_url': download_url} - self.assertEqual(_cert_info(user, course, cert_status), - {'status': 'notpassing', - 'show_disabled_download_button': False, - 'show_download_url': False, - 'show_survey_button': True, - 'survey_url': survey_url, - 'grade': '67' - }) - - # Test a course that doesn't have a survey specified - course2 = Mock(end_of_course_survey_url=None) - cert_status = {'status': 'notpassing', 'grade': '67', - 'download_url': download_url} - self.assertEqual(_cert_info(user, course2, cert_status), - {'status': 'notpassing', - 'show_disabled_download_button': False, - 'show_download_url': False, - 'show_survey_button': False, - 'grade': '67' - }) diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py index 6a2d75e3d8..4638da44b2 100644 --- a/common/djangoapps/student/tests/tests.py +++ b/common/djangoapps/student/tests/tests.py @@ -9,8 +9,8 @@ import logging from django.test import TestCase from mock import Mock -from .models import unique_id_for_user -from .views import process_survey_link, _cert_info +from student.models import unique_id_for_user +from student.views import process_survey_link, _cert_info COURSE_1 = 'edX/toy/2012_Fall' COURSE_2 = 'edx/full/6.002_Spring_2012' From 3576a3154b6386f95f8a48182791e1084d093756 Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Tue, 19 Mar 2013 16:38:51 -0400 Subject: [PATCH 060/135] Repoint factory references in lettuce tests to world. --- .../features/studio-overview-togglesection.py | 21 +++++++++---------- common/djangoapps/student/tests/factories.py | 11 ++++++++-- common/djangoapps/terrain/factories.py | 10 ++++++++- lms/djangoapps/courseware/features/common.py | 9 ++++---- .../courseware/features/problems.py | 3 +-- .../django_comment_client/tests/test_utils.py | 16 +------------- 6 files changed, 34 insertions(+), 36 deletions(-) diff --git a/cms/djangoapps/contentstore/features/studio-overview-togglesection.py b/cms/djangoapps/contentstore/features/studio-overview-togglesection.py index 00aa39455d..060d592cfd 100644 --- a/cms/djangoapps/contentstore/features/studio-overview-togglesection.py +++ b/cms/djangoapps/contentstore/features/studio-overview-togglesection.py @@ -1,5 +1,4 @@ from lettuce import world, step -from terrain.factories import * from common import * from nose.tools import assert_true, assert_false, assert_equal @@ -10,15 +9,15 @@ logger = getLogger(__name__) @step(u'I have a course with no sections$') def have_a_course(step): clear_courses() - course = CourseFactory.create() + course = world.CourseFactory.create() @step(u'I have a course with 1 section$') def have_a_course_with_1_section(step): clear_courses() - course = CourseFactory.create() - section = ItemFactory.create(parent_location=course.location) - subsection1 = ItemFactory.create( + course = world.CourseFactory.create() + section = world.ItemFactory.create(parent_location=course.location) + subsection1 = world.ItemFactory.create( parent_location=section.location, template='i4x://edx/templates/sequential/Empty', display_name='Subsection One',) @@ -27,20 +26,20 @@ def have_a_course_with_1_section(step): @step(u'I have a course with multiple sections$') def have_a_course_with_two_sections(step): clear_courses() - course = CourseFactory.create() - section = ItemFactory.create(parent_location=course.location) - subsection1 = ItemFactory.create( + course = world.CourseFactory.create() + section = world.ItemFactory.create(parent_location=course.location) + subsection1 = world.ItemFactory.create( parent_location=section.location, template='i4x://edx/templates/sequential/Empty', display_name='Subsection One',) - section2 = ItemFactory.create( + section2 = world.ItemFactory.create( parent_location=course.location, display_name='Section Two',) - subsection2 = ItemFactory.create( + subsection2 = world.ItemFactory.create( parent_location=section2.location, template='i4x://edx/templates/sequential/Empty', display_name='Subsection Alpha',) - subsection3 = ItemFactory.create( + subsection3 = world.ItemFactory.create( parent_location=section2.location, template='i4x://edx/templates/sequential/Empty', display_name='Subsection Beta',) diff --git a/common/djangoapps/student/tests/factories.py b/common/djangoapps/student/tests/factories.py index 8dd70a7a61..2acc235ce2 100644 --- a/common/djangoapps/student/tests/factories.py +++ b/common/djangoapps/student/tests/factories.py @@ -1,8 +1,8 @@ from student.models import (User, UserProfile, Registration, - CourseEnrollmentAllowed) + CourseEnrollmentAllowed, CourseEnrollment) from django.contrib.auth.models import Group from datetime import datetime -from factory import Factory +from factory import Factory, SubFactory from xmodule.modulestore import Location from xmodule.modulestore.django import modulestore from time import gmtime @@ -50,6 +50,13 @@ class UserFactory(Factory): date_joined = datetime(2011, 1, 1) +class CourseEnrollmentFactory(Factory): + FACTORY_FOR = CourseEnrollment + + user = SubFactory(UserFactory) + course_id = 'edX/toy/2012_Fall' + + class CourseEnrollmentAllowedFactory(Factory): FACTORY_FOR = CourseEnrollmentAllowed diff --git a/common/djangoapps/terrain/factories.py b/common/djangoapps/terrain/factories.py index 62b133217d..3a3381c74c 100644 --- a/common/djangoapps/terrain/factories.py +++ b/common/djangoapps/terrain/factories.py @@ -1,6 +1,6 @@ from student.tests.factories import (UserFactory, UserProfileFactory, RegistrationFactory, GroupFactory, - CourseEnrollmentAllowed, + CourseEnrollmentAllowed, CourseEnrollment, CourseFactory, ItemFactory) from lettuce import world @@ -37,6 +37,14 @@ class GroupFactory(GroupFactory): pass +@world.absorb +class CourseEnrollment(CourseEnrollment): + """ + Courses that the user is enrolled in + """ + pass + + @world.absorb class CourseEnrollmentAllowed(CourseEnrollmentAllowed): """ diff --git a/lms/djangoapps/courseware/features/common.py b/lms/djangoapps/courseware/features/common.py index 8fb2843656..bdbd952caa 100644 --- a/lms/djangoapps/courseware/features/common.py +++ b/lms/djangoapps/courseware/features/common.py @@ -5,7 +5,6 @@ from lettuce.django import django_url from django.conf import settings from django.contrib.auth.models import User from student.models import CourseEnrollment -from terrain.factories import CourseFactory, ItemFactory from xmodule.modulestore import Location from xmodule.modulestore.django import _MODULESTORES, modulestore from xmodule.templates import update_templates @@ -101,15 +100,15 @@ def create_course(step, course): # Create the course # We always use the same org and display name, # but vary the course identifier (e.g. 600x or 191x) - course = CourseFactory.create(org=TEST_COURSE_ORG, + course = world.CourseFactory.create(org=TEST_COURSE_ORG, number=course, display_name=TEST_COURSE_NAME) # Add a section to the course to contain problems - section = ItemFactory.create(parent_location=course.location, + section = world.ItemFactory.create(parent_location=course.location, display_name=TEST_SECTION_NAME) - problem_section = ItemFactory.create(parent_location=section.location, + problem_section = world.ItemFactory.create(parent_location=section.location, template='i4x://edx/templates/sequential/Empty', display_name=TEST_SECTION_NAME) @@ -131,7 +130,7 @@ def i_am_registered_for_the_course(step, course): @step(u'The course "([^"]*)" has extra tab "([^"]*)"$') def add_tab_to_course(step, course, extra_tab_name): - section_item = ItemFactory.create(parent_location=course_location(course), + section_item = world.ItemFactory.create(parent_location=course_location(course), template="i4x://edx/templates/static_tab/Empty", display_name=str(extra_tab_name)) diff --git a/lms/djangoapps/courseware/features/problems.py b/lms/djangoapps/courseware/features/problems.py index a6575c3d22..a2d37ff7d8 100644 --- a/lms/djangoapps/courseware/features/problems.py +++ b/lms/djangoapps/courseware/features/problems.py @@ -4,7 +4,6 @@ from selenium.webdriver.support.ui import Select import random import textwrap from common import i_am_registered_for_the_course, TEST_SECTION_NAME, section_location -from terrain.factories import ItemFactory from capa.tests.response_xml_factory import OptionResponseXMLFactory, \ ChoiceResponseXMLFactory, MultipleChoiceResponseXMLFactory, \ StringResponseXMLFactory, NumericalResponseXMLFactory, \ @@ -92,7 +91,7 @@ def add_problem_to_course(course, problem_type): # Create a problem item using our generated XML # We set rerandomize=always in the metadata so that the "Reset" button # will appear. - problem_item = ItemFactory.create(parent_location=section_location(course), + problem_item = world.ItemFactory.create(parent_location=section_location(course), template="i4x://edx/templates/problem/Blank_Common_Problem", display_name=str(problem_type), data=problem_xml, diff --git a/lms/djangoapps/django_comment_client/tests/test_utils.py b/lms/djangoapps/django_comment_client/tests/test_utils.py index cec006e630..fe580f1ebe 100644 --- a/lms/djangoapps/django_comment_client/tests/test_utils.py +++ b/lms/djangoapps/django_comment_client/tests/test_utils.py @@ -8,6 +8,7 @@ import factory from django.contrib.auth.models import User from student.models import UserProfile, CourseEnrollment from django_comment_client.models import Role, Permission +from student.tests.factories import UserFactory, CourseEnrollmentFactory import django_comment_client.models as models import django_comment_client.utils as utils @@ -15,21 +16,6 @@ import django_comment_client.utils as utils import xmodule.modulestore.django as django -class UserFactory(factory.Factory): - FACTORY_FOR = User - username = 'robot' - password = '123456' - email = 'robot@edx.org' - is_active = True - is_staff = False - - -class CourseEnrollmentFactory(factory.Factory): - FACTORY_FOR = CourseEnrollment - user = factory.SubFactory(UserFactory) - course_id = 'edX/toy/2012_Fall' - - class RoleFactory(factory.Factory): FACTORY_FOR = Role name = 'Student' From 16773aac638a3055df3cae4185455ec5df5b6201 Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Tue, 19 Mar 2013 17:09:57 -0400 Subject: [PATCH 061/135] More factory refactoring. --- common/djangoapps/terrain/factories.py | 4 ++-- lms/djangoapps/courseware/features/common.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/common/djangoapps/terrain/factories.py b/common/djangoapps/terrain/factories.py index 3a3381c74c..88aa94ef00 100644 --- a/common/djangoapps/terrain/factories.py +++ b/common/djangoapps/terrain/factories.py @@ -38,7 +38,7 @@ class GroupFactory(GroupFactory): @world.absorb -class CourseEnrollment(CourseEnrollment): +class CourseEnrollmentFactory(CourseEnrollment): """ Courses that the user is enrolled in """ @@ -46,7 +46,7 @@ class CourseEnrollment(CourseEnrollment): @world.absorb -class CourseEnrollmentAllowed(CourseEnrollmentAllowed): +class CourseEnrollmentAllowedFactory(CourseEnrollmentAllowed): """ Users allowed to enroll in the course outside of the usual window """ diff --git a/lms/djangoapps/courseware/features/common.py b/lms/djangoapps/courseware/features/common.py index bdbd952caa..eff3ce3743 100644 --- a/lms/djangoapps/courseware/features/common.py +++ b/lms/djangoapps/courseware/features/common.py @@ -1,8 +1,6 @@ from lettuce import world, step -from django.core.management import call_command from nose.tools import assert_equals, assert_in from lettuce.django import django_url -from django.conf import settings from django.contrib.auth.models import User from student.models import CourseEnrollment from xmodule.modulestore import Location @@ -123,6 +121,7 @@ def i_am_registered_for_the_course(step, course): u = User.objects.get(username='robot') # If the user is not already enrolled, enroll the user. + # TODO: change to factory CourseEnrollment.objects.get_or_create(user=u, course_id=course_id(course)) world.log_in('robot@edx.org', 'test') From 10c6e7615bbea19e0687e62e36b26d7515ea603c Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Wed, 20 Mar 2013 09:42:42 -0400 Subject: [PATCH 062/135] More polish for matlab input type --- common/lib/capa/capa/inputtypes.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index a62e696b20..0208f32503 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -657,8 +657,8 @@ class MatlabInput(CodeInput): self.queue_len = 0 self.queuename = 'matlab' # Flag indicating that the problem has been queued, 'msg' is length of - self.queue_msg = None - if 'queue_msg' in self.input_state: + self.queue_msg = '' + if 'queue_msg' in self.input_state and self.status in ['incomplete', 'unsubmitted']: self.queue_msg = self.input_state['queue_msg'] if 'queued' in self.input_state and self.input_state['queuestate'] is not None: self.status = 'queued' @@ -689,11 +689,10 @@ class MatlabInput(CodeInput): def _extra_context(self): ''' Set up additional context variables''' - extra_context = {'queue_len': self.queue_len} - if self.queue_msg is not None: - extra_context['queue_msg'] = self.queue_msg - else: - extra_context['queue_msg'] = '' + extra_context = { + 'queue_len': self.queue_len, + 'queue_msg': self.queue_msg + } return extra_context def _parse_data(self, queue_msg): @@ -747,7 +746,6 @@ class MatlabInput(CodeInput): (error, msg) = qinterface.send_to_queue(header=xheader, body = json.dumps(contents)) - return {'success': error == 0, 'message': msg} return {'success': False, 'message': 'Cannot connect to the queue'} From 8cf2cc6f78c34b8f16506e291c7993f71b3ccb32 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Wed, 20 Mar 2013 10:42:35 -0400 Subject: [PATCH 063/135] studio - sass cleanup: local merge with master and small comment/organization tweaks to variables and base-styles --- cms/static/sass/_variables.scss | 4 ++-- cms/static/sass/base-style.scss | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cms/static/sass/_variables.scss b/cms/static/sass/_variables.scss index 78b6f2b221..15654fe3c4 100644 --- a/cms/static/sass/_variables.scss +++ b/cms/static/sass/_variables.scss @@ -15,7 +15,6 @@ $fg-min-width: 900px; // type $sans-serif: 'Open Sans', $verdana; $body-line-height: golden-ratio(.875em, 1); -$error-red: rgb(253, 87, 87); // colors - new for re-org $black: rgb(0,0,0); @@ -97,4 +96,5 @@ $brightGreen: rgb(22, 202, 87); $disabledGreen: rgb(124, 206, 153); $darkGreen: rgb(52, 133, 76); $lightBluishGrey: rgb(197, 207, 223); -$lightBluishGrey2: rgb(213, 220, 228); \ No newline at end of file +$lightBluishGrey2: rgb(213, 220, 228); +$error-red: rgb(253, 87, 87); \ No newline at end of file diff --git a/cms/static/sass/base-style.scss b/cms/static/sass/base-style.scss index b092f0054b..c7ec38e756 100644 --- a/cms/static/sass/base-style.scss +++ b/cms/static/sass/base-style.scss @@ -46,5 +46,6 @@ @import 'assets/content-types'; +// xblock-related @import 'module/module-styles.scss'; @import 'descriptor/module-styles.scss'; From e45ccbf38929274d0be1a974925b2169c6f58225 Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Wed, 20 Mar 2013 11:12:54 -0400 Subject: [PATCH 064/135] Leave CourseEnrollment factor refactoring as a TODO. --- common/djangoapps/terrain/factories.py | 10 +--------- .../django_comment_client/tests/test_utils.py | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/common/djangoapps/terrain/factories.py b/common/djangoapps/terrain/factories.py index 88aa94ef00..1dedff133b 100644 --- a/common/djangoapps/terrain/factories.py +++ b/common/djangoapps/terrain/factories.py @@ -1,6 +1,6 @@ from student.tests.factories import (UserFactory, UserProfileFactory, RegistrationFactory, GroupFactory, - CourseEnrollmentAllowed, CourseEnrollment, + CourseEnrollmentAllowed, CourseFactory, ItemFactory) from lettuce import world @@ -37,14 +37,6 @@ class GroupFactory(GroupFactory): pass -@world.absorb -class CourseEnrollmentFactory(CourseEnrollment): - """ - Courses that the user is enrolled in - """ - pass - - @world.absorb class CourseEnrollmentAllowedFactory(CourseEnrollmentAllowed): """ diff --git a/lms/djangoapps/django_comment_client/tests/test_utils.py b/lms/djangoapps/django_comment_client/tests/test_utils.py index fe580f1ebe..cec006e630 100644 --- a/lms/djangoapps/django_comment_client/tests/test_utils.py +++ b/lms/djangoapps/django_comment_client/tests/test_utils.py @@ -8,7 +8,6 @@ import factory from django.contrib.auth.models import User from student.models import UserProfile, CourseEnrollment from django_comment_client.models import Role, Permission -from student.tests.factories import UserFactory, CourseEnrollmentFactory import django_comment_client.models as models import django_comment_client.utils as utils @@ -16,6 +15,21 @@ import django_comment_client.utils as utils import xmodule.modulestore.django as django +class UserFactory(factory.Factory): + FACTORY_FOR = User + username = 'robot' + password = '123456' + email = 'robot@edx.org' + is_active = True + is_staff = False + + +class CourseEnrollmentFactory(factory.Factory): + FACTORY_FOR = CourseEnrollment + user = factory.SubFactory(UserFactory) + course_id = 'edX/toy/2012_Fall' + + class RoleFactory(factory.Factory): FACTORY_FOR = Role name = 'Student' From 8f055ab0377726f4ec0b8729eeef4fd4a805136e Mon Sep 17 00:00:00 2001 From: Will Daly Date: Wed, 20 Mar 2013 11:14:42 -0400 Subject: [PATCH 065/135] Moved mock_xqueue_server to its own subpackage within lms/djangoapps/courseware/ Separated tests of the mock server into test_mock_xqueue_server.py --- .../courseware/features/xqueue_setup.py | 3 +- .../mock_xqueue_server.py | 70 +------------------ 2 files changed, 2 insertions(+), 71 deletions(-) rename lms/djangoapps/courseware/{features => mock_xqueue_server}/mock_xqueue_server.py (74%) diff --git a/lms/djangoapps/courseware/features/xqueue_setup.py b/lms/djangoapps/courseware/features/xqueue_setup.py index 1261f72e76..23706941a9 100644 --- a/lms/djangoapps/courseware/features/xqueue_setup.py +++ b/lms/djangoapps/courseware/features/xqueue_setup.py @@ -1,9 +1,8 @@ -from mock_xqueue_server import MockXQueueServer +from courseware.mock_xqueue_server.mock_xqueue_server import MockXQueueServer from lettuce import before, after, world from django.conf import settings import threading - @before.all def setup_mock_xqueue_server(): diff --git a/lms/djangoapps/courseware/features/mock_xqueue_server.py b/lms/djangoapps/courseware/mock_xqueue_server/mock_xqueue_server.py similarity index 74% rename from lms/djangoapps/courseware/features/mock_xqueue_server.py rename to lms/djangoapps/courseware/mock_xqueue_server/mock_xqueue_server.py index 37678ce7c2..7bc1b95662 100644 --- a/lms/djangoapps/courseware/features/mock_xqueue_server.py +++ b/lms/djangoapps/courseware/mock_xqueue_server/mock_xqueue_server.py @@ -2,7 +2,7 @@ from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler import json import urllib import urlparse -import time +import threading from logging import getLogger logger = getLogger(__name__) @@ -209,71 +209,3 @@ class MockXQueueServer(HTTPServer): # Save the response dictionary self._grade_response = grade_response_dict - - -# ---------------------------- -# Tests - -import mock -import threading -import unittest - - -class MockXQueueServerTest(unittest.TestCase): - - def setUp(self): - - # Create the server - server_port = 8034 - self.server_url = 'http://127.0.0.1:%d' % server_port - self.server = MockXQueueServer(server_port, - {'correct': True, 'score': 1, 'msg': ''}) - - # Start the server in a separate daemon thread - server_thread = threading.Thread(target=self.server.serve_forever) - server_thread.daemon = True - server_thread.start() - - def tearDown(self): - - # Stop the server, freeing up the port - self.server.shutdown() - - def test_grade_request(self): - - # Patch post_to_url() so we can intercept - # outgoing POST requests from the server - MockXQueueRequestHandler.post_to_url = mock.Mock() - - # Send a grade request - callback_url = 'http://127.0.0.1:8000/test_callback' - - grade_header = json.dumps({'lms_callback_url': callback_url, - 'lms_key': 'test_queuekey', - 'queue_name': 'test_queue'}) - - grade_body = json.dumps({'student_info': 'test', - 'grader_payload': 'test', - 'student_response': 'test'}) - - grade_request = {'xqueue_header': grade_header, - 'xqueue_body': grade_body} - - response_handle = urllib.urlopen(self.server_url + '/xqueue/submit', - urllib.urlencode(grade_request)) - - response_dict = json.loads(response_handle.read()) - - # Expect that the response is success - self.assertEqual(response_dict['return_code'], 0) - - # Wait a bit before checking that the server posted back - time.sleep(3) - - # Expect that the server tries to post back the grading info - xqueue_body = json.dumps({'correct': True, 'score': 1, - 'msg': '
    '}) - expected_callback_dict = {'xqueue_header': grade_header, - 'xqueue_body': xqueue_body} - MockXQueueRequestHandler.post_to_url.assert_called_with(callback_url, - expected_callback_dict) From 3a774264172bd5b616b865c0f7191fa2e7927d3a Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 20 Mar 2013 11:16:30 -0400 Subject: [PATCH 066/135] Cale said, 'we should delete prod-requirements.txt' --- prod-requirements.txt | 53 ------------------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 prod-requirements.txt diff --git a/prod-requirements.txt b/prod-requirements.txt deleted file mode 100644 index 20c3e9b068..0000000000 --- a/prod-requirements.txt +++ /dev/null @@ -1,53 +0,0 @@ -Django==1.3.1 -flup==1.0.3.dev-20110405 -lxml==2.3.4 -Mako==0.7.0 -Markdown==2.1.1 -markdown2==1.4.2 -python-memcached==1.48 -numpy==1.6.1 -Pygments==1.5 -boto==2.3.0 -django-storages==1.1.4 -django-masquerade==0.1.5 -fs==0.4.0 -django-jasmine==0.3.2 -path.py==2.2.2 -requests==0.12.1 -BeautifulSoup==3.2.1 -BeautifulSoup4==4.1.1 -newrelic==1.3.0.289 -ipython==0.12.1 -django-pipeline==1.2.12 -django-staticfiles==1.2.1 -glob2==0.3 -sympy==0.7.1 -pymongo==2.2.1 -rednose==0.3.3 -mock==0.8.0 -GitPython==0.3.2.RC1 -PyYAML==3.10 -feedparser==5.1.2 -MySQL-python==1.2.3 -matplotlib==1.1.0 -scipy==0.10.1 -akismet==0.2.0 -Coffin==0.3.6 -django-celery==2.2.7 -django-countries==1.0.5 -django-followit==0.0.3 -django-keyedcache==1.4-6 -django-kombu==0.9.2 -django-mako==0.1.5pre -django-recaptcha-works==0.3.4 -django-robots==0.8.1 -django-ses==0.4.1 -django-threaded-multihost==1.4-1 -html5lib==0.90 -Jinja2==2.6 -oauth2==1.5.211 -pystache==0.3.1 -python-openid==2.2.5 -South==0.7.5 -Unidecode==0.04.9 -dogstatsd-python==0.2.1 From 2867476115f54e7e008339e74949b480d1282ca3 Mon Sep 17 00:00:00 2001 From: Will Daly Date: Wed, 20 Mar 2013 11:18:41 -0400 Subject: [PATCH 067/135] Added __init__.py and test_mock_xqueue_server.py, which should have been included in the last commit --- .../courseware/mock_xqueue_server/__init__.py | 0 .../test_mock_xqueue_server.py | 78 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 lms/djangoapps/courseware/mock_xqueue_server/__init__.py create mode 100644 lms/djangoapps/courseware/mock_xqueue_server/test_mock_xqueue_server.py diff --git a/lms/djangoapps/courseware/mock_xqueue_server/__init__.py b/lms/djangoapps/courseware/mock_xqueue_server/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/courseware/mock_xqueue_server/test_mock_xqueue_server.py b/lms/djangoapps/courseware/mock_xqueue_server/test_mock_xqueue_server.py new file mode 100644 index 0000000000..4e4d95f23b --- /dev/null +++ b/lms/djangoapps/courseware/mock_xqueue_server/test_mock_xqueue_server.py @@ -0,0 +1,78 @@ +import mock +import unittest +import threading +import json +import urllib +import urlparse +import time +from mock_xqueue_server import MockXQueueServer, MockXQueueRequestHandler + + +class MockXQueueServerTest(unittest.TestCase): + ''' + A mock version of the XQueue server that listens on a local + port and responds with pre-defined grade messages. + + Used for lettuce BDD tests in lms/courseware/features/problems.feature + and lms/courseware/features/problems.py + + This is temporary and will be removed when XQueue is + rewritten using celery. + ''' + + def setUp(self): + + # Create the server + server_port = 8034 + self.server_url = 'http://127.0.0.1:%d' % server_port + self.server = MockXQueueServer(server_port, + {'correct': True, 'score': 1, 'msg': ''}) + + # Start the server in a separate daemon thread + server_thread = threading.Thread(target=self.server.serve_forever) + server_thread.daemon = True + server_thread.start() + + def tearDown(self): + + # Stop the server, freeing up the port + self.server.shutdown() + + def test_grade_request(self): + + # Patch post_to_url() so we can intercept + # outgoing POST requests from the server + MockXQueueRequestHandler.post_to_url = mock.Mock() + + # Send a grade request + callback_url = 'http://127.0.0.1:8000/test_callback' + + grade_header = json.dumps({'lms_callback_url': callback_url, + 'lms_key': 'test_queuekey', + 'queue_name': 'test_queue'}) + + grade_body = json.dumps({'student_info': 'test', + 'grader_payload': 'test', + 'student_response': 'test'}) + + grade_request = {'xqueue_header': grade_header, + 'xqueue_body': grade_body} + + response_handle = urllib.urlopen(self.server_url + '/xqueue/submit', + urllib.urlencode(grade_request)) + + response_dict = json.loads(response_handle.read()) + + # Expect that the response is success + self.assertEqual(response_dict['return_code'], 0) + + # Wait a bit before checking that the server posted back + time.sleep(3) + + # Expect that the server tries to post back the grading info + xqueue_body = json.dumps({'correct': True, 'score': 1, + 'msg': '
    '}) + expected_callback_dict = {'xqueue_header': grade_header, + 'xqueue_body': xqueue_body} + MockXQueueRequestHandler.post_to_url.assert_called_with(callback_url, + expected_callback_dict) From f181a454e4c0a1d101cabeda4e25e0a8f0bff9ac Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Wed, 20 Mar 2013 11:20:16 -0400 Subject: [PATCH 068/135] studio - added in basic JS confirm UI when deleting course updates --- cms/static/js/views/course_info_edit.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cms/static/js/views/course_info_edit.js b/cms/static/js/views/course_info_edit.js index 8382fb15eb..ce959fd443 100644 --- a/cms/static/js/views/course_info_edit.js +++ b/cms/static/js/views/course_info_edit.js @@ -142,8 +142,11 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({ onDelete: function(event) { event.preventDefault(); - // TODO ask for confirmation - // remove the dom element and delete the model + + if (!confirm('Are you sure you want to delete this update? This action cannot be undone.')) { + return; + } + var targetModel = this.eventModel(event); this.modelDom(event).remove(); var cacheThis = this; From 5411fc765e6a45ad11f4f730ae51eb8a4a569d8b Mon Sep 17 00:00:00 2001 From: Will Daly Date: Wed, 20 Mar 2013 11:27:28 -0400 Subject: [PATCH 069/135] Refactored lettuce test of capa problems to use world.css_click() helper instead of directly calling splinter. --- common/djangoapps/terrain/steps.py | 13 +++++++++++++ lms/djangoapps/courseware/features/problems.py | 14 ++------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/common/djangoapps/terrain/steps.py b/common/djangoapps/terrain/steps.py index 52eeb23c4a..371496f823 100644 --- a/common/djangoapps/terrain/steps.py +++ b/common/djangoapps/terrain/steps.py @@ -9,6 +9,7 @@ from bs4 import BeautifulSoup import time import re import os.path +from selenium.common.exceptions import WebDriverException from logging import getLogger logger = getLogger(__name__) @@ -214,3 +215,15 @@ def save_the_course_content(path='/tmp'): f = open('%s/%s' % (path, filename), 'w') f.write(output) f.close + +@world.absorb +def css_click(css_selector): + try: + world.browser.find_by_css(css_selector).click() + + except WebDriverException: + # Occassionally, MathJax or other JavaScript can cover up + # an element temporarily. + # If this happens, wait a second, then try again + time.sleep(1) + world.browser.find_by_css(css_selector).click() diff --git a/lms/djangoapps/courseware/features/problems.py b/lms/djangoapps/courseware/features/problems.py index 314ffa0dbe..715e2689fb 100644 --- a/lms/djangoapps/courseware/features/problems.py +++ b/lms/djangoapps/courseware/features/problems.py @@ -1,7 +1,5 @@ from lettuce import world, step from lettuce.django import django_url -from selenium.webdriver.support.ui import Select -from selenium.common.exceptions import WebDriverException import random import textwrap import time @@ -208,20 +206,12 @@ def answer_problem(step, problem_type, correctness): @step(u'I check a problem') def check_problem(step): - try: - world.browser.find_by_css("input.check").click() - - except WebDriverException: - # Occassionally, MathJax or other JavaScript can cover up - # the 'Check' input temporarily. - # If this happens, wait a second, then try again - time.sleep(1) - world.browser.find_by_css("input.check").click() + world.css_click("input.check") @step(u'I reset the problem') def reset_problem(step): - world.browser.find_by_css('input.reset').click() + world.css_click('input.reset') @step(u'My "([^"]*)" answer is marked "([^"]*)"') From 6048bc28320225db3b2f09cf9ff4742ce36519d1 Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Wed, 20 Mar 2013 11:31:12 -0400 Subject: [PATCH 070/135] Pep8 fixes for factory refactor --- common/djangoapps/student/tests/factories.py | 12 +++--- common/djangoapps/terrain/factories.py | 22 +++++----- lms/djangoapps/courseware/features/common.py | 16 +++---- lms/djangoapps/courseware/features/courses.py | 9 ++-- .../courseware/features/problems.py | 42 +++++++++---------- .../courseware/features/smart-accordion.py | 6 +-- 6 files changed, 53 insertions(+), 54 deletions(-) diff --git a/common/djangoapps/student/tests/factories.py b/common/djangoapps/student/tests/factories.py index 2acc235ce2..1f2378a8c9 100644 --- a/common/djangoapps/student/tests/factories.py +++ b/common/djangoapps/student/tests/factories.py @@ -1,4 +1,4 @@ -from student.models import (User, UserProfile, Registration, +from student.models import (User, UserProfile, Registration, CourseEnrollmentAllowed, CourseEnrollment) from django.contrib.auth.models import Group from datetime import datetime @@ -101,10 +101,10 @@ class XModuleCourseFactory(Factory): new_course.lms.start = gmtime() new_course.tabs = [{"type": "courseware"}, - {"type": "course_info", "name": "Course Info"}, - {"type": "discussion", "name": "Discussion"}, - {"type": "wiki", "name": "Wiki"}, - {"type": "progress", "name": "Progress"}] + {"type": "course_info", "name": "Course Info"}, + {"type": "discussion", "name": "Discussion"}, + {"type": "wiki", "name": "Wiki"}, + {"type": "progress", "name": "Progress"}] # Update the data in the mongo datastore store.update_metadata(new_course.location.url(), own_metadata(new_course)) @@ -170,7 +170,7 @@ class XModuleItemFactory(Factory): # If a display name is set, use that dest_name = display_name.replace(" ", "_") if display_name is not None else uuid4().hex dest_location = parent_location._replace(category=template.category, - name=dest_name) + name=dest_name) new_item = store.clone_item(template, dest_location) diff --git a/common/djangoapps/terrain/factories.py b/common/djangoapps/terrain/factories.py index 1dedff133b..88e29a18ef 100644 --- a/common/djangoapps/terrain/factories.py +++ b/common/djangoapps/terrain/factories.py @@ -1,7 +1,7 @@ from student.tests.factories import (UserFactory, UserProfileFactory, - RegistrationFactory, GroupFactory, - CourseEnrollmentAllowed, - CourseFactory, ItemFactory) + RegistrationFactory, GroupFactory, + CourseEnrollmentAllowed, + CourseFactory, ItemFactory) from lettuce import world @@ -9,7 +9,7 @@ from lettuce import world class UserFactory(UserFactory): """ User account for lms / cms - """ + """ pass @@ -17,7 +17,7 @@ class UserFactory(UserFactory): class UserProfileFactory(UserProfileFactory): """ Demographics etc for the User - """ + """ pass @@ -25,7 +25,7 @@ class UserProfileFactory(UserProfileFactory): class RegistrationFactory(RegistrationFactory): """ Activation key for registering the user account - """ + """ pass @@ -33,7 +33,7 @@ class RegistrationFactory(RegistrationFactory): class GroupFactory(GroupFactory): """ Groups for user permissions for courses - """ + """ pass @@ -41,7 +41,7 @@ class GroupFactory(GroupFactory): class CourseEnrollmentAllowedFactory(CourseEnrollmentAllowed): """ Users allowed to enroll in the course outside of the usual window - """ + """ pass @@ -49,13 +49,13 @@ class CourseEnrollmentAllowedFactory(CourseEnrollmentAllowed): class CourseFactory(CourseFactory): """ Courseware courses - """ + """ pass - + @world.absorb class ItemFactory(ItemFactory): """ Everything included inside a course - """ + """ pass diff --git a/lms/djangoapps/courseware/features/common.py b/lms/djangoapps/courseware/features/common.py index eff3ce3743..d03a59f776 100644 --- a/lms/djangoapps/courseware/features/common.py +++ b/lms/djangoapps/courseware/features/common.py @@ -99,16 +99,16 @@ def create_course(step, course): # We always use the same org and display name, # but vary the course identifier (e.g. 600x or 191x) course = world.CourseFactory.create(org=TEST_COURSE_ORG, - number=course, - display_name=TEST_COURSE_NAME) + number=course, + display_name=TEST_COURSE_NAME) # Add a section to the course to contain problems section = world.ItemFactory.create(parent_location=course.location, - display_name=TEST_SECTION_NAME) + display_name=TEST_SECTION_NAME) problem_section = world.ItemFactory.create(parent_location=section.location, - template='i4x://edx/templates/sequential/Empty', - display_name=TEST_SECTION_NAME) + template='i4x://edx/templates/sequential/Empty', + display_name=TEST_SECTION_NAME) @step(u'I am registered for the course "([^"]*)"$') @@ -130,8 +130,8 @@ def i_am_registered_for_the_course(step, course): @step(u'The course "([^"]*)" has extra tab "([^"]*)"$') def add_tab_to_course(step, course, extra_tab_name): section_item = world.ItemFactory.create(parent_location=course_location(course), - template="i4x://edx/templates/static_tab/Empty", - display_name=str(extra_tab_name)) + template="i4x://edx/templates/static_tab/Empty", + display_name=str(extra_tab_name)) @step(u'I am an edX user$') @@ -159,7 +159,7 @@ def flush_xmodule_store(): def course_id(course_num): return "%s/%s/%s" % (TEST_COURSE_ORG, course_num, - TEST_COURSE_NAME.replace(" ", "_")) + TEST_COURSE_NAME.replace(" ", "_")) def course_location(course_num): diff --git a/lms/djangoapps/courseware/features/courses.py b/lms/djangoapps/courseware/features/courses.py index 4fbbfd24f2..c99fb58b85 100644 --- a/lms/djangoapps/courseware/features/courses.py +++ b/lms/djangoapps/courseware/features/courses.py @@ -83,13 +83,13 @@ def get_courseware_with_tabs(course_id): course = get_course_by_id(course_id) chapters = [chapter for chapter in course.get_children() if not chapter.lms.hide_from_toc] courseware = [{'chapter_name': c.display_name_with_default, - 'sections': [{'section_name': s.display_name_with_default, + 'sections': [{'section_name': s.display_name_with_default, 'clickable_tab_count': len(s.get_children()) if (type(s) == seq_module.SequenceDescriptor) else 0, 'tabs': [{'children_count': len(t.get_children()) if (type(t) == vertical_module.VerticalDescriptor) else 0, - 'class': t.__class__.__name__} - for t in s.get_children()]} + 'class': t.__class__.__name__} + for t in s.get_children()]} for s in c.get_children() if not s.lms.hide_from_toc]} - for c in chapters] + for c in chapters] return courseware @@ -168,7 +168,6 @@ def process_section(element, num_tabs=0): assert False, "Class for element not recognized!!" - def process_problem(element, problem_id): ''' Process problem attempts to diff --git a/lms/djangoapps/courseware/features/problems.py b/lms/djangoapps/courseware/features/problems.py index a2d37ff7d8..9f3a483c57 100644 --- a/lms/djangoapps/courseware/features/problems.py +++ b/lms/djangoapps/courseware/features/problems.py @@ -5,9 +5,9 @@ import random import textwrap from common import i_am_registered_for_the_course, TEST_SECTION_NAME, section_location from capa.tests.response_xml_factory import OptionResponseXMLFactory, \ - ChoiceResponseXMLFactory, MultipleChoiceResponseXMLFactory, \ - StringResponseXMLFactory, NumericalResponseXMLFactory, \ - FormulaResponseXMLFactory, CustomResponseXMLFactory + ChoiceResponseXMLFactory, MultipleChoiceResponseXMLFactory, \ + StringResponseXMLFactory, NumericalResponseXMLFactory, \ + FormulaResponseXMLFactory, CustomResponseXMLFactory # Factories from capa.tests.response_xml_factory that we will use # to generate the problem XML, with the keyword args used to configure @@ -77,7 +77,7 @@ PROBLEM_FACTORY_DICT = { a2=0 return (a1+a2)==int(expect) """)}}, - } +} def add_problem_to_course(course, problem_type): @@ -92,10 +92,10 @@ def add_problem_to_course(course, problem_type): # We set rerandomize=always in the metadata so that the "Reset" button # will appear. problem_item = world.ItemFactory.create(parent_location=section_location(course), - template="i4x://edx/templates/problem/Blank_Common_Problem", - display_name=str(problem_type), - data=problem_xml, - metadata={'rerandomize': 'always'}) + template="i4x://edx/templates/problem/Blank_Common_Problem", + display_name=str(problem_type), + data=problem_xml, + metadata={'rerandomize': 'always'}) @step(u'I am viewing a "([^"]*)" problem') @@ -201,21 +201,21 @@ def assert_answer_mark(step, problem_type, correctness): # depending on whether the user selects an incorrect # item or submits without selecting any item) correct_selectors = {'drop down': ['span.correct'], - 'multiple choice': ['label.choicegroup_correct'], - 'checkbox': ['span.correct'], - 'string': ['div.correct'], - 'numerical': ['div.correct'], - 'formula': ['div.correct'], - 'script': ['div.correct'], } + 'multiple choice': ['label.choicegroup_correct'], + 'checkbox': ['span.correct'], + 'string': ['div.correct'], + 'numerical': ['div.correct'], + 'formula': ['div.correct'], + 'script': ['div.correct'], } incorrect_selectors = {'drop down': ['span.incorrect'], 'multiple choice': ['label.choicegroup_incorrect', - 'span.incorrect'], - 'checkbox': ['span.incorrect'], - 'string': ['div.incorrect'], - 'numerical': ['div.incorrect'], - 'formula': ['div.incorrect'], - 'script': ['div.incorrect']} + 'span.incorrect'], + 'checkbox': ['span.incorrect'], + 'string': ['div.incorrect'], + 'numerical': ['div.incorrect'], + 'formula': ['div.incorrect'], + 'script': ['div.incorrect']} assert(correctness in ['correct', 'incorrect', 'unanswered']) assert(problem_type in correct_selectors and problem_type in incorrect_selectors) @@ -257,7 +257,7 @@ def inputfield(problem_type, choice=None, input_num=1): of checkboxes. """ sel = ("input#input_i4x-edx-model_course-problem-%s_2_%s" % - (problem_type.replace(" ", "_"), str(input_num))) + (problem_type.replace(" ", "_"), str(input_num))) if choice is not None: base = "_choice_" if problem_type == "multiple choice" else "_" diff --git a/lms/djangoapps/courseware/features/smart-accordion.py b/lms/djangoapps/courseware/features/smart-accordion.py index 7c4770d632..a7eb782722 100644 --- a/lms/djangoapps/courseware/features/smart-accordion.py +++ b/lms/djangoapps/courseware/features/smart-accordion.py @@ -81,7 +81,7 @@ def browse_course(course_id): num_rendered_sections = len(rendered_sections) msg = ('%d sections expected, %d sections found on page, %s - %d - %s' % - (num_sections, num_rendered_sections, course_id, chapter_it, chapters[chapter_it]['chapter_name'])) + (num_sections, num_rendered_sections, course_id, chapter_it, chapters[chapter_it]['chapter_name'])) #logger.debug(msg) assert num_sections == num_rendered_sections, msg @@ -112,7 +112,7 @@ def browse_course(course_id): num_rendered_tabs = 0 msg = ('%d tabs expected, %d tabs found, %s - %d - %s' % - (num_tabs, num_rendered_tabs, course_id, section_it, sections[section_it]['section_name'])) + (num_tabs, num_rendered_tabs, course_id, section_it, sections[section_it]['section_name'])) #logger.debug(msg) # Save the HTML to a file for later comparison @@ -137,7 +137,7 @@ def browse_course(course_id): rendered_items = world.browser.find_by_css('div#seq_content > section > ol > li > section') num_rendered_items = len(rendered_items) msg = ('%d items expected, %d items found, %s - %d - %s - tab %d' % - (tab_children, num_rendered_items, course_id, section_it, sections[section_it]['section_name'], tab_it)) + (tab_children, num_rendered_items, course_id, section_it, sections[section_it]['section_name'], tab_it)) #logger.debug(msg) assert tab_children == num_rendered_items, msg From 192b99133461ec107a0af4240d54d24b45872746 Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Wed, 20 Mar 2013 11:52:15 -0400 Subject: [PATCH 071/135] Pylint for lettuce factory refactor. --- common/djangoapps/terrain/browser.py | 14 +++++++++---- common/djangoapps/terrain/factories.py | 27 +++++++++++++++----------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/common/djangoapps/terrain/browser.py b/common/djangoapps/terrain/browser.py index 6394959532..c8cc0c9e4b 100644 --- a/common/djangoapps/terrain/browser.py +++ b/common/djangoapps/terrain/browser.py @@ -1,7 +1,6 @@ from lettuce import before, after, world from splinter.browser import Browser from logging import getLogger -import time # Let the LMS and CMS do their one-time setup # For example, setting up mongo caches @@ -16,6 +15,9 @@ from django.core.management import call_command @before.harvest def initial_setup(server): + ''' + Launch the browser once before executing the tests + ''' # Launch the browser app (choose one of these below) world.browser = Browser('chrome') # world.browser = Browser('phantomjs') @@ -24,14 +26,18 @@ def initial_setup(server): @before.each_scenario def reset_data(scenario): - # Clean out the django test database defined in the - # envs/acceptance.py file: mitx_all/db/test_mitx.db + ''' + Clean out the django test database defined in the + envs/acceptance.py file: mitx_all/db/test_mitx.db + ''' logger.debug("Flushing the test database...") call_command('flush', interactive=False) @after.all def teardown_browser(total): - # Quit firefox + ''' + Quit the browser after executing the tests + ''' world.browser.quit() pass diff --git a/common/djangoapps/terrain/factories.py b/common/djangoapps/terrain/factories.py index 88e29a18ef..d7a1de6780 100644 --- a/common/djangoapps/terrain/factories.py +++ b/common/djangoapps/terrain/factories.py @@ -1,12 +1,17 @@ -from student.tests.factories import (UserFactory, UserProfileFactory, - RegistrationFactory, GroupFactory, - CourseEnrollmentAllowed, - CourseFactory, ItemFactory) +''' +Factories are defined in other modules and absorbed here into the +lettuce world so that they can be used by both unit tests +and integration / BDD tests. + +TODO: move the course and item factories out of student and into +xmodule/modulestore +''' +import student.tests.factories as sf from lettuce import world @world.absorb -class UserFactory(UserFactory): +class UserFactory(sf.UserFactory): """ User account for lms / cms """ @@ -14,7 +19,7 @@ class UserFactory(UserFactory): @world.absorb -class UserProfileFactory(UserProfileFactory): +class UserProfileFactory(sf.UserProfileFactory): """ Demographics etc for the User """ @@ -22,7 +27,7 @@ class UserProfileFactory(UserProfileFactory): @world.absorb -class RegistrationFactory(RegistrationFactory): +class RegistrationFactory(sf.RegistrationFactory): """ Activation key for registering the user account """ @@ -30,7 +35,7 @@ class RegistrationFactory(RegistrationFactory): @world.absorb -class GroupFactory(GroupFactory): +class GroupFactory(sf.GroupFactory): """ Groups for user permissions for courses """ @@ -38,7 +43,7 @@ class GroupFactory(GroupFactory): @world.absorb -class CourseEnrollmentAllowedFactory(CourseEnrollmentAllowed): +class CourseEnrollmentAllowedFactory(sf.CourseEnrollmentAllowed): """ Users allowed to enroll in the course outside of the usual window """ @@ -46,7 +51,7 @@ class CourseEnrollmentAllowedFactory(CourseEnrollmentAllowed): @world.absorb -class CourseFactory(CourseFactory): +class CourseFactory(sf.CourseFactory): """ Courseware courses """ @@ -54,7 +59,7 @@ class CourseFactory(CourseFactory): @world.absorb -class ItemFactory(ItemFactory): +class ItemFactory(sf.ItemFactory): """ Everything included inside a course """ From 5eba299dca0d3d3904d3b9e8c6295acc59a656cd Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Wed, 20 Mar 2013 12:10:18 -0400 Subject: [PATCH 072/135] Move course and item factories to xmodule.modulestore.tests --- common/djangoapps/student/tests/factories.py | 146 ------------------ common/djangoapps/terrain/factories.py | 8 +- .../xmodule/modulestore/tests/factories.py | 41 ++++- 3 files changed, 36 insertions(+), 159 deletions(-) diff --git a/common/djangoapps/student/tests/factories.py b/common/djangoapps/student/tests/factories.py index 1f2378a8c9..f74188725a 100644 --- a/common/djangoapps/student/tests/factories.py +++ b/common/djangoapps/student/tests/factories.py @@ -3,12 +3,7 @@ from student.models import (User, UserProfile, Registration, from django.contrib.auth.models import Group from datetime import datetime from factory import Factory, SubFactory -from xmodule.modulestore import Location -from xmodule.modulestore.django import modulestore -from time import gmtime from uuid import uuid4 -from xmodule.timeparse import stringify_time -from xmodule.modulestore.inheritance import own_metadata class GroupFactory(Factory): @@ -62,144 +57,3 @@ class CourseEnrollmentAllowedFactory(Factory): email = 'test@edx.org' course_id = 'edX/test/2012_Fall' - - -def XMODULE_COURSE_CREATION(class_to_create, **kwargs): - return XModuleCourseFactory._create(class_to_create, **kwargs) - - -def XMODULE_ITEM_CREATION(class_to_create, **kwargs): - return XModuleItemFactory._create(class_to_create, **kwargs) - - -class XModuleCourseFactory(Factory): - """ - Factory for XModule courses. - """ - - ABSTRACT_FACTORY = True - _creation_function = (XMODULE_COURSE_CREATION,) - - @classmethod - def _create(cls, target_class, *args, **kwargs): - - template = Location('i4x', 'edx', 'templates', 'course', 'Empty') - org = kwargs.get('org') - number = kwargs.get('number') - display_name = kwargs.get('display_name') - location = Location('i4x', org, number, - 'course', Location.clean(display_name)) - - store = modulestore('direct') - - # Write the data to the mongo datastore - new_course = store.clone_item(template, location) - - # This metadata code was copied from cms/djangoapps/contentstore/views.py - if display_name is not None: - new_course.display_name = display_name - - new_course.lms.start = gmtime() - new_course.tabs = [{"type": "courseware"}, - {"type": "course_info", "name": "Course Info"}, - {"type": "discussion", "name": "Discussion"}, - {"type": "wiki", "name": "Wiki"}, - {"type": "progress", "name": "Progress"}] - - # Update the data in the mongo datastore - store.update_metadata(new_course.location.url(), own_metadata(new_course)) - - return new_course - - -class Course: - pass - - -class CourseFactory(XModuleCourseFactory): - FACTORY_FOR = Course - - template = 'i4x://edx/templates/course/Empty' - org = 'MITx' - number = '999' - display_name = 'Robot Super Course' - - -class XModuleItemFactory(Factory): - """ - Factory for XModule items. - """ - - ABSTRACT_FACTORY = True - _creation_function = (XMODULE_ITEM_CREATION,) - - @classmethod - def _create(cls, target_class, *args, **kwargs): - """ - Uses *kwargs*: - - *parent_location* (required): the location of the parent module - (e.g. the parent course or section) - - *template* (required): the template to create the item from - (e.g. i4x://templates/section/Empty) - - *data* (optional): the data for the item - (e.g. XML problem definition for a problem item) - - *display_name* (optional): the display name of the item - - *metadata* (optional): dictionary of metadata attributes - - *target_class* is ignored - """ - - DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] - - parent_location = Location(kwargs.get('parent_location')) - template = Location(kwargs.get('template')) - data = kwargs.get('data') - display_name = kwargs.get('display_name') - metadata = kwargs.get('metadata', {}) - - store = modulestore('direct') - - # This code was based off that in cms/djangoapps/contentstore/views.py - parent = store.get_item(parent_location) - - # If a display name is set, use that - dest_name = display_name.replace(" ", "_") if display_name is not None else uuid4().hex - dest_location = parent_location._replace(category=template.category, - name=dest_name) - - new_item = store.clone_item(template, dest_location) - - # replace the display name with an optional parameter passed in from the caller - if display_name is not None: - new_item.display_name = display_name - - # Add additional metadata or override current metadata - item_metadata = own_metadata(new_item) - item_metadata.update(metadata) - store.update_metadata(new_item.location.url(), item_metadata) - - # replace the data with the optional *data* parameter - if data is not None: - store.update_item(new_item.location, data) - - if new_item.location.category not in DETACHED_CATEGORIES: - store.update_children(parent_location, parent.children + [new_item.location.url()]) - - return new_item - - -class Item: - pass - - -class ItemFactory(XModuleItemFactory): - FACTORY_FOR = Item - - parent_location = 'i4x://MITx/999/course/Robot_Super_Course' - template = 'i4x://edx/templates/chapter/Empty' - display_name = 'Section One' diff --git a/common/djangoapps/terrain/factories.py b/common/djangoapps/terrain/factories.py index d7a1de6780..768c51b25e 100644 --- a/common/djangoapps/terrain/factories.py +++ b/common/djangoapps/terrain/factories.py @@ -2,11 +2,9 @@ Factories are defined in other modules and absorbed here into the lettuce world so that they can be used by both unit tests and integration / BDD tests. - -TODO: move the course and item factories out of student and into -xmodule/modulestore ''' import student.tests.factories as sf +import xmodule.modulestore.tests.factories as xf from lettuce import world @@ -51,7 +49,7 @@ class CourseEnrollmentAllowedFactory(sf.CourseEnrollmentAllowed): @world.absorb -class CourseFactory(sf.CourseFactory): +class CourseFactory(xf.CourseFactory): """ Courseware courses """ @@ -59,7 +57,7 @@ class CourseFactory(sf.CourseFactory): @world.absorb -class ItemFactory(sf.ItemFactory): +class ItemFactory(xf.ItemFactory): """ Everything included inside a course """ diff --git a/common/lib/xmodule/xmodule/modulestore/tests/factories.py b/common/lib/xmodule/xmodule/modulestore/tests/factories.py index b842ffe9dd..1a82e1b708 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/factories.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/factories.py @@ -25,8 +25,7 @@ class XModuleCourseFactory(Factory): @classmethod def _create(cls, target_class, *args, **kwargs): - # This logic was taken from the create_new_course method in - # cms/djangoapps/contentstore/views.py + template = Location('i4x', 'edx', 'templates', 'course', 'Empty') org = kwargs.get('org') number = kwargs.get('number') @@ -43,8 +42,7 @@ class XModuleCourseFactory(Factory): if display_name is not None: new_course.display_name = display_name - new_course.start = gmtime() - + new_course.lms.start = gmtime() new_course.tabs = [{"type": "courseware"}, {"type": "course_info", "name": "Course Info"}, {"type": "discussion", "name": "Discussion"}, @@ -81,21 +79,41 @@ class XModuleItemFactory(Factory): @classmethod def _create(cls, target_class, *args, **kwargs): """ - kwargs must include parent_location, template. Can contain display_name - target_class is ignored + Uses *kwargs*: + + *parent_location* (required): the location of the parent module + (e.g. the parent course or section) + + *template* (required): the template to create the item from + (e.g. i4x://templates/section/Empty) + + *data* (optional): the data for the item + (e.g. XML problem definition for a problem item) + + *display_name* (optional): the display name of the item + + *metadata* (optional): dictionary of metadata attributes + + *target_class* is ignored """ DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] parent_location = Location(kwargs.get('parent_location')) template = Location(kwargs.get('template')) + data = kwargs.get('data') display_name = kwargs.get('display_name') + metadata = kwargs.get('metadata', {}) store = modulestore('direct') # This code was based off that in cms/djangoapps/contentstore/views.py parent = store.get_item(parent_location) - dest_location = parent_location._replace(category=template.category, name=uuid4().hex) + + # If a display name is set, use that + dest_name = display_name.replace(" ", "_") if display_name is not None else uuid4().hex + dest_location = parent_location._replace(category=template.category, + name=dest_name) new_item = store.clone_item(template, dest_location) @@ -103,7 +121,14 @@ class XModuleItemFactory(Factory): if display_name is not None: new_item.display_name = display_name - store.update_metadata(new_item.location.url(), own_metadata(new_item)) + # Add additional metadata or override current metadata + item_metadata = own_metadata(new_item) + item_metadata.update(metadata) + store.update_metadata(new_item.location.url(), item_metadata) + + # replace the data with the optional *data* parameter + if data is not None: + store.update_item(new_item.location, data) if new_item.location.category not in DETACHED_CATEGORIES: store.update_children(parent_location, parent.children + [new_item.location.url()]) From 36443163f6f291f34d1715ccb20e660346a25ca9 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 20 Mar 2013 12:59:34 -0400 Subject: [PATCH 073/135] Force instantiation of the user before querying Without accessing .pk, it's possible for the user object to still be a SimpleLazyObject, which breaks Django's query engine http://stackoverflow.com/questions/11875737/django-filtering-drafts-by-user-causes-error --- lms/djangoapps/courseware/model_data.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lms/djangoapps/courseware/model_data.py b/lms/djangoapps/courseware/model_data.py index 35deda5d6b..7b0c7cd74e 100644 --- a/lms/djangoapps/courseware/model_data.py +++ b/lms/djangoapps/courseware/model_data.py @@ -121,7 +121,7 @@ class ModelDataCache(object): 'module_state_key__in', (descriptor.location.url() for descriptor in self.descriptors), course_id=self.course_id, - student=self.user, + student=self.user.pk, ) elif scope == Scope.content: return self._chunked_query( @@ -145,13 +145,13 @@ class ModelDataCache(object): XModuleStudentPrefsField, 'module_type__in', set(descriptor.location.category for descriptor in self.descriptors), - student=self.user, + student=self.user.pk, field_name__in=set(field.name for field in fields), ) elif scope == Scope.student_info: return self._query( XModuleStudentInfoField, - student=self.user, + student=self.user.pk, field_name__in=set(field.name for field in fields), ) else: @@ -214,7 +214,7 @@ class ModelDataCache(object): if key.scope == Scope.student_state: field_object, _ = StudentModule.objects.get_or_create( course_id=self.course_id, - student=self.user, + student=self.user.pk, module_type=key.block_scope_id.category, module_state_key=key.block_scope_id.url(), defaults={'state': json.dumps({})}, @@ -233,12 +233,12 @@ class ModelDataCache(object): field_object, _= XModuleStudentPrefsField.objects.get_or_create( field_name=key.field_name, module_type=key.block_scope_id, - student=self.user, + student=self.user.pk, ) elif key.scope == Scope.student_info: field_object, _ = XModuleStudentInfoField.objects.get_or_create( field_name=key.field_name, - student=self.user, + student=self.user.pk, ) cache_key = self._cache_key_from_kvs_key(key) From cbed66280a12f86827f9288bf2b4d92d3935cb57 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 20 Mar 2013 13:20:28 -0400 Subject: [PATCH 074/135] Fix pep8 and pylint errors --- .pylintrc | 3 ++- lms/djangoapps/courseware/model_data.py | 32 ++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/.pylintrc b/.pylintrc index 6690bb7df0..a9f19ca667 100644 --- a/.pylintrc +++ b/.pylintrc @@ -41,7 +41,8 @@ disable= # R0902: Too many instance attributes # R0903: Too few public methods (1/2) # R0904: Too many public methods - W0141,W0142,R0201,R0901,R0902,R0903,R0904 +# R0913: Too many arguments + W0141,W0142,R0201,R0901,R0902,R0903,R0904,R0913 [REPORTS] diff --git a/lms/djangoapps/courseware/model_data.py b/lms/djangoapps/courseware/model_data.py index 7b0c7cd74e..cb24501aef 100644 --- a/lms/djangoapps/courseware/model_data.py +++ b/lms/djangoapps/courseware/model_data.py @@ -1,3 +1,7 @@ +""" +Classes to provide the LMS runtime data storage to XBlocks +""" + import json from collections import namedtuple, defaultdict from itertools import chain @@ -14,10 +18,16 @@ from xblock.core import Scope class InvalidWriteError(Exception): - pass + """ + Raised to indicate that writing to a particular key + in the KeyValueStore is disabled + """ def chunks(items, chunk_size): + """ + Yields the values from items in chunks of size chunk_size + """ items = list(items) return (items[i:i + chunk_size] for i in xrange(0, len(items), chunk_size)) @@ -67,6 +77,15 @@ class ModelDataCache(object): """ def get_child_descriptors(descriptor, depth, descriptor_filter): + """ + Return a list of all child descriptors down to the specified depth + that match the descriptor filter. Includes `descriptor` + + descriptor: The parent to search inside + depth: The number of levels to descend, or None for infinite depth + descriptor_filter(descriptor): A function that returns True + if descriptor should be included in the results + """ if descriptor_filter(descriptor): descriptors = [descriptor] else: @@ -168,6 +187,9 @@ class ModelDataCache(object): return scope_map def _cache_key_from_kvs_key(self, key): + """ + Return the key used in the ModelDataCache for the specified KeyValueStore key + """ if key.scope == Scope.student_state: return (key.scope, key.block_scope_id.url()) elif key.scope == Scope.content: @@ -180,6 +202,10 @@ class ModelDataCache(object): return (key.scope, key.field_name) def _cache_key_from_field_object(self, scope, field_object): + """ + Return the key used in the ModelDataCache for the specified scope and + field + """ if scope == Scope.student_state: return (scope, field_object.module_state_key) elif scope == Scope.content: @@ -230,7 +256,7 @@ class ModelDataCache(object): usage_id='%s-%s' % (self.course_id, key.block_scope_id.url()), ) elif key.scope == Scope.student_preferences: - field_object, _= XModuleStudentPrefsField.objects.get_or_create( + field_object, _ = XModuleStudentPrefsField.objects.get_or_create( field_name=key.field_name, module_type=key.block_scope_id, student=self.user.pk, @@ -276,6 +302,7 @@ class LmsKeyValueStore(KeyValueStore): Scope.student_info, Scope.children, ) + def __init__(self, descriptor_model_data, model_data_cache): self._descriptor_model_data = descriptor_model_data self._model_data_cache = model_data_cache @@ -357,4 +384,3 @@ class LmsKeyValueStore(KeyValueStore): LmsUsage = namedtuple('LmsUsage', 'id, def_id') - From 154a441033900fc2f1469a406213c33aebeea41e Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 20 Mar 2013 13:24:25 -0400 Subject: [PATCH 075/135] Only use .pk for queries, and not on inserts --- lms/djangoapps/courseware/model_data.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/courseware/model_data.py b/lms/djangoapps/courseware/model_data.py index cb24501aef..b725f64308 100644 --- a/lms/djangoapps/courseware/model_data.py +++ b/lms/djangoapps/courseware/model_data.py @@ -240,7 +240,7 @@ class ModelDataCache(object): if key.scope == Scope.student_state: field_object, _ = StudentModule.objects.get_or_create( course_id=self.course_id, - student=self.user.pk, + student=self.user, module_type=key.block_scope_id.category, module_state_key=key.block_scope_id.url(), defaults={'state': json.dumps({})}, @@ -259,12 +259,12 @@ class ModelDataCache(object): field_object, _ = XModuleStudentPrefsField.objects.get_or_create( field_name=key.field_name, module_type=key.block_scope_id, - student=self.user.pk, + student=self.user, ) elif key.scope == Scope.student_info: field_object, _ = XModuleStudentInfoField.objects.get_or_create( field_name=key.field_name, - student=self.user.pk, + student=self.user, ) cache_key = self._cache_key_from_kvs_key(key) From 83665515d8330e8709bd4893429539975a8c0049 Mon Sep 17 00:00:00 2001 From: Victor Shnayder Date: Wed, 20 Mar 2013 13:45:01 -0400 Subject: [PATCH 076/135] Remove director of engineering job posting per request from Tess --- lms/templates/static_templates/jobs.html | 35 ------------------------ 1 file changed, 35 deletions(-) diff --git a/lms/templates/static_templates/jobs.html b/lms/templates/static_templates/jobs.html index 988a6b18d0..e33ff62e9a 100644 --- a/lms/templates/static_templates/jobs.html +++ b/lms/templates/static_templates/jobs.html @@ -334,40 +334,6 @@ -
    -
    -

    DIRECTOR ENGINEERING, OPEN SOURCE COMMUNITY MANAGER

    -

    In edX courses, students make (and break) electronic circuits, they manipulate molecules on the fly and they do it all at once, in their tens of thousands. We have great Professors and great Universities. But we can’t possibly keep up with all the great ideas out there, so we’re making our platform open source, to turn up the volume on great education. To do that well, we’ll need a Director of Engineering who can lead our Open Source Community efforts.

    -

    Responsibilities:

    -
      -
    • Define and implement software design standards that make the open source community most welcome and productive.
    • -
    • Work with others to establish the governance standards for the edX Open Source Platform, establish the infrastructure, and manage the team to deliver releases and leverage our University partners and stakeholders to
    • make the edX platform the world’s best learning platform. -
    • Help the organization recognize the benefits and limitations inherent in open source solutions.
    • -
    • Establish best practices and key tool usage, especially those based on industry standards.
    • -
    • Provide visibility for the leadership team into the concerns and challenges faced by the open source community.
    • -
    • Foster a thriving community by providing the communication, documentation and feedback that they need to be enthusiastic.
    • -
    • Maximize the good code design coming from the open source community.
    • -
    • Provide the wit and firmness that the community needs to channel their energy productively.
    • -
    • Tactfully balance the internal needs of the organization to pursue new opportunities with the community’s need to participate in the platform’s evolution.
    • -
    • Shorten lines of communication and build trust across entire team
    • -
    -

    Qualifications:

    -
      - -
    • Bachelors, preferably Masters in Computer Science
    • -
    • Solid communication skills, especially written
    • -
    • Committed to Agile practice, Scrum and Kanban
    • -
    • Charm and humor
    • -
    • Deep familiarity with Open Source, participant and contributor
    • -
    • Python, Django, Javascript
    • -
    • Commitment to support your technical recommendations, both within and beyond the organization.
    • -
    - -

    If you are interested in this position, please send an email to jobs@edx.org.

    -
    -
    - -

    SOFTWARE ENGINEER

    @@ -413,7 +379,6 @@ Project Manager (PMO) Director of Product Management Content Engineer - Director Engineering, Open Source Community Manager Software Engineer

    How to Apply

    From af1af8c6d1f57fb45435ef037253531ce8f5e551 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Wed, 20 Mar 2013 14:08:15 -0400 Subject: [PATCH 077/135] Address code review feedback: - improve docstrings - only pass in the state for a particular input and not the whole dictionary - refactor some common code - minor syntax cleanup --- common/lib/capa/capa/capa_problem.py | 43 +++--- common/lib/capa/capa/inputtypes.py | 136 ++++++++++-------- common/lib/capa/capa/responsetypes.py | 16 +-- common/lib/capa/capa/tests/test_inputtypes.py | 2 +- common/lib/xmodule/xmodule/capa_module.py | 20 ++- .../xmodule/tests/test_combined_open_ended.py | 2 +- lms/djangoapps/courseware/module_render.py | 2 +- 7 files changed, 126 insertions(+), 95 deletions(-) diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index f1fea4d8e3..27f1066030 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -91,8 +91,12 @@ class LoncapaProblem(object): - problem_text (string): xml defining the problem - id (string): identifier for this problem; often a filename (no spaces) - - state (dict): student state - - seed (int): random number generator seed (int) + - state (dict): containing the following keys: + - 'seed' - (int) random number generator seed + - 'student_answers' - (dict) maps input id to the stored answer for that input + - 'correct_map' (CorrectMap) a map of each input to their 'correctness' + - 'done' - (bool) indicates whether or not this problem is considered done + - 'input_state' - (dict) maps input_id to a dictionary that holds the state for that input - system (ModuleSystem): ModuleSystem instance which provides OS, rendering, and user context @@ -104,27 +108,16 @@ class LoncapaProblem(object): self.system = system if self.system is None: raise Exception() - self.seed = seed - self.input_state = None - if state: - if 'seed' in state: - self.seed = state['seed'] - if 'student_answers' in state: - self.student_answers = state['student_answers'] - if 'correct_map' in state: - self.correct_map.set_dict(state['correct_map']) - if 'done' in state: - self.done = state['done'] - if 'input_state' in state: - self.input_state = state['input_state'] + state = state if state else {} + self.seed = seed if seed else state.get('seed', struct.unpack('i', os.urandom(4))[0]) + self.student_answers = state.get('student_answers', {}) + if 'correct_map' in state: + self.correct_map.set_dict(state['correct_map']) + self.done = state.get('done', False) + self.input_state = state.get('input_state', {}) - # TODO: Does this deplete the Linux entropy pool? Is this fast enough? - if not self.seed: - self.seed = struct.unpack('i', os.urandom(4))[0] - if not self.input_state: - self.input_state = {} # Convert startouttext and endouttext to proper problem_text = re.sub("startouttext\s*/", "text", problem_text) @@ -240,13 +233,14 @@ class LoncapaProblem(object): def ungraded_response(self, xqueue_msg, queuekey): ''' - Handle any responses from the xqueue that are not related to grading + Handle any responses from the xqueue that do not contain grades + Will try to pass the queue message to all inputtypes that can handle ungraded responses Does not return any value ''' # check against each inputtype for the_input in self.inputs.values(): - # if the input type has an xqueue_response function, pass in the values + # if the input type has an ungraded function, pass in the values if hasattr(the_input, 'ungraded_response'): the_input.ungraded_response(xqueue_msg, queuekey) @@ -542,11 +536,14 @@ class LoncapaProblem(object): if self.student_answers and problemid in self.student_answers: value = self.student_answers[problemid] + if input_id not in self.input_state: + self.input_state[input_id] = {} + # do the rendering state = {'value': value, 'status': status, 'id': input_id, - 'input_state': self.input_state, + 'input_state': self.input_state[input_id], 'feedback': {'message': msg, 'hint': hint, 'hintmode': hintmode, }} diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 0208f32503..d5268fed89 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -161,7 +161,7 @@ class InputTypeBase(object): self.msg = feedback.get('message', '') self.hint = feedback.get('hint', '') self.hintmode = feedback.get('hintmode', None) - self.input_state_dict = state.get('input_state', {}) + self.input_state = state.get('input_state', {}) # put hint above msg if it should be displayed if self.hintmode == 'always': @@ -591,14 +591,14 @@ class CodeInput(InputTypeBase): Attribute('tabsize', 4, transform=int), ] - def setup(self): + def setup_code_response_rendering(self): """ Implement special logic: handle queueing state, and default input. """ # if no student input yet, then use the default input given by the # problem - if not self.value: - self.value = self.xml.text + if not self.value and self.xml.text: + self.value = self.xml.text.strip() # Check if problem has been queued self.queue_len = 0 @@ -609,6 +609,11 @@ class CodeInput(InputTypeBase): self.queue_len = self.msg self.msg = self.submitted_msg + + def setup(self): + ''' setup this input type ''' + self.setup_code_response_rendering() + def _extra_context(self): """Defined queue_len, add it """ return {'queue_len': self.queue_len, } @@ -623,8 +628,10 @@ class MatlabInput(CodeInput): ''' InputType for handling Matlab code input + TODO: API_KEY will go away once we have a way to specify it per-course Example: + Initial Text %api_key=API_KEY @@ -633,51 +640,56 @@ class MatlabInput(CodeInput): template = "matlabinput.html" tags = ['matlabinput'] - # pulled out for testing - submitted_msg = ("Submitted. As soon as your submission is" - " graded, this message will be replaced with the grader's feedback.") + plot_submitted_msg = ("Submitted. As soon as a response is returned, " + "this message will be replaced by that feedback.") def setup(self): ''' Handle matlab-specific parsing ''' - # if we don't have state for this input type yet, make one - if self.id not in self.input_state_dict: - self.input_state_dict[self.id] = {} + self.setup_code_response_rendering() - self.input_state = self.input_state_dict[self.id] xml = self.xml self.plot_payload = xml.findtext('./plot_payload') - # if no student input yet, then use the default input given by the - # problem - if not self.value: - self.value = self.xml.text # Check if problem has been queued - self.queue_len = 0 self.queuename = 'matlab' - # Flag indicating that the problem has been queued, 'msg' is length of self.queue_msg = '' if 'queue_msg' in self.input_state and self.status in ['incomplete', 'unsubmitted']: self.queue_msg = self.input_state['queue_msg'] if 'queued' in self.input_state and self.input_state['queuestate'] is not None: self.status = 'queued' self.queue_len = 1 - # queue - if self.status == 'incomplete': - self.status = 'queued' - self.queue_len = self.msg - self.msg = self.submitted_msg - + self.msg = self.plot_submitted_msg def handle_ajax(self, dispatch, get): - ''' Handle AJAX calls directed to this input''' + ''' + Handle AJAX calls directed to this input + + Args: + - dispatch (str) - indicates how we want this ajax call to be handled + - get (dict) - dictionary of key-value pairs that contain useful data + Returns: + + ''' + if dispatch == 'plot': return self._plot_data(get) + return {} def ungraded_response(self, queue_msg, queuekey): - ''' Handle any XQueue responses that have to be saved and rendered ''' + ''' + Handle the response from the XQueue + Stores the response in the input_state so it can be rendered later + + Args: + - queue_msg (str) - message returned from the queue. The message to be rendered + - queuekey (str) - a key passed to the queue. Will be matched up to verify that this is the response we're waiting for + + Returns: + nothing + ''' # check the queuekey against the saved queuekey if('queuestate' in self.input_state and self.input_state['queuestate'] == 'queued' and self.input_state['queuekey'] == queuekey): @@ -697,9 +709,11 @@ class MatlabInput(CodeInput): def _parse_data(self, queue_msg): ''' - takes a queue_msg returned from the queue and parses it and returns - whatever is stored in msg - returns string msg + Parses the message out of the queue message + Args: + queue_msg (str) - a JSON encoded string + Returns: + returns the value for the the key 'msg' in queue_msg ''' try: result = json.loads(queue_msg) @@ -712,42 +726,50 @@ class MatlabInput(CodeInput): def _plot_data(self, get): - ''' send data via xqueue to the mathworks backend''' + ''' + AJAX handler for the plot button + Args: + get (dict) - should have key 'submission' which contains the student submission + Returns: + dict - 'success' - whether or not we successfully queued this submission + - 'message' - message to be rendered in case of error + ''' # only send data if xqueue exists - if self.system.xqueue is not None: - # pull relevant info out of get - response = get['submission'] + if self.system.xqueue is None: + return {'success': False, 'message': 'Cannot connect to the queue'} - # construct xqueue headers - qinterface = self.system.xqueue['interface'] - qtime = datetime.strftime(datetime.now(), xqueue_interface.dateformat) - callback_url = self.system.xqueue['construct_callback']('ungraded_response') - anonymous_student_id = self.system.anonymous_student_id - queuekey = xqueue_interface.make_hashkey(str(self.system.seed) + qtime + - anonymous_student_id + - self.id) - xheader = xqueue_interface.make_xheader( - lms_callback_url = callback_url, - lms_key = queuekey, - queue_name = self.queuename) + # pull relevant info out of get + response = get['submission'] - # save the input state - self.input_state['queuekey'] = queuekey - self.input_state['queuestate'] = 'queued' + # construct xqueue headers + qinterface = self.system.xqueue['interface'] + qtime = datetime.strftime(datetime.utcnow(), xqueue_interface.dateformat) + callback_url = self.system.xqueue['construct_callback']('ungraded_response') + anonymous_student_id = self.system.anonymous_student_id + queuekey = xqueue_interface.make_hashkey(str(self.system.seed) + qtime + + anonymous_student_id + + self.id) + xheader = xqueue_interface.make_xheader( + lms_callback_url = callback_url, + lms_key = queuekey, + queue_name = self.queuename) + + # save the input state + self.input_state['queuekey'] = queuekey + self.input_state['queuestate'] = 'queued' - # construct xqueue body - student_info = {'anonymous_student_id': anonymous_student_id, - 'submission_time': qtime} - contents = {'grader_payload': self.plot_payload, - 'student_info': json.dumps(student_info), - 'student_response': response} + # construct xqueue body + student_info = {'anonymous_student_id': anonymous_student_id, + 'submission_time': qtime} + contents = {'grader_payload': self.plot_payload, + 'student_info': json.dumps(student_info), + 'student_response': response} - (error, msg) = qinterface.send_to_queue(header=xheader, - body = json.dumps(contents)) + (error, msg) = qinterface.send_to_queue(header=xheader, + body = json.dumps(contents)) - return {'success': error == 0, 'message': msg} - return {'success': False, 'message': 'Cannot connect to the queue'} + return {'success': error == 0, 'message': msg} registry.register(MatlabInput) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index bb202e6d6e..8ab716735c 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -1147,10 +1147,10 @@ def sympy_check2(): correct = [] messages = [] for input_dict in input_list: - correct.append('correct' if input_dict[ - 'ok'] else 'incorrect') - msg = self.clean_message_html(input_dict[ - 'msg']) if 'msg' in input_dict else None + correct.append('correct' + if input_dict['ok'] else 'incorrect') + msg = (self.clean_message_html(input_dict['msg']) + if 'msg' in input_dict else None) messages.append(msg) # Otherwise, we do not recognize the dictionary @@ -1164,8 +1164,8 @@ def sympy_check2(): # indicating whether all inputs should be marked # correct or incorrect else: - correct = ['correct'] * len( - idset) if ret else ['incorrect'] * len(idset) + n = len(idset) + correct = ['correct'] * n if ret else ['incorrect'] * n # build map giving "correct"ness of the answer(s) correct_map = CorrectMap() @@ -1174,8 +1174,8 @@ def sympy_check2(): correct_map.set_overall_message(overall_message) for k in range(len(idset)): - npoints = self.maxpoints[idset[ - k]] if correct[k] == 'correct' else 0 + npoints = (self.maxpoints[idset[k]] + if correct[k] == 'correct' else 0) correct_map.set(idset[k], correct[k], msg=messages[k], npoints=npoints) return correct_map diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index b9da9df03f..250cedd549 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -357,7 +357,7 @@ class MatlabTest(unittest.TestCase): def test_rendering_with_state(self): state = {'value': 'print "good evening"', 'status': 'incomplete', - 'input_state': {'prob_1_2': {'queue_msg': 'message'}}, + 'input_state': {'queue_msg': 'message'}, 'feedback': {'message': '3'}, } elt = etree.fromstring(self.xml) diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 6ce8d3a805..da8b5b4f96 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -543,8 +543,16 @@ class CapaModule(CapaFields, XModule): def handle_ungraded_response(self, get): ''' - Delivers a response to the capa problem where the expectation where this response does - not have relevant grading information + Delivers a response from the XQueue to the capa problem + + The score of the problem will not be updated + + Args: + - get (dict) must contain keys: + queuekey - a key specific to this response + xqueue_body - the body of the response + Returns: + empty dictionary No ajax return is needed, so an empty dict is returned ''' @@ -557,8 +565,12 @@ class CapaModule(CapaFields, XModule): def handle_input_ajax(self, get): ''' - Passes information down to the capa problem so that it can handle its own ajax calls - Returns the response from the capa problem + Handle ajax calls meant for a particular input in the problem + + Args: + - get (dict) - data that should be passed to the input + Returns: + - dict containing the response from the input ''' response = self.lcp.handle_input_ajax(get) # save any state changes that may occur diff --git a/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py b/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py index aa8a077cc1..55c31ded58 100644 --- a/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py +++ b/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py @@ -183,7 +183,7 @@ class OpenEndedModuleTest(unittest.TestCase): self.test_system.location = self.location self.mock_xqueue = MagicMock() self.mock_xqueue.send_to_queue.return_value = (None, "Message") - def constructed_callback(dispatch = "score_update"): + def constructed_callback(dispatch="score_update"): return dispatch self.test_system.xqueue = {'interface': self.mock_xqueue, 'construct_callback': constructed_callback, 'default_queuename': 'testqueue', diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 0954f8d28c..973940d784 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -182,7 +182,7 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours proto=request.META.get('HTTP_X_FORWARDED_PROTO', 'https' if request.is_secure() else 'http') ) - def make_xqueue_callback(dispatch = 'score_update'): + def make_xqueue_callback(dispatch='score_update'): # Fully qualified callback URL for external queueing system xqueue_callback_url = '{proto}://{host}'.format( host=request.get_host(), From 5dbb153abe607df7f5d7376fe4af973c807956d3 Mon Sep 17 00:00:00 2001 From: Chris Dodge Date: Wed, 20 Mar 2013 14:32:18 -0400 Subject: [PATCH 078/135] ooops. didn't clean up the merge conflicts --- common/lib/xmodule/xmodule/modulestore/mongo.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/common/lib/xmodule/xmodule/modulestore/mongo.py b/common/lib/xmodule/xmodule/modulestore/mongo.py index cc4c06d23a..d2fe524a0e 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo.py @@ -622,11 +622,7 @@ class MongoModuleStore(ModuleStoreBase): self._update_single_item(location, {'metadata': metadata}) # recompute (and update) the metadata inheritance tree which is cached -<<<<<<< HEAD self.refresh_cached_metadata_inheritance_tree(loc) -======= - self.get_cached_metadata_inheritance_tree(loc, force_refresh = True) ->>>>>>> fbd409c914c2dc005fd6b46af6daaee262205e0e def delete_item(self, location): """ @@ -649,12 +645,7 @@ class MongoModuleStore(ModuleStoreBase): # from overriding our default value set in the init method. safe=self.collection.safe) # recompute (and update) the metadata inheritance tree which is cached -<<<<<<< HEAD self.refresh_cached_metadata_inheritance_tree(Location(location)) -======= - self.get_cached_metadata_inheritance_tree(Location(location), force_refresh = True) ->>>>>>> fbd409c914c2dc005fd6b46af6daaee262205e0e - def get_parent_locations(self, location, course_id): '''Find all locations that are the parents of this location in this From c5385ff42c77c184b4927f5b3c8859f3b56ed7bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Wed, 20 Mar 2013 14:46:08 -0400 Subject: [PATCH 079/135] Update Berkeley logos LMS Lighthouse [#260] --- lms/static/images/BerkeleyX-on-edx-logo.png | Bin 5011 -> 3493 bytes .../images/university/berkeley/berkeley.png | Bin 12172 -> 11091 bytes .../university/berkeley/berkeley_bw.png | Bin 8330 -> 4912 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/lms/static/images/BerkeleyX-on-edx-logo.png b/lms/static/images/BerkeleyX-on-edx-logo.png index 6c5a828503a1906fa5c2bf536d8f61478a9fd9a0..cb765ce2fa4a683eff09bbe8ead0892e202c6088 100644 GIT binary patch delta 3454 zcmV-^4T18LC#4&ZNPi7SNklNP{ z04h){Xi%UPtE5O%)GAW?RSJbviZ!+nsGnd_0Rb(bLa;@!JOlC!n&k2@kq`(eA><~x zJ?Gwk$z<5QkC=qux99s#?%v(m-PxIc{`t?Dy|LKsc43-nw|}Ggm`z}s30~7oGr?<` zX(o70`;MERw&5Q$BO~`*t-WF{z5YIlLp*qBtx7|dgx8NoFJNatzAo6#}2VnnJ^6}(l zzh0*l2`FV|hL za7h|{Jj2^|Axh@tv0EP%R@U!ZR_>aj zg&W!`O@DmUpZ%-z^k($Yo;-y79LGPyv(xP^G;S-DXFHcVJ7p(*?xg*r>2u1RO>CSo z60w%6&u5F^-%F>)IizZG^p^_oXDPQh#afrjT~-6O+E}y{)U9oxHuaU{gQ-XB9HMNPMc1L-=%;#Pk%VGSUK|kC$4`v?P&Ao((dIH zq@5&S?x#-+&P$YzbE-B;1FomwwY!+UIU2uCo=kqx1=x6G@u>WYJXhyBoSw760{~2$ z8`+=-Ue43{BWq&Ewp}U5x$7Rd=P}{TJt!Y=)1Kt$asukZw6#~7!P}t?x+oM1M>f11iu16PQfVT1rc6B8-OWgD9&Rh z;1#b?(oA620oFG156S<8y8T)L?o0SSOd2?2Aa}-Ks@&`*yAcYo`=f3pVAaJum13Yg z1ORfnaUEuZFHv4gE#~2w`=Tva-+w*sVz#NNEf*}9OCO^Fdd`Q=O-)TbMEh4_eD|Bdte zqI?^}J0?eeLjWF){s_X}Zb~WcpFilG?g@AqP?J%%8FUawd-tQF>j4T`Y=6gM`q?1? zm+iQ4E^vH?3YCF%FN4J{0=yFe?q87KN&beY&hPq4j98(=k`Z9|#iDw_AqgEhN6C<^ z2TQ&soU4k%q{i2iO^YzFdIPxH@f5VpqhQvd7^LqwraLEqFH6#*N*Wa9sGFz2)c~46 zfXt=uhXB+BTqgm~n~Pb7kAKa?F(cN!7bKLgf@}h@ z435=`c4okx#&hx1p6Wt2^n?`nSkzn!_s4zrN>XW(G}CC);V7MJGYCHs&i6z-0q$T7 z5N29U=I~(YMaN>LeLHD;CbKd6FW1ebr%2m8z>*Z;I;Y_5be2RDvi;9$16PzEJH`Dx zS^=)ZXkRgAqhdGrUw@0ZLA6&1xTQuXdBVI#Ip4+5ZchV%tH}F66DOezM~kh|c`YF~ zu7lnFj7}@kxz`!xUU>#-G8w$>z@YD;ZkqK*y)Ht^1Y#?5PzXxsY z%I=l68MKp#Wep3LvTf{S~;al#Os1mHqq;8nnhk^J)uT zSC)p341-i)?f4NuT?QRw1t{{N`AR&vu8#-D?9!B;2$tu_c9T+GTOYp&FM-a?NBzp& z)TLZvRDowGgnw2qkiHw`oL2z6cLRKx*xwJ=_U6T(SO*}g8CGNOJle z%uyg*FP893a8_q}PQrC;UOamy7&SqX5xpe#PQd3OgYVv^L*35Ac2Q*)?iY*v41k-U zlU-S-$czLB0XTENbqZdgiSI&=Oq<0lti&ssRQ@p+*Oi(42xd@24;egY zZ^+PljL;4=m@g`okI z31HrE4$73{gO%@TRF>Q4vLZE9`$S5UaT}hc;B22^;5co`WB{21m`;ZiMSC&Zk&C~% z?7}7Oh4RMX6`vqKWMX2Z-RB&^9p`_Uj54gxA(dLKi!-c8g#LHHqow5twqV1e*x3;2vx*#lk`t1W;2A z(m@>%IF%{^nB4>Lsv2lF^1{q=&miJYhqhgbed8blW{H~bENg?5S9-p}{k1j-l(g%! zh>DkDf4YulZ3YQ*MkjDf0<-ZT+a?-`5z8JVP_B?PIcHKnr+w>?F{y<1

    12pMTv~ z#QH=Z&h;w@35e_5Ns}7{)-{Mf>{7~5Yrl%Z3g06|<-S$tOE1J*{7Vjspx{t@ctj_} z4?PH&1q5I#%8x{>l!UC1mTG;U%JCY(c0UCatx?eqN)Xmrnf@aXBs_)A;}xwJl)POc z7^W|BpgW*xF5x~LV4Xmkqy@B~rhk8a067oS7K0Y0#!9N+U7pPoYbf#Hib$>RFVI6c( zghyyV9SJF-fp?zJ|0P8i1bJgE+JDUPj~Nn->7yldo(+I*h3_XYRfme|7hAR5IDAFr91xy9$7a8F2tW&AM`# zaIV|)qR10y08Kkz0U))d>VE|oG(lkBCb-uYv`-8M$4Dh0&4b=vro-x*asD+014Z{m zrd#m?Ozmet#I)SEyejsHn^Snuk3T#ut`IZb2sh;~P^SfIKLfg91YUc5-^3bt@#1Gd zGI+Joqxa50C_o$prn(?J|Gk=M7g6(#H349@M}JnL6e+yoT3kCE z*9k>?oc18?H4n4a45I?K61MLCT zqNmbg{-eFRH5VkX9CxfV-qrc$|84Nx{LCh3n1CKSa)8+sb!X-|wHck!lOZ5D{^IInCn&7=%j2Ct`;gf&cHpgs|x;Hc1V4BIi grkU2z_J08e0N5P|spNyWv;Y7A07*qoM6N<$g1(BAVgLXD literal 5011 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 diff --git a/lms/static/images/university/berkeley/berkeley.png b/lms/static/images/university/berkeley/berkeley.png index ca85266538ca132ebe566e3614993ae3f5516057..9fb7ffa6e7777755c5ef92fff6b13a8e29111f87 100644 GIT binary patch literal 11091 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 V0N3cuzmzN#Ku%S*R@(H-{{qXoe;5D& literal 12172 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 Bv)ljx diff --git a/lms/static/images/university/berkeley/berkeley_bw.png b/lms/static/images/university/berkeley/berkeley_bw.png index 9904e613150e9770c0734e2b322ca296d9c20082..77204c3913da66c63fad2dfee334ba426f834902 100644 GIT binary patch literal 4912 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 From f462cd6efa099b2bb7386b8ab2c8ad8d0086f2eb Mon Sep 17 00:00:00 2001 From: Chris Dodge Date: Wed, 20 Mar 2013 15:14:14 -0400 Subject: [PATCH 080/135] if we're loading a course module with depth = 0, then we don't need to fetch/compute inherited metadata --- common/lib/xmodule/xmodule/modulestore/mongo.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/common/lib/xmodule/xmodule/modulestore/mongo.py b/common/lib/xmodule/xmodule/modulestore/mongo.py index f6b703e806..1cbc053b62 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo.py @@ -376,7 +376,7 @@ class MongoModuleStore(ModuleStoreBase): return data - def _load_item(self, item, data_cache): + def _load_item(self, item, data_cache, depth=0): """ Load an XModuleDescriptor from item, using the children stored in data_cache """ @@ -388,7 +388,11 @@ class MongoModuleStore(ModuleStoreBase): resource_fs = OSFS(root) - metadata_inheritance_tree = self.get_cached_metadata_inheritance_tree(Location(item['location'])) + metadata_inheritance_tree = None + # if we are loading a course object, if we're not prefetching children (depth != 0) then don't + # bother with the metadata inheritence + if item['location']['category'] != 'course' or depth != 0: + metadata_inheritance_tree = self.get_cached_metadata_inheritance_tree(Location(item['location'])) # TODO (cdodge): When the 'split module store' work has been completed, we should remove # the 'metadata_inheritance_tree' parameter @@ -410,7 +414,7 @@ class MongoModuleStore(ModuleStoreBase): """ data_cache = self._cache_children(items, depth) - return [self._load_item(item, data_cache) for item in items] + return [self._load_item(item, data_cache, depth) for item in items] def get_courses(self): ''' From 133bd767d5b42a9e0e4819a03a14e8c24b9ffd93 Mon Sep 17 00:00:00 2001 From: Chris Dodge Date: Wed, 20 Mar 2013 15:26:29 -0400 Subject: [PATCH 081/135] small refactoring to use a better semantic with regards to the parameter --- common/lib/xmodule/xmodule/modulestore/mongo.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/common/lib/xmodule/xmodule/modulestore/mongo.py b/common/lib/xmodule/xmodule/modulestore/mongo.py index 1cbc053b62..ea3bdf44d0 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo.py @@ -376,7 +376,7 @@ class MongoModuleStore(ModuleStoreBase): return data - def _load_item(self, item, data_cache, depth=0): + def _load_item(self, item, data_cache, should_apply_metadata_inheritence=True): """ Load an XModuleDescriptor from item, using the children stored in data_cache """ @@ -389,9 +389,8 @@ class MongoModuleStore(ModuleStoreBase): resource_fs = OSFS(root) metadata_inheritance_tree = None - # if we are loading a course object, if we're not prefetching children (depth != 0) then don't - # bother with the metadata inheritence - if item['location']['category'] != 'course' or depth != 0: + + if should_apply_metadata_inheritence: metadata_inheritance_tree = self.get_cached_metadata_inheritance_tree(Location(item['location'])) # TODO (cdodge): When the 'split module store' work has been completed, we should remove @@ -414,7 +413,10 @@ class MongoModuleStore(ModuleStoreBase): """ data_cache = self._cache_children(items, depth) - return [self._load_item(item, data_cache, depth) for item in items] + # if we are loading a course object, if we're not prefetching children (depth != 0) then don't + # bother with the metadata inheritence + return [self._load_item(item, data_cache, + should_apply_metadata_inheritence=(item['location']['category'] != 'course' or depth != 0)) for item in items] def get_courses(self): ''' From 38df54dc53df10cd8a23705e888fdfc229cece16 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Wed, 20 Mar 2013 16:19:45 -0400 Subject: [PATCH 082/135] studio - moved handling scrollable and new window links into their own functions for use throughout the app and not just on doc.ready + removed unused js plugin reference --- cms/static/js/base.js | 33 ++++++++++++++++++++++----------- cms/templates/base.html | 1 - 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/cms/static/js/base.js b/cms/static/js/base.js index 18c31fc4e0..fa31f25906 100644 --- a/cms/static/js/base.js +++ b/cms/static/js/base.js @@ -45,9 +45,6 @@ $(document).ready(function () { (e).preventDefault(); }); - // smooth scrolling page links - $('a[rel*="view"]').smoothScroll({offset: -200, easing: 'swing', speed: 2000}); - // nav - dropdown related $body.click(function (e) { $('.nav-dropdown .nav-item .wrapper-nav-sub').removeClass('is-shown'); @@ -75,10 +72,7 @@ $(document).ready(function () { }); // general link management - new window/tab - $('a[rel="external"]').attr('title', 'This link will open in a new browser window/tab').click(function (e) { - window.open($(this).attr('href')); - e.preventDefault(); - }); + $('a[rel="external"]').attr('title', 'This link will open in a new browser window/tab').bind('click', linkNewWindow); // general link management - lean modal window $('a[rel="modal"]').attr('title', 'This link will open in a modal window').leanModal({overlay: 0.50, closeButton: '.action-modal-close' }); @@ -86,6 +80,10 @@ $(document).ready(function () { (e).preventDefault(); }); + // general link management - smooth scrolling page links + $('a[rel*="view"]').bind('click', linkSmoothScroll); + + // toggling overview section details $(function () { if ($('.courseware-section').length > 0) { @@ -155,10 +153,23 @@ $(document).ready(function () { }); }); -// function collapseAll(e) { -// $('.branch').addClass('collapsed'); -// $('.expand-collapse-icon').removeClass('collapse').addClass('expand'); -// } +function linkSmoothScroll(e) { + (e).preventDefault(); + + $.smoothScroll({ + offset: -200, + easing: 'swing', + speed: 1000, + scrollElement: null, + scrollTarget: $(this).attr('href') + }); + console.log('clicked!'); +} + +function linkNewWindow(e) { + window.open($(this).attr('href')); + e.preventDefault(); +} function toggleSections(e) { e.preventDefault(); diff --git a/cms/templates/base.html b/cms/templates/base.html index 6ce0de4b11..e852b5d7fe 100644 --- a/cms/templates/base.html +++ b/cms/templates/base.html @@ -46,7 +46,6 @@ -