Commit 18d887b9 by Giulio Gratta

Merge remote-tracking branch 'origin/nate/update-stanford-templates' into…

Merge remote-tracking branch 'origin/nate/update-stanford-templates' into feature/giulio/reset-master-stanford
parents 04e1c9e0 e2f2b059
......@@ -20,8 +20,6 @@
<%static:css group='base-style'/>
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/markitup/skins/simple/style.css')}" />
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/markitup/sets/wiki/style.css')}" />
<link rel="stylesheet" type="text/css" href="${static.url('css/vendor/symbolset.ss-symbolicons-block.css')}" />
<link rel="stylesheet" type="text/css" href="${static.url('css/vendor/symbolset.ss-standard.css')}" />
<%include file="widgets/segment-io.html" />
......@@ -43,8 +41,6 @@
<script type="text/javascript" src="${static.url('js/vendor/backbone-min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/markitup/jquery.markitup.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/markitup/sets/wiki/set.js')}"></script>
<script src="${static.url('js/vendor/symbolset.ss-standard.js')}"></script>
<script src="${static.url('js/vendor/symbolset.ss-symbolicons.js')}"></script>
<%static:js group='main'/>
<%static:js group='module-js'/>
......
<%include file="metadata-edit.html" />
<section class="combinedopenended-editor editor">
<div class="row">
%if enable_markdown:
<div class="editor-bar">
<ul class="format-buttons">
<li><a href="#" class="prompt-button" data-tooltip="Prompt"><span
class="combinedopenended-editor-icon icon-quote-left"></span></a></li>
<li><a href="#" class="rubric-button" data-tooltip="Rubric"><span
class="combinedopenended-editor-icon icon-table"></span></a></li>
<li><a href="#" class="tasks-button" data-tooltip="Tasks"><span
class="combinedopenended-editor-icon icon-sitemap"></span></a></li>
</ul>
<ul class="editor-tabs">
<li><a href="#" class="xml-tab advanced-toggle" data-tab="xml">Advanced Editor</a></li>
<li><a href="#" class="cheatsheet-toggle" data-tooltip="Toggle Cheatsheet">?</a></li>
</ul>
</div>
<textarea class="markdown-box">${markdown | h}</textarea>
%endif
<textarea class="xml-box" rows="8" cols="40">${data | h}</textarea>
</div>
</section>
<script type="text/template" id="open-ended-template">
<openended %min_max_string%>
<openendedparam>
<initial_display>Enter essay here.</initial_display>
<answer_display>This is the answer.</answer_display>
<grader_payload>{"grader_settings" : "%grading_config%", "problem_id" : "6.002x/Welcome/OETest"}</grader_payload>
</openendedparam>
</openended>
</script>
<script type="text/template" id="simple-editor-open-ended-cheatsheet">
<article class="simple-editor-open-ended-cheatsheet">
<div class="cheatsheet-wrapper">
<div class="row">
<h6>Prompt</h6>
<div class="col prompt">
</div>
<div class="col">
<pre><code>
[prompt]
Why is the sky blue?
[prompt]
</code></pre>
</div>
<div class="col">
<p>The student will respond to the prompt. The prompt can contain any html tags, such as paragraph tags and header tags.</p>
</div>
</div>
<div class="row">
<h6>Rubric</h6>
<div class="col sample rubric"><!DOCTYPE html>
</div>
<div class="col">
<pre><code>
[rubric]
+ Color Identification
- Incorrect
- Correct
+ Grammar
- Poor
- Acceptable
- Superb
[rubric]
</code></pre>
</div>
<div class="col">
<p>The rubric is used for feedback and self-assessment. The rubric can have as many categories (+) and options (-) as desired. </p>
<p>The total score for the problem will be the sum of all the points possible on the rubric. The options will be numbered sequentially from zero in each category, so each category will be worth as many points as its number of options minus one. </p>
</div>
</div>
<div class="row">
<h6>Tasks</h6>
<div class="col sample tasks">
</div>
<div class="col">
<pre><code>
[tasks]
(Self), ({1-3}AI), ({2-3}Peer)
[tasks]
</code></pre>
</div>
<div class="col">
<p>The tasks define what feedback the student will get from the problem.</p>
<p>Each task is defined with parentheses around it. Brackets (ie {2-3} above), specify the minimum and maximum score needed to attempt the given task.</p>
<p>In the example above, the student will first be asked to self-assess. If they give themselves greater than or equal to a 1/3 and less than or equal to a 3/3 on the problem, then they will be moved to AI assessment. If they score themselves a 2/3 or 3/3 on AI assessment, they will move to peer assessment.</p>
<p>Students will be given feedback from each task, and their final score for a given attempt of the problem will be their score last task that is completed.</p>
</div>
</div>
</div>
</article>
</script>
......@@ -54,21 +54,22 @@ class CombinedOpenEndedFields(object):
state = String(help="Which step within the current task that the student is on.", default="initial",
scope=Scope.user_state)
student_attempts = StringyInteger(help="Number of attempts taken by the student on this problem", default=0,
scope=Scope.user_state)
scope=Scope.user_state)
ready_to_reset = StringyBoolean(help="If the problem is ready to be reset or not.", default=False,
scope=Scope.user_state)
scope=Scope.user_state)
attempts = StringyInteger(help="Maximum number of attempts that a student is allowed.", default=1, scope=Scope.settings)
is_graded = StringyBoolean(help="Whether or not the problem is graded.", default=False, scope=Scope.settings)
accept_file_upload = StringyBoolean(help="Whether or not the problem accepts file uploads.", default=False,
scope=Scope.settings)
scope=Scope.settings)
skip_spelling_checks = StringyBoolean(help="Whether or not to skip initial spelling checks.", default=True,
scope=Scope.settings)
scope=Scope.settings)
due = Date(help="Date that this problem is due by", default=None, scope=Scope.settings)
graceperiod = String(help="Amount of time after the due date that submissions will be accepted", default=None,
scope=Scope.settings)
version = VersionInteger(help="Current version number", default=DEFAULT_VERSION, scope=Scope.settings)
data = String(help="XML data for the problem", scope=Scope.content)
weight = StringyFloat(help="How much to weight this problem by", scope=Scope.settings)
markdown = String(help="Markdown source of this module", scope=Scope.settings)
class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule):
......@@ -213,11 +214,36 @@ class CombinedOpenEndedDescriptor(CombinedOpenEndedFields, RawDescriptor):
"""
Module for adding combined open ended questions
"""
mako_template = "widgets/raw-edit.html"
mako_template = "widgets/open-ended-edit.html"
module_class = CombinedOpenEndedModule
filename_extension = "xml"
stores_state = True
has_score = True
always_recalculate_grades = True
template_dir_name = "combinedopenended"
#Specify whether or not to pass in S3 interface
needs_s3_interface = True
#Specify whether or not to pass in open ended interface
needs_open_ended_interface = True
metadata_attributes = RawDescriptor.metadata_attributes
js = {'coffee': [resource_string(__name__, 'js/src/combinedopenended/edit.coffee')]}
js_module_name = "OpenEndedMarkdownEditingDescriptor"
css = {'scss': [resource_string(__name__, 'css/editor/edit.scss'), resource_string(__name__, 'css/combinedopenended/edit.scss')]}
def get_context(self):
_context = RawDescriptor.get_context(self)
_context.update({'markdown': self.markdown,
'enable_markdown': self.markdown is not None})
return _context
@property
def non_editable_metadata_fields(self):
non_editable_fields = super(CombinedOpenEndedDescriptor, self).non_editable_metadata_fields
non_editable_fields.extend([CombinedOpenEndedDescriptor.due, CombinedOpenEndedDescriptor.graceperiod,
CombinedOpenEndedDescriptor.markdown])
return non_editable_fields
.editor-bar {
.editor-tabs {
.advanced-toggle {
@include white-button;
height: auto;
margin-top: -1px;
padding: 3px 9px;
font-size: 12px;
&.current {
border: 1px solid $lightGrey !important;
border-radius: 3px !important;
background: $lightGrey !important;
color: $darkGrey !important;
pointer-events: none;
cursor: none;
&:hover {
box-shadow: 0 0 0 0 !important;
}
}
}
.cheatsheet-toggle {
width: 21px;
height: 21px;
padding: 0;
margin: 0 5px 0 15px;
border-radius: 22px;
border: 1px solid #a5aaaf;
background: #e5ecf3;
font-size: 13px;
font-weight: 700;
color: #565d64;
text-align: center;
}
}
}
.simple-editor-open-ended-cheatsheet {
position: absolute;
top: 0;
left: 100%;
width: 0;
border-radius: 0 3px 3px 0;
@include linear-gradient(left, rgba(0, 0, 0, .1), rgba(0, 0, 0, 0) 4px);
background-color: #fff;
overflow: hidden;
@include transition(width .3s);
&.shown {
width: 300px;
height: 100%;
overflow-y: scroll;
}
.cheatsheet-wrapper {
width: 240px;
padding: 20px 30px;
}
h6 {
margin-bottom: 7px;
font-size: 15px;
font-weight: 700;
}
.row {
@include clearfix;
padding-bottom: 5px !important;
margin-bottom: 10px !important;
border-bottom: 1px solid #ddd !important;
&:last-child {
border-bottom: none !important;
margin-bottom: 0 !important;
}
}
.col {
float: left;
&.sample {
width: 60px;
margin-right: 30px;
}
}
pre {
font-size: 12px;
line-height: 18px;
}
code {
padding: 0;
background: none;
}
}
.combinedopenended-editor-icon {
display: inline-block;
vertical-align: middle;
color: #333;
}
<section class="combinedopenended-editor editor">
<div class="row">
<textarea class="markdown-box">markdown</textarea>
<textarea class="xml-box" rows="8" cols="40">xml</textarea>
</div>
</section>
<section class="combinedopenended-editor editor">
<div class="row">
<textarea class="xml-box" rows="8" cols="40">xml only</textarea>
</div>
</section>
describe 'OpenEndedMarkdownEditingDescriptor', ->
describe 'save stores the correct data', ->
it 'saves markdown from markdown editor', ->
loadFixtures 'combinedopenended-with-markdown.html'
@descriptor = new OpenEndedMarkdownEditingDescriptor($('.combinedopenended-editor'))
saveResult = @descriptor.save()
expect(saveResult.metadata.markdown).toEqual('markdown')
expect(saveResult.data).toEqual('<combinedopenended>\nmarkdown\n</combinedopenended>')
it 'clears markdown when xml editor is selected', ->
loadFixtures 'combinedopenended-with-markdown.html'
@descriptor = new OpenEndedMarkdownEditingDescriptor($('.combinedopenended-editor'))
@descriptor.createXMLEditor('replace with markdown')
saveResult = @descriptor.save()
expect(saveResult.metadata.markdown).toEqual(null)
expect(saveResult.data).toEqual('replace with markdown')
it 'saves xml from the xml editor', ->
loadFixtures 'combinedopenended-without-markdown.html'
@descriptor = new OpenEndedMarkdownEditingDescriptor($('.combinedopenended-editor'))
saveResult = @descriptor.save()
expect(saveResult.metadata.markdown).toEqual(null)
expect(saveResult.data).toEqual('xml only')
describe 'insertPrompt', ->
it 'inserts the template if selection is empty', ->
revisedSelection = OpenEndedMarkdownEditingDescriptor.insertPrompt('')
expect(revisedSelection).toEqual(OpenEndedMarkdownEditingDescriptor.promptTemplate)
it 'recognizes html in the prompt', ->
revisedSelection = OpenEndedMarkdownEditingDescriptor.insertPrompt('[prompt]<h1>Hello</h1>[prompt]')
expect(revisedSelection).toEqual('[prompt]<h1>Hello</h1>[prompt]')
describe 'insertRubric', ->
it 'inserts the template if selection is empty', ->
revisedSelection = OpenEndedMarkdownEditingDescriptor.insertRubric('')
expect(revisedSelection).toEqual(OpenEndedMarkdownEditingDescriptor.rubricTemplate)
it 'recognizes a proper rubric', ->
revisedSelection = OpenEndedMarkdownEditingDescriptor.insertRubric('[rubric]\n+1\n-1\n-2\n[rubric]')
expect(revisedSelection).toEqual('[rubric]\n+1\n-1\n-2\n[rubric]')
describe 'insertTasks', ->
it 'inserts the template if selection is empty', ->
revisedSelection = OpenEndedMarkdownEditingDescriptor.insertTasks('')
expect(revisedSelection).toEqual(OpenEndedMarkdownEditingDescriptor.tasksTemplate)
it 'recognizes a proper task string', ->
revisedSelection = OpenEndedMarkdownEditingDescriptor.insertTasks('[tasks](Self)[tasks]')
expect(revisedSelection).toEqual('[tasks](Self)[tasks]')
describe 'markdownToXml', ->
# test default templates
it 'converts prompt to xml', ->
data = OpenEndedMarkdownEditingDescriptor.markdownToXml("""[prompt]
<h1>Prompt!</h1>
This is my super awesome prompt.
[prompt]
""")
data = data.replace(/[\t\n\s]/gmi,'')
expect(data).toEqual("""
<combinedopenended>
<prompt>
<h1>Prompt!</h1>
This is my super awesome prompt.
</prompt>
</combinedopenended>
""".replace(/[\t\n\s]/gmi,''))
it 'converts rubric to xml', ->
data = OpenEndedMarkdownEditingDescriptor.markdownToXml("""[rubric]
+ 1
-1
-2
+ 2
-1
-2
+3
-1
-2
-3
[rubric]
""")
data = data.replace(/[\t\n\s]/gmi,'')
expect(data).toEqual("""
<combinedopenended>
<rubric>
<rubric>
<category>
<description>1</description>
<option>1</option>
<option>2</option>
</category>
<category>
<description>2</description>
<option>1</option>
<option>2</option>
</category>
<category>
<description>3</description>
<option>1</option>
<option>2</option>
<option>3</option>
</category>
</rubric>
</rubric>
</combinedopenended>
""".replace(/[\t\n\s]/gmi,''))
it 'converts tasks to xml', ->
data = OpenEndedMarkdownEditingDescriptor.markdownToXml("""[tasks]
(Self), ({1-2}AI), ({1-4}AI), ({1-2}Peer
[tasks]
""")
data = data.replace(/[\t\n\s]/gmi,'')
equality_list = """
<combinedopenended>
<task>
<selfassessment/>
</task>
<task>
<openended min_score_to_attempt="1" max_score_to_attempt="2">ml_grading.conf</openended>
</task>
<task>
<openended min_score_to_attempt="1" max_score_to_attempt="4">ml_grading.conf</openended>
</task>
<task>
<openended min_score_to_attempt="1" max_score_to_attempt="2">peer_grading.conf</openended>
</task>
</combinedopenended>
"""
expect(data).toEqual(equality_list.replace(/[\t\n\s]/gmi,''))
......@@ -847,8 +847,8 @@ class CombinedOpenEndedV1Descriptor():
if len(xml_object.xpath(child)) == 0:
#This is a staff_facing_error
raise ValueError(
"Combined Open Ended definition must include at least one '{0}' tag. Contact the learning sciences group for assistance.".format(
child))
"Combined Open Ended definition must include at least one '{0}' tag. Contact the learning sciences group for assistance. {1}".format(
child, xml_object))
def parse_task(k):
"""Assumes that xml_object has child k"""
......
......@@ -53,8 +53,9 @@ class GradingService(object):
except (RequestException, ConnectionError, HTTPError) as err:
# reraise as promised GradingServiceError, but preserve stacktrace.
#This is a dev_facing_error
log.error("Problem posting data to the grading controller. URL: {0}, data: {1}".format(url, data))
raise GradingServiceError, str(err), sys.exc_info()[2]
error_string = "Problem posting data to the grading controller. URL: {0}, data: {1}".format(url, data)
log.error(error_string)
raise GradingServiceError(error_string)
return r.text
......@@ -71,8 +72,9 @@ class GradingService(object):
except (RequestException, ConnectionError, HTTPError) as err:
# reraise as promised GradingServiceError, but preserve stacktrace.
#This is a dev_facing_error
log.error("Problem getting data from the grading controller. URL: {0}, params: {1}".format(url, params))
raise GradingServiceError, str(err), sys.exc_info()[2]
error_string = "Problem getting data from the grading controller. URL: {0}, params: {1}".format(url, params)
log.error(error_string)
raise GradingServiceError(error_string)
return r.text
......
......@@ -168,7 +168,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
#This is a student_facing_error
return {'success': False, 'msg': "There was an error saving your feedback. Please contact course staff."}
qinterface = system.xqueue['interface']
xqueue = system.get('xqueue')
if xqueue is None:
return {'success': False, 'msg': "Couldn't submit feedback."}
qinterface = xqueue['interface']
qtime = datetime.strftime(datetime.now(), xqueue_interface.dateformat)
anonymous_student_id = system.anonymous_student_id
queuekey = xqueue_interface.make_hashkey(str(system.seed) + qtime +
......@@ -176,7 +179,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
str(len(self.child_history)))
xheader = xqueue_interface.make_xheader(
lms_callback_url=system.xqueue['construct_callback'](),
lms_callback_url=xqueue['construct_callback'](),
lms_key=queuekey,
queue_name=self.message_queue_name
)
......@@ -219,7 +222,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
# Prepare xqueue request
#------------------------------------------------------------
qinterface = system.xqueue['interface']
xqueue = system.get('xqueue')
if xqueue is None:
return False
qinterface = xqueue['interface']
qtime = datetime.strftime(datetime.now(), xqueue_interface.dateformat)
anonymous_student_id = system.anonymous_student_id
......@@ -230,7 +236,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
str(len(self.child_history)))
xheader = xqueue_interface.make_xheader(
lms_callback_url=system.xqueue['construct_callback'](),
lms_callback_url=xqueue['construct_callback'](),
lms_key=queuekey,
queue_name=self.queue_name
)
......
......@@ -587,3 +587,6 @@ class PeerGradingDescriptor(PeerGradingFields, RawDescriptor):
has_score = True
always_recalculate_grades = True
template_dir_name = "peer_grading"
#Specify whether or not to pass in open ended interface
needs_open_ended_interface = True
......@@ -4,6 +4,8 @@ import random
from xmodule.x_module import XModule
from xmodule.seq_module import SequenceDescriptor
from lxml import etree
from xblock.core import Scope, Integer
log = logging.getLogger('mitx.' + __name__)
......
......@@ -7,6 +7,7 @@ metadata:
skip_spelling_checks: False
accept_file_upload: False
weight: ""
markdown: ""
data: |
<combinedopenended>
<rubric>
......@@ -39,5 +40,4 @@ data: |
</task>
</combinedopenended>
children: []
......@@ -407,7 +407,7 @@ class CombinedOpenEndedModuleTest(unittest.TestCase):
self.assertTrue(changed)
def test_get_max_score(self):
changed = self.combinedoe.update_task_states()
self.combinedoe.update_task_states()
self.combinedoe.state = "done"
self.combinedoe.is_scored = True
max_score = self.combinedoe.max_score()
......@@ -611,11 +611,11 @@ class OpenEndedModuleXmlTest(unittest.TestCase, DummyModulestore):
self.assertEqual(module.current_task_number, 1)
#Get html and other data client will request
html = module.get_html()
module.get_html()
legend = module.handle_ajax("get_legend", {})
self.assertTrue(isinstance(legend, basestring))
status = module.handle_ajax("get_status", {})
module.handle_ajax("get_status", {})
module.handle_ajax("skip_post_assessment", {})
self.assertTrue(isinstance(legend, basestring))
......
......@@ -136,6 +136,7 @@ class XmlDescriptor(XModuleDescriptor):
'hide_progress_tab': bool_map,
'allow_anonymous': bool_map,
'allow_anonymous_to_peers': bool_map,
'show_timezone': bool_map,
}
......
<course filename="6.002_Spring_2012" slug="6.002_Spring_2012" graceperiod="1 day 12 hours 59 minutes 59 seconds" showanswer="attempted" rerandomize="never" name="6.002 Spring 2012" start="2015-07-17T12:00" course="full" org="edX" />
<course filename="6.002_Spring_2012" slug="6.002_Spring_2012" graceperiod="1 day 12 hours 59 minutes 59 seconds" showanswer="attempted" rerandomize="never" name="6.002 Spring 2012" start="2015-07-17T12:00" course="full" org="edX" show_timezone="true"/>
......@@ -12,4 +12,13 @@
<html slug="html_95">Minor correction: Six elements (five resistors)…</html>
<customtag tag="S1" slug="discuss_96" impl="discuss"/>
</vertical>
<randomize url_name="PS1_Q4" display_name="Problem 4: Read a Molecule">
<vertical>
<html slug="html_900">
<!-- UTF-8 characters are acceptable… HTML entities are not -->
<h1>Inline content…</h1>
</html>
</vertical>
</randomize>
</sequential>
......@@ -88,7 +88,7 @@ because the `capa` package handles problem XML.
Before running tests, ensure that you have all the dependencies. You can install dependencies using:
pip install -r requirements.txt
rake install_prereqs
## Running Python Unit tests
......@@ -183,12 +183,6 @@ To start the debugger on failure, add the `--pdb` option:
To run tests faster by not collecting static files, you can use
`rake fasttest_acceptance_lms` and `rake fasttest_acceptance_cms`.
**Troubleshooting**: If you get an error message that says something about harvest not being a command, you probably are missing a requirement.
Try running:
pip install -r requirements.txt
**Note**: The acceptance tests can *not* currently run in parallel.
## Viewing Test Coverage
......
......@@ -214,22 +214,27 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
#This is a hacky way to pass settings to the combined open ended xmodule
#It needs an S3 interface to upload images to S3
#It needs the open ended grading interface in order to get peer grading to be done
#TODO: refactor these settings into module-specific settings when possible.
#this first checks to see if the descriptor is the correct one, and only sends settings if it is
is_descriptor_combined_open_ended = (descriptor.__class__.__name__ == 'CombinedOpenEndedDescriptor')
is_descriptor_peer_grading = (descriptor.__class__.__name__ == 'PeerGradingDescriptor')
#Get descriptor metadata fields indicating needs for various settings
needs_open_ended_interface = getattr(descriptor, "needs_open_ended_interface", False)
needs_s3_interface = getattr(descriptor, "needs_s3_interface", False)
#Initialize interfaces to None
open_ended_grading_interface = None
s3_interface = None
if is_descriptor_combined_open_ended or is_descriptor_peer_grading:
#Create interfaces if needed
if needs_open_ended_interface:
open_ended_grading_interface = settings.OPEN_ENDED_GRADING_INTERFACE
open_ended_grading_interface['mock_peer_grading'] = settings.MOCK_PEER_GRADING
open_ended_grading_interface['mock_staff_grading'] = settings.MOCK_STAFF_GRADING
if is_descriptor_combined_open_ended:
s3_interface = {
'access_key' : getattr(settings,'AWS_ACCESS_KEY_ID',''),
'secret_access_key' : getattr(settings,'AWS_SECRET_ACCESS_KEY',''),
'storage_bucket_name' : getattr(settings,'AWS_STORAGE_BUCKET_NAME','openended')
}
if needs_s3_interface:
s3_interface = {
'access_key': getattr(settings, 'AWS_ACCESS_KEY_ID', ''),
'secret_access_key': getattr(settings, 'AWS_SECRET_ACCESS_KEY', ''),
'storage_bucket_name': getattr(settings, 'AWS_STORAGE_BUCKET_NAME', 'openended')
}
def inner_get_module(descriptor):
"""
......
......@@ -24,7 +24,7 @@
<p>${get_course_about_section(course, 'short_description')}</p>
</div>
<div class="bottom">
<a href="${reverse('university_profile', args=[course.org])}" class="university">${get_course_about_section(course, 'university')}</a>
<span class="university">${get_course_about_section(course, 'university')}</span>
<span class="start-date">${course.start_date_text}</span>
</div>
</section>
......
......@@ -8,204 +8,37 @@
<div class="outer-wrapper">
<div class="title">
<hgroup>
<h1>The Future of Online Education</h1>
<h1>Free courses from <strong>Stanford</strong></h1>
<h2>For anyone, anywhere, anytime</h2>
</hgroup>
<section class="actions">
<div class="main-cta">
<a href="#signup-modal" id="signup_action" class="find-courses" rel="leanModal">Sign Up</a>
</div>
<div class="secondary-actions">
<div class="social-sharing">
<div class="sharing-message">Stay up to date with all edX has to offer!</div>
<a href="https://twitter.com/edXOnline" class="share">
<img src="${static.url('images/social/twitter-sharing.png')}">
</a>
<a href="http://www.facebook.com/EdxOnline" class="share">
<img src="${static.url('images/social/facebook-sharing.png')}">
</a>
<a href="https://plus.google.com/108235383044095082735/posts" class="share">
<img src="${static.url('images/social/google-plus-sharing.png')}">
</a>
</div>
</div>
</section>
</div>
<a href="#video-modal" class="media" rel="leanModal">
<div class="hero">
<div class="play-intro"></div>
</div>
</a>
<a href="#video-modal" class="media" rel="leanModal">
<div class="hero">
<div class="play-intro"></div>
</div>
</a>
</div>
</header>
<section class="container">
<section class="highlighted-courses">
<h2>Explore free courses from <span class="edx">edX</span> universities</h2>
<section class="university-partners university-partners2x6">
<ol class="partners">
<li class="partner mit">
<a href="${reverse('university_profile', args=['MITx'])}">
<img src="${static.url('images/university/mit/mit.png')}" />
<div class="name">
<span>MITx</span>
</div>
</a>
</li>
<li class="partner">
<a href="${reverse('university_profile', args=['HarvardX'])}">
<img src="${static.url('images/university/harvard/harvard.png')}" />
<div class="name">
<span>HarvardX</span>
</div>
</a>
</li>
<li class="partner">
<a href="${reverse('university_profile', args=['BerkeleyX'])}">
<img src="${static.url('images/university/berkeley/berkeley.png')}" />
<div class="name">
<span>BerkeleyX</span>
</div>
</a>
</li>
<li class="partner">
<a href="${reverse('university_profile', args=['UTx'])}">
<img src="${static.url('images/university/ut/ut-rollover_350x150.png')}" />
<div class="name">
<span>UTx</span>
</div>
</a>
</li>
<li class="partner">
<a href="${reverse('university_profile', args=['McGillX'])}">
<img src="${static.url('images/university/mcgill/mcgill.png')}" />
<div class="name">
<span>McGillX</span>
</div>
</a>
</li>
<li class="partner">
<a href="${reverse('university_profile', args=['ANUx'])}">
<img src="${static.url('images/university/anu/anu.png')}" />
<div class="name">
<span>ANUx</span>
</div>
</a>
</li>
</ol>
<hr />
<ol class="partners">
<li class="partner">
<a href="${reverse('university_profile', args=['WellesleyX'])}">
<img src="${static.url('images/university/wellesley/wellesley-rollover_350x150.png')}" />
<div class="name">
<span>WellesleyX</span>
</div>
</a>
</li>
<li class="partner">
<a href="${reverse('university_profile', args=['GeorgetownX'])}">
<img src="${static.url('images/university/georgetown/georgetown-rollover_350x150.png')}" />
<div class="name">
<span>GeorgetownX</span>
</div>
</a>
</li>
<li class="partner">
<a href="${reverse('university_profile', args=['TorontoX'])}">
<img src="${static.url('images/university/toronto/toronto.png')}" />
<div class="name">
<span>University of TorontoX</span>
</div>
</a>
</li>
<li class="partner">
<a href="${reverse('university_profile', args=['EPFLx'])}">
<img src="${static.url('images/university/epfl/epfl.png')}" />
<div class="name">
<span>EPFLx</span>
</div>
</a>
</li>
<li class="partner">
<a href="${reverse('university_profile', args=['DelftX'])}">
<img src="${static.url('images/university/delft/delft.png')}" />
<div class="name">
<span>DelftX</span>
</div>
</a>
</li>
<li class="partner">
<a href="${reverse('university_profile', args=['RiceX'])}">
<img src="${static.url('images/university/rice/rice.png')}" />
<div class="name">
<span>RiceX</span>
</div>
</a>
</li>
</ol>
</section>
<section class="courses">
<ul class="courses-listing">
%for course in courses:
<li class="courses-listing-item">
<%include file="course.html" args="course=course" />
</li>
%endfor
% for course in courses:
<li class="courses-listing-item">
<%include file="course.html" args="course=course" />
</li>
% endfor
</ul>
</section>
</section>
</section>
<section class="container">
<section class="more-info">
<header>
<h2><span class="edx">edX</span> News &amp; Announcements</h2>
<a class="action action-mediakit" href="${reverse('media-kit')}"> <span class="org-name">edX</span> MEDIA KIT</a>
</header>
<section class="news">
<section class="blog-posts">
%for entry in news:
<article>
%if entry.image:
<a href="${entry.link}" class="post-graphics" target="_blank"><img src="${entry.image}" /></a>
%endif
<div class="post-name">
<a href="${entry.link}" target="_blank">${entry.title}</a>
%if entry.summary:
<p>${entry.summary}</p>
%endif
<p class="post-date">${strftime("%m/%d/%y", entry.published_parsed)}</p>
</div>
</article>
%endfor
</section>
<section class="press-links">
<h3>edX in the News:</h3>
<a target="_blank" href="http://www.nytimes.com/2013/04/30/education/adapting-to-blended-courses-and-finding-early-benefits.html?ref=education">The New York Times</a>,
<a target="_blank" href="http://online.wsj.com/article/SB10001424127887323741004578414861572832182.html?mod=googlenews_wsj">The Wall Street Journal</a>,
<a target="_blank" href="http://www.washingtonpost.com/local/education/stanford-to-help-build-edx-mooc-platform/2013/04/02/5b53bb3e-9bbe-11e2-9a79-eb5280c81c63_story.html">The Washington Post</a>,
<a target="_blank" href="http://www.cbsnews.com/video/watch/?id=50143164n">CBS Television</a>,
<a target="_blank" href="http://bostonglobe.com/2012/12/04/edx/AqnQ808q4IEcaUa8KuZuBO/story.html">The Boston Globe</a>
<a href="${reverse('press')}" class="read-more">Read More &rarr;</a>
</section>
</section>
</section>
</section>
</section>
<section id="video-modal" class="modal home-page-video-modal video-modal">
<div class="inner-wrapper">
<iframe width="640" height="360" src="http://www.youtube.com/embed/XNaiOGxWeto?showinfo=0" frameborder="0" allowfullscreen></iframe>
<iframe width="640" height="360" src="http://www.youtube.com/embed/2gmreZObCY4?showinfo=0" frameborder="0" allowfullscreen></iframe>
</div>
</section>
......
......@@ -3,7 +3,7 @@
<!DOCTYPE html>
<html>
<head>
<%block name="title"><title>edX</title></%block>
<%block name="title"><title>Home | class.stanford.edu</title></%block>
<script type="text/javascript">
/* immediately break out of an iframe if coming
from the marketing website */
......@@ -14,12 +14,13 @@
})(this);
</script>
<link rel="icon" type="image/x-icon" href="${static.url('images/favicon.ico')}" />
<link rel="icon" type="image/x-icon" href="${static.url('themes/' + settings.THEME_NAME + '/images/favicon.ico')}" />
<%static:css group='application'/>
<%static:js group='main_vendor'/>
<%block name="headextra"/>
<%include file="lms/theme-head-extra.html" />
<!--[if lt IE 9]>
<script src="${static.url('js/html5shiv.js')}"></script>
......@@ -32,25 +33,21 @@
<meta name="google-site-verification" content="_mipQ4AtZQDNmbtOkwehQDOgCxUUV2fb_C0b6wbiRHY" />
% if not course:
<%include file="google_analytics.html" />
<%include file="lms/theme-google-analytics.html" />
% endif
</head>
<body class="<%block name='bodyclass'/>">
% if not suppress_toplevel_navigation:
<%include file="navigation.html" />
% endif
<%include file="lms/theme-header.html" />
<section class="content-wrapper">
${self.body()}
<%block name="bodyextra"/>
</section>
% if not suppress_toplevel_navigation:
<%include file="footer.html" />
% endif
<%include file="lms/theme-footer.html" />
<%static:js group='application'/>
<%static:js group='module-js'/>
......
......@@ -10,6 +10,8 @@ import branding
from status.status import get_site_status_msg
%>
<%block name="navigation_top"/>
<%block cached="False">
<%
try:
......@@ -37,10 +39,13 @@ site_status_msg = get_site_status_msg(course_id)
% endif
<nav>
<h1 class="logo">
<h1 class="logo">
<a href="${marketing_link('ROOT')}">
<img src="${static.url(branding.get_logo_url(request.META.get('HTTP_HOST')))}"/>
</a></h1>
<%block name="navigation_logo">
<img src="${static.url(branding.get_logo_url(request.META.get('HTTP_HOST')))}"/>
</%block>
</a>
</h1>
% if course:
<h2><span class="provider">${course.org}:</span> ${course.number} ${course.display_name_with_default}</h2>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment