Merge master
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import functools
|
||||
import json
|
||||
import logging
|
||||
import random
|
||||
@@ -156,7 +157,7 @@ def edXauth_signup(request, eamap=None):
|
||||
|
||||
log.debug('ExtAuth: doing signup for %s' % eamap.external_email)
|
||||
|
||||
return student_views.main_index(extra_context=context)
|
||||
return student_views.main_index(request, extra_context=context)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# MIT SSL
|
||||
@@ -206,7 +207,7 @@ def edXauth_ssl_login(request):
|
||||
pass
|
||||
if not cert:
|
||||
# no certificate information - go onward to main index
|
||||
return student_views.main_index()
|
||||
return student_views.main_index(request)
|
||||
|
||||
(user, email, fullname) = ssl_dn_extract_info(cert)
|
||||
|
||||
@@ -216,4 +217,4 @@ def edXauth_ssl_login(request):
|
||||
credentials=cert,
|
||||
email=email,
|
||||
fullname=fullname,
|
||||
retfun = student_views.main_index)
|
||||
retfun = functools.partial(student_views.main_index, request))
|
||||
|
||||
@@ -68,9 +68,9 @@ def index(request):
|
||||
from external_auth.views import edXauth_ssl_login
|
||||
return edXauth_ssl_login(request)
|
||||
|
||||
return main_index(user=request.user)
|
||||
return main_index(request, user=request.user)
|
||||
|
||||
def main_index(extra_context = {}, user=None):
|
||||
def main_index(request, extra_context={}, user=None):
|
||||
'''
|
||||
Render the edX main page.
|
||||
|
||||
@@ -93,7 +93,8 @@ def main_index(extra_context = {}, user=None):
|
||||
entry.summary = soup.getText()
|
||||
|
||||
# The course selection work is done in courseware.courses.
|
||||
universities = get_courses_by_university(None)
|
||||
universities = get_courses_by_university(None,
|
||||
domain=request.META.get('HTTP_HOST'))
|
||||
context = {'universities': universities, 'entries': entries}
|
||||
context.update(extra_context)
|
||||
return render_to_response('index.html', context)
|
||||
|
||||
@@ -49,9 +49,9 @@ class ABTestModule(XModule):
|
||||
return json.dumps({'group': self.group})
|
||||
|
||||
def displayable_items(self):
|
||||
return filter(None, [self.system.get_module(child)
|
||||
for child
|
||||
in self.definition['data']['group_content'][self.group]])
|
||||
child_locations = self.definition['data']['group_content'][self.group]
|
||||
children = [self.system.get_module(loc) for loc in child_locations]
|
||||
return [c for c in children if c is not None]
|
||||
|
||||
|
||||
# TODO (cpennington): Use Groups should be a first class object, rather than being
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
@@ -149,7 +150,7 @@ class XMLModuleStore(ModuleStoreBase):
|
||||
for course_dir in course_dirs:
|
||||
self.try_load_course(course_dir)
|
||||
|
||||
def try_load_course(self,course_dir):
|
||||
def try_load_course(self, course_dir):
|
||||
'''
|
||||
Load a course, keeping track of errors as we go along.
|
||||
'''
|
||||
@@ -170,7 +171,27 @@ class XMLModuleStore(ModuleStoreBase):
|
||||
'''
|
||||
String representation - for debugging
|
||||
'''
|
||||
return '<XMLModuleStore>data_dir=%s, %d courses, %d modules' % (self.data_dir,len(self.courses),len(self.modules))
|
||||
return '<XMLModuleStore>data_dir=%s, %d courses, %d modules' % (
|
||||
self.data_dir, len(self.courses), len(self.modules))
|
||||
|
||||
def load_policy(self, policy_path, tracker):
|
||||
"""
|
||||
Attempt to read a course policy from policy_path. If the file
|
||||
exists, but is invalid, log an error and return {}.
|
||||
|
||||
If the policy loads correctly, returns the deserialized version.
|
||||
"""
|
||||
if not os.path.exists(policy_path):
|
||||
return {}
|
||||
try:
|
||||
with open(policy_path) as f:
|
||||
return json.load(f)
|
||||
except (IOError, ValueError) as err:
|
||||
msg = "Error loading course policy from {}".format(policy_path)
|
||||
tracker(msg)
|
||||
log.warning(msg + " " + str(err))
|
||||
return {}
|
||||
|
||||
|
||||
def load_course(self, course_dir, tracker):
|
||||
"""
|
||||
@@ -214,6 +235,11 @@ class XMLModuleStore(ModuleStoreBase):
|
||||
system = ImportSystem(self, org, course, course_dir, tracker)
|
||||
|
||||
course_descriptor = system.process_xml(etree.tostring(course_data))
|
||||
policy_path = self.data_dir / course_dir / 'policy.json'
|
||||
|
||||
policy = self.load_policy(policy_path, tracker)
|
||||
XModuleDescriptor.apply_policy(course_descriptor, policy)
|
||||
|
||||
# NOTE: The descriptors end up loading somewhat bottom up, which
|
||||
# breaks metadata inheritance via get_children(). Instead
|
||||
# (actually, in addition to, for now), we do a final inheritance pass
|
||||
|
||||
@@ -219,11 +219,11 @@ class XModule(HTMLSnippet):
|
||||
Return module instances for all the children of this module.
|
||||
'''
|
||||
if self._loaded_children is None:
|
||||
child_locations = self.definition.get('children', [])
|
||||
children = [self.system.get_module(loc) for loc in child_locations]
|
||||
# get_module returns None if the current user doesn't have access
|
||||
# to the location.
|
||||
self._loaded_children = filter(None,
|
||||
[self.system.get_module(child)
|
||||
for child in self.definition.get('children', [])])
|
||||
self._loaded_children = [c for c in children if c is not None]
|
||||
|
||||
return self._loaded_children
|
||||
|
||||
@@ -298,6 +298,14 @@ class XModule(HTMLSnippet):
|
||||
return ""
|
||||
|
||||
|
||||
def policy_key(location):
|
||||
"""
|
||||
Get the key for a location in a policy file. (Since the policy file is
|
||||
specific to a course, it doesn't need the full location url).
|
||||
"""
|
||||
return '{cat}/{name}'.format(cat=location.category, name=location.name)
|
||||
|
||||
|
||||
class XModuleDescriptor(Plugin, HTMLSnippet):
|
||||
"""
|
||||
An XModuleDescriptor is a specification for an element of a course. This
|
||||
@@ -416,6 +424,24 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
|
||||
return dict((k,v) for k,v in self.metadata.items()
|
||||
if k not in self._inherited_metadata)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def apply_policy(node, policy):
|
||||
"""
|
||||
Given a descriptor, traverse all its descendants and update its metadata
|
||||
with the policy.
|
||||
|
||||
Notes:
|
||||
- this does not propagate inherited metadata. The caller should
|
||||
call compute_inherited_metadata after applying the policy.
|
||||
- metadata specified in the policy overrides metadata in the xml
|
||||
"""
|
||||
k = policy_key(node.location)
|
||||
if k in policy:
|
||||
node.metadata.update(policy[k])
|
||||
for c in node.get_children():
|
||||
XModuleDescriptor.apply_policy(c, policy)
|
||||
|
||||
@staticmethod
|
||||
def compute_inherited_metadata(node):
|
||||
"""Given a descriptor, traverse all of its descendants and do metadata
|
||||
|
||||
@@ -166,7 +166,7 @@ class XmlDescriptor(XModuleDescriptor):
|
||||
Subclasses should not need to override this except in special
|
||||
cases (e.g. html module)'''
|
||||
|
||||
# VS[compat] -- the filename tag should go away once everything is
|
||||
# VS[compat] -- the filename attr should go away once everything is
|
||||
# converted. (note: make sure html files still work once this goes away)
|
||||
filename = xml_object.get('filename')
|
||||
if filename is None:
|
||||
|
||||
@@ -65,9 +65,10 @@ def has_access(user, obj, action):
|
||||
|
||||
# Passing an unknown object here is a coding error, so rather than
|
||||
# returning a default, complain.
|
||||
raise TypeError("Unknown object type in has_access(). Object type: '{}'"
|
||||
raise TypeError("Unknown object type in has_access(): '{}'"
|
||||
.format(type(obj)))
|
||||
|
||||
|
||||
# ================ Implementation helpers ================================
|
||||
|
||||
def _has_access_course_desc(user, course, action):
|
||||
@@ -83,8 +84,12 @@ def _has_access_course_desc(user, course, action):
|
||||
'staff' -- staff access to course.
|
||||
"""
|
||||
def can_load():
|
||||
"Can this user load this course?"
|
||||
# delegate to generic descriptor check
|
||||
"""
|
||||
Can this user load this course?
|
||||
|
||||
NOTE: this is not checking whether user is actually enrolled in the course.
|
||||
"""
|
||||
# delegate to generic descriptor check to check start dates
|
||||
return _has_access_descriptor(user, course, action)
|
||||
|
||||
def can_enroll():
|
||||
@@ -169,6 +174,12 @@ def _has_access_descriptor(user, descriptor, action):
|
||||
has_access(), it will not do the right thing.
|
||||
"""
|
||||
def can_load():
|
||||
"""
|
||||
NOTE: This does not check that the student is enrolled in the course
|
||||
that contains this module. We may or may not want to allow non-enrolled
|
||||
students to see modules. If not, views should check the course, so we
|
||||
don't have to hit the enrollments table on every module load.
|
||||
"""
|
||||
# If start dates are off, can always load
|
||||
if settings.MITX_FEATURES['DISABLE_START_DATES']:
|
||||
debug("Allow: DISABLE_START_DATES")
|
||||
@@ -196,8 +207,6 @@ def _has_access_descriptor(user, descriptor, action):
|
||||
return _dispatch(checkers, action, user, descriptor)
|
||||
|
||||
|
||||
|
||||
|
||||
def _has_access_xmodule(user, xmodule, action):
|
||||
"""
|
||||
Check if user has access to this xmodule.
|
||||
|
||||
@@ -2,8 +2,8 @@ from collections import defaultdict
|
||||
from fs.errors import ResourceNotFoundError
|
||||
from functools import wraps
|
||||
import logging
|
||||
from path import path
|
||||
|
||||
from path import path
|
||||
from django.conf import settings
|
||||
from django.http import Http404
|
||||
|
||||
@@ -142,7 +142,8 @@ def get_course_info_section(course, section_key):
|
||||
|
||||
raise KeyError("Invalid about key " + str(section_key))
|
||||
|
||||
def get_courses_by_university(user):
|
||||
|
||||
def get_courses_by_university(user, domain=None):
|
||||
'''
|
||||
Returns dict of lists of courses available, keyed by course.org (ie university).
|
||||
Courses are sorted by course.number.
|
||||
@@ -152,9 +153,21 @@ def get_courses_by_university(user):
|
||||
courses = [c for c in modulestore().get_courses()
|
||||
if isinstance(c, CourseDescriptor)]
|
||||
courses = sorted(courses, key=lambda course: course.number)
|
||||
|
||||
if domain and settings.MITX_FEATURES.get('SUBDOMAIN_COURSE_LISTINGS'):
|
||||
subdomain = domain.split(".")[0]
|
||||
if subdomain not in settings.COURSE_LISTINGS:
|
||||
subdomain = 'default'
|
||||
visible_courses = frozenset(settings.COURSE_LISTINGS[subdomain])
|
||||
else:
|
||||
visible_courses = frozenset(c.id for c in courses)
|
||||
|
||||
universities = defaultdict(list)
|
||||
for course in courses:
|
||||
if has_access(user, course, 'see_exists'):
|
||||
universities[course.org].append(course)
|
||||
if not has_access(user, course, 'see_exists'):
|
||||
continue
|
||||
if course.id not in visible_courses:
|
||||
continue
|
||||
universities[course.org].append(course)
|
||||
return universities
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
"""
|
||||
A script to walk a course xml tree, generate a dictionary of all the metadata,
|
||||
and print it out as a json dict.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
|
||||
from collections import OrderedDict
|
||||
from path import path
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from xmodule.modulestore.xml import XMLModuleStore
|
||||
from xmodule.x_module import policy_key
|
||||
|
||||
def import_course(course_dir, verbose=True):
|
||||
course_dir = path(course_dir)
|
||||
data_dir = course_dir.dirname()
|
||||
course_dirs = [course_dir.basename()]
|
||||
|
||||
# No default class--want to complain if it doesn't find plugins for any
|
||||
# module.
|
||||
modulestore = XMLModuleStore(data_dir,
|
||||
default_class=None,
|
||||
eager=True,
|
||||
course_dirs=course_dirs)
|
||||
|
||||
def str_of_err(tpl):
|
||||
(msg, exc_str) = tpl
|
||||
return '{msg}\n{exc}'.format(msg=msg, exc=exc_str)
|
||||
|
||||
courses = modulestore.get_courses()
|
||||
|
||||
n = len(courses)
|
||||
if n != 1:
|
||||
sys.stderr.write('ERROR: Expect exactly 1 course. Loaded {n}: {lst}\n'.format(
|
||||
n=n, lst=courses))
|
||||
return None
|
||||
|
||||
course = courses[0]
|
||||
errors = modulestore.get_item_errors(course.location)
|
||||
if len(errors) != 0:
|
||||
sys.stderr.write('ERRORs during import: {}\n'.format('\n'.join(map(str_of_err, errors))))
|
||||
|
||||
return course
|
||||
|
||||
def node_metadata(node):
|
||||
# make a copy
|
||||
to_export = ('format', 'display_name',
|
||||
'graceperiod', 'showanswer', 'rerandomize',
|
||||
'start', 'due', 'graded', 'hide_from_toc',
|
||||
'ispublic', 'xqa_key')
|
||||
|
||||
orig = node.own_metadata
|
||||
d = {k: orig[k] for k in to_export if k in orig}
|
||||
return d
|
||||
|
||||
def get_metadata(course):
|
||||
d = OrderedDict({})
|
||||
queue = [course]
|
||||
while len(queue) > 0:
|
||||
node = queue.pop()
|
||||
d[policy_key(node.location)] = node_metadata(node)
|
||||
# want to print first children first, so put them at the end
|
||||
# (we're popping from the end)
|
||||
queue.extend(reversed(node.get_children()))
|
||||
return d
|
||||
|
||||
|
||||
def print_metadata(course_dir, output):
|
||||
course = import_course(course_dir)
|
||||
if course:
|
||||
meta = get_metadata(course)
|
||||
result = json.dumps(meta, indent=4)
|
||||
if output:
|
||||
with file(output, 'w') as f:
|
||||
f.write(result)
|
||||
else:
|
||||
print result
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = """Imports specified course.xml and prints its
|
||||
metadata as a json dict.
|
||||
|
||||
Usage: metadata_to_json PATH-TO-COURSE-DIR OUTPUT-PATH
|
||||
|
||||
if OUTPUT-PATH isn't given, print to stdout.
|
||||
"""
|
||||
def handle(self, *args, **options):
|
||||
n = len(args)
|
||||
if n < 1 or n > 2:
|
||||
print Command.help
|
||||
return
|
||||
|
||||
output_path = args[1] if n > 1 else None
|
||||
print_metadata(args[0], output_path)
|
||||
@@ -67,7 +67,7 @@ class StudentModuleCache(object):
|
||||
"""
|
||||
A cache of StudentModules for a specific student
|
||||
"""
|
||||
def __init__(self, user, descriptors, acquire_lock=False):
|
||||
def __init__(self, user, descriptors, select_for_update=False):
|
||||
'''
|
||||
Find any StudentModule objects that are needed by any descriptor
|
||||
in descriptors. Avoids making multiple queries to the database.
|
||||
@@ -77,6 +77,7 @@ class StudentModuleCache(object):
|
||||
Arguments
|
||||
user: The user for which to fetch maching StudentModules
|
||||
descriptors: An array of XModuleDescriptors.
|
||||
select_for_update: Flag indicating whether the row should be locked until end of transaction
|
||||
'''
|
||||
if user.is_authenticated():
|
||||
module_ids = self._get_module_state_keys(descriptors)
|
||||
@@ -86,7 +87,7 @@ class StudentModuleCache(object):
|
||||
self.cache = []
|
||||
chunk_size = 500
|
||||
for id_chunk in [module_ids[i:i + chunk_size] for i in xrange(0, len(module_ids), chunk_size)]:
|
||||
if acquire_lock:
|
||||
if select_for_update:
|
||||
self.cache.extend(StudentModule.objects.select_for_update().filter(
|
||||
student=user,
|
||||
module_state_key__in=id_chunk)
|
||||
@@ -102,13 +103,14 @@ class StudentModuleCache(object):
|
||||
|
||||
|
||||
@classmethod
|
||||
def cache_for_descriptor_descendents(cls, user, descriptor, depth=None, descriptor_filter=lambda descriptor: True, acquire_lock=False):
|
||||
def cache_for_descriptor_descendents(cls, user, descriptor, depth=None, descriptor_filter=lambda descriptor: True, select_for_update=False):
|
||||
"""
|
||||
descriptor: An XModuleDescriptor
|
||||
depth is the number of levels of descendent modules to load StudentModules for, in addition to
|
||||
the supplied descriptor. If depth is None, load all descendent StudentModules
|
||||
descriptor_filter is a function that accepts a descriptor and return wether the StudentModule
|
||||
should be cached
|
||||
select_for_update: Flag indicating whether the row should be locked until end of transaction
|
||||
"""
|
||||
|
||||
def get_child_descriptors(descriptor, depth, descriptor_filter):
|
||||
@@ -128,7 +130,7 @@ class StudentModuleCache(object):
|
||||
|
||||
descriptors = get_child_descriptors(descriptor, depth, descriptor_filter)
|
||||
|
||||
return StudentModuleCache(user, descriptors, acquire_lock)
|
||||
return StudentModuleCache(user, descriptors, select_for_update)
|
||||
|
||||
def _get_module_state_keys(self, descriptors):
|
||||
'''
|
||||
|
||||
@@ -320,8 +320,8 @@ def xqueue_callback(request, course_id, userid, id, dispatch):
|
||||
user = User.objects.get(id=userid)
|
||||
|
||||
student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(
|
||||
user, modulestore().get_item(id), depth=0, acquire_lock=True)
|
||||
instance = get_module(user, request, id, student_module_cache, course_id=course_id)
|
||||
user, modulestore().get_item(id), depth=0, select_for_update=True)
|
||||
instance = get_module(user, request, id, student_module_cache)
|
||||
if instance is None:
|
||||
log.debug("No module {} for user {}--access denied?".format(id, user))
|
||||
raise Http404
|
||||
|
||||
@@ -65,7 +65,8 @@ def courses(request):
|
||||
'''
|
||||
Render "find courses" page. The course selection work is done in courseware.courses.
|
||||
'''
|
||||
universities = get_courses_by_university(request.user)
|
||||
universities = get_courses_by_university(request.user,
|
||||
domain=request.META.get('HTTP_HOST'))
|
||||
return render_to_response("courses.html", {'universities': universities})
|
||||
|
||||
|
||||
@@ -112,6 +113,7 @@ def index(request, course_id, chapter=None, section=None,
|
||||
- HTTPresponse
|
||||
"""
|
||||
course = get_course_with_access(request.user, course_id, 'load')
|
||||
staff_access = has_access(request.user, course, 'staff')
|
||||
registered = registered_for_course(course, request.user)
|
||||
if not registered:
|
||||
# TODO (vshnayder): do course instructors need to be registered to see course?
|
||||
@@ -125,7 +127,8 @@ def index(request, course_id, chapter=None, section=None,
|
||||
'COURSE_TITLE': course.title,
|
||||
'course': course,
|
||||
'init': '',
|
||||
'content': ''
|
||||
'content': '',
|
||||
'staff_access': staff_access,
|
||||
}
|
||||
|
||||
look_for_module = chapter is not None and section is not None
|
||||
@@ -168,7 +171,8 @@ def index(request, course_id, chapter=None, section=None,
|
||||
position=position
|
||||
))
|
||||
try:
|
||||
result = render_to_response('courseware-error.html', {})
|
||||
result = render_to_response('courseware-error.html',
|
||||
{'staff_access': staff_access})
|
||||
except:
|
||||
result = HttpResponse("There was an unrecoverable error")
|
||||
|
||||
@@ -210,8 +214,10 @@ def course_info(request, course_id):
|
||||
Assumes the course_id is in a valid format.
|
||||
"""
|
||||
course = get_course_with_access(request.user, course_id, 'load')
|
||||
staff_access = has_access(request.user, course, 'staff')
|
||||
|
||||
return render_to_response('info.html', {'course': course})
|
||||
return render_to_response('info.html', {'course': course,
|
||||
'staff_access': staff_access,})
|
||||
|
||||
|
||||
def registered_for_course(course, user):
|
||||
@@ -243,7 +249,8 @@ def university_profile(request, org_id):
|
||||
raise Http404("University Profile not found for {0}".format(org_id))
|
||||
|
||||
# Only grab courses for this org...
|
||||
courses = get_courses_by_university(request.user)[org_id]
|
||||
courses = get_courses_by_university(request.user,
|
||||
domain=request.META.get('HTTP_HOST'))[org_id]
|
||||
context = dict(courses=courses, org_id=org_id)
|
||||
template_file = "university_profile/{0}.html".format(org_id).lower()
|
||||
|
||||
@@ -259,13 +266,14 @@ def profile(request, course_id, student_id=None):
|
||||
Course staff are allowed to see the profiles of students in their class.
|
||||
"""
|
||||
course = get_course_with_access(request.user, course_id, 'load')
|
||||
staff_access = has_access(request.user, course, 'staff')
|
||||
|
||||
if student_id is None or student_id == request.user.id:
|
||||
# always allowed to see your own profile
|
||||
student = request.user
|
||||
else:
|
||||
# Requesting access to a different student's profile
|
||||
if not has_access(request.user, course, 'staff'):
|
||||
if not staff_access:
|
||||
raise Http404
|
||||
student = User.objects.get(id=int(student_id))
|
||||
|
||||
@@ -284,8 +292,9 @@ def profile(request, course_id, student_id=None):
|
||||
'email': student.email,
|
||||
'course': course,
|
||||
'csrf': csrf(request)['csrf_token'],
|
||||
'courseware_summary' : courseware_summary,
|
||||
'grade_summary' : grade_summary
|
||||
'courseware_summary': courseware_summary,
|
||||
'grade_summary': grade_summary,
|
||||
'staff_access': staff_access,
|
||||
}
|
||||
context.update()
|
||||
|
||||
@@ -318,7 +327,10 @@ def gradebook(request, course_id):
|
||||
for student in enrolled_students]
|
||||
|
||||
return render_to_response('gradebook.html', {'students': student_info,
|
||||
'course': course, 'course_id': course_id})
|
||||
'course': course,
|
||||
'course_id': course_id,
|
||||
# Checked above
|
||||
'staff_access': True,})
|
||||
|
||||
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
@@ -327,7 +339,8 @@ def grade_summary(request, course_id):
|
||||
course = get_course_with_access(request.user, course_id, 'staff')
|
||||
|
||||
# For now, just a static page
|
||||
context = {'course': course }
|
||||
context = {'course': course,
|
||||
'staff_access': True,}
|
||||
return render_to_response('grade_summary.html', context)
|
||||
|
||||
|
||||
@@ -337,6 +350,7 @@ def instructor_dashboard(request, course_id):
|
||||
course = get_course_with_access(request.user, course_id, 'staff')
|
||||
|
||||
# For now, just a static page
|
||||
context = {'course': course }
|
||||
context = {'course': course,
|
||||
'staff_access': True,}
|
||||
return render_to_response('instructor_dashboard.html', context)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from mitxmako.shortcuts import render_to_response
|
||||
|
||||
from courseware.courses import get_opt_course_with_access
|
||||
from courseware.access import has_access
|
||||
from xmodule.course_module import CourseDescriptor
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
@@ -49,6 +50,10 @@ def update_template_dictionary(dictionary, request=None, course=None, article=No
|
||||
if request:
|
||||
dictionary.update(csrf(request))
|
||||
|
||||
if request and course:
|
||||
dictionary['staff_access'] = has_access(request.user, course, 'staff')
|
||||
else:
|
||||
dictionary['staff_access'] = False
|
||||
|
||||
def view(request, article_path, course_id=None):
|
||||
course = get_opt_course_with_access(request.user, course_id, 'load')
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from mitxmako.shortcuts import render_to_response
|
||||
|
||||
from courseware.access import has_access
|
||||
from courseware.courses import get_course_with_access
|
||||
from lxml import etree
|
||||
|
||||
@login_required
|
||||
def index(request, course_id, page=0):
|
||||
course = get_course_with_access(request.user, course_id, 'load')
|
||||
raw_table_of_contents = open('lms/templates/book_toc.xml', 'r') # TODO: This will need to come from S3
|
||||
staff_access = has_access(request.user, course, 'staff')
|
||||
|
||||
# TODO: This will need to come from S3
|
||||
raw_table_of_contents = open('lms/templates/book_toc.xml', 'r')
|
||||
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})
|
||||
'table_of_contents': table_of_contents,
|
||||
'staff_access': staff_access})
|
||||
|
||||
|
||||
def index_shifted(request, course_id, page):
|
||||
|
||||
@@ -49,6 +49,11 @@ MITX_FEATURES = {
|
||||
## 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
|
||||
|
||||
# When True, will only publicly list courses by the subdomain. Expects you
|
||||
# to define COURSE_LISTINGS, a dictionary mapping subdomains to lists of
|
||||
# course_ids (see dev_int.py for an example)
|
||||
'SUBDOMAIN_COURSE_LISTINGS' : False,
|
||||
|
||||
'ENABLE_TEXTBOOK' : True,
|
||||
'ENABLE_DISCUSSION' : True,
|
||||
|
||||
@@ -61,6 +66,7 @@ MITX_FEATURES = {
|
||||
'ACCESS_REQUIRE_STAFF_FOR_COURSE': False,
|
||||
'AUTH_USE_OPENID': False,
|
||||
'AUTH_USE_MIT_CERTIFICATES' : False,
|
||||
|
||||
}
|
||||
|
||||
# Used for A/B testing
|
||||
|
||||
@@ -54,7 +54,7 @@ CACHES = {
|
||||
}
|
||||
|
||||
XQUEUE_INTERFACE = {
|
||||
"url": "http://xqueue.sandbox.edx.org",
|
||||
"url": "http://sandbox-xqueue.edx.org",
|
||||
"django_auth": {
|
||||
"username": "lms",
|
||||
"password": "***REMOVED***"
|
||||
|
||||
32
lms/envs/dev_int.py
Normal file
32
lms/envs/dev_int.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
This enables use of course listings by subdomain. To see it in action, point the
|
||||
following domains to 127.0.0.1 in your /etc/hosts file:
|
||||
|
||||
berkeley.dev
|
||||
harvard.dev
|
||||
mit.dev
|
||||
|
||||
Note that OS X has a bug where using *.local domains is excruciatingly slow, so
|
||||
use *.dev domains instead for local testing.
|
||||
"""
|
||||
from .dev import *
|
||||
|
||||
MITX_FEATURES['SUBDOMAIN_COURSE_LISTINGS'] = True
|
||||
|
||||
COURSE_LISTINGS = {
|
||||
'default' : ['BerkeleyX/CS169.1x/2012_Fall',
|
||||
'BerkeleyX/CS188.1x/2012_Fall',
|
||||
'HarvardX/CS50x/2012',
|
||||
'HarvardX/PH207x/2012_Fall',
|
||||
'MITx/3.091x/2012_Fall',
|
||||
'MITx/6.002x/2012_Fall',
|
||||
'MITx/6.00x/2012_Fall'],
|
||||
|
||||
'berkeley': ['BerkeleyX/CS169.1x/2012_Fall',
|
||||
'BerkeleyX/CS188.1x/2012_Fall'],
|
||||
|
||||
'harvard' : ['HarvardX/CS50x/2012'],
|
||||
|
||||
'mit' : ['MITx/3.091x/2012_Fall',
|
||||
'MITx/6.00x/2012_Fall']
|
||||
}
|
||||
@@ -51,7 +51,7 @@ GITHUB_REPO_ROOT = ENV_ROOT / "data"
|
||||
|
||||
|
||||
XQUEUE_INTERFACE = {
|
||||
"url": "http://xqueue.sandbox.edx.org",
|
||||
"url": "http://sandbox-xqueue.edx.org",
|
||||
"django_auth": {
|
||||
"username": "lms",
|
||||
"password": "***REMOVED***"
|
||||
|
||||
@@ -28,7 +28,7 @@ def url_class(url):
|
||||
% if user.is_authenticated():
|
||||
<li class="profile"><a href="${reverse('profile', args=[course.id])}" class="${url_class('profile')}">Profile</a></li>
|
||||
% endif
|
||||
% if has_access(user, course, 'staff'):
|
||||
% if staff_access:
|
||||
<li class="instructor"><a href="${reverse('instructor_dashboard', args=[course.id])}" class="${url_class('instructor')}">Instructor</a></li>
|
||||
% endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user