Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
4dff8ecf
Commit
4dff8ecf
authored
May 29, 2015
by
Muhammad Shoaib
Committed by
Chris Dodge
Jun 03, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
SOL-916 Add ability to query, enable, disable, unredeem Enrollment Codes
parent
0db8bac7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
439 additions
and
30 deletions
+439
-30
lms/djangoapps/instructor/tests/test_registration_codes.py
+0
-0
lms/djangoapps/instructor/views/api.py
+1
-1
lms/djangoapps/instructor/views/instructor_dashboard.py
+1
-0
lms/djangoapps/instructor/views/registration_codes.py
+126
-0
lms/djangoapps/instructor_analytics/basic.py
+1
-1
lms/djangoapps/shoppingcart/migrations/0028_auto__add_field_courseregistrationcode_is_valid.py
+0
-0
lms/djangoapps/shoppingcart/models.py
+14
-2
lms/djangoapps/shoppingcart/tests/test_views.py
+24
-4
lms/djangoapps/shoppingcart/views.py
+19
-12
lms/static/js/instructor_dashboard/ecommerce.js
+128
-3
lms/static/sass/course/instructor/_instructor_2.scss
+23
-3
lms/templates/instructor/instructor_dashboard_2/e-commerce.html
+20
-3
lms/templates/instructor/instructor_dashboard_2/enrollment-code-lookup-links.underscore
+23
-0
lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html
+1
-1
lms/templates/instructor/instructor_dashboard_2/invalidate_registration_code_modal.html
+42
-0
lms/templates/shoppingcart/receipt.html
+2
-0
lms/urls.py
+14
-0
No files found.
lms/djangoapps/instructor/tests/test_registration_codes.py
0 → 100644
View file @
4dff8ecf
This diff is collapsed.
Click to expand it.
lms/djangoapps/instructor/views/api.py
View file @
4dff8ecf
...
...
@@ -1253,7 +1253,7 @@ def registration_codes_csv(file_name, codes_list, csv_type=None):
# csv headers
query_features
=
[
'code'
,
'redeem_code_url'
,
'course_id'
,
'company_name'
,
'created_by'
,
'redeemed_by'
,
'invoice_id'
,
'purchaser'
,
'customer_reference_number'
,
'internal_reference'
'redeemed_by'
,
'invoice_id'
,
'purchaser'
,
'customer_reference_number'
,
'internal_reference'
,
'is_valid'
]
registration_codes
=
instructor_analytics
.
basic
.
course_registration_features
(
query_features
,
codes_list
,
csv_type
)
...
...
lms/djangoapps/instructor/views/instructor_dashboard.py
View file @
4dff8ecf
...
...
@@ -205,6 +205,7 @@ def _section_e_commerce(course, access, paid_mode, coupons_enabled, reports_enab
'list_financial_report_downloads_url'
:
reverse
(
'list_financial_report_downloads'
,
kwargs
=
{
'course_id'
:
unicode
(
course_key
)}),
'list_instructor_tasks_url'
:
reverse
(
'list_instructor_tasks'
,
kwargs
=
{
'course_id'
:
unicode
(
course_key
)}),
'look_up_registration_code'
:
reverse
(
'look_up_registration_code'
,
kwargs
=
{
'course_id'
:
unicode
(
course_key
)}),
'coupons'
:
coupons
,
'sales_admin'
:
access
[
'sales_admin'
],
'coupons_enabled'
:
coupons_enabled
,
...
...
lms/djangoapps/instructor/views/registration_codes.py
0 → 100644
View file @
4dff8ecf
"""
E-commerce Tab Instructor Dashboard Query Registration Code Status.
"""
from
django.core.urlresolvers
import
reverse
from
django.views.decorators.http
import
require_GET
,
require_POST
from
instructor.enrollment
import
get_email_params
,
send_mail_to_student
from
django.utils.translation
import
ugettext
as
_
from
courseware.courses
import
get_course_by_id
from
instructor.views.api
import
require_level
from
student.models
import
CourseEnrollment
from
util.json_request
import
JsonResponse
from
shoppingcart.models
import
CourseRegistrationCode
,
RegistrationCodeRedemption
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
django.views.decorators.cache
import
cache_control
import
logging
log
=
logging
.
getLogger
(
__name__
)
@cache_control
(
no_cache
=
True
,
no_store
=
True
,
must_revalidate
=
True
)
@require_level
(
'staff'
)
@require_GET
def
look_up_registration_code
(
request
,
course_id
):
# pylint: disable=unused-argument
"""
Look for the registration_code in the database.
and check if it is still valid, allowed to redeem or not.
"""
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
code
=
request
.
GET
.
get
(
'registration_code'
)
course
=
get_course_by_id
(
course_key
,
depth
=
0
)
try
:
registration_code
=
CourseRegistrationCode
.
objects
.
get
(
code
=
code
)
except
CourseRegistrationCode
.
DoesNotExist
:
return
JsonResponse
({
'is_registration_code_exists'
:
False
,
'is_registration_code_valid'
:
False
,
'is_registration_code_redeemed'
:
False
,
'message'
:
_
(
'The enrollment code ({code}) was not found for the {course_name} course.'
)
.
format
(
code
=
code
,
course_name
=
course
.
display_name
)
},
status
=
400
)
# status code 200: OK by default
reg_code_already_redeemed
=
RegistrationCodeRedemption
.
is_registration_code_redeemed
(
code
)
registration_code_detail_url
=
reverse
(
'registration_code_details'
,
kwargs
=
{
'course_id'
:
unicode
(
course_id
)})
return
JsonResponse
({
'is_registration_code_exists'
:
True
,
'is_registration_code_valid'
:
registration_code
.
is_valid
,
'is_registration_code_redeemed'
:
reg_code_already_redeemed
,
'registration_code_detail_url'
:
registration_code_detail_url
})
# status code 200: OK by default
@cache_control
(
no_cache
=
True
,
no_store
=
True
,
must_revalidate
=
True
)
@require_level
(
'staff'
)
@require_POST
def
registration_code_details
(
request
,
course_id
):
"""
Post handler to mark the registration code as
1) valid
2) invalid
3) Unredeem.
"""
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
code
=
request
.
POST
.
get
(
'registration_code'
)
action_type
=
request
.
POST
.
get
(
'action_type'
)
course
=
get_course_by_id
(
course_key
,
depth
=
0
)
action_type_messages
=
{
'invalidate_registration_code'
:
_
(
'This enrollment code has been canceled. It can no longer be used.'
),
'unredeem_registration_code'
:
_
(
'This enrollment code has been marked as unused.'
),
'validate_registration_code'
:
_
(
'The enrollment code has been restored.'
)
}
try
:
registration_code
=
CourseRegistrationCode
.
objects
.
get
(
code
=
code
)
except
CourseRegistrationCode
.
DoesNotExist
:
return
JsonResponse
({
'message'
:
_
(
'The enrollment code ({code}) was not found for the {course_name} course.'
)
.
format
(
code
=
code
,
course_name
=
course
.
display_name
)},
status
=
400
)
if
action_type
==
'invalidate_registration_code'
:
registration_code
.
is_valid
=
False
registration_code
.
save
()
if
RegistrationCodeRedemption
.
is_registration_code_redeemed
(
code
):
code_redemption
=
RegistrationCodeRedemption
.
get_registration_code_redemption
(
code
,
course_key
)
delete_redemption_entry
(
request
,
code_redemption
,
course_key
)
if
action_type
==
'validate_registration_code'
:
registration_code
.
is_valid
=
True
registration_code
.
save
()
if
action_type
==
'unredeem_registration_code'
:
code_redemption
=
RegistrationCodeRedemption
.
get_registration_code_redemption
(
code
,
course_key
)
if
code_redemption
is
None
:
return
JsonResponse
({
'message'
:
_
(
'The redemption does not exist against enrollment code ({code}).'
)
.
format
(
code
=
code
)},
status
=
400
)
delete_redemption_entry
(
request
,
code_redemption
,
course_key
)
return
JsonResponse
({
'message'
:
action_type_messages
[
action_type
]})
def
delete_redemption_entry
(
request
,
code_redemption
,
course_key
):
"""
delete the redemption entry from the table and
unenroll the user who used the registration code
for the enrollment and send him/her the unenrollment email.
"""
user
=
code_redemption
.
redeemed_by
email_address
=
code_redemption
.
redeemed_by
.
email
full_name
=
code_redemption
.
redeemed_by
.
profile
.
name
CourseEnrollment
.
unenroll
(
user
,
course_key
,
skip_refund
=
True
)
course
=
get_course_by_id
(
course_key
,
depth
=
0
)
email_params
=
get_email_params
(
course
,
True
,
secure
=
request
.
is_secure
())
email_params
[
'message'
]
=
'enrolled_unenroll'
email_params
[
'email_address'
]
=
email_address
email_params
[
'full_name'
]
=
full_name
send_mail_to_student
(
email_address
,
email_params
)
# remove the redemption entry from the database.
log
.
info
(
'deleting redemption entry (
%
s) from the database.'
,
code_redemption
.
id
)
code_redemption
.
delete
()
lms/djangoapps/instructor_analytics/basic.py
View file @
4dff8ecf
...
...
@@ -32,7 +32,7 @@ SALE_ORDER_FEATURES = ('id', 'company_name', 'company_contact_name', 'company_co
'bill_to_country'
,
'order_type'
,)
AVAILABLE_FEATURES
=
STUDENT_FEATURES
+
PROFILE_FEATURES
COURSE_REGISTRATION_FEATURES
=
(
'code'
,
'course_id'
,
'created_by'
,
'created_at'
)
COURSE_REGISTRATION_FEATURES
=
(
'code'
,
'course_id'
,
'created_by'
,
'created_at'
,
'is_valid'
)
COUPON_FEATURES
=
(
'code'
,
'course_id'
,
'percentage_discount'
,
'description'
,
'expiration_date'
,
'is_active'
)
...
...
lms/djangoapps/shoppingcart/migrations/0028_auto__add_field_courseregistrationcode_is_valid.py
0 → 100644
View file @
4dff8ecf
This diff is collapsed.
Click to expand it.
lms/djangoapps/shoppingcart/models.py
View file @
4dff8ecf
...
...
@@ -1161,6 +1161,7 @@ class CourseRegistrationCode(models.Model):
created_at
=
models
.
DateTimeField
(
default
=
datetime
.
now
(
pytz
.
utc
))
order
=
models
.
ForeignKey
(
Order
,
db_index
=
True
,
null
=
True
,
related_name
=
"purchase_order"
)
mode_slug
=
models
.
CharField
(
max_length
=
100
,
null
=
True
)
is_valid
=
models
.
BooleanField
(
default
=
True
)
# For backwards compatibility, we maintain the FK to "invoice"
# In the future, we will remove this in favor of the FK
...
...
@@ -1196,10 +1197,21 @@ class RegistrationCodeRedemption(models.Model):
Checks the existence of the registration code
in the RegistrationCodeRedemption
"""
return
cls
.
objects
.
filter
(
registration_code
=
course_reg_code
)
.
exists
()
return
cls
.
objects
.
filter
(
registration_code__code
=
course_reg_code
)
.
exists
()
@classmethod
def
get_registration_code_redemption
(
cls
,
code
,
course_id
):
"""
Returns the registration code redemption object if found else returns None.
"""
try
:
code_redemption
=
cls
.
objects
.
get
(
registration_code__code
=
code
,
registration_code__course_id
=
course_id
)
except
cls
.
DoesNotExist
:
code_redemption
=
None
return
code_redemption
@classmethod
def
create_invoice_generated_registration_redemption
(
cls
,
course_reg_code
,
user
):
def
create_invoice_generated_registration_redemption
(
cls
,
course_reg_code
,
user
):
# pylint: disable=invalid-name
"""
This function creates a RegistrationCodeRedemption entry in case the registration codes were invoice generated
and thus the order_id is missing.
...
...
lms/djangoapps/shoppingcart/tests/test_views.py
View file @
4dff8ecf
...
...
@@ -121,12 +121,14 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
percentage_discount
=
self
.
percentage_discount
,
created_by
=
self
.
user
,
is_active
=
is_active
)
coupon
.
save
()
def
add_reg_code
(
self
,
course_key
,
mode_slug
=
'honor'
):
def
add_reg_code
(
self
,
course_key
,
mode_slug
=
'honor'
,
is_valid
=
True
):
"""
add dummy registration code into models
"""
course_reg_code
=
CourseRegistrationCode
(
code
=
self
.
reg_code
,
course_id
=
course_key
,
created_by
=
self
.
user
,
mode_slug
=
mode_slug
code
=
self
.
reg_code
,
course_id
=
course_key
,
created_by
=
self
.
user
,
mode_slug
=
mode_slug
,
is_valid
=
is_valid
)
course_reg_code
.
save
()
...
...
@@ -387,6 +389,23 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self
.
assertEqual
(
resp
.
status_code
,
404
)
self
.
assertIn
(
"Discount does not exist against code '{0}'."
.
format
(
self
.
coupon_code
),
resp
.
content
)
def
test_inactive_registration_code_returns_error
(
self
):
"""
test to redeem inactive registration code and
it returns an error.
"""
course_key
=
self
.
course_key
.
to_deprecated_string
()
self
.
add_reg_code
(
course_key
,
is_valid
=
False
)
self
.
add_course_to_user_cart
(
self
.
course_key
)
# now apply the inactive registration code
# it will raise an exception
resp
=
self
.
client
.
post
(
reverse
(
'shoppingcart.views.use_code'
),
{
'code'
:
self
.
reg_code
})
self
.
assertEqual
(
resp
.
status_code
,
400
)
self
.
assertIn
(
"This enrollment code ({enrollment_code}) is no longer valid."
.
format
(
enrollment_code
=
self
.
reg_code
),
resp
.
content
)
def
test_course_does_not_exist_in_cart_against_valid_reg_code
(
self
):
course_key
=
self
.
course_key
.
to_deprecated_string
()
+
'testing'
self
.
add_reg_code
(
course_key
)
...
...
@@ -525,7 +544,6 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self
.
assertEqual
(
coupon
.
is_active
,
False
)
def
test_course_free_discount_for_valid_active_reg_code
(
self
):
self
.
add_reg_code
(
self
.
course_key
)
self
.
add_course_to_user_cart
(
self
.
course_key
)
...
...
@@ -546,7 +564,9 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
# the item has been removed when using the registration code for the first time
resp
=
self
.
client
.
post
(
reverse
(
'shoppingcart.views.use_code'
),
{
'code'
:
self
.
reg_code
})
self
.
assertEqual
(
resp
.
status_code
,
400
)
self
.
assertIn
(
"Oops! The code '{0}' you entered is either invalid or expired"
.
format
(
self
.
reg_code
),
resp
.
content
)
self
.
assertIn
(
"This enrollment code ({enrollment_code}) is not valid."
.
format
(
enrollment_code
=
self
.
reg_code
),
resp
.
content
)
def
test_upgrade_from_valid_reg_code
(
self
):
"""Use a valid registration code to upgrade from honor to verified mode. """
...
...
lms/djangoapps/shoppingcart/views.py
View file @
4dff8ecf
...
...
@@ -287,17 +287,14 @@ def get_reg_code_validity(registration_code, request, limiter):
except
CourseRegistrationCode
.
DoesNotExist
:
reg_code_is_valid
=
False
else
:
reg_code_is_valid
=
True
try
:
RegistrationCodeRedemption
.
objects
.
get
(
registration_code__code
=
registration_code
)
except
RegistrationCodeRedemption
.
DoesNotExist
:
reg_code_already_redeemed
=
False
if
course_registration
.
is_valid
:
reg_code_is_valid
=
True
else
:
reg_code_
already_redeemed
=
Tru
e
reg_code_
is_valid
=
Fals
e
reg_code_already_redeemed
=
RegistrationCodeRedemption
.
is_registration_code_redeemed
(
registration_code
)
if
not
reg_code_is_valid
:
# tick the rate limiter counter
AUDIT_LOG
.
info
(
"Redemption of a
non existing RegistrationCode {code}"
.
format
(
code
=
registration_code
)
)
AUDIT_LOG
.
info
(
"Redemption of a
invalid RegistrationCode
%
s"
,
registration_code
)
limiter
.
tick_bad_request_counter
(
request
)
raise
Http404
()
...
...
@@ -430,15 +427,24 @@ def _is_enrollment_code_an_update(course, user, redemption_code):
def
use_registration_code
(
course_reg
,
user
):
"""
This method utilize course registration code.
If the registration code is invalid, it returns an error.
If the registration code is already redeemed, it returns an error.
Else, it identifies and removes the applicable OrderItem from the Order
and redirects the user to the Registration code redemption page.
"""
if
RegistrationCodeRedemption
.
is_registration_code_redeemed
(
course_reg
)
:
log
.
warning
(
u"
Registration code '
%
s' already used
"
,
course_reg
.
code
)
if
not
course_reg
.
is_valid
:
log
.
warning
(
u"
The enrollment code (
%
s) is no longer valid.
"
,
course_reg
.
code
)
return
HttpResponseBadRequest
(
_
(
"Oops! The code '{registration_code}' you entered is either invalid or expired"
)
.
format
(
registration_code
=
course_reg
.
code
_
(
"This enrollment code ({enrollment_code}) is no longer valid."
)
.
format
(
enrollment_code
=
course_reg
.
code
)
)
if
RegistrationCodeRedemption
.
is_registration_code_redeemed
(
course_reg
.
code
):
log
.
warning
(
u"This enrollment code ({
%
s}) has already been used."
,
course_reg
.
code
)
return
HttpResponseBadRequest
(
_
(
"This enrollment code ({enrollment_code}) is not valid."
)
.
format
(
enrollment_code
=
course_reg
.
code
)
)
try
:
...
...
@@ -893,6 +899,7 @@ def _show_receipt_html(request, order):
'course_name'
:
course
.
display_name
,
'redemption_url'
:
reverse
(
'register_code_redemption'
,
args
=
[
course_registration_code
.
code
]),
'code'
:
course_registration_code
.
code
,
'is_valid'
:
course_registration_code
.
is_valid
,
'is_redeemed'
:
RegistrationCodeRedemption
.
objects
.
filter
(
registration_code
=
course_registration_code
)
.
exists
(),
})
...
...
lms/static/js/instructor_dashboard/ecommerce.js
View file @
4dff8ecf
...
...
@@ -27,6 +27,11 @@ var edx = edx || {};
});
$
(
function
()
{
var
$registration_code_status_form
=
$
(
"form#set_regcode_status_form"
),
$lookup_button
=
$
(
'#lookup_regcode'
,
$registration_code_status_form
),
$registration_code_status_form_error
=
$
(
'#regcode_status_form_error'
,
$registration_code_status_form
),
$registration_code_status_form_success
=
$
(
'#regcode_status_form_success'
,
$registration_code_status_form
);
$
(
"#coupon_expiration_date"
).
datepicker
({
minDate
:
0
});
...
...
@@ -43,7 +48,7 @@ var edx = edx || {};
return
$
(
".reports .msg-confirm"
).
css
({
"display"
:
"block"
});
},
},
error
:
function
(
std_ajax_err
)
{
request_response_error
.
text
(
gettext
(
'Error generating grades. Please try again.'
));
return
$
(
".reports .msg-error"
).
css
({
...
...
@@ -52,5 +57,126 @@ var edx = edx || {};
}
});
});
$lookup_button
.
click
(
function
()
{
$registration_code_status_form_error
.
hide
();
$lookup_button
.
attr
(
'disabled'
,
true
);
var
url
=
$
(
this
).
data
(
'endpoint'
);
var
lookup_registration_code
=
$
(
'#set_regcode_status_form input[name="regcode_code"]'
).
val
();
if
(
lookup_registration_code
==
''
)
{
$registration_code_status_form_error
.
show
();
$registration_code_status_form_error
.
text
(
gettext
(
'Enter the enrollment code.'
));
$lookup_button
.
removeAttr
(
'disabled'
);
return
false
;
}
$
.
ajax
({
type
:
"GET"
,
data
:
{
"registration_code"
:
lookup_registration_code
},
url
:
url
,
success
:
function
(
data
)
{
var
is_registration_code_valid
=
data
.
is_registration_code_valid
,
is_registration_code_redeemed
=
data
.
is_registration_code_redeemed
,
is_registration_code_exists
=
data
.
is_registration_code_exists
;
$lookup_button
.
removeAttr
(
'disabled'
);
if
(
is_registration_code_exists
==
'false'
)
{
$registration_code_status_form_error
.
hide
();
$registration_code_status_form_error
.
show
();
$registration_code_status_form_error
.
text
(
gettext
(
data
.
message
));
}
else
{
var
actions_links
=
''
;
var
actions
=
[];
if
(
is_registration_code_valid
==
true
)
{
actions
.
push
(
{
'action_url'
:
data
.
registration_code_detail_url
,
'action_name'
:
gettext
(
'Cancel enrollment code'
),
'registration_code'
:
lookup_registration_code
,
'action_type'
:
'invalidate_registration_code'
}
);
}
else
{
actions
.
push
(
{
'action_url'
:
data
.
registration_code_detail_url
,
'action_name'
:
gettext
(
'Restore enrollment code'
),
'registration_code'
:
lookup_registration_code
,
'action_type'
:
'validate_registration_code'
}
);
}
if
(
is_registration_code_redeemed
==
true
)
{
actions
.
push
(
{
'action_url'
:
data
.
registration_code_detail_url
,
'action_name'
:
gettext
(
'Mark enrollment code as unused'
),
'registration_code'
:
lookup_registration_code
,
'action_type'
:
'unredeem_registration_code'
}
);
}
is_registration_code_redeemed
=
is_registration_code_redeemed
?
'Yes'
:
'No'
;
is_registration_code_valid
=
is_registration_code_valid
?
'Yes'
:
'No'
;
// load the underscore template.
var
template_data
=
_
.
template
(
$
(
'#enrollment-code-lookup-links-tpl'
).
text
());
var
registration_code_lookup_actions
=
template_data
(
{
lookup_registration_code
:
lookup_registration_code
,
is_registration_code_redeemed
:
is_registration_code_redeemed
,
is_registration_code_valid
:
is_registration_code_valid
,
actions
:
actions
}
);
// before insertAfter do this.
// remove the first element after the registration_code_status_form
// so it doesn't duplicate the registration_code_lookup_actions in the UI.
$registration_code_status_form
.
next
().
remove
();
$
(
registration_code_lookup_actions
).
insertAfter
(
$registration_code_status_form
);
}
},
error
:
function
(
jqXHR
,
textStatus
,
errorThrown
)
{
var
data
=
$
.
parseJSON
(
jqXHR
.
responseText
);
$lookup_button
.
removeAttr
(
'disabled'
);
$registration_code_status_form_error
.
text
(
gettext
(
data
.
message
));
$registration_code_status_form_error
.
show
();
}
});
});
$
(
"section#invalidate_registration_code_modal"
).
on
(
'click'
,
'a.registration_code_action_link'
,
function
(
event
)
{
event
.
preventDefault
();
$registration_code_status_form_error
.
attr
(
'style'
,
'display: none'
);
$lookup_button
.
attr
(
'disabled'
,
true
);
var
url
=
$
(
this
).
data
(
'endpoint'
);
var
action_type
=
$
(
this
).
data
(
'action-type'
);
var
registration_code
=
$
(
this
).
data
(
'registration-code'
);
$
.
ajax
({
type
:
"POST"
,
data
:
{
"registration_code"
:
registration_code
,
"action_type"
:
action_type
},
url
:
url
,
success
:
function
(
data
)
{
$
(
'#set_regcode_status_form input[name="regcode_code"]'
).
val
(
''
);
$registration_code_status_form
.
next
().
remove
();
$registration_code_status_form_error
.
hide
();
$lookup_button
.
removeAttr
(
'disabled'
);
$registration_code_status_form_success
.
text
(
gettext
(
data
.
message
));
$registration_code_status_form_success
.
show
();
$registration_code_status_form_success
.
fadeOut
(
3000
);
},
error
:
function
(
jqXHR
,
textStatus
,
errorThrown
)
{
var
data
=
$
.
parseJSON
(
jqXHR
.
responseText
);
$registration_code_status_form_error
.
hide
();
$lookup_button
.
removeAttr
(
'disabled'
);
$registration_code_status_form_error
.
show
();
$registration_code_status_form_error
.
text
(
gettext
(
data
.
message
));
}
});
});
});
})(
Backbone
,
$
,
_
,
gettext
);
\ No newline at end of file
})(
Backbone
,
$
,
_
,
gettext
);
lms/static/sass/course/instructor/_instructor_2.scss
View file @
4dff8ecf
...
...
@@ -1624,7 +1624,8 @@ input[name="subject"] {
width
:
930px
;
}
// coupon edit and add modals
#add-coupon-modal
,
#edit-coupon-modal
,
#set-course-mode-price-modal
,
#registration_code_generation_modal
{
#add-coupon-modal
,
#invalidate_registration_code_modal
,
#edit-coupon-modal
,
#set-course-mode-price-modal
,
#registration_code_generation_modal
{
.inner-wrapper
{
background
:
$white
;
}
...
...
@@ -1639,7 +1640,7 @@ input[name="subject"] {
@include
margin-left
(
-325px
);
border-radius
:
2px
;
input
[
type
=
"button"
]
#update_coupon_button
,
input
[
type
=
"button"
]
#add_coupon_button
,
input
[
type
=
"button"
]
#set_course_button
{
input
[
type
=
"button"
]
#set_course_button
,
input
[
type
=
"button"
]
#lookup_regcode
{
@include
button
(
simple
,
$blue
);
@extend
.button-reset
;
display
:
block
;
...
...
@@ -1689,6 +1690,25 @@ input[name="subject"] {
margin-bottom
:
0px
!
important
;
}
}
table
.tb_registration_code_status
{
margin-top
:
$baseline
;
color
:
#555
;
thead
{
font-size
:
14px
;
tr
th
:last-child
{
text-align
:
center
;
}
}
tbody
{
font-size
:
14px
;
tr
td
:last-child
{
text-align
:
center
;
a
:first-child
{
margin-right
:
10px
;
}
}
}
}
form
#generate_codes
ol
.list-input
{
li
{
width
:
278px
;
...
...
@@ -1763,7 +1783,7 @@ input[name="subject"] {
height
:
40px
;
border-radius
:
3px
;
}
#coupon-content
,
#course-content
,
#registration-content
{
#coupon-content
,
#course-content
,
#registration-content
,
#regcode-content
{
padding
:
$baseline
;
header
{
margin
:
0
;
...
...
lms/templates/instructor/instructor_dashboard_2/e-commerce.html
View file @
4dff8ecf
...
...
@@ -6,16 +6,20 @@
<
%
include
file=
"edit_coupon_modal.html"
args=
"section_data=section_data"
/>
<
%
include
file=
"set_course_mode_price_modal.html"
args=
"section_data=section_data"
/>
<
%
include
file=
"generate_registarion_codes_modal.html"
args=
"section_data=section_data"
/>
<
%
include
file=
"invalidate_registration_code_modal.html"
args=
"section_data=section_data"
/>
<div
class=
"ecommerce-wrapper"
>
<h3
class=
"error-msgs"
id=
"error-msg"
></h3>
<div
id =
"accordion"
>
<div
class=
"wrap"
>
<h2>
${_('
Registration
Codes')}
</h2>
<h2>
${_('
Enrollment
Codes')}
</h2>
<div>
%if section_data['sales_admin']:
<span
class=
"code_tip"
>
<p>
${_('Create one or more pre-paid course enrollment codes. Students can use these codes to enroll in the course.')}
</p>
<a
id=
"registration_code_generation_link"
href=
"#reg_code_generation_modal"
class=
"add blue-button"
>
${_('Create Enrollment Codes')}
</a>
<p></p>
<p>
${_('Cancel, restore, or mark an enrollment code as unused.')}
</p>
<a
id=
"query_registration_code_link"
href=
"#invalidate_registration_code_modal"
rel=
"leanModal"
class=
"add blue-button"
>
${_('Change Enrollment Code Status')}
</a>
</span>
%endif
<p>
${_('Download a .csv file of all enrollment codes for this course')}
</p>
...
...
@@ -464,6 +468,9 @@
$
(
'#course_price_link'
).
click
(
function
()
{
reset_input_fields
();
});
$
(
'#query_registration_code_link'
).
click
(
function
()
{
reset_input_fields
();
});
$
(
'#add_coupon_link'
).
click
(
function
()
{
reset_input_fields
();
});
...
...
@@ -579,6 +586,8 @@
$
(
"#edit-coupon-modal"
).
attr
(
"aria-hidden"
,
"true"
);
$
(
".edit-right"
).
focus
();
$
(
"#set-course-mode-price-modal"
).
attr
(
"aria-hidden"
,
"true"
);
$
(
"#invalidate_registration_code_modal"
).
attr
(
"aria-hidden"
,
"true"
);
$
(
"#registration_code_generation_modal"
).
attr
(
"aria-hidden"
,
"true"
);
$
(
"#add_coupon_button"
).
removeAttr
(
'disabled'
);
$
(
"#set_course_button"
).
removeAttr
(
'disabled'
);
...
...
@@ -602,10 +611,11 @@
$
(
"#edit-coupon-modal .close-modal"
).
click
(
onModalClose
);
$
(
'#registration_code_generation_modal .close-modal'
).
click
(
onModalClose
);
$
(
"#set-course-mode-price-modal .close-modal"
).
click
(
reset_input_fields
);
$
(
"#invalidate_registration_code_modal .close-modal"
).
click
(
reset_input_fields
);
// Hitting the ESC key will exit the modal
$
(
"#add-coupon-modal, #edit-coupon-modal, #set-course-mode-price-modal, #registration_code_generation_modal"
).
on
(
"keydown"
,
function
(
e
)
{
$
(
"#add-coupon-modal, #edit-coupon-modal, #set-course-mode-price-modal, #
invalidate_registration_code_modal, #
registration_code_generation_modal"
).
on
(
"keydown"
,
function
(
e
)
{
var
keyCode
=
e
.
keyCode
||
e
.
which
;
// 27 is the ESC key
if
(
keyCode
===
27
)
{
...
...
@@ -613,14 +623,20 @@
$
(
"#add-coupon-modal .close-modal"
).
click
();
$
(
"#set-course-mode-price-modal .close-modal"
).
click
();
$
(
"#edit-coupon-modal .close-modal"
).
click
();
$
(
"#invalidate_registration_code_modal .close-modal"
).
click
();
$
(
'#registration_code_generation_modal .close-modal'
).
click
();
}
});
});
var
reset_input_fields
=
function
()
{
$
(
'#error-msg'
).
val
(
''
);
$
(
'#error-msg'
).
hide
()
$
(
'#error-msg'
).
hide
()
;
$
(
'#add_coupon_form #coupon_form_error'
).
attr
(
'style'
,
'display: none'
);
$
(
"form#set_regcode_status_form"
).
next
().
remove
();
$
(
'#set_regcode_status_form #regcode_status_form_error'
).
attr
(
'style'
,
'display: none'
);
$
(
'#set_regcode_status_form #regcode_status_form_success'
).
attr
(
'style'
,
'display: none'
);
$
(
'#set_regcode_status_form input#lookup_regcode'
).
removeAttr
(
'disabled'
);
$
(
'#set_price_form #course_form_error'
).
attr
(
'style'
,
'display: none'
);
$
(
'#generate_codes #registration_code_form_error'
).
attr
(
'style'
,
'display: none'
);
$
(
'#add_coupon_form #coupon_form_error'
).
text
();
...
...
@@ -629,6 +645,7 @@
$
(
'input#coupon_discount'
).
val
(
''
);
$
(
'textarea#coupon_description'
).
val
(
''
);
$
(
'input[name="company_name"]'
).
val
(
''
);
$
(
'input[name="regcode_code"]'
).
val
(
''
);
$
(
'input[name="total_registration_codes"]'
).
val
(
''
);
$
(
'input[name="address_line_1"]'
).
val
(
''
);
$
(
'input[name="address_line_2"]'
).
val
(
''
);
...
...
lms/templates/instructor/instructor_dashboard_2/enrollment-code-lookup-links.underscore
0 → 100644
View file @
4dff8ecf
<table width="100%" class="tb_registration_code_status">
<thead>
<th width="15%"> <%- gettext("Code") %> </th>
<th width="20%"> <%- gettext("Used") %> </th>
<th width="14%"> <%- gettext("Valid") %> </th>
<th> <%- gettext("Actions") %> </th>
</thead>
<tbody>
<tr>
<td> <%- lookup_registration_code %> </td>
<td> <%- is_registration_code_redeemed %> </td>
<td> <%- is_registration_code_valid %> </td>
<td>
<% _.each(actions, function(action){ %>
<a class="registration_code_action_link" data-registration-code="<%= action.registration_code %>" data-action-type="<%= action.action_type %>" href="#" data-endpoint="<%= action.action_url %>">
<%- action.action_name %>
</a>
<% }); %>
</td>
</tr>
</tbody>
</table>
lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html
View file @
4dff8ecf
...
...
@@ -76,7 +76,7 @@
## Include Underscore templates
<
%
block
name=
"header_extras"
>
% for template_name in ["cohorts", "cohort-editor", "cohort-group-header", "cohort-selector", "cohort-form", "notification", "cohort-state", "cohort-discussions-inline", "cohort-discussions-course-wide", "cohort-discussions-category","cohort-discussions-subcategory"]:
% for template_name in ["cohorts", "
enrollment-code-lookup-links", "
cohort-editor", "cohort-group-header", "cohort-selector", "cohort-form", "notification", "cohort-state", "cohort-discussions-inline", "cohort-discussions-course-wide", "cohort-discussions-category","cohort-discussions-subcategory"]:
<script
type=
"text/template"
id=
"${template_name}-tpl"
>
<%
static
:
include
path
=
"instructor/instructor_dashboard_2/${template_name}.underscore"
/>
</script>
...
...
lms/templates/instructor/instructor_dashboard_2/invalidate_registration_code_modal.html
0 → 100644
View file @
4dff8ecf
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%!
from
django
.
core
.
urlresolvers
import
reverse
%
>
<
%
page
args=
"section_data"
/>
<section
id=
"invalidate_registration_code_modal"
class=
"modal"
role=
"dialog"
tabindex=
"-1"
aria-label=
"${_('Enrollment Code Status')}"
>
<div
class=
"inner-wrapper"
>
<button
class=
"close-modal"
>
<i
class=
"icon fa fa-remove"
></i>
<span
class=
"sr"
>
## Translators: this is a control to allow users to exit out of this modal interface (a menu or piece of UI that takes the full focus of the screen)
${_('Close')}
</span>
</button>
<div
id=
"regcode-content"
>
<header>
<h2>
${_("Enrollment Code Status")}
</h2>
</header>
<div
class=
"instructions"
>
<p>
${_("Change the status of an enrollment code.")}
</p>
</div>
<form
id=
"set_regcode_status_form"
>
<div
id=
"regcode_status_form_error"
class=
"modal-form-error"
></div>
<div
class=
"success-msgs"
id=
"regcode_status_form_success"
></div>
<fieldset
class=
"group group-form group-form-requiredinformation"
>
<legend
class=
"is-hidden"
>
${_("Required Information")}
</legend>
<ol
class=
"list-input"
>
<li
class=
"field required text"
id=
"set-course-mode-modal-field-price"
>
<label
for=
"regcode_code"
>
${_("Enrollment Code")}
</label>
<input
class=
"field"
id=
"regcode_code"
type=
"text"
name=
"regcode_code"
placeholder=
"Enter an Enrollment Code"
aria-required=
"true"
>
</li>
</ol>
</fieldset>
<div
class=
"submit"
>
<input
name=
"submit"
type=
"button"
id=
"lookup_regcode"
data-endpoint=
"${section_data['look_up_registration_code']}"
value=
"${_('Find Enrollment Code')}"
/>
</div>
</form>
</div>
</div>
</section>
lms/templates/shoppingcart/receipt.html
View file @
4dff8ecf
...
...
@@ -84,6 +84,8 @@ from courseware.courses import course_image_url, get_course_about_section, get_c
<td>
% if reg_code_info['is_redeemed']:
<span
class=
"red"
></M>
${_("Used")}
</span>
% elif not reg_code_info['is_valid']:
<span
class=
"red"
></M>
${_("Invalid")}
</span>
% else:
<span
class=
"green"
></M>
${_("Available")}
</span>
% endif
...
...
lms/urls.py
View file @
4dff8ecf
...
...
@@ -237,6 +237,19 @@ if settings.WIKI_ENABLED:
)
if
settings
.
COURSEWARE_ENABLED
:
COURSE_URLS
=
patterns
(
''
,
url
(
r'^look_up_registration_code$'
,
'instructor.views.registration_codes.look_up_registration_code'
,
name
=
'look_up_registration_code'
),
url
(
r'^registration_code_details$'
,
'instructor.views.registration_codes.registration_code_details'
,
name
=
'registration_code_details'
)
)
urlpatterns
+=
(
url
(
r'^courses/{}/jump_to/(?P<location>.*)$'
.
format
(
settings
.
COURSE_ID_PATTERN
),
'courseware.views.jump_to'
,
name
=
"jump_to"
),
...
...
@@ -357,6 +370,7 @@ if settings.COURSEWARE_ENABLED:
url
(
r'^courses/{}/get_coupon_info$'
.
format
(
settings
.
COURSE_ID_PATTERN
),
'instructor.views.coupons.get_coupon_info'
,
name
=
"get_coupon_info"
),
url
(
r'^courses/{}/'
.
format
(
settings
.
COURSE_ID_PATTERN
),
include
(
COURSE_URLS
)),
# see ENABLE_INSTRUCTOR_LEGACY_DASHBOARD section for legacy dash urls
# Open Ended grading views
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment