diff --git a/.gitignore b/.gitignore
index f98fdf7bf9ed7655054339156e7f74258dfbb332..e2340d2aa779e64e375172e045265e8358c7370e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,9 @@
 *.swp
 *.orig
 *.DS_Store
+:2e_*
+:2e#
+.AppleDouble
 database.sqlite
 courseware/static/js/mathjax/*
 db.newaskbot
diff --git a/djangoapps/courseware/capa/capa_problem.py b/djangoapps/courseware/capa/capa_problem.py
index e164429f11a0fc8b60fd185d20465d21abd9a30e..f5739fd8b06948bbbf73dcaedf176f3e9661454d 100644
--- a/djangoapps/courseware/capa/capa_problem.py
+++ b/djangoapps/courseware/capa/capa_problem.py
@@ -25,7 +25,7 @@ from mako.template import Template
 
 from util import contextualize_text
 import inputtypes
-from responsetypes import NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, MultipleChoiceResponse,  StudentInputError, TrueFalseResponse, ExternalResponse,ImageResponse
+from responsetypes import NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, MultipleChoiceResponse,  StudentInputError, TrueFalseResponse, ExternalResponse,ImageResponse,OptionResponse
 
 import calc
 import eia
@@ -40,8 +40,9 @@ response_types = {'numericalresponse':NumericalResponse,
                   'multiplechoiceresponse':MultipleChoiceResponse,
                   'truefalseresponse':TrueFalseResponse,
                   'imageresponse':ImageResponse,
+                  'optionresponse':OptionResponse,
                   }
-entry_types = ['textline', 'schematic', 'choicegroup','textbox','imageinput']
+entry_types = ['textline', 'schematic', 'choicegroup','textbox','imageinput','optioninput']
 solution_types = ['solution']	# extra things displayed after "show answers" is pressed
 response_properties = ["responseparam", "answer"]	# these get captured as student responses
 
@@ -186,6 +187,13 @@ class LoncapaProblem(object):
             if answer:
                 answer_map[entry.get('id')] = contextualize_text(answer, self.context)
 
+        # include solutions from <solution>...</solution> stanzas
+        # Tentative merge; we should figure out how we want to handle hints and solutions
+        for entry in self.tree.xpath("//"+"|//".join(solution_types)):
+            answer = etree.tostring(entry)
+            if answer:
+                answer_map[entry.get('id')] = answer
+
         return answer_map
 
     # ======= Private ========
@@ -241,7 +249,24 @@ class LoncapaProblem(object):
             if self.student_answers and problemid in self.student_answers:
                 value = self.student_answers[problemid]
 
-            return getattr(inputtypes, problemtree.tag)(problemtree, value, status) #TODO
+            #### This code is a hack. It was merged to help bring two branches
+            #### in sync, but should be replaced. msg should be passed in a 
+            #### response_type
+            # prepare the response message, if it exists in correct_map
+            if 'msg' in self.correct_map:
+                msg = self.correct_map['msg']
+            elif ('msg_%s' % problemid) in self.correct_map:
+                msg = self.correct_map['msg_%s' % problemid]
+            else:
+                msg = ''
+
+            #if settings.DEBUG:
+            #    print "[courseware.capa.capa_problem.extract_html] msg = ",msg
+
+            # do the rendering
+            #render_function = html_special_response[problemtree.tag]
+            render_function = getattr(inputtypes, problemtree.tag)
+            return render_function(problemtree, value, status, msg) # render the special response (textline, schematic,...)
 
         tree=Element(problemtree.tag)
         for item in problemtree:
@@ -287,6 +312,7 @@ class LoncapaProblem(object):
             answer_id = 1
             for entry in tree.xpath("|".join(['//'+response.tag+'[@id=$id]//'+x for x in (entry_types + solution_types)]), 
                                     id=response_id_str):
+                # assign one answer_id for each entry_type or solution_type 
                 entry.attrib['response_id'] = str(response_id)
                 entry.attrib['answer_id'] = str(answer_id)
                 entry.attrib['id'] = "%s_%i_%i"%(self.problem_id, response_id, answer_id)
diff --git a/djangoapps/courseware/capa/inputtypes.py b/djangoapps/courseware/capa/inputtypes.py
index e093a7929cf618743ec92ee314fa89c46ce8b600..0388b35d0b497391791efb464502393506329511 100644
--- a/djangoapps/courseware/capa/inputtypes.py
+++ b/djangoapps/courseware/capa/inputtypes.py
@@ -6,11 +6,16 @@
 Module containing the problem elements which render into input objects
 
 - textline
-- textbox (change this to textarea?)
+- textbox     (change this to textarea?)
 - schemmatic
+- choicegroup (for multiplechoice: checkbox, radio, or select option)
+- imageinput  (for clickable image)
+- optioninput (for option list)
 
 These are matched by *.html files templates/*.html which are mako templates with the actual html.
 
+Each input type takes the xml tree as 'element', the previous answer as 'value', and the graded status as 'status'
+
 '''
 
 # TODO: rename "state" to "status" for all below
@@ -18,6 +23,7 @@ These are matched by *.html files templates/*.html which are mako templates with
 # but it will turn into a dict containing both the answer and any associated message for the problem ID for the input element.
 
 import re
+import shlex # for splitting quoted strings
 
 from django.conf import settings
 
@@ -27,9 +33,42 @@ from lxml import etree
 from mitxmako.shortcuts import render_to_string
 
 #-----------------------------------------------------------------------------
-#takes the xml tree as 'element', the student's previous answer as 'value', and the graded status as 'state'
 
-def choicegroup(element, value, state, msg=""):
+def optioninput(element, value, status, msg=''):
+    '''
+    Select option input type.
+
+    Example:
+
+    <optioninput options="('Up','Down')" correct="Up"/><text>The location of the sky</text>
+    '''
+    eid=element.get('id')
+    options = element.get('options')
+    if not options:
+        raise Exception,"[courseware.capa.inputtypes.optioninput] Missing options specification in " + etree.tostring(element)
+    oset = shlex.shlex(options[1:-1])
+    oset.quotes = "'"
+    oset.whitespace = ","
+    oset = [x[1:-1] for x  in list(oset)]
+
+    # osetdict = dict([('option_%s_%s' % (eid,x),oset[x]) for x in range(len(oset)) ])	# make dict with IDs
+    osetdict = dict([(oset[x],oset[x]) for x in range(len(oset)) ])	# make dict with key,value same
+    if settings.DEBUG:
+        print '[courseware.capa.inputtypes.optioninput] osetdict=',osetdict
+    
+    context={'id':eid,
+             'value':value,
+             'state':status,
+             'msg':msg,
+             'options':osetdict,
+             }
+
+    html=render_to_string("optioninput.html", context)
+    return etree.XML(html)
+
+#-----------------------------------------------------------------------------
+
+def choicegroup(element, value, status, msg=''):
     '''
     Radio button inputs: multiple choice or true/false
 
@@ -47,7 +86,7 @@ def choicegroup(element, value, state, msg=""):
     for choice in element:
         assert choice.tag =="choice", "only <choice> tags should be immediate children of a <choicegroup>"
         choices[choice.get("name")] = etree.tostring(choice[0])	# TODO: what if choice[0] has math tags in it?
-    context={'id':eid, 'value':value, 'state':state, 'type':type, 'choices':choices}
+    context={'id':eid, 'value':value, 'state':status, 'type':type, 'choices':choices}
     html=render_to_string("choicegroup.html", context)
     return etree.XML(html)
 
@@ -60,9 +99,9 @@ def textline(element, value, state, msg=""):
     return etree.XML(html)
 
 #-----------------------------------------------------------------------------
-# TODO: Make a wrapper for <formulainput>
-# TODO: Make an AJAX loop to confirm equation is okay in real-time as user types
-def jstextline(element, value, state, msg=""):
+
+def js_textline(element, value, status, msg=''):
+		## TODO: Code should follow PEP8 (4 spaces per indentation level)
         '''
         textline is used for simple one-line inputs, like formularesponse and symbolicresponse.
         '''
@@ -72,7 +111,7 @@ def jstextline(element, value, state, msg=""):
         dojs = element.get('dojs')	# dojs is used for client-side javascript display & return
         				# when dojs=='math', a <span id=display_eid>`{::}`</span>
                                         # and a hidden textarea with id=input_eid_fromjs will be output
-        context = {'id':eid, 'value':value, 'state':state, 'count':count, 'size': size,
+        context = {'id':eid, 'value':value, 'state':status, 'count':count, 'size': size,
                    'dojs':dojs,
                    'msg':msg,
                    }
@@ -81,7 +120,7 @@ def jstextline(element, value, state, msg=""):
 
 #-----------------------------------------------------------------------------
 ## TODO: Make a wrapper for <codeinput>
-def textbox(element, value, state, msg=''):
+def textbox(element, value, status, msg=''):
         '''
         The textbox is used for code input.  The message is the return HTML string from
         evaluating the code, eg error messages, and output from the code tests.
@@ -91,12 +130,12 @@ def textbox(element, value, state, msg=''):
         eid=element.get('id')
         count = int(eid.split('_')[-2])-1 # HACK
         size = element.get('size')
-        context = {'id':eid, 'value':value, 'state':state, 'count':count, 'size': size, 'msg':msg}
+        context = {'id':eid, 'value':value, 'state':status, 'count':count, 'size': size, 'msg':msg}
         html=render_to_string("textbox.html", context)
         return etree.XML(html)
 
 #-----------------------------------------------------------------------------
-def schematic(element, value, state):
+def schematic(element, value, status, msg=''):
     eid = element.get('id')
     height = element.get('height')
     width = element.get('width')
@@ -120,7 +159,7 @@ def schematic(element, value, state):
 
 #-----------------------------------------------------------------------------
 ### TODO: Move out of inputtypes
-def math(element, value, state, msg=''):
+def math(element, value, status, msg=''):
     '''
     This is not really an input type.  It is a convention from Lon-CAPA, used for
     displaying a math equation.
@@ -134,21 +173,27 @@ def math(element, value, state, msg=''):
 
     TODO: use shorter tags (but this will require converting problem XML files!)
     '''
-    mathstr = element.text[1:-1]
-    if '\\displaystyle' in mathstr:
-        isinline = False
-        mathstr = mathstr.replace('\\displaystyle','')
-    else:
-        isinline = True
-
-    html=render_to_string("mathstring.html",{'mathstr':mathstr,'isinline':isinline,'tail':element.tail})
+    mathstr = re.sub('\$(.*)\$','[mathjaxinline]\\1[/mathjaxinline]',element.text)
+    mtag = 'mathjax'
+    if not '\\displaystyle' in mathstr: mtag += 'inline'
+    else: mathstr = mathstr.replace('\\displaystyle','')
+    mathstr = mathstr.replace('mathjaxinline]','%s]'%mtag)
+
+    #if '\\displaystyle' in mathstr:
+    #    isinline = False
+    #    mathstr = mathstr.replace('\\displaystyle','')
+    #else:
+    #    isinline = True
+    # html=render_to_string("mathstring.html",{'mathstr':mathstr,'isinline':isinline,'tail':element.tail})
+
+    html = '<html><html>%s</html><html>%s</html></html>' % (mathstr,element.tail)
     xhtml = etree.XML(html)
     # xhtml.tail = element.tail	# don't forget to include the tail!
     return xhtml
 
 #-----------------------------------------------------------------------------
 
-def solution(element, value, state, msg=''):
+def solution(element, value, status, msg=''):
     '''
     This is not really an input type.  It is just a <span>...</span> which is given an ID,
     that is used for displaying an extended answer (a problem "solution") after "show answers"
@@ -159,7 +204,7 @@ def solution(element, value, state, msg=''):
     size = element.get('size')
     context = {'id':eid,
                'value':value,
-               'state':state,
+               'state':status,
                'size': size,
                'msg':msg,
                }
diff --git a/djangoapps/courseware/capa/responsetypes.py b/djangoapps/courseware/capa/responsetypes.py
index 7fb682faf6aedbd190057983d615869ed20b654f..d9b18428febcbec56862cb233d2ec413b755f7e0 100644
--- a/djangoapps/courseware/capa/responsetypes.py
+++ b/djangoapps/courseware/capa/responsetypes.py
@@ -32,6 +32,8 @@ from lxml.html.soupparser import fromstring as fromstring_bs	# uses Beautiful So
 import calc
 import eia
 
+from util import contextualize_text
+
 def compare_with_tolerance(v1, v2, tol):
     ''' Compare v1 to v2 with maximum tolerance tol
     tol is relative if it ends in %; otherwise, it is absolute
@@ -61,6 +63,8 @@ class GenericResponse(object):
 
 #Every response type needs methods "grade" and "get_answers"     
 
+#-----------------------------------------------------------------------------
+
 class MultipleChoiceResponse(GenericResponse):
     '''
     Example: 
@@ -84,6 +88,7 @@ class MultipleChoiceResponse(GenericResponse):
         self.correct_choices = [choice.get('name') for choice in self.correct_choices]
         self.context = context
 
+        self.answer_field = xml.find('choicegroup')	# assumes only ONE choicegroup within this response
         self.answer_id = xml.xpath('//*[@id=$id]//choicegroup/@id',
                                    id=xml.get('id'))
         if not len(self.answer_id) == 1:
@@ -100,9 +105,14 @@ class MultipleChoiceResponse(GenericResponse):
         return {self.answer_id:self.correct_choices}
 
     def preprocess_response(self):
+        '''
+        Initialize name attributes in <choice> stanzas in the <choicegroup> in this response.
+        '''
         i=0
         for response in self.xml.xpath("choicegroup"):
-            response.set("type", "MultipleChoice")
+            rtype = response.get('type')
+            if rtype not in ["MultipleChoice"]:
+                response.set("type", "MultipleChoice")		# force choicegroup to be MultipleChoice if not valid
             for choice in list(response):
                 if choice.get("name") == None:
                     choice.set("name", "choice_"+str(i))
@@ -131,6 +141,42 @@ class TrueFalseResponse(MultipleChoiceResponse):
         
         return {self.answer_id : 'incorrect'}
 
+#-----------------------------------------------------------------------------
+
+class OptionResponse(GenericResponse):
+    '''
+    Example: 
+
+    <optionresponse direction="vertical" randomize="yes">
+        <optioninput options="('Up','Down')" correct="Up"><text>The location of the sky</text></optioninput>
+        <optioninput options="('Up','Down')" correct="Down"><text>The location of the earth</text></optioninput>
+    </optionresponse>
+
+    TODO: handle direction and randomize
+
+    '''
+    def __init__(self, xml, context):
+        self.xml = xml
+        self.answer_fields = xml.findall('optioninput')
+        if settings.DEBUG:
+            print '[courseware.capa.responsetypes.OR.init] answer_fields=%s' % (self.answer_fields)
+        self.context = context
+
+    def grade(self, student_answers):
+        cmap = {}
+        amap = self.get_answers()
+        for aid in amap:
+            if aid in student_answers and student_answers[aid]==amap[aid]:
+                cmap[aid] = 'correct'
+            else:
+                cmap[aid] = 'incorrect'
+        return cmap
+
+    def get_answers(self):
+        amap = dict([(af.get('id'),af.get('correct')) for af in self.answer_fields])
+        return amap
+
+#-----------------------------------------------------------------------------
 
 class NumericalResponse(GenericResponse):
     def __init__(self, xml, context):
@@ -219,43 +265,153 @@ def sympy_check2():
         self.answer_ids = xml.xpath('//*[@id=$id]//textline/@id',
                                     id=xml.get('id'))
         self.context = context
-        answer_list = xml.xpath('//*[@id=$id]//answer',
-                           id=xml.get('id'))
-        if len(answer_list):
-            answer=answer_list[0]
-        else: 
-            raise Exception("Invalid custom response -- no checker code")
 
-        answer_src = answer.get('src')
-        if answer_src != None:
-            self.code = open(settings.DATA_DIR+'src/'+answer_src).read()
-        else:
-            self.code = answer.text
+        # if <customresponse> has an "expect" attribute then save that
+        self.expect = xml.get('expect')
+        self.myid = xml.get('id')
+
+        # the <answer>...</answer> stanza should be local to the current <customresponse>.  So try looking there first.
+        self.code = None
+        answer = None
+        try:
+            answer = xml.xpath('//*[@id=$id]//answer',id=xml.get('id'))[0]
+        except IndexError,err:
+            # print "xml = ",etree.tostring(xml,pretty_print=True)
+
+            # if we have a "cfn" attribute then look for the function specified by cfn, in the problem context
+            # ie the comparison function is defined in the <script>...</script> stanza instead
+            cfn = xml.get('cfn')
+            if cfn:
+                if settings.DEBUG: print "[courseware.capa.responsetypes] cfn = ",cfn
+                if cfn in context:
+                    self.code = context[cfn]
+                else:
+                    print "can't find cfn in context = ",context
+
+        if not self.code:
+            if not answer:
+                # raise Exception,"[courseware.capa.responsetypes.customresponse] missing code checking script! id=%s" % self.myid
+                print "[courseware.capa.responsetypes.customresponse] missing code checking script! id=%s" % self.myid
+                self.code = ''
+            else:
+                answer_src = answer.get('src')
+                if answer_src != None:
+                    self.code = open(settings.DATA_DIR+'src/'+answer_src).read()
+                else:
+                    self.code = answer.text
 
     def grade(self, student_answers):
         '''
         student_answers is a dict with everything from request.POST, but with the first part
         of each key removed (the string before the first "_").
         '''
-        from capa_problem import global_context
-        submission = [student_answers[k] for k in sorted(self.answer_ids)]
-        self.context.update({'submission':submission})
-        exec self.code in global_context, self.context
-        return  zip(sorted(self.answer_ids), self.context['correct'])
+
+        def getkey2(dict,key,default):
+            """utilify function: get dict[key] if key exists, or return default"""
+            if dict.has_key(key):
+                return dict[key]
+            return default
+
+        idset = sorted(self.answer_ids)				# ordered list of answer id's
+        submission = [student_answers[k] for k in idset]	# ordered list of answers
+        fromjs = [ getkey2(student_answers,k+'_fromjs',None) for k in idset ]	# ordered list of fromjs_XXX responses (if exists)
+
+        # if there is only one box, and it's empty, then don't evaluate
+        if len(idset)==1 and not submission[0]:
+            return {idset[0]:'no_answer_entered'}
+
+        gctxt = self.context['global_context']
+
+        correct = ['unknown'] * len(idset)
+        messages = [''] * len(idset)
+
+        # put these in the context of the check function evaluator
+        # note that this doesn't help the "cfn" version - only the exec version
+        self.context.update({'xml' : self.xml,		# our subtree
+                             'response_id' : self.myid,	# my ID
+                             'expect': self.expect,		# expected answer (if given as attribute)
+                             'submission':submission,		# ordered list of student answers from entry boxes in our subtree
+                             'idset':idset,			# ordered list of ID's of all entry boxes in our subtree
+                             'fromjs':fromjs,			# ordered list of all javascript inputs in our subtree
+                             'answers':student_answers,		# dict of student's responses, with keys being entry box IDs
+                             'correct':correct,			# the list to be filled in by the check function
+                             'messages':messages,		# the list of messages to be filled in by the check function
+                             'testdat':'hello world',
+                             })
+
+        # exec the check function
+        if type(self.code)==str:
+            try:
+                exec self.code in self.context['global_context'], self.context
+            except Exception,err:
+                print "oops in customresponse (code) error %s" % err
+                print "context = ",self.context
+                print traceback.format_exc()
+        else:					# self.code is not a string; assume its a function
+
+            # this is an interface to the Tutor2 check functions
+            fn = self.code
+            try:
+                answer_given = submission[0] if (len(idset)==1) else submission
+                if fn.func_code.co_argcount>=4:	# does it want four arguments (the answers dict, myname)?
+                    ret = fn(self.expect,answer_given,student_answers,self.answer_ids[0])
+                elif fn.func_code.co_argcount>=3:	# does it want a third argument (the answers dict)?
+                    ret = fn(self.expect,answer_given,student_answers)
+                else:
+                    ret = fn(self.expect,answer_given)
+            except Exception,err:
+                print "oops in customresponse (cfn) error %s" % err
+                # print "context = ",self.context
+                print traceback.format_exc()
+            if settings.DEBUG: print "[courseware.capa.responsetypes.customresponse.grade] ret = ",ret
+            if type(ret)==dict:
+                correct[0] = 'correct' if ret['ok'] else 'incorrect'
+                msg = ret['msg']
+
+                if 1:
+                    # try to clean up message html
+                    msg = '<html>'+msg+'</html>'
+                    msg = etree.tostring(fromstring_bs(msg),pretty_print=True)
+                    msg = msg.replace('&#13;','')
+                    #msg = re.sub('<html>(.*)</html>','\\1',msg,flags=re.M|re.DOTALL)	# python 2.7
+                    msg = re.sub('(?ms)<html>(.*)</html>','\\1',msg)
+
+                messages[0] = msg
+            else:
+                correct[0] = 'correct' if ret else 'incorrect'
+
+        # build map giving "correct"ness of the answer(s)
+        #correct_map = dict(zip(idset, self.context['correct']))
+        correct_map = {}
+        for k in range(len(idset)):
+            correct_map[idset[k]] = correct[k]
+            correct_map['msg_%s' % idset[k]] = messages[k]
+        return  correct_map
 
     def get_answers(self):
-        # Since this is explicitly specified in the problem, this will 
-        # be handled by capa_problem
+        '''
+        Give correct answer expected for this response.
+
+        capa_problem handles correct_answers from entry objects like textline, and that
+        is what should be used when this response has multiple entry objects.
+
+        but for simplicity, if an "expect" attribute was given by the content author
+        ie <customresponse expect="foo" ...> then return it now.
+        '''
+        if len(self.answer_ids)>1:
+            return {}
+        if self.expect:
+            return {self.answer_ids[0] : self.expect}
         return {}
 
 #-----------------------------------------------------------------------------
 
 class ExternalResponse(GenericResponse):
-    '''
+    """
     Grade the student's input using an external server.
     
     Typically used by coding problems.
-    '''
+    """
     def __init__(self, xml, context):
         self.xml = xml
         self.answer_ids = xml.xpath('//*[@id=$id]//textbox/@id|//*[@id=$id]//textline/@id',
@@ -471,10 +627,6 @@ class ImageResponse(GenericResponse):
                 raise Exception,'[capamodule.capa.responsetypes.imageinput] error grading %s (input=%s)' % (err,aid,given)
             (gx,gy) = [int(x) for x in m.groups()]
             
-            if settings.DEBUG:
-                print "[capamodule.capa.responsetypes.imageinput] llx,lly,urx,ury=",(llx,lly,urx,ury)
-                print "[capamodule.capa.responsetypes.imageinput] gx,gy=",(gx,gy)
-
             # answer is correct if (x,y) is within the specified rectangle
             if (llx <= gx <= urx) and (lly <= gy <= ury):
                 correct_map[aid] = 'correct'
diff --git a/djangoapps/courseware/content_parser.py b/djangoapps/courseware/content_parser.py
index 9ae937e52e18a0c808d35855ea2f1390309fcd9b..adb10f7dfc061e53155ca820030e691a968261a0 100644
--- a/djangoapps/courseware/content_parser.py
+++ b/djangoapps/courseware/content_parser.py
@@ -24,7 +24,9 @@ try: # This lets us do __name__ == ='__main__'
     from student.models import UserTestGroup
     from mitxmako.shortcuts import render_to_string
     from util.cache import cache
+    from multicourse import multicourse_settings
 except: 
+    print "Could not import/content_parser"
     settings = None 
 
 ''' This file will eventually form an abstraction layer between the
@@ -181,7 +183,7 @@ def course_xml_process(tree):
     propogate_downward_tag(tree, "rerandomize")
     return tree
 
-def course_file(user):
+def course_file(user,coursename=None):
     ''' Given a user, return course.xml'''
 
     if user.is_authenticated():
@@ -189,6 +191,11 @@ def course_file(user):
     else:
         filename = 'guest_course.xml'
 
+    # if a specific course is specified, then use multicourse to get the right path to the course XML directory
+    if coursename and settings.ENABLE_MULTICOURSE:
+        xp = multicourse_settings.get_course_xmlpath(coursename)
+        filename = xp + filename	# prefix the filename with the path
+
     groups = user_groups(user)
     options = {'dev_content':settings.DEV_CONTENT, 
                'groups' : groups}
@@ -210,13 +217,24 @@ def course_file(user):
 
     return tree
 
-def section_file(user, section):
-    ''' Given a user and the name of a section, return that section
+def section_file(user, section, coursename=None, dironly=False):
+    '''
+    Given a user and the name of a section, return that section.
+    This is done specific to each course.
+    If dironly=True then return the sections directory.
     '''
     filename = section+".xml"
 
-    if filename not in os.listdir(settings.DATA_DIR + '/sections/'):
-        print filename+" not in "+str(os.listdir(settings.DATA_DIR + '/sections/'))
+    # if a specific course is specified, then use multicourse to get the right path to the course XML directory
+    xp = ''
+    if coursename and settings.ENABLE_MULTICOURSE: xp = multicourse_settings.get_course_xmlpath(coursename)
+
+    dirname = settings.DATA_DIR + xp + '/sections/'
+
+    if dironly: return dirname
+
+    if filename not in os.listdir(dirname):
+        print filename+" not in "+str(os.listdir(dirname))
         return None
 
     options = {'dev_content':settings.DEV_CONTENT, 
@@ -226,7 +244,7 @@ def section_file(user, section):
     return tree
 
 
-def module_xml(user, module, id_tag, module_id):
+def module_xml(user, module, id_tag, module_id, coursename=None):
     ''' Get XML for a module based on module and module_id. Assumes
         module occurs once in courseware XML file or hidden section. '''
     # Sanitize input
@@ -239,14 +257,15 @@ def module_xml(user, module, id_tag, module_id):
                                                                              id_tag=id_tag,
                                                                              id=module_id)
     #result_set=doc.xpathEval(xpath_search)
-    doc = course_file(user)
-    section_list = (s[:-4] for s in os.listdir(settings.DATA_DIR+'/sections') if s[-4:]=='.xml')
+    doc = course_file(user,coursename)
+    sdirname = section_file(user,'',coursename,True)	# get directory where sections information is stored
+    section_list = (s[:-4] for s in os.listdir(sdirname) if s[-4:]=='.xml')
 
     result_set=doc.xpath(xpath_search)
     if len(result_set)<1:
         for section in section_list:
             try: 
-                s = section_file(user, section)
+                s = section_file(user, section, coursename)
             except etree.XMLSyntaxError: 
                 ex= sys.exc_info()
                 raise ContentException("Malformed XML in " + section+ "("+str(ex[1].msg)+")")
diff --git a/djangoapps/courseware/grades.py b/djangoapps/courseware/grades.py
index 67816dc04e1ba9e49a1929cdfc743fe962a62330..2013dc28b59bc7b4e21475817f9b031c96beae36 100644
--- a/djangoapps/courseware/grades.py
+++ b/djangoapps/courseware/grades.py
@@ -67,7 +67,7 @@ course_settings = Settings()
 
 
 
-def grade_sheet(student):
+def grade_sheet(student,coursename=None):
     """
     This pulls a summary of all problems in the course. It returns a dictionary with two datastructures:
     
@@ -77,7 +77,7 @@ def grade_sheet(student):
     
     - grade_summary is the output from the course grader. More information on the format is in the docstring for CourseGrader.
     """
-    dom=content_parser.course_file(student)
+    dom=content_parser.course_file(student,coursename)
     course = dom.xpath('//course/@name')[0]
     xmlChapters = dom.xpath('//course[@name=$course]/chapter', course=course)
 
@@ -103,7 +103,7 @@ def grade_sheet(student):
             scores=[]
             if len(problems)>0:
                 for p in problems:
-                    (correct,total) = get_score(student, p, response_by_id)
+                    (correct,total) = get_score(student, p, response_by_id, coursename=coursename)
                     
                     if settings.GENERATE_PROFILE_SCORES:
                         if total > 1:
@@ -167,7 +167,7 @@ def aggregate_scores(scores, section_name = "summary"):
     return all_total, graded_total
     
 
-def get_score(user, problem, cache):
+def get_score(user, problem, cache, coursename=None):
     ## HACK: assumes max score is fixed per problem
     id = problem.get('id')
     correct = 0.0
@@ -196,7 +196,7 @@ def get_score(user, problem, cache):
         ## HACK 1: We shouldn't specifically reference capa_module
         ## HACK 2: Backwards-compatibility: This should be written when a grade is saved, and removed from the system
         from module_render import I4xSystem
-        system = I4xSystem(None, None, None)
+        system = I4xSystem(None, None, None, coursename=coursename)
         total=float(courseware.modules.capa_module.Module(system, etree.tostring(problem), "id").max_score())
         response.max_grade = total
         response.save()
diff --git a/djangoapps/courseware/management/commands/check_course.py b/djangoapps/courseware/management/commands/check_course.py
index 2f069ee5f38c1797f83e7ec681162e8d6feba282..4d0b9840ab9f29687bb888d2adc286386fe9857e 100644
--- a/djangoapps/courseware/management/commands/check_course.py
+++ b/djangoapps/courseware/management/commands/check_course.py
@@ -6,9 +6,9 @@ from django.core.management.base import BaseCommand
 from django.conf import settings
 from django.contrib.auth.models import User
 
-from courseware.content_parser import course_file
-import courseware.module_render
-import courseware.modules
+from mitx.courseware.content_parser import course_file
+import mitx.courseware.module_render
+import mitx.courseware.modules
 
 class Command(BaseCommand):
     help = "Does basic validity tests on course.xml."
@@ -25,15 +25,15 @@ class Command(BaseCommand):
                 check = False
         print "Confirming all modules render. Nothing should print during this step. "
         for module in course.xpath('//problem|//html|//video|//vertical|//sequential|/tab'):
-            module_class = courseware.modules.modx_modules[module.tag]
+            module_class=mitx.courseware.modules.modx_modules[module.tag]
             # TODO: Abstract this out in render_module.py
             try: 
-                module_class(etree.tostring(module), 
-                             module.get('id'), 
-                             ajax_url='',
-                             state=None, 
-                             track_function = lambda x,y,z:None, 
-                             render_function = lambda x: {'content':'','destroy_js':'','init_js':'','type':'video'})
+                instance=module_class(etree.tostring(module), 
+                                      module.get('id'), 
+                                      ajax_url='',
+                                      state=None, 
+                                      track_function = lambda x,y,z:None, 
+                                      render_function = lambda x: {'content':'','destroy_js':'','init_js':'','type':'video'})
             except:
                 print "==============> Error in ", etree.tostring(module)
                 check = False
diff --git a/djangoapps/courseware/module_render.py b/djangoapps/courseware/module_render.py
index 9bb7872a66d77524b650a9c1625df10c64a0d546..0e317f70046ec9d351d87950d3a7057dc3577637 100644
--- a/djangoapps/courseware/module_render.py
+++ b/djangoapps/courseware/module_render.py
@@ -22,6 +22,11 @@ import courseware.modules
 log = logging.getLogger("mitx.courseware")
 
 class I4xSystem(object):
+    '''
+    This is an abstraction such that x_modules can function independent 
+    of the courseware (e.g. import into other types of courseware, LMS, 
+    or if we want to have a sandbox server for user-contributed content)
+    '''
     def __init__(self, ajax_url, track_function, render_function, filestore=None):
         self.ajax_url = ajax_url
         self.track_function = track_function
@@ -29,6 +34,10 @@ class I4xSystem(object):
             self.filestore = OSFS(settings.DATA_DIR)
         self.render_function = render_function
         self.exception404 = Http404
+    def __repr__(self):
+        return repr(self.__dict__)
+    def __str__(self):
+        return str(self.__dict__)
 
 def object_cache(cache, user, module_type, module_id):
     # We don't look up on user -- all queries include user
@@ -50,6 +59,7 @@ def make_track_function(request):
     def f(event_type, event):
         return track.views.server_track(request, event_type, event, page='x_module')
     return f
+
 def grade_histogram(module_id):
     ''' Print out a histogram of grades on a given problem. 
         Part of staff member debug info. 
@@ -83,6 +93,10 @@ def render_x_module(user, request, xml_module, module_object_preload):
     else:
         state = smod.state
 
+    # get coursename if stored
+    if 'coursename' in request.session: coursename = request.session['coursename']
+    else: coursename = None
+
     # Create a new instance
     ajax_url = settings.MITX_ROOT_URL + '/modx/'+module_type+'/'+module_id+'/'
     system = I4xSystem(track_function = make_track_function(request), 
@@ -104,6 +118,7 @@ def render_x_module(user, request, xml_module, module_object_preload):
                            state=instance.get_state())
         smod.save()
         module_object_preload.append(smod)
+
     # Grab content
     content = instance.get_html()
     init_js = instance.get_init_js()
diff --git a/djangoapps/courseware/modules/capa_module.py b/djangoapps/courseware/modules/capa_module.py
index 7a43051026d728636a69dd83f094c6de548945de..0b75faa49176707fbddf0b88c2cb202581648024 100644
--- a/djangoapps/courseware/modules/capa_module.py
+++ b/djangoapps/courseware/modules/capa_module.py
@@ -21,6 +21,7 @@ from mitxmako.shortcuts import render_to_string
 from x_module import XModule
 from courseware.capa.capa_problem import LoncapaProblem, StudentInputError
 import courseware.content_parser as content_parser
+from multicourse import multicourse_settings
 
 log = logging.getLogger("mitx.courseware")
 
@@ -115,18 +116,19 @@ class Module(XModule):
         if len(explain) == 0:
             explain = False
 
-        html=render_to_string('problem.html', 
-                              {'problem' : content, 
-                               'id' : self.item_id, 
-                               'check_button' : check_button,
-                               'reset_button' : reset_button,
-                               'save_button' : save_button,
-                               'answer_available' : self.answer_available(),
-                               'ajax_url' : self.ajax_url,
-                               'attempts_used': self.attempts, 
-                               'attempts_allowed': self.max_attempts, 
-                               'explain': explain
-                               })
+        context = {'problem' : content, 
+                   'id' : self.item_id, 
+                   'check_button' : check_button,
+                   'reset_button' : reset_button,
+                   'save_button' : save_button,
+                   'answer_available' : self.answer_available(),
+                   'ajax_url' : self.ajax_url,
+                   'attempts_used': self.attempts, 
+                   'attempts_allowed': self.max_attempts, 
+                   'explain': explain,
+                   }
+
+        html=render_to_string('problem.html', context)
         if encapsulate:
             html = '<div id="main_{id}">'.format(id=self.item_id)+html+"</div>"
             
@@ -193,7 +195,12 @@ class Module(XModule):
             seed = 1
         else:
             seed = None
-        self.lcp=LoncapaProblem(self.filestore.open(self.filename), self.item_id, state, seed = seed)
+        try:
+            fp = self.filestore.open(self.filename)
+        except Exception,err:
+            print '[courseware.capa.capa_module.Module.init] error %s: cannot open file %s' % (err,self.filename)
+            raise Exception,err
+        self.lcp=LoncapaProblem(fp, self.item_id, state, seed = seed)
 
     def handle_ajax(self, dispatch, get):
         '''
@@ -306,7 +313,7 @@ class Module(XModule):
         except: 
             self.lcp = LoncapaProblem(self.filestore.open(self.filename), id=lcp_id, state=old_state)
             traceback.print_exc()
-            raise
+            raise Exception,"error in capa_module"
             return json.dumps({'success':'Unknown Error'})
             
         self.attempts = self.attempts + 1
diff --git a/djangoapps/courseware/test_files/optionresponse.xml b/djangoapps/courseware/test_files/optionresponse.xml
new file mode 100644
index 0000000000000000000000000000000000000000..99a17e8fac28963d27b166554350f7cd831b8bf6
--- /dev/null
+++ b/djangoapps/courseware/test_files/optionresponse.xml
@@ -0,0 +1,63 @@
+  <problem>
+    <text>
+      <p>
+Why do bicycles benefit from having larger wheels when going up a bump as shown in the picture? <br/>
+Assume that for both bicycles:<br/>
+1.)	The tires have equal air pressure.<br/>
+2.)	The bicycles never leave the contact with the bump.<br/>
+3.)	The bicycles have the same mass. The bicycle tires (regardless of size) have the same mass.<br/>
+</p>
+    </text>
+    <optionresponse texlayout="horizontal" max="10" randomize="yes">
+      <ul>
+        <li>
+          <text>
+            <p>The bicycles with larger wheels have more time to go over the bump. This decreases the magnitude of the force needed to lift the bicycle.</p>
+          </text>
+          <optioninput name="Foil1" location="random" options="('True','False')" correct="True">
+</optioninput>
+        </li>
+        <li>
+          <text>
+            <p>The bicycles with larger wheels always have a smaller vertical displacement regardless of speed.</p>
+          </text>
+          <optioninput name="Foil2" location="random" options="('True','False')" correct="False">
+</optioninput>
+        </li>
+        <li>
+          <text>
+            <p>The bicycles with larger wheels experience a force backward with less magnitude for the same amount of time.</p>
+          </text>
+          <optioninput name="Foil3" location="random" options="('True','False')" correct="False">
+</optioninput>
+        </li>
+        <li>
+          <text>
+            <p>The bicycles with larger wheels experience a force backward with less magnitude for a greater amount of time.</p>
+          </text>
+          <optioninput name="Foil4" location="random" options="('True','False')" correct="True">
+</optioninput>
+        </li>
+        <li>
+          <text>
+            <p>The bicycles with larger wheels have more kinetic energy turned into gravitational potential energy.</p>
+          </text>
+          <optioninput name="Foil5" location="random" options="('True','False')" correct="False">
+</optioninput>
+        </li>
+        <li>
+          <text>
+            <p>The bicycles with larger wheels have more rotational kinetic energy, so the horizontal velocity of the biker changes less.</p>
+          </text>
+          <optioninput name="Foil6" location="random" options="('True','False')" correct="False">
+</optioninput>
+        </li>
+      </ul>
+      <hintgroup showoncorrect="no">
+        <text>
+          <br/>
+          <br/>
+        </text>
+      </hintgroup>
+    </optionresponse>
+  </problem>
diff --git a/djangoapps/courseware/tests.py b/djangoapps/courseware/tests.py
index 5688d698b2b492ce20ebd807deb48abce2581157..682927efb71c6efe14bd3a907451db63b0bcc871 100644
--- a/djangoapps/courseware/tests.py
+++ b/djangoapps/courseware/tests.py
@@ -63,6 +63,9 @@ class ModelsTest(unittest.TestCase):
             exception_happened = True
         self.assertTrue(exception_happened)
 
+#-----------------------------------------------------------------------------
+# tests of capa_problem inputtypes
+
 class MultiChoiceTest(unittest.TestCase):
     def test_MC_grade(self):
         multichoice_file = os.path.dirname(__file__)+"/test_files/multichoice.xml"
@@ -93,6 +96,38 @@ class MultiChoiceTest(unittest.TestCase):
         self.assertEquals(test_lcp.grade_answers(false_answers)['1_2_1'], 'incorrect')
         false_answers = {'1_2_1':['choice_foil1', 'choice_foil2', 'choice_foil3']}
         self.assertEquals(test_lcp.grade_answers(false_answers)['1_2_1'], 'incorrect')
+        
+class ImageResponseTest(unittest.TestCase):
+    def test_ir_grade(self):
+        imageresponse_file = os.path.dirname(__file__)+"/test_files/imageresponse.xml"
+        test_lcp = lcp.LoncapaProblem(open(imageresponse_file), '1')
+        correct_answers = {'1_2_1':'(490,11)-(556,98)',
+                           '1_2_2':'(242,202)-(296,276)'}
+        test_answers = {'1_2_1':'[500,20]',
+                        '1_2_2':'[250,300]',
+                        }
+        self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_1'], 'correct')
+        self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_2'], 'incorrect')
+        
+class OptionResponseTest(unittest.TestCase):
+    '''
+    Run this with
+
+    python manage.py test courseware.OptionResponseTest
+    '''
+    def test_or_grade(self):
+        optionresponse_file = os.path.dirname(__file__)+"/test_files/optionresponse.xml"
+        test_lcp = lcp.LoncapaProblem(open(optionresponse_file), '1')
+        correct_answers = {'1_2_1':'True',
+                           '1_2_2':'False'}
+        test_answers = {'1_2_1':'True',
+                        '1_2_2':'True',
+                        }
+        self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_1'], 'correct')
+        self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_2'], 'incorrect')
+
+#-----------------------------------------------------------------------------
+# Grading tests
 
 class GradesheetTest(unittest.TestCase):
 
@@ -118,7 +153,7 @@ class GradesheetTest(unittest.TestCase):
         all, graded = aggregate_scores(scores)
         self.assertAlmostEqual(all, Score(earned=5, possible=15, graded=False, section="summary"))
         self.assertAlmostEqual(graded, Score(earned=5, possible=10, graded=True, section="summary"))
-        
+
 class GraderTest(unittest.TestCase):
 
     empty_gradesheet = {
diff --git a/djangoapps/courseware/views.py b/djangoapps/courseware/views.py
index 1b4dd32ad272c8fae3b768ed9413bd02026ef1ca..5f67e599c09258fc2d086de091138763d716cef0 100644
--- a/djangoapps/courseware/views.py
+++ b/djangoapps/courseware/views.py
@@ -16,6 +16,7 @@ from lxml import etree
 from module_render import render_module, make_track_function, I4xSystem
 from models import StudentModule
 from student.models import UserProfile
+from multicourse import multicourse_settings
 
 import courseware.content_parser as content_parser
 import courseware.modules
@@ -33,11 +34,16 @@ template_imports={'urllib':urllib}
 def gradebook(request):
     if 'course_admin' not in content_parser.user_groups(request.user):
         raise Http404
+
+    # TODO: This should be abstracted out. We repeat this logic many times. 
+    if 'coursename' in request.session: coursename = request.session['coursename'] 
+    else: coursename = None
+
     student_objects = User.objects.all()[:100]
     student_info = [{'username' :s.username,
                      'id' : s.id,
                      'email': s.email,
-                     'grade_info' : grades.grade_sheet(s), 
+                     'grade_info' : grades.grade_sheet(s,coursename), 
                      'realname' : UserProfile.objects.get(user = s).name
                      } for s in student_objects]
 
@@ -59,6 +65,9 @@ def profile(request, student_id = None):
 
     user_info = UserProfile.objects.get(user=student) # request.user.profile_cache # 
 
+    if 'coursename' in request.session: coursename = request.session['coursename']
+    else: coursename = None
+
     context={'name':user_info.name,
              'username':student.username,
              'location':user_info.location,
@@ -67,7 +76,7 @@ def profile(request, student_id = None):
              'format_url_params' : content_parser.format_url_params,
              'csrf':csrf(request)['csrf_token']
              }
-    context.update(grades.grade_sheet(student))
+    context.update(grades.grade_sheet(student,coursename))
 
     return render_to_response('profile.html', context)
 
@@ -77,7 +86,7 @@ def render_accordion(request,course,chapter,section):
     if not course:
         course = "6.002 Spring 2012"
 
-    toc=content_parser.toc_from_xml(content_parser.course_file(request.user), chapter, section)
+    toc=content_parser.toc_from_xml(content_parser.course_file(request.user,course), chapter, section)
     active_chapter=1
     for i in range(len(toc)):
         if toc[i]['active']:
@@ -98,8 +107,11 @@ def render_section(request, section):
     if not settings.COURSEWARE_ENABLED:
         return redirect('/')
 
+    if 'coursename' in request.session: coursename = request.session['coursename']
+    else: coursename = None
+
 #    try: 
-    dom = content_parser.section_file(user, section)
+    dom = content_parser.section_file(user, section, coursename)
     #except:
      #   raise Http404
 
@@ -128,13 +140,21 @@ def render_section(request, section):
 
 
 @cache_control(no_cache=True, no_store=True, must_revalidate=True)
-def index(request, course="6.002 Spring 2012", chapter="Using the System", section="Hints"): 
+def index(request, course=None, chapter="Using the System", section="Hints"): 
     ''' Displays courseware accordion, and any associated content. 
     ''' 
     user = request.user
     if not settings.COURSEWARE_ENABLED:
         return redirect('/')
 
+    if course==None:
+        if not settings.ENABLE_MULTICOURSE:
+            course = "6.002 Spring 2012"
+        elif 'coursename' in request.session:
+            course = request.session['coursename']
+        else:
+            course = settings.COURSE_DEFAULT
+
     # Fixes URLs -- we don't get funny encoding characters from spaces
     # so they remain readable
     ## TODO: Properly replace underscores
@@ -142,16 +162,18 @@ def index(request, course="6.002 Spring 2012", chapter="Using the System", secti
     chapter=chapter.replace("_"," ")
     section=section.replace("_"," ")
 
-    # HACK: Force course to 6.002 for now
-    # Without this, URLs break
-    if course!="6.002 Spring 2012":
+    # use multicourse module to determine if "course" is valid
+    #if course!=settings.COURSE_NAME.replace('_',' '):
+    if not multicourse_settings.is_valid_course(course):
         return redirect('/')
 
     #import logging
     #log = logging.getLogger("mitx")
     #log.info(  "DEBUG: "+str(user) )
 
-    dom = content_parser.course_file(user)
+    request.session['coursename'] = course		# keep track of current course being viewed in django's request.session
+
+    dom = content_parser.course_file(user,course)	# also pass course to it, for course-specific XML path
     dom_module = dom.xpath("//course[@name=$course]/chapter[@name=$chapter]//section[@name=$section]/*[1]", 
                            course=course, chapter=chapter, section=section)
     if len(dom_module) == 0:
@@ -179,6 +201,7 @@ def index(request, course="6.002 Spring 2012", chapter="Using the System", secti
     context={'init':module['init_js'],
              'accordion':accordion,
              'content':module['content'],
+             'COURSE_TITLE':multicourse_settings.get_course_title(course),
              'csrf':csrf(request)['csrf_token']}
 
     result = render_to_response('courseware.html', context)
@@ -206,8 +229,12 @@ def modx_dispatch(request, module=None, dispatch=None, id=None):
 
     ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/'
 
+    # get coursename if stored
+    if 'coursename' in request.session: coursename = request.session['coursename']
+    else: coursename = None
+
     # Grab the XML corresponding to the request from course.xml
-    xml = content_parser.module_xml(request.user, module, 'id', id)
+    xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
 
     # Create the module
     system = I4xSystem(track_function = make_track_function(request), 
@@ -229,3 +256,98 @@ def modx_dispatch(request, module=None, dispatch=None, id=None):
         s.save()
     # Return whatever the module wanted to return to the client/caller
     return HttpResponse(ajax_return)
+
+def quickedit(request, id=None):
+    '''
+    quick-edit capa problem.
+
+    Maybe this should be moved into capa/views.py
+    Or this should take a "module" argument, and the quickedit moved into capa_module.
+    '''
+    print "WARNING: UNDEPLOYABLE CODE. FOR DEV USE ONLY."
+    print "In deployed use, this will only edit on one server"
+    print "We need a setting to disable for production where there is"
+    print "a load balanacer"
+    if not request.user.is_staff():
+        return redirect('/')
+
+    # get coursename if stored
+    if 'coursename' in request.session: coursename = request.session['coursename']
+    else: coursename = None
+
+    def get_lcp(coursename,id):
+        # Grab the XML corresponding to the request from course.xml
+        module = 'problem'
+        xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
+    
+        ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/'
+    
+        # Create the module (instance of capa_module.Module)
+        system = I4xSystem(track_function = make_track_function(request), 
+                           render_function = None, 
+                           ajax_url = ajax_url,
+                           filestore = None,
+                           coursename = coursename,
+                           role = 'staff' if request.user.is_staff else 'student',		# TODO: generalize this
+                           )
+        instance=courseware.modules.get_module_class(module)(system, 
+                                                             xml, 
+                                                             id,
+                                                             state=None)
+        lcp = instance.lcp
+        pxml = lcp.tree
+        pxmls = etree.tostring(pxml,pretty_print=True)
+
+        return instance, pxmls
+
+    instance, pxmls = get_lcp(coursename,id)
+
+    # if there was a POST, then process it
+    msg = ''
+    if 'qesubmit' in request.POST:
+        action = request.POST['qesubmit']
+        if "Revert" in action:
+            msg = "Reverted to original"
+        elif action=='Change Problem':
+            key = 'quickedit_%s' % id
+            if not key in request.POST:
+                msg = "oops, missing code key=%s" % key
+            else:
+                newcode = request.POST[key]
+
+                # see if code changed
+                if str(newcode)==str(pxmls) or '<?xml version="1.0"?>\n'+str(newcode)==str(pxmls):
+                    msg = "No changes"
+                else:
+                    # check new code
+                    isok = False
+                    try:
+                        newxml = etree.fromstring(newcode)
+                        isok = True
+                    except Exception,err:
+                        msg = "Failed to change problem: XML error \"<font color=red>%s</font>\"" % err
+    
+                    if isok:
+                        filename = instance.lcp.fileobject.name
+                        fp = open(filename,'w')		# TODO - replace with filestore call?
+                        fp.write(newcode)
+                        fp.close()
+                        msg = "<font color=green>Problem changed!</font> (<tt>%s</tt>)"  % filename
+                        instance, pxmls = get_lcp(coursename,id)
+
+    lcp = instance.lcp
+
+    # get the rendered problem HTML
+    phtml = instance.get_problem_html()
+
+    context = {'id':id,
+               'msg' : msg,
+               'lcp' : lcp,
+               'filename' : lcp.fileobject.name,
+               'pxmls' : pxmls,
+               'phtml' : phtml,
+               'init_js':instance.get_init_js(),
+               }
+
+    result = render_to_response('quickedit.html', context)
+    return result
diff --git a/djangoapps/multicourse/__init__.py b/djangoapps/multicourse/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/djangoapps/multicourse/multicourse_settings.py b/djangoapps/multicourse/multicourse_settings.py
new file mode 100644
index 0000000000000000000000000000000000000000..99c9ef86208e026efa10f9bbff7eafc9c33836a1
--- /dev/null
+++ b/djangoapps/multicourse/multicourse_settings.py
@@ -0,0 +1,73 @@
+# multicourse/multicourse_settings.py
+#
+# central module for providing fixed settings (course name, number, title)
+# for multiple courses.  Loads this information from django.conf.settings
+#
+# Allows backward compatibility with settings configurations without
+# multiple courses specified.
+#
+# The central piece of configuration data is the dict COURSE_SETTINGS, with
+# keys being the COURSE_NAME (spaces ok), and the value being a dict of
+# parameter,value pairs. The required parameters are:
+#
+# - number  : course number (used in the simplewiki pages)
+# - title   : humanized descriptive course title
+#
+# Optional parameters:
+#
+# - xmlpath : path (relative to data directory) for this course (defaults to "")
+#
+# If COURSE_SETTINGS does not exist, then fallback to 6.002_Spring_2012 default,
+# for now.
+
+from django.conf import settings
+
+#-----------------------------------------------------------------------------
+# load course settings
+
+if hasattr(settings,'COURSE_SETTINGS'):    	# in the future, this could be replaced by reading an XML file
+    COURSE_SETTINGS = settings.COURSE_SETTINGS
+
+elif hasattr(settings,'COURSE_NAME'):		# backward compatibility
+    COURSE_SETTINGS = {settings.COURSE_NAME: {'number': settings.COURSE_NUMBER,
+                                              'title':  settings.COURSE_TITLE,
+                                              },
+                       }
+else:						# default to 6.002_Spring_2012
+    COURSE_SETTINGS = {'6.002_Spring_2012': {'number': '6.002x',
+                                              'title':  'Circuits and Electronics',
+                                              },
+                       }
+
+#-----------------------------------------------------------------------------
+# wrapper functions around course settings
+
+def get_course_settings(coursename):
+    if not coursename:
+        if hasattr(settings,'COURSE_DEFAULT'):
+            coursename = settings.COURSE_DEFAULT
+        else:
+            coursename = '6.002_Spring_2012'
+    if coursename in COURSE_SETTINGS: return COURSE_SETTINGS[coursename]
+    coursename = coursename.replace(' ','_')
+    if coursename in COURSE_SETTINGS: return COURSE_SETTINGS[coursename]
+    return None
+
+def is_valid_course(coursename):
+    return not (get_course_settings==None)
+
+def get_course_property(coursename,property):
+    cs = get_course_settings(coursename)
+    if not cs: return ''	# raise exception instead?
+    if property in cs: return cs[property]
+    return ''	# default
+
+def get_course_xmlpath(coursename):
+    return get_course_property(coursename,'xmlpath')
+
+def get_course_title(coursename):
+    return get_course_property(coursename,'title')
+
+def get_course_number(coursename):
+    return get_course_property(coursename,'number')
+    
diff --git a/djangoapps/multicourse/views.py b/djangoapps/multicourse/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..d0662b710ec2692126b2ac12777ea4e26f63677a
--- /dev/null
+++ b/djangoapps/multicourse/views.py
@@ -0,0 +1 @@
+# multicourse/views.py
diff --git a/djangoapps/simplewiki/views.py b/djangoapps/simplewiki/views.py
index fcd98bfeb17b235bd0e749645ab794723cab8821..34a81e6b570201797865fd43ab9aeb2b9fc42ef0 100644
--- a/djangoapps/simplewiki/views.py
+++ b/djangoapps/simplewiki/views.py
@@ -9,6 +9,8 @@ from django.utils import simplejson
 from django.utils.translation import ugettext_lazy as _
 from mitxmako.shortcuts import render_to_response
 
+from multicourse import multicourse_settings
+
 from models import Revision, Article, CreateArticleForm, RevisionFormWithTitle, RevisionForm
 import wiki_settings
 
@@ -17,6 +19,11 @@ def view(request, wiki_url):
     if err:
         return err
     
+    if 'coursename' in request.session: coursename = request.session['coursename']
+    else: coursename = None
+
+    course_number = multicourse_settings.get_course_number(coursename)
+
     perm_err = check_permissions(request, article, check_read=True, check_deleted=True)
     if perm_err:
         return perm_err
@@ -25,7 +32,7 @@ def view(request, wiki_url):
 			'wiki_write': article.can_write_l(request.user),
 			'wiki_attachments_write': article.can_attach(request.user),
             'wiki_current_revision_deleted' : not (article.current_revision.deleted == 0),
-            'wiki_title' : article.title + " - MITX 6.002x Wiki"
+            'wiki_title' : article.title + " - MITX %s Wiki" % course_number
 			}
     d.update(csrf(request))
     return render_to_response('simplewiki_view.html', d)
diff --git a/djangoapps/ssl_auth/__init__.py b/djangoapps/ssl_auth/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/djangoapps/ssl_auth/ssl_auth.py b/djangoapps/ssl_auth/ssl_auth.py
new file mode 100755
index 0000000000000000000000000000000000000000..6d0bb2c5b44146b610926a5b501e5a495124f85e
--- /dev/null
+++ b/djangoapps/ssl_auth/ssl_auth.py
@@ -0,0 +1,281 @@
+"""
+User authentication backend for ssl (no pw required)
+"""
+
+from django.conf import settings
+from django.contrib import auth
+from django.contrib.auth.models import User, check_password
+from django.contrib.auth.backends import ModelBackend
+from django.contrib.auth.middleware import RemoteUserMiddleware
+from django.core.exceptions import ImproperlyConfigured
+import os, string, re
+from random import choice
+
+from student.models import UserProfile
+
+#-----------------------------------------------------------------------------
+
+def ssl_dn_extract_info(dn):
+    '''
+    Extract username, email address (may be anyuser@anydomain.com) and full name
+    from the SSL DN string.  Return (user,email,fullname) if successful, and None
+    otherwise.
+    '''
+    ss = re.search('/emailAddress=(.*)@([^/]+)',dn)
+    if ss:
+        user = ss.group(1)
+        email = "%s@%s" % (user,ss.group(2))
+    else:
+        return None
+    ss = re.search('/CN=([^/]+)/',dn)
+    if ss:
+        fullname = ss.group(1)
+    else:
+        return None
+    return (user,email,fullname)
+
+def check_nginx_proxy(request):
+    '''
+    Check for keys in the HTTP header (META) to se if we are behind an ngix reverse proxy.
+    If so, get user info from the SSL DN string and return that, as (user,email,fullname)
+    '''
+    m = request.META
+    if m.has_key('HTTP_X_REAL_IP'):	# we're behind a nginx reverse proxy, which has already done ssl auth
+        if not m.has_key('HTTP_SSL_CLIENT_S_DN'):
+            return None
+        dn = m['HTTP_SSL_CLIENT_S_DN']
+        return ssl_dn_extract_info(dn)
+    return None
+
+#-----------------------------------------------------------------------------
+
+def get_ssl_username(request):
+    x = check_nginx_proxy(request)
+    if x:
+        return x[0]
+    env = request._req.subprocess_env
+    if env.has_key('SSL_CLIENT_S_DN_Email'):
+        email = env['SSL_CLIENT_S_DN_Email']
+        user = email[:email.index('@')]
+        return user
+    return None
+
+#-----------------------------------------------------------------------------
+
+class NginxProxyHeaderMiddleware(RemoteUserMiddleware):
+    '''
+    Django "middleware" function for extracting user information from HTTP request.
+    
+    '''
+    # this field is generated by nginx's reverse proxy
+    header = 'HTTP_SSL_CLIENT_S_DN'	# specify the request.META field to use
+        
+    def process_request(self, request):
+        # AuthenticationMiddleware is required so that request.user exists.
+        if not hasattr(request, 'user'):
+            raise ImproperlyConfigured(
+                "The Django remote user auth middleware requires the"
+                " authentication middleware to be installed.  Edit your"
+                " MIDDLEWARE_CLASSES setting to insert"
+                " 'django.contrib.auth.middleware.AuthenticationMiddleware'"
+                " before the RemoteUserMiddleware class.")
+
+        #raise ImproperlyConfigured('[ProxyHeaderMiddleware] request.META=%s' % repr(request.META))
+
+        try:
+            username = request.META[self.header]	# try the nginx META key first
+        except KeyError:
+            try:
+                env = request._req.subprocess_env	# else try the direct apache2 SSL key
+                if env.has_key('SSL_CLIENT_S_DN'):
+                    username = env['SSL_CLIENT_S_DN']
+                else:
+                    raise ImproperlyConfigured('no ssl key, env=%s' % repr(env))
+                    username = ''
+            except:
+                # If specified header doesn't exist then return (leaving
+                # request.user set to AnonymousUser by the
+                # AuthenticationMiddleware).
+                return
+        # If the user is already authenticated and that user is the user we are
+        # getting passed in the headers, then the correct user is already
+        # persisted in the session and we don't need to continue.
+
+        #raise ImproperlyConfigured('[ProxyHeaderMiddleware] username=%s' % username)
+
+        if request.user.is_authenticated():
+            if request.user.username == self.clean_username(username, request):
+                #raise ImproperlyConfigured('%s already authenticated (%s)' % (username,request.user.username))
+                return
+        # We are seeing this user for the first time in this session, attempt
+        # to authenticate the user.
+        #raise ImproperlyConfigured('calling auth.authenticate, remote_user=%s' % username)
+        user = auth.authenticate(remote_user=username)
+        if user:
+            # User is valid.  Set request.user and persist user in the session
+            # by logging the user in.
+            request.user = user
+            if settings.DEBUG: print "[ssl_auth.ssl_auth.NginxProxyHeaderMiddleware] logging in user=%s" % user
+            auth.login(request, user)
+            
+    def clean_username(self,username,request):
+        '''
+        username is the SSL DN string - extract the actual username from it and return
+        '''
+        info = ssl_dn_extract_info(username)
+        if not info:
+            return None
+        (username,email,fullname) = info
+        return username
+
+#-----------------------------------------------------------------------------
+
+class SSLLoginBackend(ModelBackend):
+    '''
+    Django authentication back-end which auto-logs-in a user based on having
+    already authenticated with an MIT certificate (SSL).
+    '''
+    def authenticate(self, username=None, password=None, remote_user=None):
+
+        # remote_user is from the SSL_DN string.  It will be non-empty only when
+        # the user has already passed the server authentication, which means
+        # matching with the certificate authority.
+        if not remote_user:	
+            # no remote_user, so check username (but don't auto-create user)
+            if not username:
+                return None
+            return None # pass on to another authenticator backend
+            #raise ImproperlyConfigured("in SSLLoginBackend, username=%s, remote_user=%s" % (username,remote_user))
+            try:
+                user = User.objects.get(username=username)	# if user already exists don't create it
+                return user
+            except User.DoesNotExist:
+                return None
+            return None
+
+        #raise ImproperlyConfigured("in SSLLoginBackend, username=%s, remote_user=%s" % (username,remote_user))
+        #if not os.environ.has_key('HTTPS'):
+        #    return None
+        #if not os.environ.get('HTTPS')=='on':	# only use this back-end if HTTPS on
+        #    return None
+
+        def GenPasswd(length=8, chars=string.letters + string.digits):
+            return ''.join([choice(chars) for i in range(length)])
+
+        # convert remote_user to user, email, fullname
+        info = ssl_dn_extract_info(remote_user)
+        #raise ImproperlyConfigured("[SSLLoginBackend] looking up %s" % repr(info))
+        if not info:
+            #raise ImproperlyConfigured("[SSLLoginBackend] remote_user=%s, info=%s" % (remote_user,info))
+            return None
+        (username,email,fullname) = info
+
+	try:
+	    user = User.objects.get(username=username)	# if user already exists don't create it
+	except User.DoesNotExist:
+			raise "User does not exist. Not creating user; potential schema consistency issues"
+            #raise ImproperlyConfigured("[SSLLoginBackend] creating %s" % repr(info))
+            user = User(username=username, password=GenPasswd())	# create new User
+            user.is_staff = False
+            user.is_superuser = False
+            # get first, last name from fullname
+            name = fullname
+            if not name.count(' '):
+                user.first_name = " "
+                user.last_name = name
+                mn = ''
+            else:
+                user.first_name = name[:name.find(' ')]
+                ml = name[name.find(' '):].strip()
+                if ml.count(' '):
+                    user.last_name = ml[ml.rfind(' '):]
+                    mn = ml[:ml.rfind(' ')]
+                else:
+                    user.last_name = ml
+                    mn = ''
+            # set email
+            user.email = email
+            # cleanup last name
+            user.last_name = user.last_name.strip()
+            # save
+            user.save()
+            
+            # auto-create user profile
+            up = UserProfile(user=user)
+            up.name = fullname
+            up.save()
+
+            #tui = user.get_profile()
+            #tui.middle_name = mn
+            #tui.role = 'Misc'
+            #tui.section = None	# no section assigned at first
+            #tui.save()
+            # return None
+	return user
+
+    def get_user(self, user_id):
+        #if not os.environ.has_key('HTTPS'):
+        #    return None
+        #if not os.environ.get('HTTPS')=='on':	# only use this back-end if HTTPS on
+        #    return None
+        try:
+            return User.objects.get(pk=user_id)
+        except User.DoesNotExist:
+            return None
+        
+#-----------------------------------------------------------------------------
+# OLD!
+
+class AutoLoginBackend:
+    def authenticate(self, username=None, password=None):
+        raise ImproperlyConfigured("in AutoLoginBackend, username=%s" % username)
+        if not os.environ.has_key('HTTPS'):
+            return None
+        if not os.environ.get('HTTPS')=='on':# only use this back-end if HTTPS on
+            return None
+
+        def GenPasswd(length=8, chars=string.letters + string.digits):
+            return ''.join([choice(chars) for i in range(length)])
+
+        try:
+                user = User.objects.get(username=username)
+        except User.DoesNotExist:
+            user = User(username=username, password=GenPasswd())
+            user.is_staff = False
+            user.is_superuser = False
+            # get first, last name 
+            name = os.environ.get('SSL_CLIENT_S_DN_CN').strip()
+            if not name.count(' '):
+                user.first_name = " "
+                user.last_name = name
+                mn = ''
+            else:
+                user.first_name = name[:name.find(' ')]
+                ml = name[name.find(' '):].strip()
+                if ml.count(' '):
+                    user.last_name = ml[ml.rfind(' '):]
+                    mn = ml[:ml.rfind(' ')]
+                else:
+                    user.last_name = ml
+                    mn = ''
+            # get email
+            user.email = os.environ.get('SSL_CLIENT_S_DN_Email')
+            # save
+            user.save()
+            tui = user.get_profile()
+            tui.middle_name = mn
+            tui.role = 'Misc'
+            tui.section = None# no section assigned at first
+            tui.save()
+            # return None
+            return user
+
+    def get_user(self, user_id):
+        if not os.environ.has_key('HTTPS'):
+            return None
+        if not os.environ.get('HTTPS')=='on':# only use this back-end if HTTPS on
+            return None
+        try:
+            return User.objects.get(pk=user_id)
+        except User.DoesNotExist:
+            return None
diff --git a/lib/loncapa/__init__.py b/lib/loncapa/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b734967d0a4d33ccecc21c515e03a152aa991323
--- /dev/null
+++ b/lib/loncapa/__init__.py
@@ -0,0 +1,3 @@
+#!/usr/bin/python
+
+from loncapa_check import *
diff --git a/lib/loncapa/loncapa_check.py b/lib/loncapa/loncapa_check.py
new file mode 100644
index 0000000000000000000000000000000000000000..259c7909ace5be00398775d6f6d48d1eb4f4bb4c
--- /dev/null
+++ b/lib/loncapa/loncapa_check.py
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+#
+# File:  mitx/lib/loncapa/loncapa_check.py
+#
+# Python functions which duplicate the standard comparison functions available to LON-CAPA problems.
+# Used in translating LON-CAPA problems to i4x problem specification language.
+
+import random
+
+def lc_random(lower,upper,stepsize):
+    '''
+    like random.randrange but lower and upper can be non-integer
+    '''
+    nstep = int((upper-lower)/(1.0*stepsize))
+    choices = [lower+x*stepsize for x in range(nstep)]
+    return random.choice(choices)
+
diff --git a/lib/mitxmako/shortcuts.py b/lib/mitxmako/shortcuts.py
index 432acbcba92cedc977a7dd33e13eefbf046c0448..7286a4e259d4d46738e509d92b47b89f7a9f541a 100644
--- a/lib/mitxmako/shortcuts.py
+++ b/lib/mitxmako/shortcuts.py
@@ -34,6 +34,9 @@ def render_to_string(template_name, dictionary, context=None, namespace='main'):
         context_dictionary.update(d)
     if context: 
         context_dictionary.update(context)
+    ## HACK
+    ## We should remove this, and possible set COURSE_TITLE in the middleware from the session. 
+    if 'COURSE_TITLE' not in context_dictionary: context_dictionary['COURSE_TITLE'] = ''
     # fetch and render template
     template = middleware.lookup[namespace].get_template(template_name)
     return template.render(**context_dictionary)
diff --git a/lib/util/views.py b/lib/util/views.py
index 6708e7c7a4ed6482d3e679a3dbf73a223bfa9454..d95f1e9a223cbb4f889a03b3235b75c6da5c801c 100644
--- a/lib/util/views.py
+++ b/lib/util/views.py
@@ -2,7 +2,6 @@ import datetime
 import json
 import sys
 
-from django.conf import settings
 from django.conf import settings
 from django.contrib.auth.models import User
 from django.core.context_processors import csrf
@@ -61,3 +60,9 @@ def send_feedback(request):
 def info(request):
     ''' Info page (link from main header) '''
     return render_to_response("info.html", {})
+
+def mitxhome(request):
+    ''' Home page (link from main header). List of courses.  '''
+    if settings.ENABLE_MULTICOURSE:
+        return render_to_response("mitxhome.html", {})
+    return info(request)
diff --git a/settings.py b/settings.py
index 21458b5fefad492491971bc463c82545f3467f9b..2be6be485a6b17b60d21ac00dba9ca506cf1b650 100644
--- a/settings.py
+++ b/settings.py
@@ -8,6 +8,7 @@ import djcelery
 ### Dark code. Should be enabled in local settings for devel. 
 
 ENABLE_MULTICOURSE = False	# set to False to disable multicourse display (see lib.util.views.mitxhome)
+QUICKEDIT = False
 
 ###
 
@@ -20,19 +21,11 @@ COURSE_TITLE = "Circuits and Electronics"
 
 COURSE_DEFAULT = '6.002_Spring_2012'
 
-COURSE_LIST =  {'6.002_Spring_2012': {'number' : '6.002x',
-                                      'title'  :  'Circuits and Electronics',
-                                      'datapath': '6002x/',
-                                       },
-                '8.02_Spring_2013': {'number' : '8.02x',
-                                     'title'  : 'Electricity &amp; Magnetism',
-                                     'datapath': '802x/',
-                                     },
-                '8.01_Spring_2013': {'number' : '8.01x',
-                                     'title'  : 'Mechanics',
-                                     'datapath': '801x/',
-                                     },
-               }
+COURSE_SETTINGS =  {'6.002_Spring_2012': {'number' : '6.002x',
+                                          'title'  :  'Circuits and Electronics',
+                                          'xmlpath': '6002x/',
+                                          }
+                    }
 
 ROOT_URLCONF = 'urls'
 
@@ -150,6 +143,7 @@ MIDDLEWARE_CLASSES = (
     'django.contrib.messages.middleware.MessageMiddleware',
     'track.middleware.TrackMiddleware',
     'mitxmako.middleware.MakoMiddleware',
+    #'ssl_auth.ssl_auth.NginxProxyHeaderMiddleware',		# ssl authentication behind nginx proxy
     #'debug_toolbar.middleware.DebugToolbarMiddleware',
 
     # Uncommenting the following will prevent csrf token from being re-set if you
@@ -179,6 +173,8 @@ INSTALLED_APPS = (
     'util',
     'masquerade',
     'django_jasmine',
+    #'ssl_auth', ## Broken. Disabled for now. 
+    'multicourse',		# multiple courses
     # Uncomment the next line to enable the admin:
     # 'django.contrib.admin',
     # Uncomment the next line to enable admin documentation:
diff --git a/templates/quickedit.html b/templates/quickedit.html
new file mode 100644
index 0000000000000000000000000000000000000000..c08c5e3f51b49d3ece01d643b9afe48acf930d2d
--- /dev/null
+++ b/templates/quickedit.html
@@ -0,0 +1,95 @@
+<html>
+<head>
+  <link rel="stylesheet" href="${ settings.LIB_URL }jquery.treeview.css" type="text/css" media="all" />
+ <link rel="stylesheet" href="/static/css/codemirror.css" type="text/css" media="all" />
+
+  <script type="text/javascript" src="${ settings.LIB_URL }jquery-1.6.2.min.js"></script>
+  <script type="text/javascript" src="${ settings.LIB_URL }jquery-ui-1.8.16.custom.min.js"></script>
+<script type="text/javascript" src="${ settings.LIB_URL }codemirror-compressed.js"></script>
+  <script type="text/javascript" src="/static/js/schematic.js"></script>
+<%include file="mathjax_include.html" />
+
+<script>
+function postJSON(url, data, callback) {
+  $.ajax({type:'POST',
+      url: url,
+      dataType: 'json',
+      data: data,
+      success: callback,
+      headers : {'X-CSRFToken':'none'} // getCookie('csrftoken')}
+      });
+}
+</script>
+
+</head>
+<body>
+
+  <!--[if lt IE 9]>
+  <script src="/static/js/html5shiv.js"></script>
+  <![endif]-->
+
+<style type="text/css">
+  .CodeMirror {border-style: solid;
+              border-width: 1px;}
+.CodeMirror-scroll {
+ height: 500;
+ width: 100%
+}
+</style>
+
+## -----------------------------------------------------------------------------
+## information and i4x PSL code
+
+<hr width="100%">
+<h2>QuickEdit</h2>
+<hr width="100%">
+<ul>
+<li>File = ${filename}</li>
+<li>ID = ${id}</li>
+</ul>
+
+<form method="post">
+  <textarea rows="40" cols="160" name="quickedit_${id}" id="quickedit_${id}">${pxmls|h}</textarea>
+<br/>
+<input type="submit" value="Change Problem" name="qesubmit" />
+<input type="submit" value="Revert to original" name="qesubmit" />
+</form>
+
+<span>${msg|n}</span>
+
+## -----------------------------------------------------------------------------
+## rendered problem display
+
+<script>
+//  height: auto;
+//  overflow-y: hidden;
+//  overflow-x: auto;
+ 
+$(function(){
+  var cm = CodeMirror.fromTextArea(document.getElementById("quickedit_${id}"), 
+			{ 'mode': {name: "xml", alignCDATA: true},
+                        lineNumbers: true
+                        });
+
+// $('.my-wymeditor').wymeditor();
+
+});
+</script>
+
+<hr width="100%">
+
+<script>
+${init_js} 
+</script>
+
+<style type="text/css">
+  .staff {display:none;}
+}
+</style>
+
+<form>
+ ${phtml}
+</form>
+
+</body>
+</html>
diff --git a/urls.py b/urls.py
index 6eda9953d590951a877e4f95f20a01d5a34a5a9f..f286b17760a48ae4a6e56d2493daca7436e61a83 100644
--- a/urls.py
+++ b/urls.py
@@ -69,6 +69,12 @@ if settings.COURSEWARE_ENABLED:
         url(r'^calculate$', 'util.views.calculate'),
     )
 
+if settings.ENABLE_MULTICOURSE:
+	urlpatterns += (url(r'^mitxhome$', 'util.views.mitxhome'),)
+
+if settings.QUICKEDIT:
+	urlpatterns += (url(r'^quickedit/(?P<id>[^/]*)$', 'courseware.views.quickedit'),)
+
 if settings.ASKBOT_ENABLED:
     urlpatterns += (url(r'^%s' % settings.ASKBOT_URL, include('askbot.urls')), \
                     url(r'^admin/', include(admin.site.urls)), \