From 8da6543d901e8339383d4a06bda18d85ff2003f3 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Tue, 2 Jul 2013 18:31:37 -0400 Subject: [PATCH 01/36] Studio: adds static states for all authorship cases on dashboard (WIP) --- cms/templates/index.html | 379 +++++++++++++++++++++++++++++++++++---- 1 file changed, 349 insertions(+), 30 deletions(-) diff --git a/cms/templates/index.html b/cms/templates/index.html index e7205c9430..5c5b631ef3 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -57,37 +57,356 @@
+ + +
-
-

- ${_("Welcome, %(name)s") % dict(name= user.username)}. - ${_("Here are all of the courses you are currently authoring in Studio:")}

-
+
+ +
+

${_("Thanks for signing up, %(name)s!") % dict(name= user.username)}

+
+ +
+

${_('Next Steps to Authoring in Studio')}

+
+

${_('Your on your way to authoring courses online using Studio. In order to complete your registration, we need you verify your registration by checking your $emailaddress email account. An activation message and next steps should be waiting for you there.')}

+ +

${_('Need another copy of the verification email? You can also request another message be sent.')}

+
+ + +
+
+ + +
+ + + + + +
+
+
+

${_("Welcome, %(name)s!") % dict(name= user.username)}

+
+ +
+

${_('Your Account Has Been Verified')}

+
+

${_('Thanks for verifying your edX Studio account.Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam.')}

+
+
+ +
+

${_('Creating Your Own Courses in Studio')}

+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+ +
+

${_('Your Authorship Request Status')}

+
+ +
+ + +
+
+
+ + +
+ + + + + +
+
+
+

${_("Welcome, %(name)s !") % dict(name= user.username)}

+
+ +
+

${_('Creating Your Own Courses in Studio')}

+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+ +
+

${_('Your Authorship Request Status')}

+
+ +
+ +
+
${_('Your authorship request is:')}
+
+ ${_('Pending')} + ${_('Your request is currently being reviewed by edX staff and should be updated shortly.')} +
+
+
+
+
+ + +
+ + + + + +
+
+
+

${_("Welcome, %(name)s !") % dict(name= user.username)}

+
+ +
+

${_('Creating Your Own Courses in Studio')}

+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+ +
+

${_('Your Authorship Request Status')}

+
+ +
+ +
+
${_('Your authorship request is:')}
+
+ ${_('Rejected')} + ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} +
+
+ +
+
${_('The following feedback was given by edX staff when making this decision')}
+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+
+
+
+
+ + +
+ + + + + +
+
+ +
+ + +
+ + + + + +
+
+
+

${_("Welcome, %(name)s") % dict(name= user.username)}!

+
+

${_("Here are all of the courses you currently have access to in Studio:")}

+
+
+ + + +
+

${_('Creating Your Own Courses in Studio')}

+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+ +
+

${_('Your Authorship Request Status')}

+
+ +
+ +
+
${_('Your authorship request is:')}
+
+ ${_('Rejected')} + ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} +
+
+ +
+
${_('The following feedback was given by edX staff when making this decision')}
+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+
+
+
+
+ + +
+ + + + + +
+ + +
- -
-
-
- % if user.is_active: -
    - %for course, url, lms_link in sorted(courses, key=lambda s: s[0].lower() if s[0] is not None else ''): -
  • - - ${course} - - View Live -
  • - %endfor -
- % else: -
-

- ${_("In order to start authoring courses using edX Studio, please click on the activation link in your email.")} -

-
- % endif -
-
-
+ +#
+# % if user.is_active: +# + +# % else: +#
+#

${_("In order to start authoring courses using edX Studio, please click on the activation link in your email.")}

+#
+# % endif +#
From 8f5c972e79cccec81d0b641646f7dc70cc835085 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Wed, 3 Jul 2013 10:36:19 -0400 Subject: [PATCH 02/36] Studio: revises static states for all authorship cases on dashboard (WIP) --- cms/templates/index.html | 342 ++++++++++++++++++++------------------- 1 file changed, 175 insertions(+), 167 deletions(-) diff --git a/cms/templates/index.html b/cms/templates/index.html index 5c5b631ef3..5a1003b391 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -67,7 +67,7 @@

${_("Thanks for signing up, %(name)s!") % dict(name= user.username)}

-
+

${_('Next Steps to Authoring in Studio')}

${_('Your on your way to authoring courses online using Studio. In order to complete your registration, we need you verify your registration by checking your $emailaddress email account. An activation message and next steps should be waiting for you there.')}

@@ -97,10 +97,6 @@
-
-

${_("Welcome, %(name)s!") % dict(name= user.username)}

-
-

${_('Your Account Has Been Verified')}

@@ -108,24 +104,54 @@
-
-

${_('Creating Your Own Courses in Studio')}

+
+

${_("Welcome, %(name)s!") % dict(name= user.username)}

+
-

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+

${_("You don't have any courses you're working on in Studio yet.")}

+
-
-

${_('Your Authorship Request Status')}

+
+ +
+

${_('Create Your First Course')}

- +

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+ + +
+

+ ${_('Becoming a Course Author in Studio')} +

+ +
+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+ +
+

${_('Request the Ability to Author Courses in Studio')}

+
+ +
+ + +
+
+
@@ -150,27 +176,36 @@

${_("Welcome, %(name)s !") % dict(name= user.username)}

+ +
+

${_("You don't have any courses you're working on in Studio yet.")}

+
-
-

${_('Creating Your Own Courses in Studio')}

-
-

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

-
+
+ +
+

+ ${_('Becoming a Course Author in Studio')} +

-
-

${_('Your Authorship Request Status')}

-
+
+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+
+

${_('Your Authorship Request Status')}

+ +
+
${_('Your authorship request is:')}
+
+ ${_('Pending')} + ${_('Your request is currently being reviewed by edX staff and should be updated shortly.')} +
+
+
- -
-
${_('Your authorship request is:')}
-
- ${_('Pending')} - ${_('Your request is currently being reviewed by edX staff and should be updated shortly.')} -
-
@@ -188,34 +223,42 @@

${_("Welcome, %(name)s !") % dict(name= user.username)}

-
-
-

${_('Creating Your Own Courses in Studio')}

-

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+

${_("You don't have any courses you're working on in Studio yet.")}

+
-
-

${_('Your Authorship Request Status')}

-
+
+ + - -
-
${_('Your authorship request is:')}
-
- ${_('Rejected')} - ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} -
-
- -
-
${_('The following feedback was given by edX staff when making this decision')}
-
+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

-
-
+
+ +
+

${_('Your Authorship Request Status')}

+ +
+
${_('Your authorship request is:')}
+
+ ${_('Rejected')} + ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} +
+
+ +
+
${_('The following feedback was given by edX staff when making this decision')}
+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+
+
@@ -226,91 +269,93 @@
- - - -
-
- -
- - -
- - - - +
-

${_("Welcome, %(name)s") % dict(name= user.username)}!

+

${_("Welcome, %(name)s !") % dict(name= user.username)}

+

${_("Here are all of the courses you currently have access to in Studio:")}

-
-
-
-

${_('Your Authorship Request Status')}

-
+ + - -
-
${_('Your authorship request is:')}
-
- ${_('Rejected')} - ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} -
-
- -
-
${_('The following feedback was given by edX staff when making this decision')}
-
+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

-
-
+
+ +
+

${_('Your Authorship Request Status')}

+ +
+
${_('Your authorship request is:')}
+
+ ${_('Rejected')} + ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} +
+
+ +
+
${_('The following feedback was given by edX staff when making this decision')}
+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+
+
+
+ + +
+

+ ${_('Becoming a Course Author in Studio')} +

+ +
+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+ +
+

${_('Your Authorship Request Status')}

+ +
+
${_('Your authorship request is:')}
+
+ ${_('Rejected')} + ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} +
+
+ +
+
${_('The following feedback was given by edX staff when making this decision')}
+
+

${_('Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus.')}

+
+
+
@@ -329,8 +374,7 @@
- - +
@@ -390,23 +415,6 @@
-#
-# % if user.is_active: -#
    -# %for course, url, lms_link in sorted(courses, key=lambda s: s[0].lower() if s[0] is not None else ''): -#
  • -# -# ${course} -# - -# View Live -#
  • -# %endfor -#
- -# % else: -#
-#

${_("In order to start authoring courses using edX Studio, please click on the activation link in your email.")}

