diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2da2e05261..3f1a2f0049 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,11 @@ Change Log Unreleased ---------- +[2.0.14] - 2019-11-07 +--------------------- + +* Add Enterprise selection page to allow a learner to select one of linked enterprises + [2.0.13] - 2019-11-07 --------------------- diff --git a/enterprise/__init__.py b/enterprise/__init__.py index 5db2da5cfa..6794c93467 100644 --- a/enterprise/__init__.py +++ b/enterprise/__init__.py @@ -4,6 +4,6 @@ from __future__ import absolute_import, unicode_literals -__version__ = "2.0.13" +__version__ = "2.0.14" default_app_config = "enterprise.apps.EnterpriseConfig" # pylint: disable=invalid-name diff --git a/enterprise/forms.py b/enterprise/forms.py new file mode 100644 index 0000000000..d7ae07f694 --- /dev/null +++ b/enterprise/forms.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +""" +User-facing forms for the Enterprise app. +""" +from __future__ import absolute_import, unicode_literals + +from django import forms +from django.urls import Resolver404, resolve +from django.utils.translation import ugettext as _ + +from enterprise.models import EnterpriseCustomer, EnterpriseCustomerUser + +ENTERPRISE_SELECT_SUBTITLE = _( + u'You have access to multiple organizations. Select the organization that you will use ' + 'to sign up for courses. If you want to change organizations, sign out and sign back in.' +) + + +class EnterpriseSelectionForm(forms.Form): + """ + Enterprise Selection Form. + """ + + enterprise = forms.ChoiceField(choices=(), label='Organization') + success_url = forms.CharField(widget=forms.HiddenInput(), required=False) + + def __init__(self, *args, **kwargs): + """ + Initialize form. + """ + super(EnterpriseSelectionForm, self).__init__(*args, **kwargs) + initial = kwargs['initial'] + self._user_id = kwargs['initial'].pop('user_id') + self.fields['enterprise'].choices = initial['enterprises'] + self.fields['success_url'].initial = initial['success_url'] + + def clean(self): + """ + Validate POST data. + """ + cleaned_data = super(EnterpriseSelectionForm, self).clean() + enterprise = cleaned_data.get('enterprise') + + try: + EnterpriseCustomer.objects.get(uuid=enterprise) # pylint: disable=no-member + except EnterpriseCustomer.DoesNotExist: + raise forms.ValidationError(_("Enterprise not found")) + + # verify that learner is really a member of selected enterprise + if not EnterpriseCustomerUser.objects.filter(enterprise_customer=enterprise, user_id=self._user_id).exists(): + raise forms.ValidationError(_("Wrong Enterprise")) + + try: + # validate if path is a valid + resolve(cleaned_data['success_url']) + except Resolver404: + raise forms.ValidationError(_("Incorrect success url")) + + return cleaned_data diff --git a/enterprise/settings/test.py b/enterprise/settings/test.py index 8fe5cbf226..47838d9348 100644 --- a/enterprise/settings/test.py +++ b/enterprise/settings/test.py @@ -106,6 +106,8 @@ def root(*args): }, ] +REPO_ROOT = root('enterprise') + STATIC_ROOT = root('enterprise/assets') STATIC_URL = '/enterprise/static/' diff --git a/enterprise/static/enterprise/bundles/main.style.css b/enterprise/static/enterprise/bundles/main.style.css index 74f5c722c4..9e2dc8c307 100644 --- a/enterprise/static/enterprise/bundles/main.style.css +++ b/enterprise/static/enterprise/bundles/main.style.css @@ -1 +1 @@ -@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,600,700,);.modal,.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;outline:0}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translateY(-25%)}.modal.show .modal-dialog{transform:translate(0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;display:flex;flex-direction:column;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;align-items:center;justify-content:space-between;padding:15px;border-bottom:1px solid #e9ecef}.modal-header .close{margin-left:auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:15px}.modal-footer{display:flex;align-items:center;justify-content:flex-end;padding:15px;border-top:1px solid #e9ecef}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:30px auto}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg{max-width:800px}}*{font-family:Open Sans,Helvetica Neue,Helvetica,Arial,sans-serif;font-size:16px}strong{font-weight:600}s{text-decoration:line-through}q{font-style:italic}.depth-0,.zebra-stripe>:nth-child(odd),body{background-color:#fff;padding-bottom:100px}.text-underline{text-decoration:underline}.expand-list-link button{background:none;border:none}.logo-container{padding-top:10px;width:100%;max-width:768px;min-width:300px;height:auto;margin:0 auto;color:#383838}.pinstripe{border-top:2px solid #0075b4}#header-tagline{font-size:14px;margin-left:.5em;width:300px;display:inline-block;vertical-align:middle}.consent-title{font-size:24px;color:#126f9a}.row:after{content:" ";display:table;clear:both}.enterprise-container{min-width:300px;max-width:768px;margin:0 auto;line-height:1.5;font-size:14px;color:#383838}.enterprise-container:after{content:" ";display:table;clear:both}.enterprise-container *{font-size:14px}.enterprise-container a{color:#126f9a}.enterprise-container a:hover{color:#0b5c8a}.enterprise-logo{width:auto;max-width:150px;max-height:150px}.header-logo{height:47px}.partnered-text{font-size:16px;color:#414141;margin-top:5px;margin-bottom:20px}.partnered-text strong{font-size:1.05em}.media{margin-bottom:10px;display:table;width:100%}.media .media-content,.media .thumbnail{display:table-cell;vertical-align:top}.media .thumbnail{width:55px;padding-top:6px}.media .media-content{padding-left:15px}.media .media-content .course-title{margin-bottom:5px;font-size:16px;font-weight:700}.radio-block{margin-bottom:15px}.radio-block .radio,.radio-block label{display:table-cell;vertical-align:top}.radio-block .radio{width:25px;padding-top:5px}.radio-block label>span{display:block}.btn-confirm{background:#126f9a;border:none;color:#fff;border-radius:5px;margin:10px 0;padding:10px 15px;box-shadow:none;font-weight:700}.btn-confirm:focus,.btn-confirm:hover{cursor:pointer;background:#0b5177}#messages .alert{position:relative;padding:10px 10px 10px 35px;border:1px solid transparent;box-shadow:none;border-radius:2px;margin-bottom:8px}#messages .alert>.fa{position:absolute;left:11px;top:13px;font-size:16px}#messages .alert span{display:block}#messages .success{background-color:#ecfaec;border-color:#b9edb9;color:#3c763d}#messages .info{background-color:#f2f8fb;border-color:#cce3f0;color:#31708f}#messages .warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}#messages .error{background-color:#f2dede;border-color:#ebccd1;color:#a94442}@media (max-width:720px){.logo-container{text-align:center}#header-tagline{text-align:left}.enterprise-container{padding:0 30px 30px}}@media (min-width:720px){[class*=col-]{float:left;display:inline}[class*=col-].col-3{width:30%;padding-left:0;padding-right:30px}[class*=col-].col-7{padding-left:30px;width:70%}.border-left{border-left:1px solid #d9d9d9}.enterprise-logo{display:block}}.modal{width:100%;left:50%;transform:translateX(-50%);box-shadow:none;background:none}.modal-header{background-color:#126f9a;padding:5px}button.close{font-weight:500;background:none;border:none;color:#fff}button.close i{font-size:1.5rem}button.close:hover{color:#ced4da}h2.modal-header-text{margin:0;font-weight:700;font-size:18px}.header-wrapper{border-bottom:1px groove #000;padding-bottom:5px;margin-bottom:20px}.staff-container .row{display:flex;margin-bottom:40px}.staff-container .row img{width:110px;height:110px;margin-right:30px}.staff-container .row h2{font-weight:700}.staff-container .row .col-7{flex-grow:100}.staff-container .row:last-child{margin-bottom:0}.modal-content .modal-header-wrapper{display:flex}.modal-content .modal-header-wrapper .image{flex-basis:33%}.modal-content .modal-header-wrapper{margin-bottom:10px}.modal-content .modal-header-wrapper .image img{border:1px solid #126f9a;max-width:210px}.modal-content .modal-header-wrapper .details h1.modal-header-text{font-weight:700;margin-bottom:0;text-align:left}.modal-content .modal-header-wrapper .details .organization img{height:25px}.modal-content .modal-header-wrapper .details .short-description{font-size:14px;margin-bottom:10px;display:flex}.modal-content .modal-body>.details{border:1px solid #126f9a;margin-bottom:20px}.modal-content .modal-body>.details .icon{font-weight:700;margin-right:10px;opacity:.6}.modal-content .modal-body>.details .discount,.modal-content .modal-body>.details .text,.modal-content .modal-body>.details .title{font-size:13px}.modal-content .modal-body>.details .detail-container{display:flex}.modal-content .modal-body>.details .detail-title-container{flex-basis:25%}.modal-content .modal-body>.details .detail-value-container{flex-shrink:1}.modal-content .modal-body>.details .text strike{font-size:11px}.modal-content .modal-body>.details .text .discount{color:green}.modal-content .modal-body>.details ol li span{font-size:16px}.modal-content .modal-body>.details ol{list-style:none;margin:0;padding:0 10px}.modal-content .modal-body>.details li{border-bottom:1px dotted gray;padding:15px 10px}.modal-content .modal-body>.details li:last-child{border-bottom:none}.modal-content .modal-body>.details li:after{content:"";display:table;clear:both}.modal-content .modal-body .overview *{font-size:14px}.modal-content .modal-body .overview .col-7{padding-left:0}.modal-content .modal-body .overview .header-wrapper h2{font-size:18px;font-weight:700;margin:0}.modal-content .modal-body a{color:#126f9a;display:inline-block;text-decoration:underline;border:none;background-color:transparent;padding:0!important;vertical-align:bottom;font-size:16px;font-weight:400}@media (max-width:720px){.modal-header-wrapper .image,.modal-header-wrapper .organization img{display:none}.staff-container .row img{margin-bottom:10px}}@media (min-width:720px){.modal-content .body .details .icon{display:inline}.modal-content .modal-header-wrapper{margin-bottom:25px}.modal-content .modal-header-wrapper .image{display:block;width:210px;float:left;margin-right:35px}.modal-content .modal-header-wrapper .details .organization{display:block}}.consent-container{width:auto;max-width:700px;min-width:300px;margin:0 auto}.consent-container *{font-size:16px}.consent-container p{margin-bottom:15px}.consent-input-container{border-radius:10px;border:1px solid #e5e5e5;padding:15px 20px;margin-bottom:20px}.agreement-text{overflow:hidden;margin-bottom:15px;font-weight:600}.consent-agreement-button{border-color:transparent;height:auto;max-width:180px;min-width:125px;border-radius:5px;font-size:16px;font-weight:700;background:#0075b4;border:none;color:#fff;margin-right:10px;padding:10px 15px;box-shadow:none;width:auto}.consent-agreement-button:hover{cursor:pointer;background:#0b5177}.consent-agreement-button:focus{background-color:#065683}.consent-agreement-button:active{background-color:#0075b4}.consent-agreement-button:disabled{background-color:#a0a0a0;color:#d9d9d9}.consent-policy-dropdown-bar{text-align:left;border:none;width:100%;height:60px;font-weight:600;display:block;background:#f2f8fb;border-radius:5px}.consent-policy-dropdown-bar:active,.consent-policy-dropdown-bar:focus,.consent-policy-dropdown-bar:hover{font-weight:600}.failure-link,.policy-dropdown-link{text-decoration:underline}.failure-link{color:#0075b4;display:inline-block;border:none;background-color:transparent;padding:10px 15px;vertical-align:bottom;font-size:16px}.failure-link:focus,.failure-link:hover{font-weight:400}.dropdown-text{line-height:60px;white-space:nowrap;padding-left:2%;font-size:18px;color:#126f9a}.dropdown-icon-container{float:right;padding-right:10%;padding-top:10px}#consent-policy-dropdown-icon-surround{font-size:40px;color:#fff}#consent-policy-dropdown-icon{font-size:18px;color:#126f9a;padding-top:12%;padding-left:5%}.consent-policy{padding-top:2%;padding-left:3%;padding-right:3%}.data-consent-checkbox{float:left;margin-top:4px;margin-right:10px}.consent-title{font-size:20px!important;color:#126f9a!important;font-weight:400;margin-bottom:10px}.caption{font-size:1.1em}.enterprise-container .consent-input-container #data-sharing{margin:0}.enterprise-container .consent-input-container input{position:relative;top:2px}.enterprise-container ul.consent-items{margin-left:25px}.enterprise-container ul.consent-items li{margin-bottom:4px}.course-confirmation-title{font-size:20px;color:#126f9a;font-weight:400;margin-bottom:6px}.course-image{width:auto;height:50px}.course-detail{font-size:14px;margin-bottom:10px}.course-detail .course-org{font-size:18px}.course-info{font-size:1em}.course-info .fa,.course-info span{display:inline-block;vertical-align:middle;margin-right:5px}.course-info span{font-size:1em}.course-in-future{font-size:17px;margin-bottom:40px}.caption{margin-bottom:15px;font-size:16px}.media{margin-bottom:40px}.media .media-content .course-info{font-size:.8em;margin:8px 0}.media .media-content .course-info .fa,.media .media-content .course-info span{display:inline-block;vertical-align:middle;margin-right:5px}.media .media-content .course-info .fa{font-size:16px}.program-type-logo{width:auto;max-width:150px;max-height:100px;margin-bottom:15px}.platform-description-container,.program-type-container{margin:15px 0}.platform-description-header,.program-type-description-header{font-size:16px;font-weight:700}.platform-description,.program-type-description{margin-top:-15px;font-size:15px}.program-confirmation-title{font-size:1.6em;color:#126f9a;font-weight:400;margin-bottom:6px}.program-detail .program-title{font-size:1.5em;font-weight:700;margin:0}.program-detail .program-org{font-size:1.2em;font-weight:600;margin:0}.program-detail .program-overview{font-size:16px;margin:.5em 0}.program-metadata-header-text{font-size:1.2em;font-weight:600;margin:0}.program-bullet-points{list-style:none;margin:1em 0}.program-bullet-points li:before{content:"\2022"}.program-bullet-points .bullet-item{font-size:16px;margin-left:20px}.program-bullet-points .bullet-item a{font-size:16px}.program-price,.program-price *{font-size:16px;margin:0}.final-price{color:#3c763d}.program-metadata-item~.program-metadata-item{margin-top:20px}.program-metadata-header{margin:10px 0;padding:5px 0;border-bottom:2px solid #ccc}.endorsement-container~.endorsement-container{padding-top:10px}.endorsement-image{max-height:30px;width:auto;display:block;margin:10px 0}.individual-endorsement-quote{margin:10px 0;font-size:16px;font-weight:300}.endorser{width:100%;margin:10px 0}.endorser-description-container{padding-left:50px}.endorser-description{font-weight:600;font-size:16px}.expand-list-link-container *{font-size:16px;color:#126f9a}.expand-list-link-container :hover{color:#0b5c8a}.program-summary-container{margin-top:20px;margin-left:-10px}.program-summary-header{padding:10px 0}.program-summary-header-text{font-size:1.2em;padding-left:3px;margin:0;font-weight:600}table.program-summary{border:1px solid #ccc;border-radius:5px;table-layout:fixed;width:100%}table.program-summary tr td{text-align:left;vertical-align:top;padding:15px 15px 15px 0;width:80%}table.program-summary th.summary-item{text-align:left;vertical-align:top;font-size:16px;font-weight:600;padding:15px 0 15px 15px;width:20%}table.program-summary tr.row-separator td{padding:0 10px}.summary-content,.summary-content *{font-size:16px}.course-item~.course-item{padding-top:10px}.row-separator .table-row-separator{width:100%;border:1px solid hsla(0,0%,80%,.5)}@media (max-width:540px){.program-type-container{text-align:center}.program-type-description-container{text-align:left}table.program-summary tr td{width:70%}table.program-summary th.summary-item{width:30%}}@media (max-width:720px){.program-type-logo{display:block}table.program-summary tr td{width:70%}table.program-summary th.summary-item{width:30%}}@media (min-width:720px){.program-type-logo{display:block}} \ No newline at end of file +@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,600,700,);.modal,.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;outline:0}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translateY(-25%)}.modal.show .modal-dialog{transform:translate(0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;display:flex;flex-direction:column;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.1875rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;align-items:center;justify-content:space-between;padding:15px;border-bottom:1px solid #e7e7e7}.modal-header .close{margin-left:auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:15px}.modal-footer{display:flex;align-items:center;justify-content:flex-end;padding:15px;border-top:1px solid #e7e7e7}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:30px auto}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg{max-width:800px}}*{font-family:Open Sans,Helvetica Neue,Helvetica,Arial,sans-serif;font-size:16px}strong{font-weight:600}s{text-decoration:line-through}q{font-style:italic}.depth-0,.zebra-stripe>:nth-child(odd),body{background-color:#fff;padding-bottom:100px}.text-underline{text-decoration:underline}.expand-list-link button{background:none;border:none}.logo-container{padding-top:10px;width:100%;max-width:768px;min-width:300px;height:auto;margin:0 auto;color:#383838}.pinstripe{border-top:2px solid #0075b4}#header-tagline{font-size:14px;margin-left:.5em;width:300px;display:inline-block;vertical-align:middle}.consent-title{font-size:24px;color:#126f9a}.row:after{content:" ";display:table;clear:both}.enterprise-container{min-width:300px;max-width:768px;margin:0 auto;line-height:1.5;font-size:14px;color:#383838}.enterprise-container:after{content:" ";display:table;clear:both}.enterprise-container *{font-size:14px}.enterprise-container a{color:#126f9a}.enterprise-container a:hover{color:#0b5c8a}.enterprise-logo{width:auto;max-width:150px;max-height:150px}.header-logo{height:47px}.partnered-text{font-size:16px;color:#414141;margin-top:5px;margin-bottom:20px}.partnered-text strong{font-size:1.05em}.media{margin-bottom:10px;display:table;width:100%}.media .media-content,.media .thumbnail{display:table-cell;vertical-align:top}.media .thumbnail{width:55px;padding-top:6px}.media .media-content{padding-left:15px}.media .media-content .course-title{margin-bottom:5px;font-size:16px;font-weight:700}.radio-block{margin-bottom:15px}.radio-block .radio,.radio-block label{display:table-cell;vertical-align:top}.radio-block .radio{width:25px;padding-top:5px}.radio-block label>span{display:block}.btn-confirm{background:#126f9a;border:none;color:#fff;border-radius:5px;margin:10px 0;padding:10px 15px;box-shadow:none;font-weight:700}.btn-confirm:focus,.btn-confirm:hover{cursor:pointer;background:#0b5177}#messages .alert{position:relative;padding:10px 10px 10px 35px;border:1px solid transparent;box-shadow:none;border-radius:2px;margin-bottom:8px}#messages .alert>.fa{position:absolute;left:11px;top:13px;font-size:16px}#messages .alert span{display:block}#messages .success{background-color:#ecfaec;border-color:#b9edb9;color:#3c763d}#messages .info{background-color:#f2f8fb;border-color:#cce3f0;color:#31708f}#messages .warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}#messages .error{background-color:#f2dede;border-color:#ebccd1;color:#a94442}@media (max-width:720px){.logo-container{text-align:center}#header-tagline{text-align:left}.enterprise-container{padding:0 30px 30px}}@media (min-width:720px){[class*=col-]{float:left;display:inline}[class*=col-].col-3{width:30%;padding-left:0;padding-right:30px}[class*=col-].col-7{padding-left:30px;width:70%}.border-left{border-left:1px solid #d9d9d9}.enterprise-logo{display:block}}.modal{width:100%;left:50%;transform:translateX(-50%);box-shadow:none;background:none}.modal-header{background-color:#126f9a;padding:5px}button.close{font-weight:500;background:none;border:none;color:#fff}button.close i{font-size:1.5rem}button.close:hover{color:#c8c8c8}h2.modal-header-text{margin:0;font-weight:700;font-size:18px}.header-wrapper{border-bottom:1px groove #000;padding-bottom:5px;margin-bottom:20px}.staff-container .row{display:flex;margin-bottom:40px}.staff-container .row img{width:110px;height:110px;margin-right:30px}.staff-container .row h2{font-weight:700}.staff-container .row .col-7{flex-grow:100}.staff-container .row:last-child{margin-bottom:0}.modal-content .modal-header-wrapper{display:flex}.modal-content .modal-header-wrapper .image{flex-basis:33%}.modal-content .modal-header-wrapper{margin-bottom:10px}.modal-content .modal-header-wrapper .image img{border:1px solid #126f9a;max-width:210px}.modal-content .modal-header-wrapper .details h1.modal-header-text{font-weight:700;margin-bottom:0;text-align:left}.modal-content .modal-header-wrapper .details .organization img{height:25px}.modal-content .modal-header-wrapper .details .short-description{font-size:14px;margin-bottom:10px;display:flex}.modal-content .modal-body>.details{border:1px solid #126f9a;margin-bottom:20px}.modal-content .modal-body>.details .icon{font-weight:700;margin-right:10px;opacity:.6}.modal-content .modal-body>.details .discount,.modal-content .modal-body>.details .text,.modal-content .modal-body>.details .title{font-size:13px}.modal-content .modal-body>.details .detail-container{display:flex}.modal-content .modal-body>.details .detail-title-container{flex-basis:25%}.modal-content .modal-body>.details .detail-value-container{flex-shrink:1}.modal-content .modal-body>.details .text strike{font-size:11px}.modal-content .modal-body>.details .text .discount{color:green}.modal-content .modal-body>.details ol li span{font-size:16px}.modal-content .modal-body>.details ol{list-style:none;margin:0;padding:0 10px}.modal-content .modal-body>.details li{border-bottom:1px dotted gray;padding:15px 10px}.modal-content .modal-body>.details li:last-child{border-bottom:none}.modal-content .modal-body>.details li:after{content:"";display:table;clear:both}.modal-content .modal-body .overview *{font-size:14px}.modal-content .modal-body .overview .col-7{padding-left:0}.modal-content .modal-body .overview .header-wrapper h2{font-size:18px;font-weight:700;margin:0}.modal-content .modal-body a{color:#126f9a;display:inline-block;text-decoration:underline;border:none;background-color:transparent;padding:0!important;vertical-align:bottom;font-size:16px;font-weight:400}@media (max-width:720px){.modal-header-wrapper .image,.modal-header-wrapper .organization img{display:none}.staff-container .row img{margin-bottom:10px}}@media (min-width:720px){.modal-content .body .details .icon{display:inline}.modal-content .modal-header-wrapper{margin-bottom:25px}.modal-content .modal-header-wrapper .image{display:block;width:210px;float:left;margin-right:35px}.modal-content .modal-header-wrapper .details .organization{display:block}}.consent-container{width:auto;max-width:700px;min-width:300px;margin:0 auto}.consent-container *{font-size:16px}.consent-container p{margin-bottom:15px}.consent-input-container{border-radius:10px;border:1px solid #e5e5e5;padding:15px 20px;margin-bottom:20px}.agreement-text{overflow:hidden;margin-bottom:15px;font-weight:600}.consent-agreement-button{border-color:transparent;height:auto;max-width:180px;min-width:125px;border-radius:5px;font-size:16px;font-weight:700;background:#0075b4;border:none;color:#fff;margin-right:10px;padding:10px 15px;box-shadow:none;width:auto}.consent-agreement-button:hover{cursor:pointer;background:#0b5177}.consent-agreement-button:focus{background-color:#065683}.consent-agreement-button:active{background-color:#0075b4}.consent-agreement-button:disabled{background-color:#a0a0a0;color:#d9d9d9}.consent-policy-dropdown-bar{text-align:left;border:none;width:100%;height:60px;font-weight:600;display:block;background:#f2f8fb;border-radius:5px}.consent-policy-dropdown-bar:active,.consent-policy-dropdown-bar:focus,.consent-policy-dropdown-bar:hover{font-weight:600}.failure-link,.policy-dropdown-link{text-decoration:underline}.failure-link{color:#0075b4;display:inline-block;border:none;background-color:transparent;padding:10px 15px;vertical-align:bottom;font-size:16px}.failure-link:focus,.failure-link:hover{font-weight:400}.dropdown-text{line-height:60px;white-space:nowrap;padding-left:2%;font-size:18px;color:#126f9a}.dropdown-icon-container{float:right;padding-right:10%;padding-top:10px}#consent-policy-dropdown-icon-surround{font-size:40px;color:#fff}#consent-policy-dropdown-icon{font-size:18px;color:#126f9a;padding-top:12%;padding-left:5%}.consent-policy{padding-top:2%;padding-left:3%;padding-right:3%}.data-consent-checkbox{float:left;margin-top:4px;margin-right:10px}.consent-title{font-size:20px!important;color:#126f9a!important;font-weight:400;margin-bottom:10px}.caption{font-size:1.1em}.enterprise-container .consent-input-container #data-sharing{margin:0}.enterprise-container .consent-input-container input{position:relative;top:2px}.enterprise-container ul.consent-items{margin-left:25px}.enterprise-container ul.consent-items li{margin-bottom:4px}.course-confirmation-title{font-size:20px;color:#126f9a;font-weight:400;margin-bottom:6px}.course-image{width:auto;height:50px}.course-detail{font-size:14px;margin-bottom:10px}.course-detail .course-org{font-size:18px}.course-info{font-size:1em}.course-info .fa,.course-info span{display:inline-block;vertical-align:middle;margin-right:5px}.course-info span{font-size:1em}.course-in-future{font-size:17px;margin-bottom:40px}.caption{margin-bottom:15px;font-size:16px}.media{margin-bottom:40px}.media .media-content .course-info{font-size:.8em;margin:8px 0}.media .media-content .course-info .fa,.media .media-content .course-info span{display:inline-block;vertical-align:middle;margin-right:5px}.media .media-content .course-info .fa{font-size:16px}.program-type-logo{width:auto;max-width:150px;max-height:100px;margin-bottom:15px}.platform-description-container,.program-type-container{margin:15px 0}.platform-description-header,.program-type-description-header{font-size:16px;font-weight:700}.platform-description,.program-type-description{margin-top:-15px;font-size:15px}.program-confirmation-title{font-size:1.6em;color:#126f9a;font-weight:400;margin-bottom:6px}.program-detail .program-title{font-size:1.5em;font-weight:700;margin:0}.program-detail .program-org{font-size:1.2em;font-weight:600;margin:0}.program-detail .program-overview{font-size:16px;margin:.5em 0}.program-metadata-header-text{font-size:1.2em;font-weight:600;margin:0}.program-bullet-points{list-style:none;margin:1em 0}.program-bullet-points li:before{content:"\2022"}.program-bullet-points .bullet-item{font-size:16px;margin-left:20px}.program-bullet-points .bullet-item a{font-size:16px}.program-price,.program-price *{font-size:16px;margin:0}.final-price{color:#3c763d}.program-metadata-item~.program-metadata-item{margin-top:20px}.program-metadata-header{margin:10px 0;padding:5px 0;border-bottom:2px solid #ccc}.endorsement-container~.endorsement-container{padding-top:10px}.endorsement-image{max-height:30px;width:auto;display:block;margin:10px 0}.individual-endorsement-quote{margin:10px 0;font-size:16px;font-weight:300}.endorser{width:100%;margin:10px 0}.endorser-description-container{padding-left:50px}.endorser-description{font-weight:600;font-size:16px}.expand-list-link-container *{font-size:16px;color:#126f9a}.expand-list-link-container :hover{color:#0b5c8a}.program-summary-container{margin-top:20px;margin-left:-10px}.program-summary-header{padding:10px 0}.program-summary-header-text{font-size:1.2em;padding-left:3px;margin:0;font-weight:600}table.program-summary{border:1px solid #ccc;border-radius:5px;table-layout:fixed;width:100%}table.program-summary tr td{text-align:left;vertical-align:top;padding:15px 15px 15px 0;width:80%}table.program-summary th.summary-item{text-align:left;vertical-align:top;font-size:16px;font-weight:600;padding:15px 0 15px 15px;width:20%}table.program-summary tr.row-separator td{padding:0 10px}.summary-content,.summary-content *{font-size:16px}.course-item~.course-item{padding-top:10px}.row-separator .table-row-separator{width:100%;border:1px solid hsla(0,0%,80%,.5)}@media (max-width:540px){.program-type-container{text-align:center}.program-type-description-container{text-align:left}table.program-summary tr td{width:70%}table.program-summary th.summary-item{width:30%}}@media (max-width:720px){.program-type-logo{display:block}table.program-summary tr td{width:70%}table.program-summary th.summary-item{width:30%}}@media (min-width:720px){.program-type-logo{display:block}}.select-enterprise-container{width:auto;max-width:600px;min-width:300px;margin:0 auto}.select-enterprise-container *{font-size:16px}.select-enterprise-container p{margin-bottom:15px}.select-enterprise-container .select-enterprise-title{font-size:20px!important;color:#126f9a!important;font-weight:400;margin-bottom:10px}.select-enterprise-container label{margin-right:10px}.select-enterprise-container select{width:60%;max-width:350px;background-color:#fff;color:#333;box-sizing:border-box;margin-bottom:10px;border-radius:5px;font-size:large}.select-enterprise-container .select-enterprise.errorlist{display:list-item;color:#cb0712;margin-left:40px;margin-bottom:20px}.select-enterprise-container .is-hidden{display:none!important}.select-enterprise-container button.select-enterprise-submit-button{border-color:transparent;height:auto;max-width:180px;min-width:125px;border-radius:5px;font-size:16px;font-weight:700;background:#0075b4;border:none;color:#fff;margin-right:10px;padding:10px 15px;box-shadow:none;width:auto}.select-enterprise-container button.select-enterprise-submit-button:hover{cursor:pointer;background:#0b5177}.select-enterprise-container button.select-enterprise-submit-button:focus{background-color:#065683}.select-enterprise-container button.select-enterprise-submit-button:active{background-color:#0075b4}.select-enterprise-container button.select-enterprise-submit-button:disabled{background-color:#a0a0a0;color:#d9d9d9} \ No newline at end of file diff --git a/enterprise/static/enterprise/js/enterprise_selection_page.js b/enterprise/static/enterprise/js/enterprise_selection_page.js new file mode 100644 index 0000000000..df0ba0e0a5 --- /dev/null +++ b/enterprise/static/enterprise/js/enterprise_selection_page.js @@ -0,0 +1,46 @@ +function redirectToURL(redirectURL) { + location.href = redirectURL; +} + +function setupFormSubmit() { + $('#select-enterprise-form').submit(function(event){ + event.preventDefault(); + var selectedEnterpriseUUID = $("#id_enterprise").val(); + var successURL = $("#id_success_url").val(); + + $("#activate-progress-icon").removeClass("is-hidden"); + $("#select-enterprise-submit").attr("disabled", true); + + $.ajax({ + url : "/enterprise/select/active", + method : "POST", + beforeSend: function (xhr) { + xhr.setRequestHeader("X-CSRFToken", $.cookie("csrftoken")); + }, + data : { + enterprise : selectedEnterpriseUUID, + success_url: successURL + }, + + success: function(xhr) { + redirectToURL(xhr.success_url); + }, + + error : function(xhr) { + $("#activate-progress-icon").addClass("is-hidden"); + $("#select-enterprise-submit").attr("disabled", false); + $("#select-enterprise-form-error") + .text(xhr.responseJSON.errors.join(", ")) + .removeClass( "is-hidden" ); + } + }); + }); +} + +(function() { + "use strict"; + + $(document).ready(function() { + setupFormSubmit(); + }); +}).call(this); \ No newline at end of file diff --git a/enterprise/static/enterprise/sass/main.scss b/enterprise/static/enterprise/sass/main.scss index b2a74a5fe1..94ea840aa3 100644 --- a/enterprise/static/enterprise/sass/main.scss +++ b/enterprise/static/enterprise/sass/main.scss @@ -21,3 +21,4 @@ @import 'partials/views/data_sharing_consent'; @import 'partials/views/course_landing_page'; @import 'partials/views/program_landing_page'; +@import 'partials/views/enterprise_select_page'; diff --git a/enterprise/static/enterprise/sass/partials/views/_enterprise_select_page.scss b/enterprise/static/enterprise/sass/partials/views/_enterprise_select_page.scss new file mode 100644 index 0000000000..937dd3be57 --- /dev/null +++ b/enterprise/static/enterprise/sass/partials/views/_enterprise_select_page.scss @@ -0,0 +1,82 @@ +.select-enterprise-container { + width: auto; + max-width: 600px; + min-width: 300px; + margin: 0 auto; + + * { + font-size: 16px; + } + + p { + margin-bottom: 15px; + } + + .select-enterprise-title { + font-size: 20px !important; + color: $blue !important; + font-weight: normal; + margin-bottom: 10px; + } + + label { + margin-right: 10px; + } + + select { + width: 60%; + max-width: 350px; + background-color: white; + color: #333; + box-sizing: border-box; + margin-bottom: 10px; + border-radius: 5px; + font-size: large; + } + + .select-enterprise.errorlist { + display: list-item; + color: rgb(203, 7, 18); + margin-left: 40px; + margin-bottom: 20px; + } + + .is-hidden { + display: none !important; + } + + button.select-enterprise-submit-button { + border-color: transparent; + height: auto; + max-width: 180px; + min-width: 125px; + border-radius: 5px; + font-size: 16px; + font-weight: bold; + background: #0075B4; + border: none; + color: #fff; + margin-right: 10px; + padding: 10px 15px; + box-shadow: none; + width: auto; + + &:hover { + cursor: pointer; + background: #0b5177; + } + + &:focus { + background-color: #065683; + } + + &:active { + background-color: #0075B4; + } + + &:disabled { + background-color: #a0a0a0; + color: #d9d9d9; + } + } +} diff --git a/enterprise/templates/enterprise/enterprise_customer_select_form.html b/enterprise/templates/enterprise/enterprise_customer_select_form.html new file mode 100644 index 0000000000..e207bdaa8e --- /dev/null +++ b/enterprise/templates/enterprise/enterprise_customer_select_form.html @@ -0,0 +1,59 @@ + +{% extends 'enterprise/base.html' %} + +{% load i18n staticfiles enterprise %} + +{% block extrahead %} + + +{% endblock %} + +{% block contents %} +
+
+
+ +

