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
03d04c1f
Commit
03d04c1f
authored
Aug 13, 2012
by
Calen Pennington
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #392 from MITx/feature/victor/dark-launch
Feature/victor/dark launch
parents
9433eafb
b0dc60ee
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
209 additions
and
48 deletions
+209
-48
common/djangoapps/pipeline_mako/__init__.py
+5
-4
common/djangoapps/static_replace.py
+21
-2
lms/djangoapps/courseware/courses.py
+20
-10
lms/djangoapps/courseware/tests/tests.py
+134
-9
lms/djangoapps/courseware/views.py
+8
-7
lms/djangoapps/simplewiki/views.py
+12
-12
lms/djangoapps/staticbook/views.py
+1
-1
lms/envs/common.py
+1
-0
lms/urls.py
+7
-3
No files found.
common/djangoapps/pipeline_mako/__init__.py
View file @
03d04c1f
from
staticfiles.storage
import
staticfiles_storage
from
mitxmako.shortcuts
import
render_to_string
from
pipeline.conf
import
settings
from
pipeline.packager
import
Packager
from
pipeline.utils
import
guess_type
from
static_replace
import
try_staticfiles_lookup
def
compressed_css
(
package_name
):
...
...
@@ -25,9 +24,11 @@ def compressed_css(package_name):
def
render_css
(
package
,
path
):
template_name
=
package
.
template_name
or
"mako/css.html"
context
=
package
.
extra_context
url
=
try_staticfiles_lookup
(
path
)
context
.
update
({
'type'
:
guess_type
(
path
,
'text/css'
),
'url'
:
staticfiles_storage
.
url
(
path
)
'url'
:
url
,
})
return
render_to_string
(
template_name
,
context
)
...
...
@@ -58,7 +59,7 @@ def render_js(package, path):
context
=
package
.
extra_context
context
.
update
({
'type'
:
guess_type
(
path
,
'text/javascript'
),
'url'
:
staticfiles_storage
.
url
(
path
)
'url'
:
try_staticfiles_lookup
(
path
)
})
return
render_to_string
(
template_name
,
context
)
...
...
common/djangoapps/static_replace.py
View file @
03d04c1f
import
logging
import
re
from
staticfiles.storage
import
staticfiles_storage
from
staticfiles
import
finders
from
django.conf
import
settings
import
re
log
=
logging
.
getLogger
(
__name__
)
def
try_staticfiles_lookup
(
path
):
"""
Try to lookup a path in staticfiles_storage. If it fails, return
a dead link instead of raising an exception.
"""
try
:
url
=
staticfiles_storage
.
url
(
path
)
except
Exception
as
err
:
log
.
warning
(
"staticfiles_storage couldn't find path {}: {}"
.
format
(
path
,
str
(
err
)))
# Just return a dead link--don't kill everything.
url
=
"file_not_found"
return
url
def
replace
(
static_url
,
prefix
=
None
):
...
...
@@ -22,7 +40,8 @@ def replace(static_url, prefix=None):
if
servable
:
return
static_url
.
group
(
0
)
else
:
url
=
staticfiles_storage
.
url
(
prefix
+
static_url
.
group
(
'rest'
))
# don't error if file can't be found
url
=
try_staticfiles_lookup
(
prefix
+
static_url
.
group
(
'rest'
))
return
""
.
join
([
quote
,
url
,
quote
])
...
...
lms/djangoapps/courseware/courses.py
View file @
03d04c1f
...
...
@@ -10,17 +10,16 @@ from django.http import Http404
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
static_replace
import
replace_urls
from
staticfiles.storage
import
staticfiles_storage
from
static_replace
import
replace_urls
,
try_staticfiles_lookup
log
=
logging
.
getLogger
(
__name__
)
def
check_course
(
course_id
,
course_must_be_open
=
True
,
course_required
=
True
):
def
check_course
(
user
,
course_id
,
course_must_be_open
=
True
,
course_required
=
True
):
"""
Given a
course_id, this returns the course object. By default,
if the course is not found or the course is not open yet, th
is
method will raise a 404.
Given a
django user and a course_id, this returns the course
object. By default, if the course is not found or the course
is
not open yet, this
method will raise a 404.
If course_must_be_open is False, the course will be returned
without a 404 even if it is not open.
...
...
@@ -28,6 +27,10 @@ def check_course(course_id, course_must_be_open=True, course_required=True):
If course_required is False, a course_id of None is acceptable. The
course returned will be None. Even if the course is not required,
if a course_id is given that does not exist a 404 will be raised.
This behavior is modified by MITX_FEATURES['DARK_LAUNCH']:
if dark launch is enabled, course_must_be_open is ignored for
users that have staff access.
"""
course
=
None
if
course_required
or
course_id
:
...
...
@@ -39,16 +42,23 @@ def check_course(course_id, course_must_be_open=True, course_required=True):
raise
Http404
(
"Course not found."
)
started
=
course
.
has_started
()
or
settings
.
MITX_FEATURES
[
'DISABLE_START_DATES'
]
if
course_must_be_open
and
not
started
:
must_be_open
=
course_must_be_open
if
(
settings
.
MITX_FEATURES
[
'DARK_LAUNCH'
]
and
has_staff_access_to_course
(
user
,
course
)):
must_be_open
=
False
if
must_be_open
and
not
started
:
raise
Http404
(
"This course has not yet started."
)
return
course
def
course_image_url
(
course
):
return
staticfiles_storage
.
url
(
course
.
metadata
[
'data_dir'
]
+
"/images/course_image.jpg"
)
"""Try to look up the image url for the course. If it's not found,
log an error and return the dead link"""
path
=
course
.
metadata
[
'data_dir'
]
+
"/images/course_image.jpg"
return
try_staticfiles_lookup
(
path
)
def
get_course_about_section
(
course
,
section_key
):
"""
...
...
lms/djangoapps/courseware/tests/tests.py
View file @
03d04c1f
import
copy
import
json
from
path
import
path
import
os
import
sys
import
time
from
pprint
import
pprint
from
nose
import
SkipTest
from
path
import
path
from
pprint
import
pprint
from
django.contrib.auth.models
import
User
,
Group
from
django.test
import
TestCase
from
django.test.client
import
Client
from
django.conf
import
settings
...
...
@@ -13,12 +16,11 @@ from django.core.urlresolvers import reverse
from
mock
import
patch
,
Mock
from
override_settings
import
override_settings
from
django.contrib.auth.models
import
User
,
Group
import
xmodule.modulestore.django
from
student.models
import
Registration
from
courseware.courses
import
course_staff_group_name
from
xmodule.modulestore.django
import
modulestore
import
xmodule.modulestore.django
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.xml_importer
import
import_from_xml
...
...
@@ -206,13 +208,14 @@ class TestCoursesLoadTestCase(PageLoader):
@override_settings
(
MODULESTORE
=
TEST_DATA_MODULESTORE
)
class
Test
Instructor
Auth
(
PageLoader
):
"""Check that authentication works properly"""
class
Test
View
Auth
(
PageLoader
):
"""Check that
view
authentication works properly"""
# NOTE: setUpClass() runs before override_settings takes effect, so
# can't do imports there without manually hacking settings.
def
setUp
(
self
):
print
"sys.path: {}"
.
format
(
sys
.
path
)
xmodule
.
modulestore
.
django
.
_MODULESTORES
=
{}
modulestore
()
.
collection
.
drop
()
import_from_xml
(
modulestore
(),
TEST_DATA_DIR
,
[
'toy'
])
...
...
@@ -237,12 +240,16 @@ class TestInstructorAuth(PageLoader):
# TODO (vshnayder): once we're returning 404s, get rid of this if.
if
code
!=
404
:
self
.
assertEqual
(
resp
.
status_code
,
code
)
# And 'page not found' shouldn't be in the returned page
self
.
assertTrue
(
resp
.
content
.
lower
()
.
find
(
'page not found'
)
==
-
1
)
else
:
# look for "page not found" instead of the status code
#print resp.content
self
.
assertTrue
(
resp
.
content
.
lower
()
.
find
(
'page not found'
)
!=
-
1
)
def
test_instructor_page
(
self
):
"Make sure only instructors can load it"
def
test_instructor_pages
(
self
):
"""Make sure only instructors for the course or staff can load the instructor
dashboard, the grade views, and student profile pages"""
# First, try with an enrolled student
self
.
login
(
self
.
student
,
self
.
password
)
...
...
@@ -297,7 +304,125 @@ class TestInstructorAuth(PageLoader):
self
.
check_for_get_code
(
200
,
url
)
def
test_dark_launch
(
self
):
"""Make sure that when dark launch is on, students can't access course
pages, but instructors can"""
# test.py turns off start dates, enable them and set them correctly.
# Because settings is global, be careful not to mess it up for other tests
# (Can't use override_settings because we're only changing part of the
# MITX_FEATURES dict)
oldDSD
=
settings
.
MITX_FEATURES
[
'DISABLE_START_DATES'
]
oldDL
=
settings
.
MITX_FEATURES
[
'DARK_LAUNCH'
]
try
:
settings
.
MITX_FEATURES
[
'DISABLE_START_DATES'
]
=
False
settings
.
MITX_FEATURES
[
'DARK_LAUNCH'
]
=
True
self
.
_do_test_dark_launch
()
finally
:
settings
.
MITX_FEATURES
[
'DISABLE_START_DATES'
]
=
oldDSD
settings
.
MITX_FEATURES
[
'DARK_LAUNCH'
]
=
oldDL
def
_do_test_dark_launch
(
self
):
"""Actually do the test, relying on settings to be right."""
# Make courses start in the future
tomorrow
=
time
.
time
()
+
24
*
3600
self
.
toy
.
start
=
self
.
toy
.
metadata
[
'start'
]
=
time
.
gmtime
(
tomorrow
)
self
.
full
.
start
=
self
.
full
.
metadata
[
'start'
]
=
time
.
gmtime
(
tomorrow
)
self
.
assertFalse
(
self
.
toy
.
has_started
())
self
.
assertFalse
(
self
.
full
.
has_started
())
self
.
assertFalse
(
settings
.
MITX_FEATURES
[
'DISABLE_START_DATES'
])
self
.
assertTrue
(
settings
.
MITX_FEATURES
[
'DARK_LAUNCH'
])
def
reverse_urls
(
names
,
course
):
return
[
reverse
(
name
,
kwargs
=
{
'course_id'
:
course
.
id
})
for
name
in
names
]
def
dark_student_urls
(
course
):
"""
list of urls that students should be able to see only
after launch, but staff should see before
"""
urls
=
reverse_urls
([
'info'
,
'book'
,
'courseware'
,
'profile'
],
course
)
return
urls
def
light_student_urls
(
course
):
"""
list of urls that students should be able to see before
launch.
"""
urls
=
reverse_urls
([
'about_course'
],
course
)
urls
.
append
(
reverse
(
'courses'
))
# Need separate test for change_enrollment, since it's a POST view
#urls.append(reverse('change_enrollment'))
return
urls
def
instructor_urls
(
course
):
"""list of urls that only instructors/staff should be able to see"""
urls
=
reverse_urls
([
'instructor_dashboard'
,
'gradebook'
,
'grade_summary'
],
course
)
urls
.
append
(
reverse
(
'student_profile'
,
kwargs
=
{
'course_id'
:
course
.
id
,
'student_id'
:
user
(
self
.
student
)
.
id
}))
return
urls
def
check_non_staff
(
course
):
"""Check that access is right for non-staff in course"""
print
'=== Checking non-staff access for {}'
.
format
(
course
.
id
)
for
url
in
instructor_urls
(
course
)
+
dark_student_urls
(
course
):
print
'checking for 404 on {}'
.
format
(
url
)
self
.
check_for_get_code
(
404
,
url
)
for
url
in
light_student_urls
(
course
):
print
'checking for 200 on {}'
.
format
(
url
)
self
.
check_for_get_code
(
200
,
url
)
def
check_staff
(
course
):
"""Check that access is right for staff in course"""
print
'=== Checking staff access for {}'
.
format
(
course
.
id
)
for
url
in
(
instructor_urls
(
course
)
+
dark_student_urls
(
course
)
+
light_student_urls
(
course
)):
print
'checking for 200 on {}'
.
format
(
url
)
self
.
check_for_get_code
(
200
,
url
)
# First, try with an enrolled student
print
'=== Testing student access....'
self
.
login
(
self
.
student
,
self
.
password
)
self
.
enroll
(
self
.
toy
)
self
.
enroll
(
self
.
full
)
# shouldn't be able to get to anything except the light pages
check_non_staff
(
self
.
toy
)
check_non_staff
(
self
.
full
)
print
'=== Testing course instructor access....'
# Make the instructor staff in the toy course
group_name
=
course_staff_group_name
(
self
.
toy
)
g
=
Group
.
objects
.
create
(
name
=
group_name
)
g
.
user_set
.
add
(
user
(
self
.
instructor
))
self
.
logout
()
self
.
login
(
self
.
instructor
,
self
.
password
)
# Enroll in the classes---can't see courseware otherwise.
self
.
enroll
(
self
.
toy
)
self
.
enroll
(
self
.
full
)
# should now be able to get to everything for toy course
check_non_staff
(
self
.
full
)
check_staff
(
self
.
toy
)
print
'=== Testing staff access....'
# now also make the instructor staff
u
=
user
(
self
.
instructor
)
u
.
is_staff
=
True
u
.
save
()
# and now should be able to load both
check_staff
(
self
.
toy
)
check_staff
(
self
.
full
)
@override_settings
(
MODULESTORE
=
REAL_DATA_MODULESTORE
)
...
...
lms/djangoapps/courseware/views.py
View file @
03d04c1f
...
...
@@ -110,9 +110,10 @@ def index(request, course_id, chapter=None, section=None,
- HTTPresponse
'''
course
=
check_course
(
course_id
)
course
=
check_course
(
request
.
user
,
course_id
)
registered
=
registered_for_course
(
course
,
request
.
user
)
if
not
registered
:
# TODO (vshnayder): do course instructors need to be registered to see course?
log
.
debug
(
'User
%
s tried to view course
%
s but is not enrolled'
%
(
request
.
user
,
course
.
location
.
url
()))
return
redirect
(
reverse
(
'about_course'
,
args
=
[
course
.
id
]))
...
...
@@ -203,7 +204,7 @@ def course_info(request, course_id):
Assumes the course_id is in a valid format.
"""
course
=
check_course
(
course_id
)
course
=
check_course
(
request
.
user
,
course_id
)
return
render_to_response
(
'info.html'
,
{
'course'
:
course
})
...
...
@@ -220,7 +221,7 @@ def registered_for_course(course, user):
@ensure_csrf_cookie
@cache_if_anonymous
def
course_about
(
request
,
course_id
):
course
=
check_course
(
course_id
,
course_must_be_open
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_must_be_open
=
False
)
registered
=
registered_for_course
(
course
,
request
.
user
)
return
render_to_response
(
'portal/course_about.html'
,
{
'course'
:
course
,
'registered'
:
registered
})
...
...
@@ -252,7 +253,7 @@ def profile(request, course_id, student_id=None):
Course staff are allowed to see the profiles of students in their class.
"""
course
=
check_course
(
course_id
)
course
=
check_course
(
request
.
user
,
course_id
)
if
student_id
is
None
or
student_id
==
request
.
user
.
id
:
# always allowed to see your own profile
...
...
@@ -299,7 +300,7 @@ def gradebook(request, course_id):
if
not
has_staff_access_to_course_id
(
request
.
user
,
course_id
):
raise
Http404
course
=
check_course
(
course_id
)
course
=
check_course
(
request
.
user
,
course_id
)
enrolled_students
=
User
.
objects
.
filter
(
courseenrollment__course_id
=
course_id
)
.
order_by
(
'username'
)
...
...
@@ -324,7 +325,7 @@ def grade_summary(request, course_id):
if
not
has_staff_access_to_course_id
(
request
.
user
,
course_id
):
raise
Http404
course
=
check_course
(
course_id
)
course
=
check_course
(
request
.
user
,
course_id
)
# For now, just a static page
context
=
{
'course'
:
course
}
...
...
@@ -337,7 +338,7 @@ def instructor_dashboard(request, course_id):
if
not
has_staff_access_to_course_id
(
request
.
user
,
course_id
):
raise
Http404
course
=
check_course
(
course_id
)
course
=
check_course
(
request
.
user
,
course_id
)
# For now, just a static page
context
=
{
'course'
:
course
}
...
...
lms/djangoapps/simplewiki/views.py
View file @
03d04c1f
...
...
@@ -51,7 +51,7 @@ def update_template_dictionary(dictionary, request=None, course=None, article=No
def
view
(
request
,
article_path
,
course_id
=
None
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
(
article
,
err
)
=
get_article
(
request
,
article_path
,
course
)
if
err
:
...
...
@@ -67,7 +67,7 @@ def view(request, article_path, course_id=None):
def
view_revision
(
request
,
revision_number
,
article_path
,
course_id
=
None
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
(
article
,
err
)
=
get_article
(
request
,
article_path
,
course
)
if
err
:
...
...
@@ -91,7 +91,7 @@ def view_revision(request, revision_number, article_path, course_id=None):
def
root_redirect
(
request
,
course_id
=
None
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
#TODO: Add a default namespace to settings.
namespace
=
course
.
wiki_namespace
if
course
else
"edX"
...
...
@@ -109,7 +109,7 @@ def root_redirect(request, course_id=None):
def
create
(
request
,
article_path
,
course_id
=
None
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
article_path_components
=
article_path
.
split
(
'/'
)
...
...
@@ -170,7 +170,7 @@ def create(request, article_path, course_id=None):
def
edit
(
request
,
article_path
,
course_id
=
None
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
(
article
,
err
)
=
get_article
(
request
,
article_path
,
course
)
if
err
:
...
...
@@ -218,7 +218,7 @@ def edit(request, article_path, course_id=None):
def
history
(
request
,
article_path
,
page
=
1
,
course_id
=
None
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
(
article
,
err
)
=
get_article
(
request
,
article_path
,
course
)
if
err
:
...
...
@@ -300,7 +300,7 @@ def history(request, article_path, page=1, course_id=None):
def
revision_feed
(
request
,
page
=
1
,
namespace
=
None
,
course_id
=
None
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
page_size
=
10
...
...
@@ -333,7 +333,7 @@ def revision_feed(request, page=1, namespace=None, course_id=None):
def
search_articles
(
request
,
namespace
=
None
,
course_id
=
None
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
# blampe: We should check for the presence of other popular django search
# apps and use those if possible. Only fall back on this as a last resort.
...
...
@@ -382,7 +382,7 @@ def search_articles(request, namespace=None, course_id=None):
def
search_add_related
(
request
,
course_id
,
slug
,
namespace
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
(
article
,
err
)
=
get_article
(
request
,
slug
,
namespace
if
namespace
else
course_id
)
if
err
:
...
...
@@ -415,7 +415,7 @@ def search_add_related(request, course_id, slug, namespace):
def
add_related
(
request
,
course_id
,
slug
,
namespace
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
(
article
,
err
)
=
get_article
(
request
,
slug
,
namespace
if
namespace
else
course_id
)
if
err
:
...
...
@@ -439,7 +439,7 @@ def add_related(request, course_id, slug, namespace):
def
remove_related
(
request
,
course_id
,
namespace
,
slug
,
related_id
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
(
article
,
err
)
=
get_article
(
request
,
slug
,
namespace
if
namespace
else
course_id
)
...
...
@@ -462,7 +462,7 @@ def remove_related(request, course_id, namespace, slug, related_id):
def
random_article
(
request
,
course_id
=
None
):
course
=
check_course
(
course_id
,
course_required
=
False
)
course
=
check_course
(
request
.
user
,
course_id
,
course_required
=
False
)
from
random
import
randint
num_arts
=
Article
.
objects
.
count
()
...
...
lms/djangoapps/staticbook/views.py
View file @
03d04c1f
...
...
@@ -6,7 +6,7 @@ from lxml import etree
@login_required
def
index
(
request
,
course_id
,
page
=
0
):
course
=
check_course
(
course_id
)
course
=
check_course
(
request
.
user
,
course_id
)
raw_table_of_contents
=
open
(
'lms/templates/book_toc.xml'
,
'r'
)
# TODO: This will need to come from S3
table_of_contents
=
etree
.
parse
(
raw_table_of_contents
)
.
getroot
()
return
render_to_response
(
'staticbook.html'
,
{
'page'
:
int
(
page
),
'course'
:
course
,
'table_of_contents'
:
table_of_contents
})
...
...
lms/envs/common.py
View file @
03d04c1f
...
...
@@ -48,6 +48,7 @@ MITX_FEATURES = {
## DO NOT SET TO True IN THIS FILE
## Doing so will cause all courses to be released on production
'DISABLE_START_DATES'
:
False
,
# When True, all courses will be active, regardless of start date
'DARK_LAUNCH'
:
False
,
# When True, courses will be active for staff only
'ENABLE_TEXTBOOK'
:
True
,
'ENABLE_DISCUSSION'
:
True
,
...
...
lms/urls.py
View file @
03d04c1f
...
...
@@ -97,12 +97,16 @@ if settings.PERFSTATS:
if
settings
.
COURSEWARE_ENABLED
:
urlpatterns
+=
(
# Hook django-masquerade, allowing staff to view site as other users
url
(
r'^masquerade/'
,
include
(
'masquerade.urls'
)),
url
(
r'^jump_to/(?P<location>.*)$'
,
'courseware.views.jump_to'
,
name
=
"jump_to"
),
url
(
r'^modx/(?P<id>.*?)/(?P<dispatch>[^/]*)$'
,
'courseware.module_render.modx_dispatch'
),
#reset_problem'),
url
(
r'^xqueue/(?P<userid>[^/]*)/(?P<id>.*?)/(?P<dispatch>[^/]*)$'
,
'courseware.module_render.xqueue_callback'
),
url
(
r'^change_setting$'
,
'student.views.change_setting'
),
url
(
r'^modx/(?P<id>.*?)/(?P<dispatch>[^/]*)$'
,
'courseware.module_render.modx_dispatch'
,
name
=
'modx_dispatch'
),
url
(
r'^xqueue/(?P<userid>[^/]*)/(?P<id>.*?)/(?P<dispatch>[^/]*)$'
,
'courseware.module_render.xqueue_callback'
,
name
=
'xqueue_callback'
),
url
(
r'^change_setting$'
,
'student.views.change_setting'
,
name
=
'change_setting'
),
# TODO: These views need to be updated before they work
# url(r'^calculate$', 'util.views.calculate'),
...
...
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