-#
-# % endif -#
+# % if user.is_active: +# % else: +# % endif From 4d9d6a4522acb58aa35b93b6ea9dcbd96498ee72 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Sun, 7 Jul 2013 18:49:11 -0400 Subject: [PATCH 03/36] Studio: resolves blue primary button default + hover color states --- cms/static/sass/elements/_controls.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cms/static/sass/elements/_controls.scss b/cms/static/sass/elements/_controls.scss index 6859560361..4fac3de01f 100644 --- a/cms/static/sass/elements/_controls.scss +++ b/cms/static/sass/elements/_controls.scss @@ -26,13 +26,13 @@ // blue primary button .btn-primary-blue { @extend .btn-primary; - background: $blue; - border-color: $blue-s1; + background: $blue-u1; + border-color: $blue-u1; color: $white; &:hover, &:active { - background: $blue-s2; - border-color: $blue-s2; + background: $blue-s1; + border-color: $blue-s1; } &.current, &.active { From 394292ec1bda2bc680e4f2c59e111c7719f45f23 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Sun, 7 Jul 2013 18:50:36 -0400 Subject: [PATCH 04/36] Studio: revises static dashboard states for all authorship rights scenarios --- cms/static/sass/_base.scss | 36 -- cms/static/sass/elements/_system-help.scss | 153 ++++- cms/static/sass/views/_dashboard.scss | 248 ++++++++- cms/templates/index.html | 613 +++++++++++++++------ 4 files changed, 821 insertions(+), 229 deletions(-) diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index 4a20a98eb3..fe107d7511 100644 --- a/cms/static/sass/_base.scss +++ b/cms/static/sass/_base.scss @@ -368,42 +368,6 @@ p, ul, ol, dl { color: $gray-d3; } } - - .introduction { - @include box-sizing(border-box); - @extend .t-copy-sub1; - width: flex-grid(12); - margin: 0 0 $baseline 0; - - .copy strong { - font-weight: 600; - } - - &.has-links { - @include clearfix(); - - .copy { - float: left; - width: flex-grid(8,12); - margin-right: flex-gutter(); - } - - .nav-introduction-supplementary { - @extend .t-copy-sub2; - float: right; - width: flex-grid(4,12); - display: block; - text-align: right; - - .icon { - @extend .t-action3; - display: inline-block; - vertical-align: middle; - margin-right: ($baseline/4); - } - } - } - } } .content-primary, .content-supplementary { diff --git a/cms/static/sass/elements/_system-help.scss b/cms/static/sass/elements/_system-help.scss index d1b0584fc4..ca8fa38154 100644 --- a/cms/static/sass/elements/_system-help.scss +++ b/cms/static/sass/elements/_system-help.scss @@ -1,40 +1,169 @@ // studio - elements - system help // ==================== -// notices - in-context: to be used as notices to users within the context of a form/action -.notice-incontext { - @extend .ui-well; - border-radius: ($baseline/10); +// view introductions - common greeting/starting points for the UI +.content .introduction { + @include box-sizing(border-box); + margin-bottom: $baseline; .title { - @extend .t-title7; - margin-bottom: ($baseline/4); + @extend .t-title4; font-weight: 600; } .copy { @extend .t-copy-sub1; - @include transition(opacity $tmg-f2 ease-in-out 0s); - opacity: 0.75; } strong { font-weight: 600; } - &:hover { + // CASE: has links alongside + &.has-links { + @include clearfix(); .copy { - opacity: 1.0; + float: left; + width: flex-grid(8,12); + margin-right: flex-gutter(); + } + + .nav-introduction-supplementary { + @extend .t-copy-sub2; + float: right; + width: flex-grid(4,12); + display: block; + text-align: right; + + .icon { + @extend .t-action3; + display: inline-block; + vertical-align: middle; + margin-right: ($baseline/4); + } } } } -// particular warnings around a workflow for something +// notices - in-context: to be used as notices to users within the context of a form/action +.notice-incontext { + @extend .ui-well; + border-radius: ($baseline/10); + position: relative; + overflow: hidden; + margin-bottom: $baseline; + + .title { + @extend .t-title7; + margin-bottom: ($baseline/4); + font-weight: 700; + } + + .copy { + @extend .t-copy-sub1; + @include transition(opacity $tmg-f2 ease-in-out 0s); + opacity: 0.75; + margin-bottom: $baseline; + + &:last-child { + margin-bottom: 0; + } + } + + strong { + font-weight: 600; + } + + &.has-status { + + .status-indicator { + position: absolute; + top: 0; + left: 0; + display: block; + width: 100%; + height: ($baseline/4); + opacity: 0.40; + } + } + + // CASE: notice has actions { + &.has-actions { + + .list-actions { + margin-top: ($baseline*0.75); + + .action-item { + + } + + .action-primary { + @extend .btn-primary-blue; + @extend .t-action3; + } + } + } +} + +// particular notice - warnings around a workflow for something .notice-workflow { background: $yellow-l5; - .copy { + .status-indicator { + background: $yellow; + } + + title { color: $gray-d1; } + + .copy { + color: $gray; + } +} + +// particular notice - instructional +.notice-instruction { + background-color: $gray-l4; + + .title { + color: $gray-d1; + } + + .copy { + color: $gray; + } + + &.has-actions { + + .list-actions { + + .action-item { + + } + + .action-primary { + @extend .btn-primary-blue; + @extend .t-action3; + } + } + } +} + +// particular notice - confirmation +.notice-confirmation { + background-color: $green-l5; + + .status-indicator { + background: $green-s1; + } + + .title { + color: $green; + } + + .copy { + color: $gray; + } } diff --git a/cms/static/sass/views/_dashboard.scss b/cms/static/sass/views/_dashboard.scss index 8d1b068256..a1cd5a47f6 100644 --- a/cms/static/sass/views/_dashboard.scss +++ b/cms/static/sass/views/_dashboard.scss @@ -3,8 +3,254 @@ body.dashboard { + // temp + .content { + margin-bottom: ($baseline*5); + + &:last-child { + margin-bottom: 0; + } + } + + // ==================== + + // basic layout + .content-primary, .content-supplementary { + @include box-sizing(border-box); + float: left; + } + + .content-primary { + width: flex-grid(9, 12); + margin-right: flex-gutter(); + } + + .content-supplementary { + width: flex-grid(3, 12); + } + + // ==================== + + // elements - notices + .content .notice-incontext { + width: flexgrid(9, 9); + + // CASE: notice has actions { + &.has-actions { + + .msg, .list-actions { + display: inline-block; + vertical-align: middle; + } + + .msg { + width: flex-grid(6, 9); + margin-right: flex-gutter(); + } + + .list-actions { + width: flex-grid(3, 9); + text-align: right; + margin-top: 0; + + .action-item { + + } + + .action-create-course { + @extend .btn-primary-green; + } + } + } + } + + + + // elements - authorship controls + .wrapper-authorshiprights { + overflow: hidden; + + .ui-toggle-control { + @extend .depth2; + @extend .btn-secondary-gray; + @include clearfix(); + display: block; + text-align: left; + + // STATE: hover - syncing up colors with current so transition is smoother + &:hover { + background: $gray-d1; + color: $white; + } + + .label { + @extend .t-action3; + float: left; + width: flex-grid(8, 9); + margin: 3px flex-gutter() 0 0; + } + + .icon-remove-sign { + @extend .t-action1; + @include transition(rotate 10.0s ease-in-out 0s); + @include transform(rotate(45deg)); + @include transform-origin(center center); + float: right; + text-align: right; + } + } + + .ui-toggle-target { + @extend .depth1; + @include transition(opacity 0.50s ease-in-out 0s); + position: relative; + top: -2px; + display: none; + opacity: 0; + } + + // CASE: when the content area is shown + &.is-shown { + + .ui-toggle-control { + @include border-bottom-radius(0); + + .icon-remove-sign { + @include transform(rotate(90deg)); + @include transform-origin(center center); + } + } + + .ui-toggle-target { + display: block; + opacity: 1.0; + } + } + + + } + + // elements - authorship controls + .status-authorship { + margin-top: $baseline; + + .title { + @extend .t-title7; + margin-bottom: ($baseline/4); + font-weight: 700; + color: $gray-d1; + } + + .copy { + + } + + .list-actions { + margin-top: ($baseline*0.75); + + .action-item { + + } + + .action-primary { + @extend .btn-primary-blue; + @extend .t-action3; + } + } + + .status-update { + + .label { + @extend .text-sr; + } + + .value { + @include border-radius(($baseline/4)); + position: relative; + overflow: hidden; + padding: ($baseline/5) ($baseline/2); + background: $gray; + + .status-indicator { + position: absolute; + top: 0; + left: 0; + display: block; + width: 100%; + height: ($baseline/4); + opacity: 0.40; + } + } + + .value-formal, .value-description { + @include border-radius(($baseline/10)); + display: inline-block; + vertical-align: middle; + color: $white; + } + + .value-formal { + @extend .t-title5; + margin: ($baseline/2); + font-weight: 700; + + [class^="icon-"] { + margin-right: ($baseline/4); + } + } + + .value-description { + @extend .t-copy-sub1; + position: relative; + color: $white; + opacity: 0.65; + } + } + + // CASE: rights are not requested yet + &.is-unrequested { + + .title { + @extend .text-sr; + } + } + + // CASE: status is pending + &.is-pending { + + .status-update { + + .value { + background: $orange; + } + + .status-indicator { + background: $orange-d1; + } + } + } + + + // CASE: status is rejected + &.is-rejected { + + .status-update { + + .value { + background: $red-l1; + } + + .status-indicator { + background: $red-s1; + } + } + } + } + + // ==================== + .my-classes { - margin-top: $baseline; + margin: $baseline 0; } .class-list { diff --git a/cms/templates/index.html b/cms/templates/index.html index 5a1003b391..21cbc26799 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -5,6 +5,22 @@ <%block name="title">${_("My Courses")} <%block name="bodyclass">is-signedin index dashboard +<%block name="jsextra"> + + + + <%block name="header_extras"> - - <%block name="header_extras"> + + <%block name="title">${_("My Courses")} <%block name="bodyclass">is-signedin index dashboard @@ -45,13 +61,12 @@ @@ -65,18 +80,18 @@
- - - - - -

${_("Welcome, %(name)s!") % dict(name= user.username)}

-

${_("Here are all of the courses you currently have access to in Studio:")}

+ %if len(courses) > 0: +

${_("Here are all of the courses you currently have access to in Studio:")}

+ %else: + +

${_("Sorry, you don't have any courses!")}

+ %endif
+
@@ -103,20 +118,21 @@

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while Course Authorship privileges are granted by edX. Our team will evaluate your request and provide you feedback within 24 hours during the work week.')}

- !-- if request is unrequested --> - %if course_creator_status = "unrequested": + + %if course_creator_status == "unrequested": - !-- if request is pending --> - %elif course_creator_status = "pending": + + %elif course_creator_status == "pending":

${_('Your Authorship Request Status:')}

@@ -130,8 +146,8 @@
- !-- if request is denied --> - %elif course_creator_status = "denied": + + %elif course_creator_status == "denied":

${_('Your Authorship Request Status:')}

@@ -174,14 +190,14 @@ % endif - % if not disable_course_creation and course_creator_status = "unrequested": + % if not disable_course_creation and course_creator_status == "unrequested":

${_('Can I create courses in Studio?')}

${_('In order to create courses in Studio, you must have authorship rights to create your own course.')}

- % elif not disable_course_creation and course_creator_status = "denied": + % elif not disable_course_creation and course_creator_status == "denied":

${_('Can I create courses in Studio?')}

${_('Your request to author courses in studio has been denied. Please')} ${_('contact edX Staff with further questions')}

@@ -204,7 +220,7 @@

${_('We need to verify your email address')}

-

${_('Almost there! In order to complete your sign up we need you verify your $emailaddress email address. An activation message and next steps should be waiting for you there.')}

+

${_('Almost there! In order to complete your sign up we need you verify your email address (%(email)s). An activation message and next steps should be waiting for you there.') % dict(email=user.email)}

