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 | 373 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 346 insertions(+), 27 deletions(-) diff --git a/cms/templates/index.html b/cms/templates/index.html index e7205c943070..5c5b631ef3f0 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -57,37 +57,356 @@

${_("Page Actions")}

+ + +
-
-

- ${_("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.')}

+
+ + +
+
+ +
-
-
-
-
- % 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.")} -

+ + + + +
+
+
+

${_("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.')}

- % endif + +
+

${_('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 +#
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 | 336 ++++++++++++++++++++------------------- 1 file changed, 172 insertions(+), 164 deletions(-) diff --git a/cms/templates/index.html b/cms/templates/index.html index 5c5b631ef3f0..5a1003b39179 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -67,7 +67,7 @@

${_("Page Actions")}

${_("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 @@

${_('Need Help?')}

-
-

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

-
-

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

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

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

-
-

${_('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 @@

${_('Need Help?')}

${_("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')}

-
+
+ +
+

+ ${_('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:')}
+
+ ${_('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 @@

${_('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.')}

+

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

+
-
-

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

-
- -
+
+ +
+

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

-
-
${_('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 @@

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

- - - -
-
- -
- - -
- - - - +
-

${_("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:")}

-
- -
- -
-

${_('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.')}

+
+
+

${_('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.')}

+
+
+
-
-

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

-
- -
+ +
+

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

-
-
${_('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.')}

+
+
+
@@ -329,8 +374,7 @@

${_('Contact edX')}

- - +
@@ -390,23 +415,6 @@

${_('Contact edX')}

-#
-# % 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 685956036106..4fac3de01fad 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 | 139 ++++- cms/static/sass/views/_dashboard.scss | 248 ++++++++- cms/templates/index.html | 615 +++++++++++++++------ 4 files changed, 815 insertions(+), 223 deletions(-) diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index 4a20a98eb356..fe107d751182 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 d1b0584fc4c2..ca8fa381542b 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 // ==================== +// view introductions - common greeting/starting points for the UI +.content .introduction { + @include box-sizing(border-box); + margin-bottom: $baseline; + + .title { + @extend .t-title4; + font-weight: 600; + } + + .copy { + @extend .t-copy-sub1; + } + + strong { + font-weight: 600; + } + + // CASE: has links alongside + &.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); + } + } + } +} + // 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: 600; + 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; } - &:hover { + &.has-status { - .copy { - opacity: 1.0; + .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 warnings around a workflow for something +// particular notice - warnings around a workflow for something .notice-workflow { background: $yellow-l5; + .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 8d1b06825615..a1cd5a47f68d 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 5a1003b39179..21cbc2679967 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 @@

${_("Page Actions")}

@@ -65,18 +80,18 @@

${_("Page Actions")}

- - - - - -

${_("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 @@

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

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

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

@@ -174,14 +190,14 @@

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

% 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 @@

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

${_('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 607dae4af293..31bcbc561112 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 | 237 +++++++++++++-------- 4 files changed, 190 insertions(+), 104 deletions(-) diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index fe107d751182..e9b01509fe99 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 ca8fa381542b..0f90d9db5c6e 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 7132874c142f..b00306e5c5e3 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 0f10758dd687..4166542f352e 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 @@

${_("My Courses")}

${_("Page Actions")}

@@ -75,7 +71,6 @@

${_("Page Actions")}

- % if user.is_active:
@@ -83,86 +78,146 @@

${_("Page Actions")}

${_("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 +
+
- % if not disable_course_creation and course_creator_status != "granted": -
-

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

+ %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.')}

+
+
+
-
-
-

${_('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 not disable_course_creation and course_creator_status == "granted": +
+
+

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

+
+

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

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

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

- - + +
+ % endif + +
+ % endif + + % if not disable_course_creation and course_creator_status != "granted": + + %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')} +

- - %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.')} -
-
+
+
+

${_('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 == "denied": -
-

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

- -
-
${_('Your authorship 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 + % endif
@@ -181,7 +236,6 @@

${_('Need help?')}

- % if disable_course_creation and settings.MITX_FEATURES.get('STAFF_EMAIL',''):

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

@@ -189,14 +243,12 @@

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

% 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 @@

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

- % 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 | 227 +++++++++++++++++++++++++-------------- 1 file changed, 146 insertions(+), 81 deletions(-) diff --git a/cms/templates/index.html b/cms/templates/index.html index 1df0878bd004..3b7b2d74b473 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 @@

${_("My Courses")}

${_("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 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 +
+
- % if not disable_course_creation and course_creator_status != "granted": -
-

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

+ %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.')}

+
+
+
-
-
-

${_('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 == "granted": +
+
+

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

+
+

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

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

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

+ +
+ % endif - -
+
+ % endif - !-- 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 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:')}

+ +
+ + + +
+ +
+
- % 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 +
- - % 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 @@

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

- % else:
@@ -204,7 +269,7 @@

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

${_('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 dae0d246a51b..dbb2fbddb6a1 100644 --- a/cms/djangoapps/contentstore/views/user.py +++ b/cms/djangoapps/contentstore/views/user.py @@ -11,6 +11,7 @@ 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 course_filter(course): 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 course_filter(course): 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 c2f401c77e2c..a6218541ef57 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 b00306e5c5e3..a2a3c72fb141 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 3b7b2d74b473..74831d19849f 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 56efd1a557a1..67314178f882 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -12,6 +12,7 @@ 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 | 33 ++++++++++++++++------ cms/templates/activation_complete.html | 33 ++++++++++++++++++---- cms/templates/activation_invalid.html | 39 +++++++++++++++++++------- 3 files changed, 81 insertions(+), 24 deletions(-) diff --git a/cms/templates/activation_active.html b/cms/templates/activation_active.html index 712c73abf936..51c01f115556 100644 --- a/cms/templates/activation_active.html +++ b/cms/templates/activation_active.html @@ -1,14 +1,31 @@ <%inherit file="base.html" /> <%block name="content"> +
+
+

${_("Studio Account Activation")}

+
+
-
-
+
+
+
+
-
-

Account already active!

-

This account has already been activated. Log in here.

-
-
+
+
+

${_("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)}

+
+
- \ No newline at end of file + +
+ +
+ diff --git a/cms/templates/activation_complete.html b/cms/templates/activation_complete.html index 1e195a632ca7..d6bdab36dc00 100644 --- a/cms/templates/activation_complete.html +++ b/cms/templates/activation_complete.html @@ -1,12 +1,33 @@ <%inherit file="base.html" /> <%block name="content"> +
+
+

${_("Studio Account Activation")}

+
+
+ +
+
+
+
-
-
-

Activation Complete!

-

Thanks for activating your account. Log in here.

+
+
+

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

+
+

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

+
+
+ + +
+
- + + - \ No newline at end of file diff --git a/cms/templates/activation_invalid.html b/cms/templates/activation_invalid.html index c4eb16875b53..0c663aa30758 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

+
+
+

${_("Studio Account Activation")}

+
+
+ +
+
+
+
-

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.

+
+
+

${_('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.')}

+
+
-

Or you can go back to the home page.

+ +
+
-
- \ No newline at end of file + + + + 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 | 125 ++++++++++++++++++++------------------- 2 files changed, 65 insertions(+), 62 deletions(-) diff --git a/cms/static/js/base.js b/cms/static/js/base.js index a6218541ef57..50427642124a 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 d182f4c174ce..dd36f19b44c9 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -113,6 +113,9 @@

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

%else: +
+
+
@@ -144,82 +147,82 @@

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

% 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 is:')}
-
- - ${_('Denied')} - ${_('Your request did not meet the criteria/guidelines specified by edX Staff.')} -
-
+
+

${_('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')} -

+ %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 is:')}
-
- - ${_('Pending')} - ${_('Your request is currently being reviewed by edX staff and should be updated shortly.')} -
-
+
+

${_('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 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 7645b215752a..b370ac423ad3 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 bd91208b9c6e..943120f3c0cb 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.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 @@ def setUp(self): 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 @@ def test_table_initially_empty(self): 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 @@ def test_add_granted(self): 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 @@ def test_update_creator_group(self): 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 12993d1be4b9..6b2d91acb218 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 f945ef50fc13..bba4d42f4d79 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 @@ def test_empty(self): 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 b2a97d4f0130..f8e341f2cdef 100644 --- a/cms/djangoapps/contentstore/views/user.py +++ b/cms/djangoapps/contentstore/views/user.py @@ -35,21 +35,6 @@ def course_filter(course): 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 course_filter(course): 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 course_filter(course): @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 03a1c9524a4b..9706cfe7ea3a 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 398f8d074d3d..363cb4e31613 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 dd36f19b44c9..7cd660c088dc 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -276,7 +276,7 @@

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

${_('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 f64efb184d7d..a968a2c3688e 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 7cd660c088dc..8ef701e3bdb8 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 64248734c304..f349ccb96309 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 5022a9f677a3..90de604aa80c 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 0f90d9db5c6e..3b33946e19de 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 53f01eee6d4f..c2cf13940009 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 8ef701e3bdb8..3f55e29db919 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 bba4d42f4d79..701dd246d867 100644 --- a/cms/djangoapps/contentstore/tests/test_users.py +++ b/cms/djangoapps/contentstore/tests/test_users.py @@ -47,6 +47,9 @@ def setUp(self): 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 @@ 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() + 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 @@ def test_course_creator_group_enabled(self): 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 @@ def _set_user_denied(self): 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 @@ def _set_user_denied(self): 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 943120f3c0cb..95c50ffb76d4 100644 --- a/cms/djangoapps/course_creators/tests/test_views.py +++ b/cms/djangoapps/course_creators/tests/test_views.py @@ -80,3 +80,13 @@ def test_user_requested_already_granted(self): # 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 6b2d91acb218..e9b38ed16951 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 48a3e2578225..e4495d0248cc 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 a968a2c3688e..6772f8addb70 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 f8e6f4605f27..30ddcc4c865c 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 @@

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

-

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 6772f8addb70..63c8ab36fd07 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 30ddcc4c865c..ba24099e63c8 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 b370ac423ad3..4975705407ff 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 f2232f6f406f..d7133062de32 100644 --- a/cms/templates/activation_active.html +++ b/cms/templates/activation_active.html @@ -17,7 +17,7 @@

${_("Studio Account Activation")}

${_("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 ba24099e63c8..586762eae5ee 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -95,7 +95,7 @@

${_("Page Actions")}

-

${_("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 586762eae5ee..df3e4672001d 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -39,7 +39,7 @@ <%block name="header_extras">