{{ select_enterprise_message_title|safe }}

+
+

{{ select_enterprise_message_subtitle|safe }}

+
+ +
+ {% csrf_token %} + + + + {% for field in form.visible_fields %} +
+ {{ field.label_tag }} + {{ field.errors }} + {{ field }} + {{ field.help_text }} +
+ {% endfor %} + + {% for hidden_field in form.hidden_fields %} +
+ {{ hidden_field.errors }} + {{ hidden_field }} +
+ {% endfor %} + + + +
+ +
+
+
+{% endblock %} diff --git a/enterprise/urls.py b/enterprise/urls.py index 5f99e9839a..95e10c4d40 100644 --- a/enterprise/urls.py +++ b/enterprise/urls.py @@ -8,7 +8,7 @@ from django.conf.urls import include, url from enterprise.constants import COURSE_KEY_URL_PATTERN -from enterprise.views import GrantDataSharingPermissions, RouterView +from enterprise.views import EnterpriseSelectionView, GrantDataSharingPermissions, RouterView ENTERPRISE_ROUTER = RouterView.as_view() @@ -18,6 +18,11 @@ GrantDataSharingPermissions.as_view(), name='grant_data_sharing_permissions' ), + url( + r'^enterprise/select/active', + EnterpriseSelectionView.as_view(), + name='enterprise_select_active' + ), url( r'^enterprise/handle_consent_enrollment/(?P[^/]+)/course/{}/$'.format( settings.COURSE_ID_PATTERN diff --git a/enterprise/views.py b/enterprise/views.py index addb5105dd..d2d2edbd5c 100644 --- a/enterprise/views.py +++ b/enterprise/views.py @@ -20,7 +20,7 @@ from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.core.urlresolvers import reverse from django.db import IntegrityError, transaction -from django.http import Http404 +from django.http import Http404, JsonResponse from django.shortcuts import redirect, render from django.utils.decorators import method_decorator from django.utils.text import slugify @@ -28,14 +28,17 @@ from django.utils.translation import ugettext as _ from django.utils.translation import ungettext from django.views.generic import View +from django.views.generic.edit import FormView from consent.helpers import get_data_sharing_consent from consent.models import DataSharingConsent from enterprise import constants, messages +from enterprise.api.v1.serializers import EnterpriseCustomerUserWriteSerializer from enterprise.api_client.discovery import get_course_catalog_api_service_client from enterprise.api_client.ecommerce import EcommerceApiClient from enterprise.api_client.lms import CourseApiClient, EmbargoApiClient, EnrollmentApiClient from enterprise.decorators import enterprise_login_required, force_fresh_session +from enterprise.forms import ENTERPRISE_SELECT_SUBTITLE, EnterpriseSelectionForm from enterprise.models import EnterpriseCourseEnrollment, EnterpriseCustomerCatalog, EnterpriseCustomerUser from enterprise.utils import ( CourseEnrollmentDowngradeError, @@ -91,13 +94,13 @@ def verify_edx_resources(): ) -def get_global_context(request, enterprise_customer): +def get_global_context(request, enterprise_customer=None): """ Get the set of variables that are needed by default across views. """ platform_name = get_configuration_value("PLATFORM_NAME", settings.PLATFORM_NAME) # pylint: disable=no-member - return { + context = { 'enterprise_customer': enterprise_customer, 'LMS_SEGMENT_KEY': settings.LMS_SEGMENT_KEY, 'LANGUAGE_CODE': get_language_from_request(request), @@ -110,19 +113,25 @@ def get_global_context(request, enterprise_customer): 'platform_name': platform_name, 'header_logo_alt_text': _('{platform_name} home page').format(platform_name=platform_name), 'welcome_text': constants.WELCOME_TEXT.format(platform_name=platform_name), - 'enterprise_welcome_text': constants.ENTERPRISE_WELCOME_TEXT.format( - enterprise_customer_name=enterprise_customer.name, - platform_name=platform_name, - strong_start='', - strong_end='', - line_break='
', - privacy_policy_link_start="".format( - pp_url=get_configuration_value('PRIVACY', 'https://www.edx.org/edx-privacy-policy', type='url'), - ), - privacy_policy_link_end="", - ), } + if enterprise_customer is not None: + context.update({ + 'enterprise_welcome_text': constants.ENTERPRISE_WELCOME_TEXT.format( + enterprise_customer_name=enterprise_customer.name, + platform_name=platform_name, + strong_start='', + strong_end='', + line_break='
', + privacy_policy_link_start="".format( + pp_url=get_configuration_value('PRIVACY', 'https://www.edx.org/edx-privacy-policy', type='url'), + ), + privacy_policy_link_end="", + ), + }) + + return context + def get_price_text(price, request): """ @@ -808,6 +817,69 @@ def post(self, request): return redirect(success_url if consent_provided else failure_url) +@method_decorator(login_required, name='dispatch') +class EnterpriseSelectionView(FormView): + """ + Allow an enterprise learner to activate one of learner's linked enterprises. + """ + + form_class = EnterpriseSelectionForm + template_name = 'enterprise/enterprise_customer_select_form.html' + + def get_initial(self): + """Return the initial data to use for forms on this view.""" + initial = super(EnterpriseSelectionView, self).get_initial() + enterprises = EnterpriseCustomerUser.objects.filter( + user_id=self.request.user.id + ).values_list( + 'enterprise_customer__uuid', 'enterprise_customer__name' + ) + initial.update({ + 'enterprises': [(str(uuid), name) for uuid, name in enterprises], + 'success_url': self.request.GET.get('success_url'), + 'user_id': self.request.user.id + }) + return initial + + def get_context_data(self, **kwargs): + """Return the context data needed to render the view.""" + context_data = super(EnterpriseSelectionView, self).get_context_data(**kwargs) + context_data.update({ + 'page_title': _(u'Select Organization'), + 'select_enterprise_message_title': _(u'Select an organization'), + 'select_enterprise_message_subtitle': ENTERPRISE_SELECT_SUBTITLE, + }) + context_data.update(get_global_context(self.request, None)) + return context_data + + def form_invalid(self, form): + """ + If the form is invalid then return the errors. + """ + # flatten the list of lists + errors = [item for sublist in form.errors.values() for item in sublist] + return JsonResponse({'errors': errors}, status=400) + + def form_valid(self, form): + """ + If the form is valid, activate the selected enterprise and return `success_url`. + """ + enterprise_customer = form.cleaned_data['enterprise'] + serializer = EnterpriseCustomerUserWriteSerializer(data={ + 'enterprise_customer': enterprise_customer, + 'username': self.request.user.username, + 'active': True + }) + if serializer.is_valid(): + serializer.save() + LOGGER.info( + '[Enterprise Selection Page] Learner activated an enterprise. User: %s, EnterpriseCustomer: %s', + enterprise_customer, + self.request.user.username + ) + return JsonResponse({'success_url': form.cleaned_data['success_url']}) + + class HandleConsentEnrollment(View): """ Handle enterprise course enrollment at providing data sharing consent. diff --git a/spec/javascripts/enterprise_select_spec.js b/spec/javascripts/enterprise_select_spec.js new file mode 100644 index 0000000000..c9bd886503 --- /dev/null +++ b/spec/javascripts/enterprise_select_spec.js @@ -0,0 +1,90 @@ +describe('Enterprise Selection Page', function () { + beforeEach(function () { + jasmine.getFixtures().fixturesPath = '__spec__/fixtures'; + loadFixtures('enterprise_select.html'); + setupFormSubmit(); + }); + + describe('Rendering', function () { + it('renders page correctly', function () { + expect($('.select-enterprise-title').text()).toBe('Select an organization'); + expect($('.select-enterprise-message p').text()).toBe( + 'You have access to multiple organizations. Select the organization that you will use ' + + 'to sign up for courses. If you want to change organizations, sign out and sign back in.' + ); + expect($('#select-enterprise-form label').text()).toBe('Organization:'); + + var optionValues = $.map($('#id_enterprise option') ,function(option) { + return option.value; + }); + var optionTexts = $.map($('#id_enterprise option') ,function(option) { + return option.text; + }); + expect(optionValues).toEqual( + ['6ae013d4-c5c4-474d-8da9-0e559b2448e2', '885f4d97-5a21-4e8a-8723-a434bc527e74'] + ); + expect(optionTexts).toEqual(['batman', 'riddler']); + + expect($('#select-enterprise-submit').text().trim()).toBe('Continue'); + }); + }); + + describe('Form', function () { + beforeEach(function () { + jasmine.Ajax.install(); + }); + + afterEach(function () { + jasmine.Ajax.uninstall(); + }); + + it('works expected on correct post data', function () { + var response = { + 'success_url': '/dashboard' + }; + var redirectSpy = spyOn(window, 'redirectToURL'); + + jasmine.Ajax + .stubRequest('/enterprise/select/active') + .andReturn({ + responseText: JSON.stringify(response) + }); + + $( '#select-enterprise-submit' ).trigger( 'click' ); + + var request = jasmine.Ajax.requests.mostRecent(); + expect(request.url).toBe('/enterprise/select/active'); + expect(request.method).toBe('POST'); + expect(request.data().enterprise).toEqual(['6ae013d4-c5c4-474d-8da9-0e559b2448e2']); + expect(request.data().success_url).toEqual(['/dashboard']); + expect(redirectSpy.calls.count()).toEqual(1); + expect(redirectSpy.calls.first().args).toEqual(['/dashboard']); + }); + + it('works expected on incorrect post data', function () { + var response = { + 'errors': ['Incorrect success url'] + }; + + jasmine.Ajax + .stubRequest('/enterprise/select/active') + .andReturn({ + status: 400, + responseText: JSON.stringify(response) + }); + + // remove success url value from hidden input + $('#id_success_url').removeAttr('value'); + + $( '#select-enterprise-submit' ).trigger( 'click' ); + + var request = jasmine.Ajax.requests.mostRecent(); + expect(request.url).toBe('/enterprise/select/active'); + expect(request.method).toBe('POST'); + expect(request.data().enterprise).toEqual(['6ae013d4-c5c4-474d-8da9-0e559b2448e2']); + expect(request.data().success_url).toEqual(['']); + + expect($('#select-enterprise-form-error').text().trim()).toEqual(response.errors[0]); + }); + }); +}); diff --git a/spec/javascripts/fixtures/enterprise_select.html b/spec/javascripts/fixtures/enterprise_select.html new file mode 100644 index 0000000000..908850aa70 --- /dev/null +++ b/spec/javascripts/fixtures/enterprise_select.html @@ -0,0 +1,41 @@ +
+
+
+ +

Select an organization

+
+

You have access to multiple organizations. Select the organization that you will use to sign up for courses. If you want to change organizations, sign out and sign back in.

+
+ +
+ + + +
+ + +
+ +
+ +
+ + + +
+ +
+
+
\ No newline at end of file diff --git a/tests/test_enterprise/views/test_enterprise_selection.py b/tests/test_enterprise/views/test_enterprise_selection.py new file mode 100644 index 0000000000..36038faf64 --- /dev/null +++ b/tests/test_enterprise/views/test_enterprise_selection.py @@ -0,0 +1,166 @@ +# -*- coding: utf-8 -*- +""" +Tests for the ``EnterpriseSelectionView`` view of the Enterprise app. +""" + +from __future__ import absolute_import, unicode_literals + +import os +import tempfile + +import ddt +import mock +from pytest import mark + +from django.conf import settings +from django.core.urlresolvers import reverse +from django.test import Client, TestCase + +from enterprise.forms import ENTERPRISE_SELECT_SUBTITLE +from enterprise.models import EnterpriseCustomerUser +from test_utils.factories import EnterpriseCustomerFactory, EnterpriseCustomerUserFactory, UserFactory + + +@mark.django_db +@ddt.ddt +class TestEnterpriseSelectionView(TestCase): + """ + Test EnterpriseSelectionView. + """ + url = reverse('enterprise_select_active') + + def setUp(self): + self.user = UserFactory.create(is_active=True) + self.user.set_password("QWERTY") + self.user.save() + self.client = Client() + super(TestEnterpriseSelectionView, self).setUp() + + self.success_url = '/enterprise/grant_data_sharing_permissions' + enterprises = ['Gryffindor', 'Hufflepuff', 'Ravenclaw', 'Slytherin'] + for enterprise in enterprises: + enterprise_customer = EnterpriseCustomerFactory(name=enterprise) + EnterpriseCustomerUserFactory( + user_id=self.user.id, + enterprise_customer=enterprise_customer + ) + + enterprises = EnterpriseCustomerUser.objects.filter( + user_id=self.user.id + ).values_list( + 'enterprise_customer__uuid', 'enterprise_customer__name' + ) + self.enterprise_choices = [(str(uuid), name) for uuid, name in enterprises] + + # create a temporary template file + # rendering `enterprise/enterprise_customer_select_form.html` fails becuase of dependency on edx-platform + tpl = tempfile.NamedTemporaryFile( + prefix='test_template.', + suffix=".html", + dir=settings.REPO_ROOT + '/templates/enterprise/', + delete=False, + ) + tpl.close() + self.addCleanup(os.remove, tpl.name) + + patcher = mock.patch( + 'enterprise.views.EnterpriseSelectionView.template_name', + mock.PropertyMock(return_value=tpl.name) + ) + patcher.start() + self.addCleanup(patcher.stop) + + def _login(self): + """ + Log user in. + """ + assert self.client.login(username=self.user.username, password="QWERTY") + + def test_view_unauthenticated_user(self): + """ + Test that view will be available to logged in user only. + """ + response = self.client.get(self.url) + assert response.status_code == 302 + assert response.url == '/accounts/login/?next=/enterprise/select/active' + + def test_view_get(self): + """ + Test that view HTTP GET works as expected. + """ + self._login() + response = self.client.get(self.url + '?success_url={}'.format(self.success_url)) + assert response.status_code == 200 + + assert response.context['select_enterprise_message_title'] == u'Select an organization' + assert response.context['select_enterprise_message_subtitle'] == ENTERPRISE_SELECT_SUBTITLE + + assert sorted(response.context['form'].fields.keys()) == sorted(['enterprise', 'success_url']) + assert response.context['form'].fields['enterprise'].choices == self.enterprise_choices + assert response.context['form'].fields['success_url'].initial == self.success_url + + def test_view_post(self): + """ + Test that view HTTP POST works as expected. + """ + self._login() + user_id = self.user.pk + + # before selection all enterprises are active for learner + for obj in EnterpriseCustomerUser.objects.filter(user_id=user_id): + assert obj.active + + new_enterprise = self.enterprise_choices[2][0] + post_data = { + 'enterprise': new_enterprise, + 'success_url': self.success_url + } + + with mock.patch('enterprise.views.LOGGER.info') as mock_logger: + response = self.client.post(self.url, post_data) + assert mock_logger.called + assert mock_logger.call_args.args == ( + u'[Enterprise Selection Page] Learner activated an enterprise. User: %s, EnterpriseCustomer: %s', + new_enterprise, + self.user.username + ) + + assert response.status_code == 200 + assert response.json().get('success_url') == self.success_url + + # after selection only the selected enterprise should be active for learner + assert EnterpriseCustomerUser.objects.get(user_id=user_id, enterprise_customer=new_enterprise).active + + # all other enterprises for learner should be non-active + for obj in EnterpriseCustomerUser.objects.filter(user_id=user_id).exclude(enterprise_customer=new_enterprise): + assert not obj.active + + @ddt.data( + { + 'enterprise': '111', + 'success_url': '', + 'errors': [ + u'Enterprise not found', + u'Select a valid choice. 111 is not one of the available choices.' + ] + }, + { + 'enterprise': None, + 'success_url': '', + 'errors': [u'Incorrect success url'] + }, + ) + @ddt.unpack + def test_post_errors(self, enterprise, success_url, errors): + """ + Test errors are raised if incorrect data is POSTed. + """ + self._login() + selected_enterprise = self.enterprise_choices[2][0] + post_data = { + 'enterprise': enterprise or selected_enterprise, + 'success_url': success_url, + } + response = self.client.post(self.url, post_data) + assert response.status_code == 400 + assert sorted(response.json().get('errors')) == sorted(errors)