From 135d10d4c554114a33838937c3d50c681e5b9ec6 Mon Sep 17 00:00:00 2001 From: cahrens Date: Wed, 10 Jul 2013 17:26:55 -0400 Subject: [PATCH 15/36] Fix unicode return value. --- cms/djangoapps/course_creators/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/djangoapps/course_creators/models.py b/cms/djangoapps/course_creators/models.py index 607dae4af2..31bcbc5611 100644 --- a/cms/djangoapps/course_creators/models.py +++ b/cms/djangoapps/course_creators/models.py @@ -39,7 +39,7 @@ class CourseCreator(models.Model): "why course creation access was denied)")) def __unicode__(self): - return u'%str | %str [%str] | %str' % (self.user, self.state, self.state_changed, self.note) + return u'%s | %s [%s]' % (self.user, self.state, self.state_changed) @receiver(post_init, sender=CourseCreator) From aaa67b28a9f86e0ba206ddd44464ee9cd0f29d27 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Thu, 11 Jul 2013 11:10:54 -0400 Subject: [PATCH 16/36] Studio: revises template logic, html, and styling for Dashboard UI states --- cms/static/sass/_base.scss | 18 ++ cms/static/sass/elements/_system-help.scss | 20 +- cms/static/sass/views/_dashboard.scss | 19 +- cms/templates/index.html | 247 +++++++++++++-------- 4 files changed, 195 insertions(+), 109 deletions(-) diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index fe107d7511..e9b01509fe 100644 --- a/cms/static/sass/_base.scss +++ b/cms/static/sass/_base.scss @@ -446,6 +446,24 @@ p, ul, ol, dl { } } + // actions + .list-actions { + @extend .no-list; + + .action-item { + margin-bottom: ($baseline/4); + border-bottom: 1px dotted $gray-l4; + padding-bottom: ($baseline/4); + + + &:last-child { + margin-bottom: 0; + border: none; + padding-bottom: 0; + } + } + } + // navigation .nav-related, .nav-page { diff --git a/cms/static/sass/elements/_system-help.scss b/cms/static/sass/elements/_system-help.scss index ca8fa38154..0f90d9db5c 100644 --- a/cms/static/sass/elements/_system-help.scss +++ b/cms/static/sass/elements/_system-help.scss @@ -104,6 +104,22 @@ } } } + + // list of notices all in one + &.list-notices { + + .notice-item { + margin-bottom: $baseline; + border-bottom: 1px solid $gray-l3; + padding-bottom: $baseline; + + &:last-child { + margin-bottom: 0; + border: none; + padding-bottom: 0; + } + } + } } // particular notice - warnings around a workflow for something @@ -128,11 +144,11 @@ background-color: $gray-l4; .title { - color: $gray-d1; + color: $gray-d3; } .copy { - color: $gray; + color: $gray-d2; } &.has-actions { diff --git a/cms/static/sass/views/_dashboard.scss b/cms/static/sass/views/_dashboard.scss index 7132874c14..b00306e5c5 100644 --- a/cms/static/sass/views/_dashboard.scss +++ b/cms/static/sass/views/_dashboard.scss @@ -36,7 +36,7 @@ body.dashboard { width: flexgrid(9, 9); // CASE: notice has actions { - &.has-actions { + &.has-actions, &.list-notices .notice-item.has-actions { .msg, .list-actions { display: inline-block; @@ -59,6 +59,7 @@ body.dashboard { .action-create-course { @extend .btn-primary-green; + @extend .t-action3; } } } @@ -66,8 +67,8 @@ body.dashboard { - // elements - authorship controls - .wrapper-authorshiprights { + // elements - course creation rights controls + .wrapper-creationrights { overflow: hidden; .ui-toggle-control { @@ -129,8 +130,8 @@ body.dashboard { } - // elements - authorship controls - .status-authorship { + // elements - course creation rights controls + .status-creationrights { margin-top: $baseline; .title { @@ -144,7 +145,7 @@ body.dashboard { } - .list-actions { + .list-actions, .form-actions { margin-top: ($baseline*0.75); .action-item { @@ -253,10 +254,10 @@ body.dashboard { } .class-list { - margin-top: 20px; + margin-top: $baseline; border-radius: 3px; - border: 1px solid $darkGrey; - background: #fff; + border: 1px solid $gray-d2; + background: $white; box-shadow: 0 1px 2px rgba(0, 0, 0, .1); li { diff --git a/cms/templates/index.html b/cms/templates/index.html index 0f10758dd6..4166542f35 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -2,22 +2,6 @@ <%inherit file="base.html" /> -<%block name="jsextra"> - - - - <%block name="title">${_("My Courses")} <%block name="bodyclass">is-signedin index dashboard @@ -50,6 +34,19 @@ +<%block name="jsextra"> + + + <%block name="content">
@@ -60,13 +57,12 @@

${_("Page Actions")}

@@ -75,7 +71,6 @@
- % if user.is_active:
@@ -83,86 +78,146 @@

${_("Welcome, %(name)s!") % dict(name= user.username)}

+ %if len(courses) > 0:
- %if len(courses) > 0: -

${_("Here are all of the courses you currently have access to in Studio:")}

- %else: - -

${_("Sorry, you don't have any courses!")}

- %endif +

${_("Here are all of the courses you currently have access to in Studio:")}

+ %else: +
+

${_("You currently aren't associated with any Studio Courses.")}

+
+ %endif
-
-
    - %for course, url, lms_link in sorted(courses, key=lambda s: s[0].lower() if s[0] is not None else ''): -
  • - - ${course} - - View Live -
  • - %endfor -
+ %if len(courses) > 0: +
+
+
    + %for course, url, lms_link in sorted(courses, key=lambda s: s[0].lower() if s[0] is not None else ''): +
  • + + ${course} + + View Live +
  • + %endfor +
+
+ %else: +
+
+
+

${_("Are you staff on an existing Studio course?")}

+
+

${_('You will need to be added to the course in Studio by the Course Creator. Please get in touch with the course creator or administrator for the specific course you are helping to author.')}

+
+
+
+ + %if not disable_course_creation and course_creator_status == "granted": +
+
+

${_('Create Your First Course')}

+
+

${_('Your new course is just a click away!')}

+
+
+ + +
+ % endif + +
+ % endif + % if not disable_course_creation and course_creator_status != "granted": -
-

- ${_('Becoming a Course Author in Studio')} -

-
-
-

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while Course Authorship privileges are granted by edX. Our team will evaluate your request and provide you feedback within 24 hours during the work week.')}

+ %if course_creator_status == "unrequested": +
+

+ ${_('Becoming a Course Creator in Studio')} +

+ +
+
+

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while Course Creator privileges are granted by edX. Our team will evaluate your request and provide you feedback within 24 hours during the work week.')}

+
+ +
+

${_('Your Course Creator Request Status:')}

+ +
+ + + +
+ +
+
+
- - - %if course_creator_status == "unrequested": -
-

${_('Your Authorship Request Status:')}

- - -
- - - %elif course_creator_status == "pending": -
-

${_('Your Authorship Request Status:')}

- -
-
${_('Your authorship request is:')}
-
- - ${_('Pending')} - ${_('Your request is currently being reviewed by edX staff and should be updated shortly.')} -
-
-
- - - %elif course_creator_status == "denied": -
-

${_('Your Authorship Request Status:')}

- -
-
${_('Your authorship request is:')}
-
- - ${_('Denied')} - ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} -
-
-
- % endif
-
+ + %elif course_creator_status == "denied": +
+

+ ${_('Your Course Creator Request Status')} +

+ +
+
+

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while Course Creator privileges are granted by edX. Our team is has completed evaluating your request.')}

+
+ +
+

${_('Your Course Creator Request Status:')}

+ +
+
${_('Your Course Creator request is:')}
+
+ + ${_('Denied')} + ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} +
+
+
+
+
+ + %elif course_creator_status == "pending": +
+

+ ${_('Your Course Creator Request Status')} +

+ +
+
+

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while Course Creator privileges are granted by edX. Our team is currently evaluating your request.')}

+
+ +
+

${_('Your Course Creator Request Status:')}

+ +
+
${_('Your Course Creator request is:')}
+
+ + ${_('Pending')} + ${_('Your request is currently being reviewed by edX staff and should be updated shortly.')} +
+
+
+
+
+ % endif + % endif
@@ -181,7 +236,6 @@
- % if disable_course_creation and settings.MITX_FEATURES.get('STAFF_EMAIL',''):

${_('Can I create courses in Studio?')}

@@ -189,14 +243,12 @@
% endif - % if not disable_course_creation and course_creator_status == "unrequested":

${_('Can I create courses in Studio?')}

-

${_('In order to create courses in Studio, you must have authorship rights to create your own course.')}

+

${_('In order to create courses in Studio, you must have Course Creator privileges to create your own course.')}

- % elif not disable_course_creation and course_creator_status == "denied":

${_('Can I create courses in Studio?')}

@@ -208,7 +260,6 @@
- % else:
From 51c9523f979a0916d3cb89b4c17e765b2ab2a1b6 Mon Sep 17 00:00:00 2001 From: cahrens Date: Thu, 11 Jul 2013 12:36:30 -0400 Subject: [PATCH 17/36] Only send a single variable to index.html about authorship rights. --- cms/templates/index.html | 241 +++++++++++++++++++++++++-------------- 1 file changed, 153 insertions(+), 88 deletions(-) diff --git a/cms/templates/index.html b/cms/templates/index.html index 1df0878bd0..3b7b2d74b4 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -34,6 +34,19 @@ +<%block name="jsextra"> + + + <%block name="content">
@@ -44,12 +57,10 @@

${_("Page Actions")}

- % if user.is_active:
- - - - - -

${_("Welcome, %(name)s!") % dict(name= user.username)}

+ %if len(courses) > 0:

${_("Here are all of the courses you currently have access to in Studio:")}

+ + %else: +
+

${_("You currently aren't associated with any Studio Courses.")}

+
+ %endif
-
-
    - %for course, url, lms_link in sorted(courses, key=lambda s: s[0].lower() if s[0] is not None else ''): -
  • - - ${course} - - View Live -
  • - %endfor -
-
- - % if not disable_course_creation and course_creator_status != "granted": -
-

- ${_('Becoming a Course Author in Studio')} -

- -
-
-

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while Course Authorship privileges are granted by edX. Our team will evaluate your request and provide you feedback within 24 hours during the work week.')}

-
- - !-- if request is unrequested --> - %if course_creator_status = "unrequested": -
-

${_('Your Authorship Request Status:')}

- - -
- - !-- if request is pending --> - %elif course_creator_status = "pending": -
-

${_('Your Authorship Request Status:')}

- -
-
${_('Your authorship request is:')}
-
- - ${_('Pending')} - ${_('Your request is currently being reviewed by edX staff and should be updated shortly.')} -
-
-
- - !-- if request is denied --> - %elif course_creator_status = "denied": -
-

${_('Your Authorship Request Status:')}

- -
-
${_('Your authorship request is:')}
-
- - ${_('Denied')} - ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} -
-
-
- % endif + %if len(courses) > 0: +
+
+
    + %for course, url, lms_link in sorted(courses, key=lambda s: s[0].lower() if s[0] is not None else ''): +
  • + + ${course} + + View Live +
  • + %endfor +
