From cfe553304cb01d55e06e4b9f7de85428b4f61487 Mon Sep 17 00:00:00 2001
From: Bridger Maxwell
Date: Tue, 21 Aug 2012 12:00:05 -0400
Subject: [PATCH 01/32] Removed link to wiki settings until we have a better
integration with notifications.
---
lms/templates/wiki/includes/article_menu.html | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/lms/templates/wiki/includes/article_menu.html b/lms/templates/wiki/includes/article_menu.html
index 5088ae9570..9faab31831 100644
--- a/lms/templates/wiki/includes/article_menu.html
+++ b/lms/templates/wiki/includes/article_menu.html
@@ -35,6 +35,10 @@
%endif
%endfor
+
+<%doc>
+The settings link has been disabled because the notifications app hasn't been integrated yet and those are the only useful settings.
+
%if not user.is_anonymous():
{% trans "You cannot delete this article because you do not have permission to delete articles with children. Try to remove the children manually one-by-one." %}
-
- {% endif %}
-
- {% if delete_children %}
-
-
{% trans "You are deleting an article. This means that its children will be deleted as well. If you choose to purge, children will also be purged!" %}
- {% with "settings" as selected %}
- {% include "wiki/includes/article_menu.html" %}
- {% endwith %}
-
-
-
-
-{% endblock %}
-
From 0b6bf77373fe9aec22c7be46329f625ccad2d031 Mon Sep 17 00:00:00 2001
From: Arjun Singh
Date: Wed, 22 Aug 2012 04:58:30 -0700
Subject: [PATCH 13/32] Adding a course-specific css class to body so that we
can have course-targeted styles as a last resort
---
common/lib/xmodule/xmodule/course_module.py | 4 ++++
lms/templates/courseware/courseware.html | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py
index 5cc4a09165..5eeb5ee11c 100644
--- a/common/lib/xmodule/xmodule/course_module.py
+++ b/common/lib/xmodule/xmodule/course_module.py
@@ -197,6 +197,10 @@ class CourseDescriptor(SequenceDescriptor):
def start_date_text(self):
return time.strftime("%b %d, %Y", self.start)
+ @property
+ def css_class(self):
+ return self.metadata.get('css_class', '')
+
@property
def title(self):
return self.display_name
diff --git a/lms/templates/courseware/courseware.html b/lms/templates/courseware/courseware.html
index 02149ec463..3f6588420a 100644
--- a/lms/templates/courseware/courseware.html
+++ b/lms/templates/courseware/courseware.html
@@ -1,6 +1,6 @@
<%inherit file="/main.html" />
<%namespace name='static' file='/static_content.html'/>
-<%block name="bodyclass">courseware%block>
+<%block name="bodyclass">courseware ${course.css_class}%block>
<%block name="title">${course.number} Courseware%block>
<%block name="headextra">
From 9a21c5d4a8dadc9af882f579df5fde52b22bcb51 Mon Sep 17 00:00:00 2001
From: Arjun Singh
Date: Wed, 22 Aug 2012 05:00:11 -0700
Subject: [PATCH 14/32] Adding reasoning for adding a new metadata field
---
common/lib/xmodule/xmodule/course_module.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py
index 5eeb5ee11c..4e799a6f0d 100644
--- a/common/lib/xmodule/xmodule/course_module.py
+++ b/common/lib/xmodule/xmodule/course_module.py
@@ -197,6 +197,10 @@ class CourseDescriptor(SequenceDescriptor):
def start_date_text(self):
return time.strftime("%b %d, %Y", self.start)
+ # An extra property is used rather than the wiki_slug/number because
+ # there are courses that change the number for different runs. This allows
+ # courses to share the same css_class across runs even if they have
+ # different numbers.
@property
def css_class(self):
return self.metadata.get('css_class', '')
From 03248cc3241bf59787de3c95a08b113efab6a332 Mon Sep 17 00:00:00 2001
From: Bridger Maxwell
Date: Wed, 22 Aug 2012 10:09:06 -0400
Subject: [PATCH 15/32] Updated django-wiki.
---
repo-requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/repo-requirements.txt b/repo-requirements.txt
index eaba29c197..787b504269 100644
--- a/repo-requirements.txt
+++ b/repo-requirements.txt
@@ -1,6 +1,6 @@
-e git://github.com/MITx/django-staticfiles.git@6d2504e5c8#egg=django-staticfiles
-e git://github.com/MITx/django-pipeline.git#egg=django-pipeline
--e git://github.com/benjaoming/django-wiki.git@9865b5c#egg=django-wiki
+-e git://github.com/benjaoming/django-wiki.git@6ccd08e#egg=django-wiki
-e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
-e common/lib/capa
-e common/lib/xmodule
From 96f0f7cbde9f8756722526d738660086d80a85b6 Mon Sep 17 00:00:00 2001
From: Bridger Maxwell
Date: Wed, 22 Aug 2012 11:36:06 -0400
Subject: [PATCH 16/32] Changed default contents of new course wiki.
---
lms/djangoapps/course_wiki/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lms/djangoapps/course_wiki/views.py b/lms/djangoapps/course_wiki/views.py
index cfe802bbd7..aa0b286aa6 100644
--- a/lms/djangoapps/course_wiki/views.py
+++ b/lms/djangoapps/course_wiki/views.py
@@ -81,7 +81,7 @@ def course_wiki_redirect(request, course_id):
root,
course_slug,
title=course.number,
- content="{0}\n===\nThis is the wiki for **{1}**'s _{2}_.".format(course.number, course.org, course.title),
+ content="This is the wiki for **{0}**'s _{1}_.".format(course.org, course.title),
user_message="Course page automatically created.",
user=None,
ip_address=None,
From 390823bdea103e2a729b399c48519bd1783da6df Mon Sep 17 00:00:00 2001
From: Bridger Maxwell
Date: Wed, 22 Aug 2012 11:37:01 -0400
Subject: [PATCH 17/32] Added django-wiki's requirements to requirements.txt,
because they weren't being included automatically.
---
requirements.txt | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/requirements.txt b/requirements.txt
index c5cafe64b8..72b13e63ba 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@ django>=1.4,<1.5
pip
numpy
scipy
-markdown
+Markdown<2.3.0
pygments
lxml
boto
@@ -43,5 +43,7 @@ django-ses
django-storages
django-threaded-multihost
django-sekizai<0.7
+django-mptt>=0.5.3
+sorl-thumbnail
networkx
-r repo-requirements.txt
From edefa04de108c4aa7e3d927e1cdc0847a2ebbc24 Mon Sep 17 00:00:00 2001
From: kimth
Date: Wed, 22 Aug 2012 12:41:30 -0400
Subject: [PATCH 18/32] Workaround for 500 on showanswer for choiceresponse
---
common/lib/capa/capa/responsetypes.py | 2 +-
common/lib/xmodule/xmodule/capa_module.py | 14 ++++++++++++--
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py
index cc67389da9..b2d56b48ca 100644
--- a/common/lib/capa/capa/responsetypes.py
+++ b/common/lib/capa/capa/responsetypes.py
@@ -557,7 +557,7 @@ class ChoiceResponse(LoncapaResponse):
return CorrectMap(self.answer_id, 'incorrect')
def get_answers(self):
- return {self.answer_id: self.correct_choices}
+ return {self.answer_id: list(self.correct_choices)}
#-----------------------------------------------------------------------------
diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py
index e6da87b5c6..d2ed3912a4 100644
--- a/common/lib/xmodule/xmodule/capa_module.py
+++ b/common/lib/xmodule/xmodule/capa_module.py
@@ -390,9 +390,19 @@ class CapaModule(XModule):
raise NotFoundError('Answer is not available')
else:
answers = self.lcp.get_question_answers()
+
# answers (eg ) may have embedded images
- answers = dict( (k,self.system.replace_urls(answers[k], self.metadata['data_dir'])) for k in answers )
- return {'answers': answers}
+ # but be careful, some problems are using non-string answer dicts
+ new_answers = dict()
+ for answer_id in answers:
+ try:
+ new_answer = {answer_id: self.system.replace_urls(answers[answer_id], self.metadata['data_dir'])}
+ except TypeError:
+ log.debug('Unable to perform URL substitution on answers[%s]: %s' % (answer_id, answers[answer_id]))
+ new_answer = {answer_id: answers[answer_id]}
+ new_answers.update(new_answer)
+
+ return {'answers': new_answers}
# Figure out if we should move these to capa_problem?
def get_problem(self, get):
From 3796bb4a124acb2fe74e9e0dcf1e043dabf449e7 Mon Sep 17 00:00:00 2001
From: Bridger Maxwell
Date: Wed, 22 Aug 2012 12:42:03 -0400
Subject: [PATCH 19/32] Changed title of default root wiki page to just be
Wiki.
---
lms/djangoapps/course_wiki/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lms/djangoapps/course_wiki/views.py b/lms/djangoapps/course_wiki/views.py
index aa0b286aa6..e754e1cdcd 100644
--- a/lms/djangoapps/course_wiki/views.py
+++ b/lms/djangoapps/course_wiki/views.py
@@ -114,7 +114,7 @@ def get_or_create_root():
"===",
"Visit a course wiki to add an article."))
- root = URLPath.create_root(title="edX Wiki",
+ root = URLPath.create_root(title="Wiki",
content=starting_content)
article = root.article
article.group = None
From 7b4c86e55cd01880bb4c6446fdfad1d93d87b91c Mon Sep 17 00:00:00 2001
From: Bridger Maxwell
Date: Wed, 22 Aug 2012 12:42:21 -0400
Subject: [PATCH 20/32] Updated django-wiki with bug fix.
---
repo-requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/repo-requirements.txt b/repo-requirements.txt
index 787b504269..6e2572a732 100644
--- a/repo-requirements.txt
+++ b/repo-requirements.txt
@@ -1,6 +1,6 @@
-e git://github.com/MITx/django-staticfiles.git@6d2504e5c8#egg=django-staticfiles
-e git://github.com/MITx/django-pipeline.git#egg=django-pipeline
--e git://github.com/benjaoming/django-wiki.git@6ccd08e#egg=django-wiki
+-e git://github.com/benjaoming/django-wiki.git@afdd97d#egg=django-wiki
-e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
-e common/lib/capa
-e common/lib/xmodule
From 365495521e0fb81668c307d97239f79179a2d59a Mon Sep 17 00:00:00 2001
From: Victor Shnayder
Date: Wed, 22 Aug 2012 12:58:46 -0400
Subject: [PATCH 21/32] Catch errors in module load
* if error is in xmodule_constructor(), catch and return an ErrorModule
* if error is somewhere else in get_module(), return None
---
common/lib/xmodule/xmodule/error_module.py | 29 ++++++++++++-
lms/djangoapps/courseware/access.py | 1 -
lms/djangoapps/courseware/module_render.py | 47 ++++++++++++++++++----
lms/templates/module-error.html | 15 +++++--
4 files changed, 80 insertions(+), 12 deletions(-)
diff --git a/common/lib/xmodule/xmodule/error_module.py b/common/lib/xmodule/xmodule/error_module.py
index bdd7179a0a..f8e2467910 100644
--- a/common/lib/xmodule/xmodule/error_module.py
+++ b/common/lib/xmodule/xmodule/error_module.py
@@ -15,18 +15,39 @@ from xmodule.errortracker import exc_info_to_str
log = logging.getLogger(__name__)
+# NOTE: This is not the most beautiful design in the world, but there's no good
+# way to tell if the module is being used in a staff context or not. Errors that get discovered
+# at course load time are turned into ErrorDescriptor objects, and automatically hidden from students.
+# Unfortunately, we can also have errors when loading modules mid-request, and then we need to decide
+# what to show, and the logic for that belongs in the LMS (e.g. in get_module), so the error handler
+# decides whether to create a staff or not-staff module.
+
class ErrorModule(XModule):
def get_html(self):
- '''Show an error.
+ '''Show an error to staff.
TODO (vshnayder): proper style, divs, etc.
'''
# staff get to see all the details
return self.system.render_template('module-error.html', {
+ 'staff_access' : True,
'data' : self.definition['data']['contents'],
'error' : self.definition['data']['error_msg'],
})
+class NonStaffErrorModule(XModule):
+ def get_html(self):
+ '''Show an error to a student.
+ TODO (vshnayder): proper style, divs, etc.
+ '''
+ # staff get to see all the details
+ return self.system.render_template('module-error.html', {
+ 'staff_access' : False,
+ 'data' : "",
+ 'error' : "",
+ })
+
+
class ErrorDescriptor(EditingDescriptor):
"""
Module that provides a raw editing view of broken xml.
@@ -99,3 +120,9 @@ class ErrorDescriptor(EditingDescriptor):
err_node = etree.SubElement(root, 'error_msg')
err_node.text = self.definition['data']['error_msg']
return etree.tostring(root)
+
+class NonStaffErrorDescriptor(ErrorDescriptor):
+ """
+ Module that provides non-staff error messages.
+ """
+ module_class = NonStaffErrorModule
diff --git a/lms/djangoapps/courseware/access.py b/lms/djangoapps/courseware/access.py
index 281580cf33..dbe4ff376d 100644
--- a/lms/djangoapps/courseware/access.py
+++ b/lms/djangoapps/courseware/access.py
@@ -13,7 +13,6 @@ from xmodule.modulestore import Location
from xmodule.timeparse import parse_time
from xmodule.x_module import XModule, XModuleDescriptor
-
DEBUG_ACCESS = False
log = logging.getLogger(__name__)
diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py
index 65e30475f2..7967452647 100644
--- a/lms/djangoapps/courseware/module_render.py
+++ b/lms/djangoapps/courseware/module_render.py
@@ -1,5 +1,6 @@
import json
import logging
+import sys
from django.conf import settings
from django.contrib.auth.models import User
@@ -15,10 +16,12 @@ from courseware.access import has_access
from mitxmako.shortcuts import render_to_string
from models import StudentModule, StudentModuleCache
from static_replace import replace_urls
+from xmodule.errortracker import exc_info_to_str
from xmodule.exceptions import NotFoundError
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from xmodule.x_module import ModuleSystem
+from xmodule.error_module import ErrorDescriptor, NonStaffErrorDescriptor
from xmodule_modifiers import replace_course_urls, replace_static_urls, add_histogram, wrap_xmodule
log = logging.getLogger("mitx.courseware")
@@ -73,6 +76,8 @@ def toc_for_course(user, request, course, active_chapter, active_section, course
student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(
course_id, user, course, depth=2)
course = get_module(user, request, course.location, student_module_cache, course_id)
+ if course is None:
+ return None
chapters = list()
for chapter in course.get_display_items():
@@ -131,9 +136,9 @@ def get_section(course_module, chapter, section):
return section_module
-
def get_module(user, request, location, student_module_cache, course_id, position=None):
- ''' Get an instance of the xmodule class identified by location,
+ """
+ Get an instance of the xmodule class identified by location,
setting the state based on an existing StudentModule, or creating one if none
exists.
@@ -146,9 +151,22 @@ def get_module(user, request, location, student_module_cache, course_id, positio
- position : extra information from URL for user-specified
position within module
- Returns: xmodule instance
+ Returns: xmodule instance, or None if the user does not have access to the
+ module. If there's an error, will try to return an instance of ErrorModule
+ if possible. If not possible, return None.
+ """
+ try:
+ return _get_module(user, request, location, student_module_cache, course_id, position)
+ except:
+ # Something has gone terribly wrong, but still not letting it turn into a 500.
+ log.exception("Error in get_module")
+ return None
- '''
+def _get_module(user, request, location, student_module_cache, course_id, position=None):
+ """
+ Actually implement get_module. See docstring there for details.
+ """
+ location = Location(location)
descriptor = modulestore().get_instance(course_id, location)
# Short circuit--if the user shouldn't have access, bail without doing any work
@@ -198,7 +216,7 @@ def get_module(user, request, location, student_module_cache, course_id, positio
'callback_url': xqueue_callback_url,
'default_queuename': xqueue_default_queuename.replace(' ', '_')}
- def _get_module(location):
+ def inner_get_module(location):
"""
Delegate to get_module. It does an access check, so may return None
"""
@@ -214,7 +232,7 @@ def get_module(user, request, location, student_module_cache, course_id, positio
xqueue=xqueue,
# TODO (cpennington): Figure out how to share info between systems
filestore=descriptor.system.resources_fs,
- get_module=_get_module,
+ get_module=inner_get_module,
user=user,
# TODO (cpennington): This should be removed when all html from
# a module is coming through get_html and is therefore covered
@@ -226,7 +244,22 @@ def get_module(user, request, location, student_module_cache, course_id, positio
system.set('position', position)
system.set('DEBUG', settings.DEBUG)
- module = descriptor.xmodule_constructor(system)(instance_state, shared_state)
+ try:
+ module = descriptor.xmodule_constructor(system)(instance_state, shared_state)
+ except:
+ log.exception("Error creating module from descriptor {0}".format(descriptor))
+
+ # make an ErrorDescriptor -- assuming that the descriptor's system is ok
+ import_system = descriptor.system
+ if has_access(user, location, 'staff'):
+ err_descriptor = ErrorDescriptor.from_xml(str(descriptor), import_system,
+ error_msg=exc_info_to_str(sys.exc_info()))
+ else:
+ err_descriptor = NonStaffErrorDescriptor.from_xml(str(descriptor), import_system,
+ error_msg=exc_info_to_str(sys.exc_info()))
+
+ # Make an error module
+ return err_descriptor.xmodule_constructor(system)(None, None)
module.get_html = replace_static_urls(
wrap_xmodule(module.get_html, module, 'xmodule_display.html'),
diff --git a/lms/templates/module-error.html b/lms/templates/module-error.html
index 2a51f5b11a..8855c5be48 100644
--- a/lms/templates/module-error.html
+++ b/lms/templates/module-error.html
@@ -2,10 +2,19 @@
There has been an error on the MITx servers
We're sorry, this module is temporarily unavailable. Our staff is working to fix it as soon as possible. Please email us at technical@mitx.mit.edu to report any problems or downtime.
-
Details below:
+% if staff_access:
+
Details
-
Error: ${error | h}
+
Error:
+
+${error | h}
+
+
-
Raw data: ${data | h}
+
Raw data:
+
${data | h}
+
+
+% endif
From ce0bc1f7825d8f466ff0fcbe49a2715c6e7cb2a5 Mon Sep 17 00:00:00 2001
From: Victor Shnayder
Date: Wed, 22 Aug 2012 13:18:33 -0400
Subject: [PATCH 22/32] Separate caches per-domain-name
---
common/djangoapps/util/cache.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/common/djangoapps/util/cache.py b/common/djangoapps/util/cache.py
index 85b8ed3369..89b5dffd5e 100644
--- a/common/djangoapps/util/cache.py
+++ b/common/djangoapps/util/cache.py
@@ -9,6 +9,7 @@ from functools import wraps
from django.core import cache
+
# If we can't find a 'general' CACHE defined in settings.py, we simply fall back
# to returning the default cache. This will happen with dev machines.
try:
@@ -41,7 +42,10 @@ def cache_if_anonymous(view_func):
def _decorated(request, *args, **kwargs):
if not request.user.is_authenticated():
#Use the cache
- cache_key = "cache_if_anonymous." + request.path
+ # same view accessed through different domain names may
+ # return different things, so include the domain name in the key.
+ domain = str(request.META.get('HTTP_HOST')) + '.'
+ cache_key = domain + "cache_if_anonymous." + request.path
response = cache.get(cache_key)
if not response:
response = view_func(request, *args, **kwargs)
From 02f29c22c92aac3b3df47951d17043c16ad301fe Mon Sep 17 00:00:00 2001
From: Kyle Fiedler
Date: Wed, 22 Aug 2012 13:41:20 -0400
Subject: [PATCH 23/32] Added stsyles for toc
---
lms/static/sass/course/wiki/_wiki.scss | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/lms/static/sass/course/wiki/_wiki.scss b/lms/static/sass/course/wiki/_wiki.scss
index 9abe349119..e122a6f871 100644
--- a/lms/static/sass/course/wiki/_wiki.scss
+++ b/lms/static/sass/course/wiki/_wiki.scss
@@ -199,6 +199,17 @@ section.wiki {
font-size: 0.9em;
font-family: Monaco, monospace;
}
+
+ .toc {
+ border: 1px solid #ccc;
+ background-color: $sidebar-color;
+ padding: 20px;
+ margin: 10px 0;
+
+ ul {
+ margin: 0;
+ }
+ }
}
From 82bab71ccf8e60a632852593f9831d4fb8006888 Mon Sep 17 00:00:00 2001
From: Calen Pennington
Date: Wed, 22 Aug 2012 14:05:12 -0400
Subject: [PATCH 24/32] First pass at branding the header logo on subdomains
---
lms/djangoapps/branding/__init__.py | 42 ++++++++++++++++++++
lms/djangoapps/courseware/courses.py | 21 +++-------
lms/envs/common.py | 14 ++++++-
lms/envs/dev.py | 24 +++++++++++
lms/static/images/BerkeleyX-on-edx-logo.png | Bin 0 -> 5011 bytes
lms/static/sass/shared/_header.scss | 7 +---
lms/templates/footer.html | 2 +-
lms/templates/name_changes.html | 1 -
lms/templates/navigation.html | 9 ++++-
lms/templates/textbook.html | 10 -----
10 files changed, 92 insertions(+), 38 deletions(-)
create mode 100644 lms/djangoapps/branding/__init__.py
create mode 100644 lms/static/images/BerkeleyX-on-edx-logo.png
delete mode 100644 lms/templates/textbook.html
diff --git a/lms/djangoapps/branding/__init__.py b/lms/djangoapps/branding/__init__.py
new file mode 100644
index 0000000000..a19cff4d51
--- /dev/null
+++ b/lms/djangoapps/branding/__init__.py
@@ -0,0 +1,42 @@
+
+from xmodule.modulestore.django import modulestore
+from xmodule.course_module import CourseDescriptor
+from django.conf import settings
+
+
+def get_subdomain(domain):
+ return domain.split(".")[0]
+
+
+def get_visible_courses(domain=None):
+ """
+ Return the set of CourseDescriptors that should be visible in this branded instance
+ """
+ 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 = get_subdomain(domain)
+ if subdomain not in settings.COURSE_LISTINGS:
+ subdomain = 'default'
+ visible_ids = frozenset(settings.COURSE_LISTINGS[subdomain])
+ return [course for course in courses if course.id in visible_ids]
+ else:
+ return courses
+
+
+def get_logo_url(domain=None):
+ """
+ Return the url for the branded logo image to be used
+ """
+ if not settings.MITX_FEATURES['SUBDOMAIN_BRANDING'] or domain is None:
+ return '/static/images/header-logo.png'
+
+ subdomain = get_subdomain(domain)
+ if subdomain not in settings.SUBDOMAIN_BRANDING:
+ return '/static/images/header-logo.png'
+
+ return '/static/images/{uni}-on-edx-logo.png'.format(
+ uni=settings.SUBDOMAIN_BRANDING[subdomain]
+ )
diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py
index c92cbb1425..e5ef915e25 100644
--- a/lms/djangoapps/courseware/courses.py
+++ b/lms/djangoapps/courseware/courses.py
@@ -13,6 +13,7 @@ from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from static_replace import replace_urls, try_staticfiles_lookup
from courseware.access import has_access
+import branding
log = logging.getLogger(__name__)
@@ -141,9 +142,10 @@ def get_course_info_section(course, section_key):
raise KeyError("Invalid about key " + str(section_key))
+
# TODO: Fix this such that these are pulled in as extra course-specific tabs.
# arjun will address this by the end of October if no one does so prior to
-# then.
+# then.
def get_course_syllabus_section(course, section_key):
"""
This returns the snippet of html to be rendered on the syllabus page,
@@ -178,24 +180,11 @@ def get_courses_by_university(user, domain=None):
'''
# TODO: Clean up how 'error' is done.
# filter out any courses that errored.
- 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)
+ visible_courses = branding.get_visible_courses(domain)
universities = defaultdict(list)
- for course in courses:
+ for course in visible_courses:
if not has_access(user, course, 'see_exists'):
continue
- if course.id not in visible_courses:
- continue
universities[course.org].append(course)
return universities
-
diff --git a/lms/envs/common.py b/lms/envs/common.py
index c99423c7a1..1cc6ae8d89 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -55,9 +55,14 @@ MITX_FEATURES = {
# course_ids (see dev_int.py for an example)
'SUBDOMAIN_COURSE_LISTINGS' : False,
+ # When True, will override certain branding with university specific values
+ # Expects a SUBDOMAIN_BRANDING dictionary that maps the subdomain to the
+ # university to use for branding purposes
+ 'SUBDOMAIN_BRANDING': False,
+
# TODO: This will be removed once course-specific tabs are in place. see
# courseware/courses.py
- 'ENABLE_SYLLABUS' : True,
+ 'ENABLE_SYLLABUS' : True,
'ENABLE_TEXTBOOK' : True,
'ENABLE_DISCUSSION' : False,
@@ -66,7 +71,7 @@ MITX_FEATURES = {
'ENABLE_SQL_TRACKING_LOGS': False,
'ENABLE_LMS_MIGRATION': False,
- 'DISABLE_LOGIN_BUTTON': False, # used in systems where login is automatic, eg MIT SSL
+ 'DISABLE_LOGIN_BUTTON': False, # used in systems where login is automatic, eg MIT SSL
# extrernal access methods
'ACCESS_REQUIRE_STAFF_FOR_COURSE': False,
@@ -199,6 +204,11 @@ COURSE_SETTINGS = {'6.002x_Fall_2012': {'number' : '6.002x',
# TODO (vshnayder): Will probably need to change as we get real access control in.
LMS_MIGRATION_ALLOWED_IPS = []
+######################## subdomain specific settings ###########################
+COURSE_LISTINGS = {}
+SUBDOMAIN_BRANDING = {}
+
+
############################### XModule Store ##################################
MODULESTORE = {
'default': {
diff --git a/lms/envs/dev.py b/lms/envs/dev.py
index b269d293dd..d798815543 100644
--- a/lms/envs/dev.py
+++ b/lms/envs/dev.py
@@ -15,6 +15,8 @@ TEMPLATE_DEBUG = True
MITX_FEATURES['DISABLE_START_DATES'] = True
MITX_FEATURES['ENABLE_SQL_TRACKING_LOGS'] = True
+MITX_FEATURES['SUBDOMAIN_COURSE_LISTINGS'] = True
+MITX_FEATURES['SUBDOMAIN_BRANDING'] = True
WIKI_ENABLED = True
@@ -68,6 +70,28 @@ CACHE_TIMEOUT = 0
# Dummy secret key for dev
SECRET_KEY = '85920908f28904ed733fe576320db18cabd7b6cd'
+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/Cal_2012_Fall',
+ 'BerkeleyX/CS188.1x/Cal_2012_Fall'],
+ 'harvard': ['HarvardX/CS50x/2012H'],
+ 'mit': [],
+ 'sjsu': ['MITx/6.002x-EE98/2012_Fall_SJSU'],
+}
+
+SUBDOMAIN_BRANDING = {
+ 'sjsu': 'MITx',
+ 'mit': 'MITx',
+ 'berkeley': 'BerkeleyX',
+ 'harvard': 'HarvardX',
+}
+
################################ LMS Migration #################################
MITX_FEATURES['ENABLE_LMS_MIGRATION'] = True
MITX_FEATURES['ACCESS_REQUIRE_STAFF_FOR_COURSE'] = False # require that user be in the staff_* group to be able to enroll
diff --git a/lms/static/images/BerkeleyX-on-edx-logo.png b/lms/static/images/BerkeleyX-on-edx-logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c5a828503a1906fa5c2bf536d8f61478a9fd9a0
GIT binary patch
literal 5011
zcmaJ_c|26@`#v*vA&MwujIv}IgRzgumaVKIVul$zGsYN3LV7J_&z2TMA(dnq>uU*F
zBTLFYvKt{}4PU+6_xH#9{hiM_=Xsv{b6?keJ(Hp3G3?agM_Zs)I%Ze7$nphVIpgS*T%ZJ
zUk)N*ErPCCItO_=t6-oPH6ZGNsz(IgSRxt{=ry7Wa{=utyH2)QbK_P#n5IvF5
zKSkM?m_oF11S|w0BQNbN3x`7#6=mQG2t`G>6huxIE(eo6x)r723aSW2RXHWdUl;VK
z8Uf>?YObUASFNKJ66!`I;#Fa=fPes*0C^c4!4(EqQBnEDAtxt&lp*a$_93DJrG5NF
z{$|j@`Z*Ka@kDo=59Ajk+6m`RL_&|0{<#Hj{6Dllet#X)(SgAN(RdhKM)ud1{!TP8
z`TtYBz5mJfBbsCXtM^}t{Vd6NEX*A1hw~>mA2rTJ9ixzENWo11@Z6zIT#HD}N@+TJaFIxV>D*sO`>_`mkS8M-ItN#ui
z`RCX4ALAY^{;@vR=g9AbBV%(6dLjUTlfgho%QA3y$)1JC^XYWm`uH&x1G=_TL*O=O
zj$4^O187E|!b3~?0@TCa)Q;9y=(`Ypy^xt-ut8A<9;w3ztE-WBh
z3kvh?>EAOB4yS5X=VrURV(y1!2K4O6C#waxERXIi2OJ*O1Y}N;Gk5b|nCyjIcycLO
z+r@jDoi^J>Am+z1?BDc;xq|m-BCS9geQE%xpK{4cv
z4>!!&086`J_70zaEU;R4RE8z%IM@w)hH{5Xs+M{o<24F)HFV^)z|H
zCE8!ADda&XYOs}*$xD?9WGsE;m33KL9$^~H8%eYV-penp2>eD~Hn&DXYC~{^ihK%6%hp0s^F_(O9FRVXL^C)hXk|Lk81+BRE_{dHY4*jIxGt~(o1+BDNJ2UKR3oW
z$mgvuQO{+2*4DGc<^9`8HaXFxF)c-*!#B{Es%?!A$Llmn{FpSV0?*JijAT5_DFG98hP
zbJ3eg3aZoym=^CMU4=CZ7Mpk}3->rXtnAIMlz0Y+B|P3h*;Y_SzAu>QFK*;pkQ~6PPeiG^Go;7#Np%Ht$KdR#^}7mA{~kT%iZ+YRIsw
zPMCsI%H3`Iis2KSy3L9npeV7$XPqsnw@4p0W6eP3rBjQv#h@X}B6|4;mJnrIrjVWj+W##gwtdpqxTh`u_I&Bh??UWX5SNG@p
z?C{~6;-CQA8_(o=Ecpr=xB9M?ei#k&OEBM47?RH3`^LD3fO<+g5V(_Wo#)FE%7aD#
zqVee0g5cNmdJ*i5d{dQkEQuq5(bwbG;Jxlmd0zgf*}H=6O9eiU+zYPbl~j;UYW>`!
zcW_-JYdER#BYn?OgO#0UFl_#4y0c64IE4KEnBY#Wb;MxdO6fSB|H1bnz5LUc
z6$Q59!8-c@+u%JhYHfPb7RTa-lF%$7KMEPk_U7ryEH;-X(R&Fk`;qsN`kN{aZWg1o
zOJ*x9%)#j>Vl}t-$sf*+ppRfMMqZl0VCfzxGU3bvD#INm5
z>NoZDj_+#0FFIZ>mh_IL(A&<`?qs=csie|^Kn!k&Cub(avbIL1_2zIZjBko
z+*g+D+6sOym(@1HaO~%8FX)g>NkAik?(S*I_3%eGCt+i(tMik)inR@%jdek)6xJdw
z#OQCS(dufsOE-Zkl@pbEoateQ3f%X{rF>sW041hCdVeVJj9HWPl+_{mrA~GWN}1QI
z#%dtWHkh$Np5a>w{a%ob@q9c$io5Nv-+yQ|&zE@|pTSuK>Q93{y48Jh-bO4*DQa^C
zrO0?EEwXso-zm$Mlnolil
zj(1LBvT5CK`ThF(4>92qjWgL<5-E?5nH@;edum#{;|yMXXxT`M
z=B>#5f=W)4EATaHa2nm4fY#h!;t{xbRzr+QdOYusGon(t-9WGbzZ&VJotR`11pQE+If&~7@EgE8j;dU75uGsi1CYSR9HnEm)oI?
zILB~JH8E81k(xdxr@x3**|n}d{{~5go6XK6b*Z2Q`@1)r7u}l_oReNdS~jB#=jOG&
zQ$_d$GCyd*Oneg_+26r^UTDGMeZNGFR(v5=Y@5%W}!2yxSA&~}_r09h`y~SM!J=KujjI{~Hv@~}w
zz4lnci_XIGXl%h|@M8ayC8hauD$E~Cq_snIip!_y?8Pwm8@zI7hsU(@f5dW0e$7p-
zXC%viYuy==bfzD`3K1)Ua;XJ0}IB@XQWJe*BI0>7`sp
z0gTH2UhqTt>3BqiaSIcN5ftPBMJq*5;jYX)Th2ev_U><0+$
zNa-)}mgV`ObCZu7S6go!Qwx$H3Ro8poE3*!pQe(6M`1TySQkgn!Xoc9AE(L{SY~D2
zU@RH(7w6AY{MiD@UEnT$f=L<}zJKe>`oeQ$g-KwZljP7iEKT11cU=f_7(9B#)UgC}
zP-@b>Ep0FU0>65FJPx`7*Vc$RH`DdCM@Tkk+-A6?HBe_8shV>{lk?%pRaSQekTdRC*_
zhVBsFIIT^?fdTDII-zBmHr?R;9{iHvK-GEGQA;)~xiUS?hY`R_Qafwr&mrMp6Fq9%
zT-qe|2cyjjFWam979+r+Xe%b%-+S3sJ5O!3E1&smHQP1G8GW^~VU5)&?1d8xQ10xP
z5f^cCPBRb6^&i_f$4k8Y4*v8if^kt7ud5I!WWEWLs1|9fX;vw@;V6CFzq!ndUdx7i
zmzG{>XCclu6o|F}$?1QxH|^(wc_$WD;aw##lzcdm5;pOf^>eJ#JE!BA0yt0|!?+Yh
zTkvPTo+T)e7bC>cOXiicQDo
z17)lhJy&zia!nS_ugp}MxN>UZZc}Kd4=P3ysh_eyT>Z07hJkKv5&{Yib%B8igjiLy
z5VP0QsP?`r(6_7f4sSk75N2$iXwW&Wb}&B~!qqr59UFTqAL++ijjKXM7fgCP*odQ}i0gLVGPjlRR$pN_3H>GXL+d^X
zomXIJFYjV6e=K+K+d$fStQBT2op6?Qm34xmv@!1|3(CDgxhhLA7PZd3zD=2H6jb9c
zjTlbYXBH&GkjCv=Ot8bzWEEoucDwgmR_LUl>>xDo$?D+ZGS^0VIQe4z8-Zu>h2Uw~
z&-!}Fb}C#>v@kR5q2)!;XAdAH^1<^u13Bds>g)8IKfUfb)JWw)hLs)imk}fSo>eW_
z-bB>Jq=VNbyGC%(I9oC9W~IC~wMfV6gD!1vrhEgJ`dks7c;!d>wDrl_9;5SzFE^!m
zKFFpph+&9^`KnjUpYh)2POAVc2>e}BPF;ihYE>Fjj
zy`-L%Y$Pl{AB$|d@cq6{8ar7pai%_s!L(eG3t^f1t5C*PF(h&j5P=DN&B*_->OWPu
z2{X()`jCw>aYC)l86>dEcJ|zKDx@eB^cWpBFDUu)m&JP?IGj3svoAlHKNPa?k)j|w
zZ;jb7ihJ>}6PjAM-=7=5cBrmDs~@NOz`u8EKvc|Q+9`O1%mu2AP7<;zFGqLJwI>-j
o9d`+PPmU`qQ@~qXJ>*3Jhb_ZKyuWX1{`yU3pnFB9=n^XYKV?e~-2eap
literal 0
HcmV?d00001
diff --git a/lms/static/sass/shared/_header.scss b/lms/static/sass/shared/_header.scss
index 116761ddc8..49c9ac250b 100644
--- a/lms/static/sass/shared/_header.scss
+++ b/lms/static/sass/shared/_header.scss
@@ -19,7 +19,7 @@ header.global {
h1.logo {
float: left;
- margin: 6px 15px 0px 0px;
+ margin: 0px 15px 0px 0px;
padding-right: 20px;
position: relative;
@@ -46,12 +46,7 @@ header.global {
}
a {
- @include background-image(url('/static/images/header-logo.png'));
- background-position: 0 0;
- background-repeat: no-repeat;
display: block;
- height: 31px;
- width: 64px;
}
}
diff --git a/lms/templates/footer.html b/lms/templates/footer.html
index 85ed6e1769..52c2b45526 100644
--- a/lms/templates/footer.html
+++ b/lms/templates/footer.html
@@ -6,7 +6,7 @@