+ + %else: +
+
+
+

${_("Are you staff on an existing Studio course?")}

+
+

${_('You will need to be added to the course in Studio by the course creator. Please get in touch with the course creator or administrator for the specific course you are helping to author.')}

+
+
+
+ + %if course_creator_status == "granted": +
+
+

${_('Create Your First Course')}

+
+

${_('Your new course is just a click away!')}

+
+
+ + +
+ % endif + +
% endif + + + %if course_creator_status == "unrequested": +
+

+ ${_('Becoming a Course Creator in Studio')} +

+ +
+
+

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while course creator privileges are granted by edX. Our team will evaluate your request and provide you feedback within 24 hours during the work week.')}

+
+ +
+

${_('Your Course Creator Request Status:')}

+ +
+ + + +
+ +
+
+
+
+
+ + %elif course_creator_status == "denied": +
+

+ ${_('Your Course Creator Request Status')} +

+ +
+
+

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while course creator privileges are granted by edX. Our team is has completed evaluating your request.')}

+
+ +
+

${_('Your Course Creator Request Status:')}

+ +
+
${_('Your Course Creator request is:')}
+
+ + ${_('Denied')} + ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} +
+
+
+
+
+ + %elif course_creator_status == "pending": +
+

+ ${_('Your Course Creator Request Status')} +

+ +
+
+

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while course creator privileges are granted by edX. Our team is currently evaluating your request.')}

+
+ +
+

${_('Your Course Creator Request Status:')}

+ +
+
${_('Your Course Creator request is:')}
+
+ + ${_('Pending')} + ${_('Your request is currently being reviewed by edX staff and should be updated shortly.')} +
+
+
+
+
+ % endif +
- - % if disable_course_creation and settings.MITX_FEATURES.get('STAFF_EMAIL',''): + % if course_creator_status=='disallowed_for_this_site' and settings.MITX_FEATURES.get('STAFF_EMAIL',''):

${_('Can I create courses in Studio?')}

${_('In order to create courses in Studio, you must')} ${_("contact edX staff to help you create a course")}

% endif - - % if not disable_course_creation and course_creator_status = "unrequested": + % if course_creator_status == "unrequested":

${_('Can I create courses in Studio?')}

-

${_('In order to create courses in Studio, you must have authorship rights to create your own course.')}

+

${_('In order to create courses in Studio, you must have course creator privileges to create your own course.')}

- - % elif not disable_course_creation and course_creator_status = "denied": + % elif course_creator_status == "denied":

${_('Can I create courses in Studio?')}

${_('Your request to author courses in studio has been denied. Please')} ${_('contact edX Staff with further questions')}

@@ -192,7 +258,6 @@
- % else:
@@ -204,7 +269,7 @@

${_('We need to verify your email address')}

-

${_('Almost there! In order to complete your sign up we need you verify your $emailaddress email address. An activation message and next steps should be waiting for you there.')}

+

${_('Almost there! In order to complete your sign up we need you verify your email address (%(email)s). An activation message and next steps should be waiting for you there.') % dict(email=user.email)}

From 3715d6c429865f4ef6e869daff1a10f7b9d12fe8 Mon Sep 17 00:00:00 2001 From: cahrens Date: Thu, 11 Jul 2013 12:39:51 -0400 Subject: [PATCH 18/36] Only send a single variable to index.html about authorship rights. --- cms/djangoapps/contentstore/views/user.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cms/djangoapps/contentstore/views/user.py b/cms/djangoapps/contentstore/views/user.py index dae0d246a5..dbb2fbddb6 100644 --- a/cms/djangoapps/contentstore/views/user.py +++ b/cms/djangoapps/contentstore/views/user.py @@ -11,6 +11,7 @@ from contentstore.utils import get_url_reverse, get_lms_link_for_item from util.json_request import expect_json, JsonResponse from auth.authz import STAFF_ROLE_NAME, INSTRUCTOR_ROLE_NAME, get_users_in_course_group_by_role from auth.authz import get_user_by_email, add_user_to_course_group, remove_user_from_course_group +from course_creators.views import get_course_creator_status, add_user_with_status_unrequested from .access import has_access @@ -32,6 +33,18 @@ def index(request): and course.location.name != '') courses = filter(course_filter, courses) + if settings.MITX_FEATURES.get('DISABLE_COURSE_CREATION', False): + course_creator_status = 'granted' if request.user.is_staff else 'disallowed_for_this_site' + elif settings.MITX_FEATURES.get('ENABLE_CREATOR_GROUP', False): + course_creator_status = get_course_creator_status(request.user) + if course_creator_status is None: + # User not grandfathered in as an existing user, has not previously visited the dashboard page. + # Add the user to the course creator admin table with status 'unrequested'. + add_user_with_status_unrequested(request.user) + course_creator_status = get_course_creator_status(request.user) + else: + course_creator_status = 'granted' + return render_to_response('index.html', { 'new_course_template': Location('i4x', 'edx', 'templates', 'course', 'Empty'), 'courses': [(course.display_name, @@ -39,7 +52,7 @@ def index(request): get_lms_link_for_item(course.location, course_id=course.location.course_id)) for course in courses], 'user': request.user, - 'disable_course_creation': settings.MITX_FEATURES.get('DISABLE_COURSE_CREATION', False) and not request.user.is_staff + 'course_creator_status': course_creator_status }) From 59850cb4a3a24e6289222926628de7f4d751856c Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Thu, 11 Jul 2013 12:44:13 -0400 Subject: [PATCH 19/36] Studio: cleans up new course form and course listings display --- cms/static/js/base.js | 6 ++-- cms/static/sass/views/_dashboard.scss | 43 ++++++++++++++++----------- cms/templates/index.html | 25 ++++++++-------- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/cms/static/js/base.js b/cms/static/js/base.js index c2f401c77e..a6218541ef 100644 --- a/cms/static/js/base.js +++ b/cms/static/js/base.js @@ -577,10 +577,10 @@ function cancelNewSection(e) { function addNewCourse(e) { e.preventDefault(); - $(e.target).hide(); + $(e.target).addClass('disabled'); var $newCourse = $($('#new-course-template').html()); var $cancelButton = $newCourse.find('.new-course-cancel'); - $('.inner-wrapper').prepend($newCourse); + $('.courses').prepend($newCourse); $newCourse.find('.new-course-name').focus().select(); $newCourse.find('form').bind('submit', saveNewCourse); $cancelButton.bind('click', cancelNewCourse); @@ -627,7 +627,7 @@ function saveNewCourse(e) { function cancelNewCourse(e) { e.preventDefault(); - $('.new-course-button').show(); + $('.new-course-button').removeClass('disabled'); $(this).parents('section.new-course').remove(); } diff --git a/cms/static/sass/views/_dashboard.scss b/cms/static/sass/views/_dashboard.scss index b00306e5c5..a2a3c72fb1 100644 --- a/cms/static/sass/views/_dashboard.scss +++ b/cms/static/sass/views/_dashboard.scss @@ -203,7 +203,7 @@ body.dashboard { @extend .t-copy-sub1; position: relative; color: $white; - opacity: 0.65; + opacity: 0.85; } } @@ -249,20 +249,21 @@ body.dashboard { // ==================== - .my-classes { + // course listings + .courses { margin: $baseline 0; } - .class-list { + .list-courses { margin-top: $baseline; border-radius: 3px; - border: 1px solid $gray-d2; + border: 1px solid $gray; background: $white; - box-shadow: 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: 0 1px 2px $shadow-l1; - li { + .course-item { position: relative; - border-bottom: 1px solid $mediumGrey; + border-bottom: 1px solid $gray-l1; &:last-child { border-bottom: none; @@ -302,7 +303,7 @@ body.dashboard { .view-live-button { z-index: 10000; position: absolute; - top: 15px; + top: ($baseline*0.75); right: $baseline; padding: ($baseline/4) ($baseline/2); opacity: 0.0; @@ -316,17 +317,25 @@ body.dashboard { } .new-course { - padding: 15px 25px; - margin-top: 20px; + @include clearfix(); + padding: ($baseline*0.75) ($baseline*1.25); + margin-top: $baseline; border-radius: 3px; - border: 1px solid $darkGrey; - background: #fff; + border: 1px solid $gray; + background: $white; box-shadow: 0 1px 2px rgba(0, 0, 0, .1); - @include clearfix; + + .title { + @extend .t-title4; + font-weight: 600; + margin-bottom: ($baseline/2); + border-bottom: 1px solid $gray-l3; + padding-bottom: ($baseline/2); + } .row { - margin-bottom: 15px; - @include clearfix; + @include clearfix(); + margin-bottom: ($baseline*0.75); } .column { @@ -343,8 +352,8 @@ body.dashboard { } label { + @extend .t-title7; display: block; - font-size: 13px; font-weight: 700; } @@ -355,7 +364,7 @@ body.dashboard { } .new-course-name { - font-size: 19px; + @extend .t-title5; font-weight: 300; } diff --git a/cms/templates/index.html b/cms/templates/index.html index 3b7b2d74b4..74831d1984 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -8,6 +8,7 @@ <%block name="header_extras"> + + <%block name="header_extras"> @@ -151,10 +158,8 @@

${_('Your Course Creator Request Status:')}

-
- - - + +
diff --git a/cms/urls.py b/cms/urls.py index 56efd1a557..67314178f8 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -12,6 +12,7 @@ admin.autodiscover() urlpatterns = ('', # nopep8 url(r'^$', 'contentstore.views.howitworks', name='homepage'), url(r'^listing', 'contentstore.views.index', name='index'), + url(r'^request_course_creator$', 'contentstore.views.request_course_creator', name='request_course_creator'), url(r'^edit/(?P.*?)$', 'contentstore.views.edit_unit', name='edit_unit'), url(r'^subsection/(?P.*?)$', 'contentstore.views.edit_subsection', name='edit_subsection'), url(r'^preview_component/(?P.*?)$', 'contentstore.views.preview_component', name='preview_component'), From b4457371a86713051a3c3c72669d3c1e28bc47e7 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Thu, 11 Jul 2013 14:53:02 -0400 Subject: [PATCH 22/36] Studio: revises and syncs up activation message UI with dashboard UI --- cms/templates/activation_active.html | 35 +++++++++++++++------ cms/templates/activation_complete.html | 35 ++++++++++++++++----- cms/templates/activation_invalid.html | 43 +++++++++++++++++++------- 3 files changed, 85 insertions(+), 28 deletions(-) diff --git a/cms/templates/activation_active.html b/cms/templates/activation_active.html index 712c73abf9..51c01f1155 100644 --- a/cms/templates/activation_active.html +++ b/cms/templates/activation_active.html @@ -1,14 +1,31 @@ <%inherit file="base.html" /> <%block name="content"> - -
-
- -
-

Account already active!

-

This account has already been activated. Log in here.

+
+
+

${_("Studio Account Activation")}

+
-
- \ No newline at end of file +
+
+
+
+ +
+
+

${_("Your account is already active")}

+
+

${_("This account, set up using (%(email)s), has already been activated. Please sign in to start working within edX Studio.") % dict(email=user.email)}

+
+
+ + +
+
+
+ diff --git a/cms/templates/activation_complete.html b/cms/templates/activation_complete.html index 1e195a632c..d6bdab36dc 100644 --- a/cms/templates/activation_complete.html +++ b/cms/templates/activation_complete.html @@ -1,12 +1,33 @@ <%inherit file="base.html" /> <%block name="content"> - -
-
-

Activation Complete!

-

Thanks for activating your account. Log in here.

+
+
+

${_("Studio Account Activation")}

+
-
- \ No newline at end of file +
+
+
+
+ +
+
+

${_("Your account activation is complete!")}

+
+

${_("Thank you for activating your account. You may now sign in and start using edX Studio to author courses.")}

+
+
+ + +
+
+
+ + + diff --git a/cms/templates/activation_invalid.html b/cms/templates/activation_invalid.html index c4eb16875b..0c663aa307 100644 --- a/cms/templates/activation_invalid.html +++ b/cms/templates/activation_invalid.html @@ -1,16 +1,35 @@ <%inherit file="base.html" /> <%block name="content"> -
-
-

Activation Invalid

- -

Something went wrong. Check to make sure the URL you went to was - correct -- e-mail programs will sometimes split it into two - lines. If you still have issues, e-mail us to let us know what happened - at bugs@mitx.mit.edu.

- -

Or you can go back to the home page.

+
+
+

${_("Studio Account Activation")}

+
-
- \ No newline at end of file + +
+
+
+
+ +
+
+

${_('Your account activation is invalid')}

+
+

${_("We're sorry. Something went wrong with your activation. Check to make sure the URL you went to was correct — e-mail programs will sometimes split it into two lines.")}

+

${_("If you still have issues, contact edX Support. In the meatime, you can also return to")} {_('the Studio homepage.')}

+
+
+ + +
+
+
+ + + + From c360faed5893c276a409093c648ec4789f094d0c Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Thu, 11 Jul 2013 16:30:30 -0400 Subject: [PATCH 23/36] Studio: revises dashboard course creation button and form interaction --- cms/static/js/base.js | 2 +- cms/templates/index.html | 121 ++++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 60 deletions(-) diff --git a/cms/static/js/base.js b/cms/static/js/base.js index a6218541ef..5042764212 100644 --- a/cms/static/js/base.js +++ b/cms/static/js/base.js @@ -576,7 +576,7 @@ function cancelNewSection(e) { function addNewCourse(e) { e.preventDefault(); - + $('.new-course-button').addClass('disabled'); $(e.target).addClass('disabled'); var $newCourse = $($('#new-course-template').html()); var $cancelButton = $newCourse.find('.new-course-cancel'); diff --git a/cms/templates/index.html b/cms/templates/index.html index d182f4c174..dd36f19b44 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -113,6 +113,9 @@
%else: +
+
+
@@ -144,82 +147,82 @@ % endif - %if course_creator_status == "unrequested": -
-

- ${_('Becoming a Course Creator in Studio')} -

+ %if course_creator_status == "unrequested": +
+

+ ${_('Becoming a Course Creator in Studio')} +

-
-
-

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while course creator privileges are granted by edX. Our team will evaluate your request and provide you feedback within 24 hours during the work week.')}

-
+
+
+

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while course creator privileges are granted by edX. Our team will evaluate your request and provide you feedback within 24 hours during the work week.')}

+
-
-

${_('Your Course Creator Request Status:')}

+
+

${_('Your Course Creator Request Status:')}

- - -
- -
- +
+ +
+ +
+
+
-
- %elif course_creator_status == "denied": -
-

- ${_('Your Course Creator Request Status')} -

+ %elif course_creator_status == "denied": +
+

+ ${_('Your Course Creator Request Status')} +

-
-
-

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while course creator privileges are granted by edX. Our team is has completed evaluating your request.')}

-
+
+
+

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while course creator privileges are granted by edX. Our team is has completed evaluating your request.')}

+
-
-

${_('Your Course Creator Request Status:')}

+
+

${_('Your Course Creator Request Status:')}

-
-
${_('Your Course Creator request is:')}
-
- - ${_('Denied')} - ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} -
-
+
+
${_('Your Course Creator request is:')}
+
+ + ${_('Denied')} + ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} +
+
+
-
- %elif course_creator_status == "pending": -
-

- ${_('Your Course Creator Request Status')} -

+ %elif course_creator_status == "pending": +
+

+ ${_('Your Course Creator Request Status')} +

-
-
-

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while course creator privileges are granted by edX. Our team is currently evaluating your request.')}

-
+
+
+

${_('edX Studio is a hosted solution for our xConsortium partners and selected guests. Courses for which you are a team member appear above for you to edit, while course creator privileges are granted by edX. Our team is currently evaluating your request.')}

+
-
-

${_('Your Course Creator Request Status:')}

+
+

${_('Your Course Creator Request Status:')}

-
-
${_('Your Course Creator request is:')}
-
- - ${_('Pending')} - ${_('Your request is currently being reviewed by edX staff and should be updated shortly.')} -
-
+
+
${_('Your Course Creator request is:')}
+
+ + ${_('Pending')} + ${_('Your request is currently being reviewed by edX staff and should be updated shortly.')} +
+
+
-
- % endif + % endif From 4ecceb292e272bba960b445adea15a6230112e74 Mon Sep 17 00:00:00 2001 From: cahrens Date: Fri, 12 Jul 2013 09:30:36 -0400 Subject: [PATCH 24/36] Add tests for changing table status without staff permissions. --- cms/djangoapps/course_creators/models.py | 9 ++++--- .../course_creators/tests/test_views.py | 25 +++++++++++++------ cms/djangoapps/course_creators/views.py | 15 ++++++----- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/cms/djangoapps/course_creators/models.py b/cms/djangoapps/course_creators/models.py index 7645b21575..b370ac423a 100644 --- a/cms/djangoapps/course_creators/models.py +++ b/cms/djangoapps/course_creators/models.py @@ -54,20 +54,23 @@ def post_init_callback(sender, **kwargs): @receiver(post_save, sender=CourseCreator) def post_save_callback(sender, **kwargs): """ - Extend to update state_changed time and modify the course creator group in authz.py. + Extend to update state_changed time and fire event to update course creator group, if appropriate. """ instance = kwargs['instance'] # We only wish to modify the state_changed time if the state has been modified. We don't wish to # modify it for changes to the notes field. if instance.state != instance.orig_state: - if hasattr(instance, 'admin'): + # If either old or new state is 'granted', we must manipulate the course creator + # group maintained by authz. That requires staff permissions (stored admin). + if instance.state == CourseCreator.GRANTED or instance.orig_state == CourseCreator.GRANTED: + assert hasattr(instance, 'admin'), 'Must have stored staff user to change course creator group' update_creator_state.send( sender=sender, caller=instance.admin, user=instance.user, add=instance.state == CourseCreator.GRANTED ) - # TODO: Else must be sure that state change does not switch to or from granted + instance.state_changed = timezone.now() instance.orig_state = instance.state instance.save() diff --git a/cms/djangoapps/course_creators/tests/test_views.py b/cms/djangoapps/course_creators/tests/test_views.py index bd91208b9c..943120f3c0 100644 --- a/cms/djangoapps/course_creators/tests/test_views.py +++ b/cms/djangoapps/course_creators/tests/test_views.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import User from django.core.exceptions import PermissionDenied from course_creators.views import add_user_with_status_unrequested, add_user_with_status_granted -from course_creators.views import get_course_creator_status, update_course_creator_group +from course_creators.views import get_course_creator_status, update_course_creator_group, user_requested_access from course_creators.models import CourseCreator from auth.authz import is_user_in_creator_group import mock @@ -26,14 +26,11 @@ class CourseCreatorView(TestCase): def test_staff_permission_required(self): """ - Tests that add methods and course creator group method must be called with staff permissions. + Tests that any method changing the course creator authz group must be called with staff permissions. """ with self.assertRaises(PermissionDenied): add_user_with_status_granted(self.user, self.user) - with self.assertRaises(PermissionDenied): - add_user_with_status_unrequested(self.user, self.user) - with self.assertRaises(PermissionDenied): update_course_creator_group(self.user, self.user, True) @@ -41,7 +38,7 @@ class CourseCreatorView(TestCase): self.assertIsNone(get_course_creator_status(self.user)) def test_add_unrequested(self): - add_user_with_status_unrequested(self.admin, self.user) + add_user_with_status_unrequested(self.user) self.assertEqual('unrequested', get_course_creator_status(self.user)) # Calling add again will be a no-op (even if state is different). @@ -57,7 +54,7 @@ class CourseCreatorView(TestCase): self.assertEqual('granted', get_course_creator_status(self.user)) # Calling add again will be a no-op (even if state is different). - add_user_with_status_unrequested(self.admin, self.user) + add_user_with_status_unrequested(self.user) self.assertEqual('granted', get_course_creator_status(self.user)) self.assertTrue(is_user_in_creator_group(self.user)) @@ -69,3 +66,17 @@ class CourseCreatorView(TestCase): self.assertTrue(is_user_in_creator_group(self.user)) update_course_creator_group(self.admin, self.user, False) self.assertFalse(is_user_in_creator_group(self.user)) + + def test_user_requested_access(self): + add_user_with_status_unrequested(self.user) + self.assertEqual('unrequested', get_course_creator_status(self.user)) + user_requested_access(self.user) + self.assertEqual('pending', get_course_creator_status(self.user)) + + def test_user_requested_already_granted(self): + add_user_with_status_granted(self.admin, self.user) + self.assertEqual('granted', get_course_creator_status(self.user)) + # Will not "downgrade" to pending because that would require removing the + # user from the authz course creator group (and that can only be done by an admin). + user_requested_access(self.user) + self.assertEqual('granted', get_course_creator_status(self.user)) diff --git a/cms/djangoapps/course_creators/views.py b/cms/djangoapps/course_creators/views.py index 12993d1be4..6b2d91acb2 100644 --- a/cms/djangoapps/course_creators/views.py +++ b/cms/djangoapps/course_creators/views.py @@ -20,10 +20,11 @@ def add_user_with_status_granted(caller, user): """ Adds a user to the course creator table with status 'granted'. - If the user is already in the table, this method is a no-op - (state will not be changed). Caller must have staff permissions. - This method also adds the user to the course creator group maintained by authz.py. + Caller must have staff permissions. + + If the user is already in the table, this method is a no-op + (state will not be changed). """ _add_user(user, CourseCreator.GRANTED) update_course_creator_group(caller, user, True) @@ -64,11 +65,13 @@ def user_requested_access(user): """ User has requested course creator access. - This changes the user state to CourseCreator.PENDING. + This changes the user state to CourseCreator.PENDING, unless the user + state is already CourseCreator.GRANTED, in which case this method is a no-op. """ user = CourseCreator.objects.get(user=user) - user.state = CourseCreator.PENDING - user.save() + if user.state != CourseCreator.GRANTED: + user.state = CourseCreator.PENDING + user.save() def _add_user(user, state): From d417a78c7493630a2f8ed1d701dd7d78ae266689 Mon Sep 17 00:00:00 2001 From: cahrens Date: Fri, 12 Jul 2013 11:25:12 -0400 Subject: [PATCH 25/36] Tests for course creator status as returned in index page. --- .../contentstore/tests/test_users.py | 186 ++++++++++++++++++ cms/djangoapps/contentstore/views/user.py | 50 +++-- 2 files changed, 217 insertions(+), 19 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_users.py b/cms/djangoapps/contentstore/tests/test_users.py index f945ef50fc..bba4d42f4d 100644 --- a/cms/djangoapps/contentstore/tests/test_users.py +++ b/cms/djangoapps/contentstore/tests/test_users.py @@ -1,6 +1,18 @@ +""" +Tests for user.py. +""" import json +import mock from .utils import CourseTestCase from django.core.urlresolvers import reverse +from contentstore.views.user import _get_course_creator_status +from course_creators.views import add_user_with_status_granted +from course_creators.admin import CourseCreatorAdmin +from course_creators.models import CourseCreator + +from django.http import HttpRequest +from django.contrib.auth.models import User +from django.contrib.admin.sites import AdminSite class UsersTestCase(CourseTestCase): @@ -13,3 +25,177 @@ class UsersTestCase(CourseTestCase): self.assertEqual(resp.status_code, 400) content = json.loads(resp.content) self.assertEqual(content["Status"], "Failed") + + +class IndexCourseCreatorTests(CourseTestCase): + """ + Tests the various permutations of course creator status. + """ + def setUp(self): + super(IndexCourseCreatorTests, self).setUp() + + self.index_url = reverse("index") + self.request_access_url = reverse("request_course_creator") + + # Disable course creation takes precedence over enable creator group. I have enabled the + # latter to make this clear. + self.disable_course_creation = { + "DISABLE_COURSE_CREATION": True, + "ENABLE_CREATOR_GROUP": True, + 'STAFF_EMAIL': 'mark@marky.mark', + } + + self.enable_creator_group = {"ENABLE_CREATOR_GROUP": True} + + def test_get_course_creator_status_disable_creation(self): + # DISABLE_COURSE_CREATION is True (this is the case on edx, where we have a marketing site). + # Only edx staff can create courses. + with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.disable_course_creation): + self.assertTrue(self.user.is_staff) + self.assertEquals('granted', _get_course_creator_status(self.user)) + self._set_user_non_staff() + self.assertFalse(self.user.is_staff) + self.assertEquals('disallowed_for_this_site', _get_course_creator_status(self.user)) + + def test_get_course_creator_status_default_cause(self): + # Neither ENABLE_CREATOR_GROUP nor DISABLE_COURSE_CREATION are enabled. Anyone can create a course. + self.assertEquals('granted', _get_course_creator_status(self.user)) + self._set_user_non_staff() + self.assertEquals('granted', _get_course_creator_status(self.user)) + + def test_get_course_creator_status_creator_group(self): + # ENABLE_CREATOR_GROUP is True. This is the case on edge. + # Only staff members and users who have been granted access can create courses. + with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.enable_creator_group): + # Staff members can always create courses. + self.assertEquals('granted', _get_course_creator_status(self.user)) + # Non-staff must request access. + self._set_user_non_staff() + self.assertEquals('unrequested', _get_course_creator_status(self.user)) + # Staff user requests access. + self.client.post(self.request_access_url) + self.assertEquals('pending', _get_course_creator_status(self.user)) + + def test_get_course_creator_status_creator_group_granted(self): + # ENABLE_CREATOR_GROUP is True. This is the case on edge. + # Check return value for a non-staff user who has been granted access. + with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.enable_creator_group): + # self.user has staff permissions, can call this method. + add_user_with_status_granted(self.user, self.user) + # now make self.user non-staff + self._set_user_non_staff() + self.assertEquals('granted', _get_course_creator_status(self.user)) + + def test_get_course_creator_status_creator_group_denied(self): + # ENABLE_CREATOR_GROUP is True. This is the case on edge. + # Check return value for a non-staff user who has been denied access. + with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.enable_creator_group): + # make self.user non-staff + self._set_user_non_staff() + self._set_user_denied() + self.assertEquals('denied', _get_course_creator_status(self.user)) + + def test_disable_course_creation_enabled_non_staff(self): + # Test index page content when DISABLE_COURSE_CREATION is True, non-staff member. + with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.disable_course_creation): + self._set_user_non_staff() + self._assert_cannot_create() + + def test_disable_course_creation_enabled_staff(self): + # Test index page content when DISABLE_COURSE_CREATION is True, staff member. + with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.disable_course_creation): + resp = self._assert_can_create() + self.assertFalse('Email staff to create course' in resp.content) + + def test_can_create_by_default(self): + # Test index page content with neither ENABLE_CREATOR_GROUP nor DISABLE_COURSE_CREATION enabled. + # Anyone can create a course. + self._assert_can_create() + self._set_user_non_staff() + self._assert_can_create() + + def test_course_creator_group_enabled(self): + # Test index page content with ENABLE_CREATOR_GROUP True. + # Staff can always create a course, others must request access. + with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.enable_creator_group): + # Staff members can always create courses. + self._assert_can_create() + + # Non-staff case. + self._set_user_non_staff() + resp = self._assert_cannot_create() + self.assertTrue(self.request_access_url in resp.content) + + # Now request access. + self.client.post(self.request_access_url) + + # Still cannot create a course, but the "request access button" is no longer there. + resp = self._assert_cannot_create() + self.assertFalse(self.request_access_url in resp.content) + self.assertTrue('has-status is-pending' in resp.content) + + def test_course_creator_group_granted(self): + # Test index page content with ENABLE_CREATOR_GROUP True, non-staff member with access granted. + with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.enable_creator_group): + # self.user has staff permissions, can call this method. + add_user_with_status_granted(self.user, self.user) + # now make self.user non-staff + self._set_user_non_staff() + self._assert_can_create() + + def test_course_creator_group_denied(self): + # Test index page content with ENABLE_CREATOR_GROUP True, non-staff member with access denied. + with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.enable_creator_group): + self._set_user_non_staff() + self._set_user_denied() + resp = self._assert_cannot_create() + self.assertFalse(self.request_access_url in resp.content) + self.assertTrue('has-status is-denied' in resp.content) + + def _assert_can_create(self): + """ + Helper method that posts to the index page and checks that the user can create a course. + + Returns the response from the post. + """ + resp = self.client.post(self.index_url) + self.assertTrue('new-course-button' in resp.content) + self.assertFalse(self.request_access_url in resp.content) + self.assertFalse('Email staff to create course' in resp.content) + return resp + + def _assert_cannot_create(self): + """ + Helper method that posts to the index page and checks that the user cannot create a course. + + Returns the response from the post. + """ + resp = self.client.post(self.index_url) + self.assertFalse('new-course-button' in resp.content) + return resp + + def _set_user_non_staff(self): + """ + Sets user as non-staff. + """ + self.user.is_staff = False + self.user.save() + + def _set_user_denied(self): + """ + Sets course creator status to denied in admin table. + """ + self.table_entry = CourseCreator(user=self.user) + self.table_entry.save() + + self.admin = User.objects.create_user('Mark', 'admin+courses@edx.org', 'foo') + self.admin.is_staff = True + + self.deny_request = HttpRequest() + self.deny_request.user = self.admin + + self.creator_admin = CourseCreatorAdmin(self.table_entry, AdminSite()) + + self.table_entry.state = CourseCreator.DENIED + self.creator_admin.save_model(self.deny_request, self.table_entry, None, True) + diff --git a/cms/djangoapps/contentstore/views/user.py b/cms/djangoapps/contentstore/views/user.py index b2a97d4f01..f8e341f2cd 100644 --- a/cms/djangoapps/contentstore/views/user.py +++ b/cms/djangoapps/contentstore/views/user.py @@ -35,21 +35,6 @@ def index(request): and course.location.name != '') courses = filter(course_filter, courses) - if settings.MITX_FEATURES.get('DISABLE_COURSE_CREATION', False): - course_creator_status = 'granted' if request.user.is_staff else 'disallowed_for_this_site' - elif settings.MITX_FEATURES.get('ENABLE_CREATOR_GROUP', False): - course_creator_status = get_course_creator_status(request.user) - if course_creator_status is None: - # User not grandfathered in as an existing user, has not previously visited the dashboard page. - # Add the user to the course creator admin table with status 'unrequested'. - add_user_with_status_unrequested(request.user) - course_creator_status = get_course_creator_status(request.user) - else: - course_creator_status = 'granted' - - request_course_creator_url = reverse('request_course_creator') - csrf_token = csrf(request)['csrf_token'] - return render_to_response('index.html', { 'new_course_template': Location('i4x', 'edx', 'templates', 'course', 'Empty'), 'courses': [(course.display_name, @@ -57,9 +42,9 @@ def index(request): get_lms_link_for_item(course.location, course_id=course.location.course_id)) for course in courses], 'user': request.user, - 'request_course_creator_url': request_course_creator_url, - 'course_creator_status': course_creator_status, - 'csrf': csrf_token + 'request_course_creator_url': reverse('request_course_creator'), + 'course_creator_status': _get_course_creator_status(request.user), + 'csrf': csrf(request)['csrf_token'] }) @@ -67,11 +52,13 @@ def index(request): @ensure_csrf_cookie @login_required def request_course_creator(request): + """ + User has requested course creation access. + """ user_requested_access(request.user) return JsonResponse({"Status": "OK"}) - @login_required @ensure_csrf_cookie def manage_users(request, location): @@ -169,3 +156,28 @@ def remove_user(request, location): remove_user_from_course_group(request.user, user, location, STAFF_ROLE_NAME) return JsonResponse({"Status": "OK"}) + + +def _get_course_creator_status(user): + """ + Helper method for returning the course creator status for a particular user, + taking into account the values of DISABLE_COURSE_CREATION and ENABLE_CREATOR_GROUP. + + If the user passed in has not previously visited the index page, it will be + added with status 'unrequested' if the course creator group is in use. + """ + if user.is_staff: + course_creator_status = 'granted' + elif settings.MITX_FEATURES.get('DISABLE_COURSE_CREATION', False): + course_creator_status = 'disallowed_for_this_site' + elif settings.MITX_FEATURES.get('ENABLE_CREATOR_GROUP', False): + course_creator_status = get_course_creator_status(user) + if course_creator_status is None: + # User not grandfathered in as an existing user, has not previously visited the dashboard page. + # Add the user to the course creator admin table with status 'unrequested'. + add_user_with_status_unrequested(user) + course_creator_status = get_course_creator_status(user) + else: + course_creator_status = 'granted' + + return course_creator_status From 08a1055cbabdb7a3e0fa6b705bce1205e5bd5da8 Mon Sep 17 00:00:00 2001 From: cahrens Date: Fri, 12 Jul 2013 13:35:27 -0400 Subject: [PATCH 26/36] Updates to index page text. --- cms/djangoapps/contentstore/features/signup.feature | 2 +- cms/djangoapps/contentstore/features/signup.py | 3 +-- cms/templates/index.html | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cms/djangoapps/contentstore/features/signup.feature b/cms/djangoapps/contentstore/features/signup.feature index 03a1c9524a..9706cfe7ea 100644 --- a/cms/djangoapps/contentstore/features/signup.feature +++ b/cms/djangoapps/contentstore/features/signup.feature @@ -9,4 +9,4 @@ Feature: Sign in And I fill in the registration form And I press the Create My Account button on the registration form Then I should see be on the studio home page - And I should see the message "please click on the activation link in your email." + And I should see the message "complete your sign up we need you to verify your email address" diff --git a/cms/djangoapps/contentstore/features/signup.py b/cms/djangoapps/contentstore/features/signup.py index 398f8d074d..363cb4e316 100644 --- a/cms/djangoapps/contentstore/features/signup.py +++ b/cms/djangoapps/contentstore/features/signup.py @@ -2,7 +2,6 @@ #pylint: disable=W0621 from lettuce import world, step -from common import * @step('I fill in the registration form$') @@ -23,7 +22,7 @@ def i_press_the_button_on_the_registration_form(step): @step('I should see be on the studio home page$') def i_should_see_be_on_the_studio_home_page(step): - assert world.browser.find_by_css('div.inner-wrapper') + step.given('I should see the message "My Courses"') @step(u'I should see the message "([^"]*)"$') diff --git a/cms/templates/index.html b/cms/templates/index.html index dd36f19b44..7cd660c088 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -276,7 +276,7 @@

${_('We need to verify your email address')}

-

${_('Almost there! In order to complete your sign up we need you verify your email address (%(email)s). An activation message and next steps should be waiting for you there.') % dict(email=user.email)}

+

${_('Almost there! In order to complete your sign up we need you to verify your email address (%(email)s). An activation message and next steps should be waiting for you there.') % dict(email=user.email)}

From c75041e168f3db047f0ee32e2245e31196c0b28e Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Fri, 12 Jul 2013 14:24:22 -0400 Subject: [PATCH 27/36] Studio: adds in disabled/submitting state and logic for course creation button --- cms/static/sass/views/_dashboard.scss | 29 ++++++++++++++++- cms/templates/index.html | 46 ++++++++++++++++----------- common/static/sass/_mixins.scss | 2 +- 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/cms/static/sass/views/_dashboard.scss b/cms/static/sass/views/_dashboard.scss index f64efb184d..a968a2c368 100644 --- a/cms/static/sass/views/_dashboard.scss +++ b/cms/static/sass/views/_dashboard.scss @@ -95,6 +95,7 @@ body.dashboard { @extend .t-action1; @include transform(rotate(45deg)); @include transform-origin(center center); + @include transition(all $tmg-f1 linear 0s); float: right; text-align: right; } @@ -102,7 +103,7 @@ body.dashboard { .ui-toggle-target { @extend .depth1; - @include transition(opacity 0.50s ease-in-out 0s); + @include transition(opacity $tmg-f1 ease-in-out 0s); position: relative; top: -2px; display: none; @@ -156,6 +157,32 @@ body.dashboard { @extend .btn-primary-blue; @extend .t-action3; } + + // specific - request button + .action-request { + position: relative; + overflow: hidden; + + .icon-cog { + @include transition(all $tmg-f1 ease-in-out $tmg-f1); + @include font-size(20); + position: absolute; + top: ($baseline/2); + left: -($baseline); + visibility: hidden; + opacity: 0.0; + } + + &.is-submitting { + padding-left: ($baseline*2); + + .icon-cog { + left: ($baseline*0.75); + visibility: visible; + opacity: 1.0; + } + } + } } .status-update { diff --git a/cms/templates/index.html b/cms/templates/index.html index 7cd660c088..8ef701e3bd 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -8,6 +8,28 @@ +<%block name="jsextra"> + + + <%block name="header_extras"> -<%block name="jsextra"> - - - <%block name="content">
@@ -162,9 +167,14 @@

${_('Your Course Creator Request Status:')}

+ +
+

There was a problem submitting your request

+
+
- +
diff --git a/common/static/sass/_mixins.scss b/common/static/sass/_mixins.scss index 64248734c3..f349ccb963 100644 --- a/common/static/sass/_mixins.scss +++ b/common/static/sass/_mixins.scss @@ -125,7 +125,7 @@ } - &.disabled, &[disabled] { + &.disabled, &[disabled], &.is-disabled { cursor: default; pointer-events: none; opacity: 0.5; From b28b4bfb754f2d4569613edf188a2259a83fdeca Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Fri, 12 Jul 2013 14:25:23 -0400 Subject: [PATCH 28/36] Studio: abstracts and revises form/notice UI --- .../sass/elements/_system-feedback.scss | 42 +++++++++++++++++++ cms/static/sass/elements/_system-help.scss | 2 +- cms/static/sass/views/_account.scss | 41 ------------------ 3 files changed, 43 insertions(+), 42 deletions(-) diff --git a/cms/static/sass/elements/_system-feedback.scss b/cms/static/sass/elements/_system-feedback.scss index 5022a9f677..90de604aa8 100644 --- a/cms/static/sass/elements/_system-feedback.scss +++ b/cms/static/sass/elements/_system-feedback.scss @@ -1,4 +1,46 @@ // studio - elements - system feedback +// ==================== + +// messages +.message { + @extend .t-copy-sub1; + 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; + + [class^="icon-"] { + position: relative; + top: 1px; + @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; + } +} + + // alerts, notifications, prompts, and status communication // ==================== diff --git a/cms/static/sass/elements/_system-help.scss b/cms/static/sass/elements/_system-help.scss index 0f90d9db5c..3b33946e19 100644 --- a/cms/static/sass/elements/_system-help.scss +++ b/cms/static/sass/elements/_system-help.scss @@ -144,7 +144,7 @@ background-color: $gray-l4; .title { - color: $gray-d3; + color: $gray-d2; } .copy { diff --git a/cms/static/sass/views/_account.scss b/cms/static/sass/views/_account.scss index 53f01eee6d..c2cf139400 100644 --- a/cms/static/sass/views/_account.scss +++ b/cms/static/sass/views/_account.scss @@ -252,44 +252,3 @@ body.signup, body.signin { } } } - -// ==================== - -// messages -.message { - @extend .t-copy-sub1; - 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; - - [class^="icon-"] { - position: relative; - top: 1px; - @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; - } -} From e1c02b1b31d70da899b98f293c9af12735de94e0 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Fri, 12 Jul 2013 16:50:00 -0400 Subject: [PATCH 29/36] Studio: removing preventDefault from course creator request button --- cms/templates/index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/cms/templates/index.html b/cms/templates/index.html index 8ef701e3bd..3f55e29db9 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -23,7 +23,6 @@ }); $('#request-coursecreator-submit').click(function(e){ - (e).preventDefault(); $(this).toggleClass('is-disabled is-submitting').find('.label').text('Submitting Your Request'); }); }); From 5168a080ffe050866ecf04fc065fb6c98cd002c0 Mon Sep 17 00:00:00 2001 From: cahrens Date: Mon, 22 Jul 2013 14:45:22 -0400 Subject: [PATCH 30/36] Don't add users marked with is_staff to course creation table. --- .../contentstore/tests/test_users.py | 16 ++++--------- .../course_creators/tests/test_views.py | 10 ++++++++ cms/djangoapps/course_creators/views.py | 23 +++++++++++++++---- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_users.py b/cms/djangoapps/contentstore/tests/test_users.py index bba4d42f4d..701dd246d8 100644 --- a/cms/djangoapps/contentstore/tests/test_users.py +++ b/cms/djangoapps/contentstore/tests/test_users.py @@ -47,6 +47,9 @@ class IndexCourseCreatorTests(CourseTestCase): self.enable_creator_group = {"ENABLE_CREATOR_GROUP": True} + self.admin = User.objects.create_user('Mark', 'mark+courses@edx.org', 'foo') + self.admin.is_staff = True + def test_get_course_creator_status_disable_creation(self): # DISABLE_COURSE_CREATION is True (this is the case on edx, where we have a marketing site). # Only edx staff can create courses. @@ -80,17 +83,14 @@ class IndexCourseCreatorTests(CourseTestCase): # ENABLE_CREATOR_GROUP is True. This is the case on edge. # Check return value for a non-staff user who has been granted access. with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.enable_creator_group): - # self.user has staff permissions, can call this method. - add_user_with_status_granted(self.user, self.user) - # now make self.user non-staff self._set_user_non_staff() + add_user_with_status_granted(self.admin, self.user) self.assertEquals('granted', _get_course_creator_status(self.user)) def test_get_course_creator_status_creator_group_denied(self): # ENABLE_CREATOR_GROUP is True. This is the case on edge. # Check return value for a non-staff user who has been denied access. with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.enable_creator_group): - # make self.user non-staff self._set_user_non_staff() self._set_user_denied() self.assertEquals('denied', _get_course_creator_status(self.user)) @@ -137,10 +137,8 @@ class IndexCourseCreatorTests(CourseTestCase): def test_course_creator_group_granted(self): # Test index page content with ENABLE_CREATOR_GROUP True, non-staff member with access granted. with mock.patch.dict('django.conf.settings.MITX_FEATURES', self.enable_creator_group): - # self.user has staff permissions, can call this method. - add_user_with_status_granted(self.user, self.user) - # now make self.user non-staff self._set_user_non_staff() + add_user_with_status_granted(self.admin, self.user) self._assert_can_create() def test_course_creator_group_denied(self): @@ -188,9 +186,6 @@ class IndexCourseCreatorTests(CourseTestCase): self.table_entry = CourseCreator(user=self.user) self.table_entry.save() - self.admin = User.objects.create_user('Mark', 'admin+courses@edx.org', 'foo') - self.admin.is_staff = True - self.deny_request = HttpRequest() self.deny_request.user = self.admin @@ -198,4 +193,3 @@ class IndexCourseCreatorTests(CourseTestCase): self.table_entry.state = CourseCreator.DENIED self.creator_admin.save_model(self.deny_request, self.table_entry, None, True) - diff --git a/cms/djangoapps/course_creators/tests/test_views.py b/cms/djangoapps/course_creators/tests/test_views.py index 943120f3c0..95c50ffb76 100644 --- a/cms/djangoapps/course_creators/tests/test_views.py +++ b/cms/djangoapps/course_creators/tests/test_views.py @@ -80,3 +80,13 @@ class CourseCreatorView(TestCase): # user from the authz course creator group (and that can only be done by an admin). user_requested_access(self.user) self.assertEqual('granted', get_course_creator_status(self.user)) + + def test_add_user_unrequested_staff(self): + # Users marked as is_staff will not be added to the course creator table. + add_user_with_status_unrequested(self.admin) + self.assertIsNone(get_course_creator_status(self.admin)) + + def test_add_user_granted_staff(self): + # Users marked as is_staff will not be added to the course creator table. + add_user_with_status_granted(self.admin, self.admin) + self.assertIsNone(get_course_creator_status(self.admin)) diff --git a/cms/djangoapps/course_creators/views.py b/cms/djangoapps/course_creators/views.py index 6b2d91acb2..e9b38ed169 100644 --- a/cms/djangoapps/course_creators/views.py +++ b/cms/djangoapps/course_creators/views.py @@ -12,6 +12,9 @@ def add_user_with_status_unrequested(user): If the user is already in the table, this method is a no-op (state will not be changed). + + If the user is marked as is_staff, this method is a no-op (user + will not be added to table). """ _add_user(user, CourseCreator.UNREQUESTED) @@ -20,14 +23,17 @@ def add_user_with_status_granted(caller, user): """ Adds a user to the course creator table with status 'granted'. - This method also adds the user to the course creator group maintained by authz.py. + If appropriate, this method also adds the user to the course creator group maintained by authz.py. Caller must have staff permissions. If the user is already in the table, this method is a no-op (state will not be changed). + + If the user is marked as is_staff, this method is a no-op (user + will not be added to table, nor added to authz.py group). """ - _add_user(user, CourseCreator.GRANTED) - update_course_creator_group(caller, user, True) + if _add_user(user, CourseCreator.GRANTED): + update_course_creator_group(caller, user, True) def update_course_creator_group(caller, user, add): @@ -78,9 +84,16 @@ def _add_user(user, state): """ Adds a user to the course creator table with the specified state. + Returns True if user was added to table, else False. + If the user is already in the table, this method is a no-op - (state will not be changed). + (state will not be changed, method will return False). + + If the user is marked as is_staff, this method is a no-op (False will be returned). """ - if CourseCreator.objects.filter(user=user).count() == 0: + if not user.is_staff and CourseCreator.objects.filter(user=user).count() == 0: entry = CourseCreator(user=user, state=state) entry.save() + return True + + return False From 0833181eccece0c1e2946c676d6a6ee5ed5818a7 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Mon, 22 Jul 2013 16:30:26 -0400 Subject: [PATCH 31/36] Studio: updates older extend references and solves Sass warnings/dupe display of accessibility-based text --- cms/static/sass/_base.scss | 2 +- cms/static/sass/views/_dashboard.scss | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index 48a3e25782..e4495d0248 100644 --- a/cms/static/sass/_base.scss +++ b/cms/static/sass/_base.scss @@ -448,7 +448,7 @@ p, ul, ol, dl { // actions .list-actions { - @extend .no-list; + @extend .cont-no-list; .action-item { margin-bottom: ($baseline/4); diff --git a/cms/static/sass/views/_dashboard.scss b/cms/static/sass/views/_dashboard.scss index a968a2c368..6772f8addb 100644 --- a/cms/static/sass/views/_dashboard.scss +++ b/cms/static/sass/views/_dashboard.scss @@ -72,7 +72,7 @@ body.dashboard { overflow: hidden; .ui-toggle-control { - @extend .depth2; + @extend .ui-depth2; @extend .btn-secondary-gray; @include clearfix(); display: block; @@ -102,7 +102,7 @@ body.dashboard { } .ui-toggle-target { - @extend .depth1; + @extend .ui-depth1; @include transition(opacity $tmg-f1 ease-in-out 0s); position: relative; top: -2px; @@ -188,7 +188,7 @@ body.dashboard { .status-update { .label { - @extend .text-sr; + @extend .cont-text-sr; } .value { @@ -238,7 +238,7 @@ body.dashboard { &.is-unrequested { .title { - @extend .text-sr; + @extend .cont-text-sr; } } From 3d49a4619765e53a2beece40f4e62da9687a6b8c Mon Sep 17 00:00:00 2001 From: cahrens Date: Tue, 23 Jul 2013 10:16:45 -0400 Subject: [PATCH 32/36] Add error handler. --- cms/templates/index.html | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cms/templates/index.html b/cms/templates/index.html index f8e6f4605f..30ddcc4c86 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -18,9 +18,15 @@ $(this).closest('.wrapper-creationrights').toggleClass('is-shown').find('.ui-toggle-control').toggleClass('current'); }); - $('#request-coursecreator').ajaxForm(function() { + var reloadPage = function () { location.reload(); - }); + }; + + var showError = function () { + + }; + + $('#request-coursecreator').ajaxForm({error: showError, success: reloadPage}); $('#request-coursecreator-submit').click(function(e){ $(this).toggleClass('is-disabled is-submitting').find('.label').text('Submitting Your Request'); @@ -168,7 +174,7 @@
-

There was a problem submitting your request

+

${_('There was a problem submitting your request')}

From 1da5af53d9a161059da5a1b0146677019f738913 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Tue, 23 Jul 2013 10:37:07 -0400 Subject: [PATCH 33/36] Studio: revises styling/message copy for authorship rights request error --- cms/static/sass/views/_dashboard.scss | 15 +++++++++++++++ cms/templates/index.html | 8 ++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/cms/static/sass/views/_dashboard.scss b/cms/static/sass/views/_dashboard.scss index 6772f8addb..63c8ab36fd 100644 --- a/cms/static/sass/views/_dashboard.scss +++ b/cms/static/sass/views/_dashboard.scss @@ -159,6 +159,7 @@ body.dashboard { } // specific - request button + // BT: should we abstract these states out for all buttons like this .action-request { position: relative; overflow: hidden; @@ -173,6 +174,7 @@ body.dashboard { opacity: 0.0; } + // state: submitting &.is-submitting { padding-left: ($baseline*2); @@ -182,6 +184,19 @@ body.dashboard { opacity: 1.0; } } + + // state: has an error + &.has-error { + padding-left: ($baseline*2); + background: $red; + border-color: $red-d1; + + .icon-cog { + left: ($baseline*0.75); + visibility: visible; + opacity: 1.0; + } + } } } diff --git a/cms/templates/index.html b/cms/templates/index.html index 30ddcc4c86..ba24099e63 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -23,7 +23,8 @@ }; var showError = function () { - + $('#request-coursecreator-submit').toggleClass('has-error').find('.label').text('Sorry, there was error with your request'); + $('#request-coursecreator-submit').find('.icon-cog').toggleClass('icon-spin'); }; $('#request-coursecreator').ajaxForm({error: showError, success: reloadPage}); @@ -172,11 +173,6 @@

${_('Your Course Creator Request Status:')}

- -
-

${_('There was a problem submitting your request')}

-
-
From 60d60de2c44dfb91f48a25df5a8e78e64be8a2fb Mon Sep 17 00:00:00 2001 From: cahrens Date: Thu, 25 Jul 2013 12:21:17 -0400 Subject: [PATCH 34/36] Use format for strings instead of %. --- cms/djangoapps/course_creators/models.py | 2 +- cms/templates/activation_active.html | 2 +- cms/templates/index.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cms/djangoapps/course_creators/models.py b/cms/djangoapps/course_creators/models.py index b370ac423a..4975705407 100644 --- a/cms/djangoapps/course_creators/models.py +++ b/cms/djangoapps/course_creators/models.py @@ -39,7 +39,7 @@ class CourseCreator(models.Model): "why course creation access was denied)")) def __unicode__(self): - return u'%s | %s [%s]' % (self.user, self.state, self.state_changed) + return u"{0} | {1} [{2}]".format(self.user, self.state, self.state_changed) @receiver(post_init, sender=CourseCreator) diff --git a/cms/templates/activation_active.html b/cms/templates/activation_active.html index f2232f6f40..d7133062de 100644 --- a/cms/templates/activation_active.html +++ b/cms/templates/activation_active.html @@ -17,7 +17,7 @@

${_("Your account is already active")}

-

${_("This account, set up using (%(email)s), has already been activated. Please sign in to start working within edX Studio.") % dict(email=user.email)}

+

${_("This account, set up using {0}, has already been activated. Please sign in to start working within edX Studio.".format(user.email))}

diff --git a/cms/templates/index.html b/cms/templates/index.html index ba24099e63..586762eae5 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -95,7 +95,7 @@
-

${_("Welcome, %(name)s!") % dict(name= user.username)}

+

${_("Welcome, {0}!".format(user.username))}

%if len(courses) > 0:
From 628994daf11771153a21ea342f8f252f3a2a1c8d Mon Sep 17 00:00:00 2001 From: cahrens Date: Thu, 25 Jul 2013 12:25:06 -0400 Subject: [PATCH 35/36] i18n "Create a New Course". --- cms/templates/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/templates/index.html b/cms/templates/index.html index 586762eae5..df3e467200 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -39,7 +39,7 @@ <%block name="header_extras">