Merge remote-tracking branch 'origin/master' into feature/brian/pdf_js
@@ -2,7 +2,7 @@
|
||||
[run]
|
||||
data_file = reports/cms/.coverage
|
||||
source = cms,common/djangoapps
|
||||
omit = cms/envs/*, cms/manage.py
|
||||
omit = cms/envs/*, cms/manage.py, common/djangoapps/terrain/*, common/djangoapps/*/migrations/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
||||
|
||||
@@ -20,7 +20,8 @@ def i_visit_the_studio_homepage(step):
|
||||
# LETTUCE_SERVER_PORT = 8001
|
||||
# in your settings.py file.
|
||||
world.browser.visit(django_url('/'))
|
||||
assert world.browser.is_element_present_by_css('body.no-header', 10)
|
||||
signin_css = 'a.action-signin'
|
||||
assert world.browser.is_element_present_by_css(signin_css, 10)
|
||||
|
||||
|
||||
@step('I am logged into Studio$')
|
||||
@@ -113,7 +114,11 @@ def log_into_studio(
|
||||
create_studio_user(uname=uname, email=email, is_staff=is_staff)
|
||||
world.browser.cookies.delete()
|
||||
world.browser.visit(django_url('/'))
|
||||
world.browser.is_element_present_by_css('body.no-header', 10)
|
||||
signin_css = 'a.action-signin'
|
||||
world.browser.is_element_present_by_css(signin_css, 10)
|
||||
|
||||
# click the signin button
|
||||
css_click(signin_css)
|
||||
|
||||
login_form = world.browser.find_by_css('form#login_form')
|
||||
login_form.find_by_name('email').fill(email)
|
||||
@@ -127,16 +132,19 @@ def create_a_course():
|
||||
css_click('a.new-course-button')
|
||||
fill_in_course_info()
|
||||
css_click('input.new-course-save')
|
||||
assert_true(world.browser.is_element_present_by_css('a#courseware-tab', 5))
|
||||
course_title_css = 'span.course-title'
|
||||
assert_true(world.browser.is_element_present_by_css(course_title_css, 5))
|
||||
|
||||
|
||||
def add_section(name='My Section'):
|
||||
link_css = 'a.new-courseware-section-button'
|
||||
css_click(link_css)
|
||||
name_css = '.new-section-name'
|
||||
save_css = '.new-section-name-save'
|
||||
name_css = 'input.new-section-name'
|
||||
save_css = 'input.new-section-name-save'
|
||||
css_fill(name_css, name)
|
||||
css_click(save_css)
|
||||
span_css = 'span.section-name-span'
|
||||
assert_true(world.browser.is_element_present_by_css(span_css, 5))
|
||||
|
||||
|
||||
def add_subsection(name='Subsection One'):
|
||||
|
||||
@@ -34,8 +34,8 @@ def i_click_the_course_link_in_my_courses(step):
|
||||
|
||||
@step('the Courseware page has loaded in Studio$')
|
||||
def courseware_page_has_loaded_in_studio(step):
|
||||
courseware_css = 'a#courseware-tab'
|
||||
assert world.browser.is_element_present_by_css(courseware_css)
|
||||
course_title_css = 'span.course-title'
|
||||
assert world.browser.is_element_present_by_css(course_title_css)
|
||||
|
||||
|
||||
@step('I see the course listed in My Courses$')
|
||||
@@ -59,4 +59,4 @@ def i_am_on_tab(step, tab_name):
|
||||
@step('I see a link for adding a new section$')
|
||||
def i_see_new_section_link(step):
|
||||
link_css = 'a.new-courseware-section-button'
|
||||
assert_css_with_text(link_css, 'New Section')
|
||||
assert_css_with_text(link_css, '+ New Section')
|
||||
|
||||
@@ -5,8 +5,8 @@ Feature: Sign in
|
||||
|
||||
Scenario: Sign up from the homepage
|
||||
Given I visit the Studio homepage
|
||||
When I click the link with the text "Sign up"
|
||||
When I click the link with the text "Sign Up"
|
||||
And I fill in the registration form
|
||||
And I press the "Create My Account" button on the registration form
|
||||
And I press the Create My Account button on the registration form
|
||||
Then I should see be on the studio home page
|
||||
And I should see the message "please click on the activation link in your email."
|
||||
And I should see the message "please click on the activation link in your email."
|
||||
|
||||
@@ -11,10 +11,11 @@ def i_fill_in_the_registration_form(step):
|
||||
register_form.find_by_name('terms_of_service').check()
|
||||
|
||||
|
||||
@step('I press the "([^"]*)" button on the registration form$')
|
||||
def i_press_the_button_on_the_registration_form(step, button):
|
||||
@step('I press the Create My Account button on the registration form$')
|
||||
def i_press_the_button_on_the_registration_form(step):
|
||||
register_form = world.browser.find_by_css('form#register_form')
|
||||
register_form.find_by_value(button).click()
|
||||
submit_css = 'button#submit'
|
||||
register_form.find_by_css(submit_css).click()
|
||||
|
||||
|
||||
@step('I should see be on the studio home page$')
|
||||
|
||||
@@ -5,7 +5,7 @@ from django.test.utils import override_settings
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from path import path
|
||||
from tempfile import mkdtemp
|
||||
from tempdir import mkdtemp_clean
|
||||
import json
|
||||
from fs.osfs import OSFS
|
||||
import copy
|
||||
@@ -194,7 +194,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
import_from_xml(ms, 'common/test/data/', ['full'])
|
||||
location = CourseDescriptor.id_to_location('edX/full/6.002_Spring_2012')
|
||||
|
||||
root_dir = path(mkdtemp())
|
||||
root_dir = path(mkdtemp_clean())
|
||||
|
||||
print 'Exporting to tempdir = {0}'.format(root_dir)
|
||||
|
||||
@@ -214,12 +214,21 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
fs = OSFS(root_dir / 'test_export/policies/6.002_Spring_2012')
|
||||
self.assertTrue(fs.exists('grading_policy.json'))
|
||||
|
||||
course = ms.get_item(location)
|
||||
# compare what's on disk compared to what we have in our course
|
||||
with fs.open('grading_policy.json','r') as grading_policy:
|
||||
on_disk = loads(grading_policy.read())
|
||||
course = ms.get_item(location)
|
||||
on_disk = loads(grading_policy.read())
|
||||
self.assertEqual(on_disk, course.definition['data']['grading_policy'])
|
||||
|
||||
#check for policy.json
|
||||
self.assertTrue(fs.exists('policy.json'))
|
||||
|
||||
# compare what's on disk to what we have in the course module
|
||||
with fs.open('policy.json','r') as course_policy:
|
||||
on_disk = loads(course_policy.read())
|
||||
self.assertIn('course/6.002_Spring_2012', on_disk)
|
||||
self.assertEqual(on_disk['course/6.002_Spring_2012'], course.metadata)
|
||||
|
||||
# remove old course
|
||||
delete_course(ms, cs, location)
|
||||
|
||||
@@ -255,6 +264,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertContains(resp, '/c4x/edX/full/asset/handouts_schematic_tutorial.pdf')
|
||||
|
||||
|
||||
|
||||
class ContentStoreTest(ModuleStoreTestCase):
|
||||
"""
|
||||
Tests for the CMS ContentStore application.
|
||||
@@ -333,7 +343,7 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
# Create a course so there is something to view
|
||||
resp = self.client.get(reverse('index'))
|
||||
self.assertContains(resp,
|
||||
'<h1>My Courses</h1>',
|
||||
'<h1 class="title-1">My Courses</h1>',
|
||||
status_code=200,
|
||||
html=True)
|
||||
|
||||
@@ -369,7 +379,7 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
|
||||
resp = self.client.get(reverse('course_index', kwargs=data))
|
||||
self.assertContains(resp,
|
||||
'<a href="/MITx/999/course/Robot_Super_Course" class="class-name">Robot Super Course</a>',
|
||||
'<article class="courseware-overview" data-course-id="i4x://MITx/999/course/Robot_Super_Course">',
|
||||
status_code=200,
|
||||
html=True)
|
||||
|
||||
@@ -392,11 +402,11 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
|
||||
def test_capa_module(self):
|
||||
"""Test that a problem treats markdown specially."""
|
||||
CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course')
|
||||
course = CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course')
|
||||
|
||||
problem_data = {
|
||||
'parent_location': 'i4x://MITx/999/course/Robot_Super_Course',
|
||||
'template': 'i4x://edx/templates/problem/Empty'
|
||||
'template': 'i4x://edx/templates/problem/Blank_Common_Problem'
|
||||
}
|
||||
|
||||
resp = self.client.post(reverse('clone_item'), problem_data)
|
||||
@@ -412,6 +422,64 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
self.assertIn('markdown', problem.metadata, "markdown is missing from metadata")
|
||||
self.assertNotIn('markdown', problem.editable_metadata_fields, "Markdown slipped into the editable metadata fields")
|
||||
|
||||
def test_import_metadata_with_attempts_empty_string(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['simple'])
|
||||
ms = modulestore('direct')
|
||||
did_load_item = False
|
||||
try:
|
||||
ms.get_item(Location(['i4x', 'edX', 'simple', 'problem', 'ps01-simple', None]))
|
||||
did_load_item = True
|
||||
except ItemNotFoundError:
|
||||
pass
|
||||
|
||||
# make sure we found the item (e.g. it didn't error while loading)
|
||||
self.assertTrue(did_load_item)
|
||||
|
||||
def test_metadata_inheritance(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
|
||||
ms = modulestore('direct')
|
||||
course = ms.get_item(Location(['i4x', 'edX', 'full', 'course', '6.002_Spring_2012', None]))
|
||||
|
||||
verticals = ms.get_items(['i4x', 'edX', 'full', 'vertical', None, None])
|
||||
|
||||
# let's assert on the metadata_inheritance on an existing vertical
|
||||
for vertical in verticals:
|
||||
self.assertIn('xqa_key', vertical.metadata)
|
||||
self.assertEqual(course.metadata['xqa_key'], vertical.metadata['xqa_key'])
|
||||
|
||||
self.assertGreater(len(verticals), 0)
|
||||
|
||||
new_component_location = Location('i4x', 'edX', 'full', 'html', 'new_component')
|
||||
source_template_location = Location('i4x', 'edx', 'templates', 'html', 'Blank_HTML_Page')
|
||||
|
||||
# crate a new module and add it as a child to a vertical
|
||||
ms.clone_item(source_template_location, new_component_location)
|
||||
parent = verticals[0]
|
||||
ms.update_children(parent.location, parent.definition.get('children', []) + [new_component_location.url()])
|
||||
|
||||
# flush the cache
|
||||
ms.get_cached_metadata_inheritance_tree(new_component_location, -1)
|
||||
new_module = ms.get_item(new_component_location)
|
||||
|
||||
# check for grace period definition which should be defined at the course level
|
||||
self.assertIn('graceperiod', new_module.metadata)
|
||||
|
||||
self.assertEqual(course.metadata['graceperiod'], new_module.metadata['graceperiod'])
|
||||
|
||||
#
|
||||
# now let's define an override at the leaf node level
|
||||
#
|
||||
new_module.metadata['graceperiod'] = '1 day'
|
||||
ms.update_metadata(new_module.location, new_module.metadata)
|
||||
|
||||
# flush the cache and refetch
|
||||
ms.get_cached_metadata_inheritance_tree(new_component_location, -1)
|
||||
new_module = ms.get_item(new_component_location)
|
||||
|
||||
self.assertIn('graceperiod', new_module.metadata)
|
||||
self.assertEqual('1 day', new_module.metadata['graceperiod'])
|
||||
|
||||
|
||||
class TemplateTestCase(ModuleStoreTestCase):
|
||||
|
||||
@@ -420,7 +488,7 @@ class TemplateTestCase(ModuleStoreTestCase):
|
||||
|
||||
# insert a bogus template in the store
|
||||
bogus_template_location = Location('i4x', 'edx', 'templates', 'html', 'bogus')
|
||||
source_template_location = Location('i4x', 'edx', 'templates', 'html', 'Empty')
|
||||
source_template_location = Location('i4x', 'edx', 'templates', 'html', 'Blank_HTML_Page')
|
||||
|
||||
ms.clone_item(source_template_location, bogus_template_location)
|
||||
|
||||
|
||||
@@ -143,10 +143,6 @@ class CourseDetailsViewTest(CourseTestCase):
|
||||
def test_update_and_fetch(self):
|
||||
details = CourseDetails.fetch(self.course_location)
|
||||
|
||||
resp = self.client.get(reverse('course_settings', kwargs={'org': self.course_location.org, 'course': self.course_location.course,
|
||||
'name': self.course_location.name}))
|
||||
self.assertContains(resp, '<li><a href="#" class="is-shown" data-section="details">Course Details</a></li>', status_code=200, html=True)
|
||||
|
||||
# resp s/b json from here on
|
||||
url = reverse('course_settings', kwargs={'org': self.course_location.org, 'course': self.course_location.course,
|
||||
'name': self.course_location.name, 'section': 'details'})
|
||||
|
||||
@@ -4,7 +4,6 @@ from django.test.client import Client
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from path import path
|
||||
from tempfile import mkdtemp
|
||||
import json
|
||||
from fs.osfs import OSFS
|
||||
import copy
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import json
|
||||
import copy
|
||||
from time import time
|
||||
from uuid import uuid4
|
||||
from django.test import TestCase
|
||||
from django.conf import settings
|
||||
|
||||
@@ -20,13 +20,12 @@ class ModuleStoreTestCase(TestCase):
|
||||
def _pre_setup(self):
|
||||
super(ModuleStoreTestCase, self)._pre_setup()
|
||||
|
||||
# Use the current seconds since epoch to differentiate
|
||||
# Use a uuid to differentiate
|
||||
# the mongo collections on jenkins.
|
||||
sec_since_epoch = '%s' % int(time() * 100)
|
||||
self.orig_MODULESTORE = copy.deepcopy(settings.MODULESTORE)
|
||||
self.test_MODULESTORE = self.orig_MODULESTORE
|
||||
self.test_MODULESTORE['default']['OPTIONS']['collection'] = 'modulestore_%s' % sec_since_epoch
|
||||
self.test_MODULESTORE['direct']['OPTIONS']['collection'] = 'modulestore_%s' % sec_since_epoch
|
||||
self.test_MODULESTORE['default']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex
|
||||
self.test_MODULESTORE['direct']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex
|
||||
settings.MODULESTORE = self.test_MODULESTORE
|
||||
|
||||
# Flush and initialize the module store
|
||||
|
||||
@@ -59,6 +59,7 @@ from cms.djangoapps.models.settings.course_details import CourseDetails,\
|
||||
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
|
||||
from cms.djangoapps.contentstore.utils import get_modulestore
|
||||
from lxml import etree
|
||||
from django.shortcuts import redirect
|
||||
|
||||
# to install PIL on MacOSX: 'easy_install http://dist.repoze.org/PIL-1.1.6.tar.gz'
|
||||
|
||||
@@ -81,6 +82,11 @@ def signup(request):
|
||||
csrf_token = csrf(request)['csrf_token']
|
||||
return render_to_response('signup.html', {'csrf': csrf_token})
|
||||
|
||||
def old_login_redirect(request):
|
||||
'''
|
||||
Redirect to the active login url.
|
||||
'''
|
||||
return redirect('login', permanent=True)
|
||||
|
||||
@ssl_login_shortcut
|
||||
@ensure_csrf_cookie
|
||||
@@ -94,6 +100,11 @@ def login_page(request):
|
||||
'forgot_password_link': "//{base}/#forgot-password-modal".format(base=settings.LMS_BASE),
|
||||
})
|
||||
|
||||
def howitworks(request):
|
||||
if request.user.is_authenticated():
|
||||
return index(request)
|
||||
else:
|
||||
return render_to_response('howitworks.html', {})
|
||||
|
||||
# ==== Views for any logged-in user ==================================
|
||||
|
||||
@@ -120,7 +131,8 @@ def index(request):
|
||||
reverse('course_index', args=[
|
||||
course.location.org,
|
||||
course.location.course,
|
||||
course.location.name]))
|
||||
course.location.name]),
|
||||
get_lms_link_for_item(course.location))
|
||||
for course in courses],
|
||||
'user': request.user,
|
||||
'disable_course_creation': settings.MITX_FEATURES.get('DISABLE_COURSE_CREATION', False) and not request.user.is_staff
|
||||
@@ -161,6 +173,8 @@ def course_index(request, org, course, name):
|
||||
if not has_access(request.user, location):
|
||||
raise PermissionDenied()
|
||||
|
||||
lms_link = get_lms_link_for_item(location)
|
||||
|
||||
upload_asset_callback_url = reverse('upload_asset', kwargs={
|
||||
'org': org,
|
||||
'course': course,
|
||||
@@ -173,6 +187,7 @@ def course_index(request, org, course, name):
|
||||
return render_to_response('overview.html', {
|
||||
'active_tab': 'courseware',
|
||||
'context_course': course,
|
||||
'lms_link': lms_link,
|
||||
'sections': sections,
|
||||
'course_graders': json.dumps(CourseGradingModel.fetch(course.location).graders),
|
||||
'parent_location': course.location,
|
||||
@@ -273,7 +288,7 @@ def edit_unit(request, location):
|
||||
template.display_name,
|
||||
template.location.url(),
|
||||
'markdown' in template.metadata,
|
||||
template.location.name == 'Empty'
|
||||
'empty' in template.metadata
|
||||
))
|
||||
|
||||
components = [
|
||||
@@ -730,8 +745,6 @@ def clone_item(request):
|
||||
|
||||
#@login_required
|
||||
#@ensure_csrf_cookie
|
||||
|
||||
|
||||
def upload_asset(request, org, course, coursename):
|
||||
'''
|
||||
cdodge: this method allows for POST uploading of files into the course asset library, which will
|
||||
@@ -796,8 +809,6 @@ def upload_asset(request, org, course, coursename):
|
||||
'''
|
||||
This view will return all CMS users who are editors for the specified course
|
||||
'''
|
||||
|
||||
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
def manage_users(request, location):
|
||||
@@ -819,7 +830,7 @@ def manage_users(request, location):
|
||||
})
|
||||
|
||||
|
||||
def create_json_response(errmsg=None):
|
||||
def create_json_response(errmsg = None):
|
||||
if errmsg is not None:
|
||||
resp = HttpResponse(json.dumps({'Status': 'Failed', 'ErrMsg': errmsg}))
|
||||
else:
|
||||
@@ -831,8 +842,6 @@ def create_json_response(errmsg=None):
|
||||
This POST-back view will add a user - specified by email - to the list of editors for
|
||||
the specified course
|
||||
'''
|
||||
|
||||
|
||||
@expect_json
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
@@ -865,8 +874,6 @@ def add_user(request, location):
|
||||
This POST-back view will remove a user - specified by email - from the list of editors for
|
||||
the specified course
|
||||
'''
|
||||
|
||||
|
||||
@expect_json
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
@@ -1124,8 +1131,31 @@ def get_course_settings(request, org, course, name):
|
||||
course_details = CourseDetails.fetch(location)
|
||||
|
||||
return render_to_response('settings.html', {
|
||||
'active_tab': 'settings',
|
||||
'context_course': course_module,
|
||||
'course_location' : location,
|
||||
'course_details' : json.dumps(course_details, cls=CourseSettingsEncoder)
|
||||
})
|
||||
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
def course_config_graders_page(request, org, course, name):
|
||||
"""
|
||||
Send models and views as well as html for editing the course settings to the client.
|
||||
|
||||
org, course, name: Attributes of the Location for the item to edit
|
||||
"""
|
||||
location = ['i4x', org, course, 'course', name]
|
||||
|
||||
# check that logged in user has permissions to this item
|
||||
if not has_access(request.user, location):
|
||||
raise PermissionDenied()
|
||||
|
||||
course_module = modulestore().get_item(location)
|
||||
course_details = CourseGradingModel.fetch(location)
|
||||
|
||||
return render_to_response('settings_graders.html', {
|
||||
'context_course': course_module,
|
||||
'course_location' : location,
|
||||
'course_details': json.dumps(course_details, cls=CourseSettingsEncoder)
|
||||
})
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ Longer TODO:
|
||||
"""
|
||||
|
||||
import sys
|
||||
import tempfile
|
||||
import os.path
|
||||
import os
|
||||
import lms.envs.common
|
||||
@@ -59,7 +58,8 @@ sys.path.append(COMMON_ROOT / 'lib')
|
||||
|
||||
############################# WEB CONFIGURATION #############################
|
||||
# This is where we stick our compiled template files.
|
||||
MAKO_MODULE_DIR = tempfile.mkdtemp('mako')
|
||||
from tempdir import mkdtemp_clean
|
||||
MAKO_MODULE_DIR = mkdtemp_clean('mako')
|
||||
MAKO_TEMPLATES = {}
|
||||
MAKO_TEMPLATES['main'] = [
|
||||
PROJECT_ROOT / 'templates',
|
||||
@@ -74,8 +74,8 @@ TEMPLATE_DIRS = MAKO_TEMPLATES['main']
|
||||
|
||||
MITX_ROOT_URL = ''
|
||||
|
||||
LOGIN_REDIRECT_URL = MITX_ROOT_URL + '/login'
|
||||
LOGIN_URL = MITX_ROOT_URL + '/login'
|
||||
LOGIN_REDIRECT_URL = MITX_ROOT_URL + '/signin'
|
||||
LOGIN_URL = MITX_ROOT_URL + '/signin'
|
||||
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
|
||||
@@ -1,69 +1,37 @@
|
||||
<li class="input input-existing multi course-grading-assignment-list-item">
|
||||
<div class="row row-col2">
|
||||
<label for="course-grading-assignment-name">Assignment Type Name:</label>
|
||||
<li class="field-group course-grading-assignment-list-item">
|
||||
<div class="field text" id="field-course-grading-assignment-name">
|
||||
<label for="course-grading-assignment-name">Assignment Type Name</label>
|
||||
<input type="text" class="long" id="course-grading-assignment-name" value="<%= model.get('type') %>" />
|
||||
<span class="tip tip-stacked">e.g. Homework, Midterm Exams</span>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="input course-grading-assignment-name">
|
||||
<input type="text" class="long"
|
||||
id="course-grading-assignment-name" value="<%= model.get('type') %>">
|
||||
<span class="tip tip-stacked">e.g. Homework, Labs, Midterm Exams, Final Exam</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-grading-shortname">Abbreviation:</label>
|
||||
|
||||
<div class="field">
|
||||
<div class="input course-grading-shortname">
|
||||
<input type="text" class="short"
|
||||
id="course-grading-assignment-shortname"
|
||||
value="<%= model.get('short_label') %>">
|
||||
<span class="tip tip-inline">e.g. HW, Midterm, Final</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-grading-gradeweight">Weight of Total
|
||||
Grade:</label>
|
||||
|
||||
<div class="field">
|
||||
<div class="input course-grading-gradeweight">
|
||||
<input type="text" class="short"
|
||||
id="course-grading-assignment-gradeweight"
|
||||
value = "<%= model.get('weight') %>">
|
||||
<span class="tip tip-inline">e.g. 25%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-grading-assignment-totalassignments">Total
|
||||
Number:</label>
|
||||
|
||||
<div class="field">
|
||||
<div class="input course-grading-totalassignments">
|
||||
<input type="text" class="short"
|
||||
id="course-grading-assignment-totalassignments"
|
||||
value = "<%= model.get('min_count') %>">
|
||||
<span class="tip tip-inline">total exercises assigned</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-grading-assignment-droppable">Number of
|
||||
Droppable:</label>
|
||||
|
||||
<div class="field">
|
||||
<div class="input course-grading-droppable">
|
||||
<input type="text" class="short"
|
||||
id="course-grading-assignment-droppable"
|
||||
value = "<%= model.get('drop_count') %>">
|
||||
<span class="tip tip-inline">total exercises that won't be graded</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" class="delete-button standard remove-item remove-grading-data"><span class="delete-icon"></span>Delete</a>
|
||||
<div class="field text" id="field-course-grading-assignment-shortname">
|
||||
<label for="course-grading-assignment-shortname">Abbreviation:</label>
|
||||
<input type="text" class="short" id="course-grading-assignment-shortname" value="<%= model.get('short_label') %>" />
|
||||
<span class="tip tip-inline">e.g. HW, Midterm</span>
|
||||
</div>
|
||||
|
||||
<div class="field text" id="field-course-grading-assignment-gradeweight">
|
||||
<label for="course-grading-assignment-gradeweight">Weight of Total Grade</label>
|
||||
<input type="text" class="short" id="course-grading-assignment-gradeweight" value = "<%= model.get('weight') %>" />
|
||||
<span class="tip tip-inline">e.g. 25%</span>
|
||||
</div>
|
||||
|
||||
<div class="field text" id="field-course-grading-assignment-totalassignments">
|
||||
<label for="course-grading-assignment-totalassignments">Total
|
||||
Number</label>
|
||||
<input type="text" class="short" id="course-grading-assignment-totalassignments" value = "<%= model.get('min_count') %>" />
|
||||
<span class="tip tip-inline">total exercises assigned</span>
|
||||
</div>
|
||||
|
||||
<div class="field text" id="field-course-grading-assignment-droppable">
|
||||
<label for="course-grading-assignment-droppable">Number of
|
||||
Droppable</label>
|
||||
<input type="text" class="short" id="course-grading-assignment-droppable" value = "<%= model.get('drop_count') %>" />
|
||||
<span class="tip tip-inline">total exercises that won't be graded</span>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<a href="#" class="button delete-button standard remove-item remove-grading-data"><span class="delete-icon"></span>Delete</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
class CMS.Views.TabsEdit extends Backbone.View
|
||||
events:
|
||||
'click .new-tab': 'addNewTab'
|
||||
|
||||
initialize: =>
|
||||
@$('.component').each((idx, element) =>
|
||||
@@ -13,6 +11,7 @@ class CMS.Views.TabsEdit extends Backbone.View
|
||||
)
|
||||
)
|
||||
|
||||
@options.mast.find('.new-tab').on('click', @addNewTab)
|
||||
@$('.components').sortable(
|
||||
handle: '.drag-handle'
|
||||
update: @tabMoved
|
||||
|
||||
BIN
cms/static/img/hiw-feature1.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
cms/static/img/hiw-feature2.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
cms/static/img/hiw-feature3.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 581 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 737 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 412 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 797 B |
|
Before Width: | Height: | Size: 994 B After Width: | Height: | Size: 234 B |
BIN
cms/static/img/logo-edx-studio-white.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
cms/static/img/logo-edx-studio.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
cms/static/img/pl-1x1-000.png
Normal file
|
After Width: | Height: | Size: 924 B |
BIN
cms/static/img/pl-1x1-fff.png
Normal file
|
After Width: | Height: | Size: 925 B |
BIN
cms/static/img/preview-lms-staticpages.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
cms/static/img/thumb-hiw-feature1.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
cms/static/img/thumb-hiw-feature2.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
cms/static/img/thumb-hiw-feature3.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
@@ -5,7 +5,7 @@ var $newComponentItem;
|
||||
var $changedInput;
|
||||
var $spinner;
|
||||
|
||||
$(document).ready(function() {
|
||||
$(document).ready(function () {
|
||||
$body = $('body');
|
||||
$modal = $('.history-modal');
|
||||
$modalCover = $('<div class="modal-cover">');
|
||||
@@ -13,7 +13,7 @@ $(document).ready(function() {
|
||||
// pipelining (note, this doesn't happen on local runtimes). So if we set it on window, when we can access it from other
|
||||
// scopes (namely the course-info tab)
|
||||
window.$modalCover = $modalCover;
|
||||
|
||||
|
||||
// Control whether template caching in local memory occurs (see template_loader.js). Caching screws up development but may
|
||||
// be a good optimization in production (it works fairly well)
|
||||
window.cachetemplates = false;
|
||||
@@ -31,25 +31,73 @@ $(document).ready(function() {
|
||||
|
||||
$modal.bind('click', hideModal);
|
||||
$modalCover.bind('click', hideModal);
|
||||
$('.assets .upload-button').bind('click', showUploadModal);
|
||||
$('.uploads .upload-button').bind('click', showUploadModal);
|
||||
$('.upload-modal .close-button').bind('click', hideModal);
|
||||
|
||||
$body.on('click', '.embeddable-xml-input', function(){ $(this).select(); });
|
||||
$body.on('click', '.embeddable-xml-input', function () {
|
||||
$(this).select();
|
||||
});
|
||||
|
||||
$('.unit .item-actions .delete-button').bind('click', deleteUnit);
|
||||
$('.new-unit-item').bind('click', createNewUnit);
|
||||
|
||||
$('body').addClass('js');
|
||||
|
||||
// lean/simple modal
|
||||
$('a[rel*=modal]').leanModal({overlay : 0.80, closeButton: '.action-modal-close' });
|
||||
$('a.action-modal-close').click(function(e){
|
||||
(e).preventDefault();
|
||||
});
|
||||
|
||||
// nav - dropdown related
|
||||
$body.click(function (e) {
|
||||
$('.nav-dropdown .nav-item .wrapper-nav-sub').removeClass('is-shown');
|
||||
$('.nav-dropdown .nav-item .title').removeClass('is-selected');
|
||||
});
|
||||
|
||||
$('.nav-dropdown .nav-item .title').click(function (e) {
|
||||
|
||||
$subnav = $(this).parent().find('.wrapper-nav-sub');
|
||||
$title = $(this).parent().find('.title');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if ($subnav.hasClass('is-shown')) {
|
||||
$subnav.removeClass('is-shown');
|
||||
$title.removeClass('is-selected');
|
||||
}
|
||||
|
||||
else {
|
||||
$('.nav-dropdown .nav-item .title').removeClass('is-selected');
|
||||
$('.nav-dropdown .nav-item .wrapper-nav-sub').removeClass('is-shown');
|
||||
$title.addClass('is-selected');
|
||||
$subnav.addClass('is-shown');
|
||||
}
|
||||
});
|
||||
|
||||
// general link management - new window/tab
|
||||
$('a[rel="external"]').attr('title', 'This link will open in a new browser window/tab').click(function (e) {
|
||||
window.open($(this).attr('href'));
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// general link management - lean modal window
|
||||
$('a[rel="modal"]').attr('title', 'This link will open in a modal window').leanModal({overlay: 0.50, closeButton: '.action-modal-close' });
|
||||
$('.action-modal-close').click(function (e) {
|
||||
(e).preventDefault();
|
||||
});
|
||||
|
||||
// toggling overview section details
|
||||
$(function(){
|
||||
if($('.courseware-section').length > 0) {
|
||||
$('.toggle-button-sections').addClass('is-shown');
|
||||
}
|
||||
$(function () {
|
||||
if ($('.courseware-section').length > 0) {
|
||||
$('.toggle-button-sections').addClass('is-shown');
|
||||
}
|
||||
});
|
||||
$('.toggle-button-sections').bind('click', toggleSections);
|
||||
|
||||
// autosave when a field is updated on the subsection page
|
||||
$body.on('keyup', '.subsection-display-name-input, .unit-subtitle, .policy-list-value', checkForNewValue);
|
||||
$('.subsection-display-name-input, .unit-subtitle, .policy-list-name, .policy-list-value').each(function(i) {
|
||||
$('.subsection-display-name-input, .unit-subtitle, .policy-list-name, .policy-list-value').each(function (i) {
|
||||
this.val = $(this).val();
|
||||
});
|
||||
$("#start_date, #start_time, #due_date, #due_time").bind('change', autosaveInput);
|
||||
@@ -61,7 +109,7 @@ $(document).ready(function() {
|
||||
// add new/delete section
|
||||
$('.new-courseware-section-button').bind('click', addNewSection);
|
||||
$('.delete-section-button').bind('click', deleteSection);
|
||||
|
||||
|
||||
// add new/delete subsection
|
||||
$('.new-subsection-item').bind('click', addNewSubsection);
|
||||
$('.delete-subsection-button').bind('click', deleteSubsection);
|
||||
@@ -75,7 +123,7 @@ $(document).ready(function() {
|
||||
|
||||
// import form setup
|
||||
$('.import .file-input').bind('change', showImportSubmit);
|
||||
$('.import .choose-file-button, .import .choose-file-button-inline').bind('click', function(e) {
|
||||
$('.import .choose-file-button, .import .choose-file-button-inline').bind('click', function (e) {
|
||||
e.preventDefault();
|
||||
$('.import .file-input').click();
|
||||
});
|
||||
@@ -98,12 +146,12 @@ $(document).ready(function() {
|
||||
$body.on('click', '.section-published-date .schedule-button', editSectionPublishDate);
|
||||
$body.on('click', '.edit-subsection-publish-settings .save-button', saveSetSectionScheduleDate);
|
||||
$body.on('click', '.edit-subsection-publish-settings .cancel-button', hideModal);
|
||||
$body.on('change', '.edit-subsection-publish-settings .start-date', function() {
|
||||
if($('.edit-subsection-publish-settings').find('.start-time').val() == '') {
|
||||
$('.edit-subsection-publish-settings').find('.start-time').val('12:00am');
|
||||
$body.on('change', '.edit-subsection-publish-settings .start-date', function () {
|
||||
if ($('.edit-subsection-publish-settings').find('.start-time').val() == '') {
|
||||
$('.edit-subsection-publish-settings').find('.start-time').val('12:00am');
|
||||
}
|
||||
});
|
||||
$('.edit-subsection-publish-settings').on('change', '.start-date, .start-time', function() {
|
||||
$('.edit-subsection-publish-settings').on('change', '.start-date, .start-time', function () {
|
||||
$('.edit-subsection-publish-settings').find('.save-button').show();
|
||||
});
|
||||
});
|
||||
@@ -114,26 +162,26 @@ $(document).ready(function() {
|
||||
// }
|
||||
|
||||
function toggleSections(e) {
|
||||
e.preventDefault();
|
||||
e.preventDefault();
|
||||
|
||||
$section = $('.courseware-section');
|
||||
sectionCount = $section.length;
|
||||
$button = $(this);
|
||||
$labelCollapsed = $('<i class="ss-icon ss-symbolicons-block">up</i> <span class="label">Collapse All Sections</span>');
|
||||
$labelExpanded = $('<i class="ss-icon ss-symbolicons-block">down</i> <span class="label">Expand All Sections</span>');
|
||||
$section = $('.courseware-section');
|
||||
sectionCount = $section.length;
|
||||
$button = $(this);
|
||||
$labelCollapsed = $('<i class="ss-icon ss-symbolicons-block">up</i> <span class="label">Collapse All Sections</span>');
|
||||
$labelExpanded = $('<i class="ss-icon ss-symbolicons-block">down</i> <span class="label">Expand All Sections</span>');
|
||||
|
||||
var buttonLabel = $button.hasClass('is-activated') ? $labelCollapsed : $labelExpanded;
|
||||
$button.toggleClass('is-activated').html(buttonLabel);
|
||||
var buttonLabel = $button.hasClass('is-activated') ? $labelCollapsed : $labelExpanded;
|
||||
$button.toggleClass('is-activated').html(buttonLabel);
|
||||
|
||||
if($button.hasClass('is-activated')) {
|
||||
$section.addClass('collapsed');
|
||||
// first child in order to avoid the icons on the subsection lists which are not in the first child
|
||||
$section.find('header .expand-collapse-icon').removeClass('collapse').addClass('expand');
|
||||
} else {
|
||||
$section.removeClass('collapsed');
|
||||
// first child in order to avoid the icons on the subsection lists which are not in the first child
|
||||
$section.find('header .expand-collapse-icon').removeClass('expand').addClass('collapse');
|
||||
}
|
||||
if ($button.hasClass('is-activated')) {
|
||||
$section.addClass('collapsed');
|
||||
// first child in order to avoid the icons on the subsection lists which are not in the first child
|
||||
$section.find('header .expand-collapse-icon').removeClass('collapse').addClass('expand');
|
||||
} else {
|
||||
$section.removeClass('collapsed');
|
||||
// first child in order to avoid the icons on the subsection lists which are not in the first child
|
||||
$section.find('header .expand-collapse-icon').removeClass('expand').addClass('collapse');
|
||||
}
|
||||
}
|
||||
|
||||
function editSectionPublishDate(e) {
|
||||
@@ -143,16 +191,16 @@ function editSectionPublishDate(e) {
|
||||
$modal.attr('data-id', $(this).attr('data-id'));
|
||||
$modal.find('.start-date').val($(this).attr('data-date'));
|
||||
$modal.find('.start-time').val($(this).attr('data-time'));
|
||||
if($modal.find('.start-date').val() == '' && $modal.find('.start-time').val() == '') {
|
||||
if ($modal.find('.start-date').val() == '' && $modal.find('.start-time').val() == '') {
|
||||
$modal.find('.save-button').hide();
|
||||
}
|
||||
}
|
||||
$modal.find('.section-name').html('"' + $(this).closest('.courseware-section').find('.section-name-span').text() + '"');
|
||||
$modalCover.show();
|
||||
}
|
||||
|
||||
function showImportSubmit(e) {
|
||||
var filepath = $(this).val();
|
||||
if(filepath.substr(filepath.length - 6, 6) == 'tar.gz') {
|
||||
if (filepath.substr(filepath.length - 6, 6) == 'tar.gz') {
|
||||
$('.error-block').hide();
|
||||
$('.file-name').html($(this).val().replace('C:\\fakepath\\', ''));
|
||||
$('.file-name-block').show();
|
||||
@@ -173,7 +221,7 @@ function syncReleaseDate(e) {
|
||||
|
||||
function addPolicyMetadata(e) {
|
||||
e.preventDefault();
|
||||
var template =$('#add-new-policy-element-template > li');
|
||||
var template = $('#add-new-policy-element-template > li');
|
||||
var newNode = template.clone();
|
||||
var _parent_el = $(this).parent('ol:.policy-list');
|
||||
newNode.insertBefore('.add-policy-data');
|
||||
@@ -195,7 +243,7 @@ function cancelPolicyMetadata(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $policyElement = $(this).parents('.policy-list-element');
|
||||
if(!$policyElement.hasClass('editing')) {
|
||||
if (!$policyElement.hasClass('editing')) {
|
||||
$policyElement.remove();
|
||||
} else {
|
||||
$policyElement.removeClass('new-policy-list-element');
|
||||
@@ -208,13 +256,13 @@ function cancelPolicyMetadata(e) {
|
||||
function removePolicyMetadata(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if(!confirm('Are you sure you wish to delete this item. It cannot be reversed!'))
|
||||
return;
|
||||
|
||||
if (!confirm('Are you sure you wish to delete this item. It cannot be reversed!'))
|
||||
return;
|
||||
|
||||
policy_name = $(this).data('policy-name');
|
||||
var _parent_el = $(this).parent('li:.policy-list-element');
|
||||
if ($(_parent_el).hasClass("new-policy-list-element")) {
|
||||
_parent_el.remove();
|
||||
_parent_el.remove();
|
||||
} else {
|
||||
_parent_el.appendTo("#policy-to-delete");
|
||||
}
|
||||
@@ -225,7 +273,7 @@ function getEdxTimeFromDateTimeVals(date_val, time_val, format) {
|
||||
var edxTimeStr = null;
|
||||
|
||||
if (date_val != '') {
|
||||
if (time_val == '')
|
||||
if (time_val == '')
|
||||
time_val = '00:00';
|
||||
|
||||
// Note, we are using date.js utility which has better parsing abilities than the built in JS date parsing
|
||||
@@ -240,30 +288,30 @@ function getEdxTimeFromDateTimeVals(date_val, time_val, format) {
|
||||
}
|
||||
|
||||
function getEdxTimeFromDateTimeInputs(date_id, time_id, format) {
|
||||
var input_date = $('#'+date_id).val();
|
||||
var input_time = $('#'+time_id).val();
|
||||
var input_date = $('#' + date_id).val();
|
||||
var input_time = $('#' + time_id).val();
|
||||
|
||||
return getEdxTimeFromDateTimeVals(input_date, input_time, format);
|
||||
}
|
||||
|
||||
function checkForNewValue(e) {
|
||||
if($(this).parents('.new-policy-list-element')[0]) {
|
||||
if ($(this).parents('.new-policy-list-element')[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.val) {
|
||||
this.hasChanged = this.val != $(this).val();
|
||||
if (this.val) {
|
||||
this.hasChanged = this.val != $(this).val();
|
||||
} else {
|
||||
this.hasChanged = false;
|
||||
}
|
||||
|
||||
this.val = $(this).val();
|
||||
if(this.hasChanged) {
|
||||
if(this.saveTimer) {
|
||||
if (this.hasChanged) {
|
||||
if (this.saveTimer) {
|
||||
clearTimeout(this.saveTimer);
|
||||
}
|
||||
|
||||
this.saveTimer = setTimeout(function() {
|
||||
this.saveTimer = setTimeout(function () {
|
||||
$changedInput = $(e.target);
|
||||
saveSubsection();
|
||||
this.saveTimer = null;
|
||||
@@ -272,11 +320,11 @@ function checkForNewValue(e) {
|
||||
}
|
||||
|
||||
function autosaveInput(e) {
|
||||
if(this.saveTimer) {
|
||||
if (this.saveTimer) {
|
||||
clearTimeout(this.saveTimer);
|
||||
}
|
||||
|
||||
this.saveTimer = setTimeout(function() {
|
||||
this.saveTimer = setTimeout(function () {
|
||||
$changedInput = $(e.target);
|
||||
saveSubsection();
|
||||
this.saveTimer = null;
|
||||
@@ -284,7 +332,7 @@ function autosaveInput(e) {
|
||||
}
|
||||
|
||||
function saveSubsection() {
|
||||
if($changedInput && !$changedInput.hasClass('no-spinner')) {
|
||||
if ($changedInput && !$changedInput.hasClass('no-spinner')) {
|
||||
$spinner.css({
|
||||
'position': 'absolute',
|
||||
'top': Math.floor($changedInput.position().top + ($changedInput.outerHeight() / 2) + 3),
|
||||
@@ -294,30 +342,30 @@ function saveSubsection() {
|
||||
$changedInput.after($spinner);
|
||||
$spinner.show();
|
||||
}
|
||||
|
||||
|
||||
var id = $('.subsection-body').data('id');
|
||||
|
||||
// pull all 'normalized' metadata editable fields on page
|
||||
var metadata_fields = $('input[data-metadata-name]');
|
||||
|
||||
|
||||
var metadata = {};
|
||||
for(var i=0; i< metadata_fields.length;i++) {
|
||||
var el = metadata_fields[i];
|
||||
metadata[$(el).data("metadata-name")] = el.value;
|
||||
}
|
||||
for (var i = 0; i < metadata_fields.length; i++) {
|
||||
var el = metadata_fields[i];
|
||||
metadata[$(el).data("metadata-name")] = el.value;
|
||||
}
|
||||
|
||||
// now add 'free-formed' metadata which are presented to the user as dual input fields (name/value)
|
||||
$('ol.policy-list > li.policy-list-element').each( function(i, element) {
|
||||
$('ol.policy-list > li.policy-list-element').each(function (i, element) {
|
||||
var name = $(element).children('.policy-list-name').val();
|
||||
metadata[name] = $(element).children('.policy-list-value').val();
|
||||
});
|
||||
|
||||
// now add any 'removed' policy metadata which is stored in a separate hidden div
|
||||
// 'null' presented to the server means 'remove'
|
||||
$("#policy-to-delete > li.policy-list-element").each(function(i, element) {
|
||||
$("#policy-to-delete > li.policy-list-element").each(function (i, element) {
|
||||
var name = $(element).children('.policy-list-name').val();
|
||||
if (name != "")
|
||||
metadata[name] = null;
|
||||
metadata[name] = null;
|
||||
});
|
||||
|
||||
// Piece back together the date/time UI elements into one date/time string
|
||||
@@ -327,18 +375,18 @@ function saveSubsection() {
|
||||
metadata['due'] = getEdxTimeFromDateTimeInputs('due_date', 'due_time', 'MMMM dd HH:mm');
|
||||
|
||||
$.ajax({
|
||||
url: "/save_item",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
data:JSON.stringify({ 'id' : id, 'metadata' : metadata}),
|
||||
success: function() {
|
||||
url: "/save_item",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ 'id': id, 'metadata': metadata}),
|
||||
success: function () {
|
||||
$spinner.delay(500).fadeOut(150);
|
||||
},
|
||||
error: function() {
|
||||
},
|
||||
error: function () {
|
||||
showToastMessage('There has been an error while saving your changes.');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -349,14 +397,14 @@ function createNewUnit(e) {
|
||||
template = $(this).data('template');
|
||||
|
||||
$.post('/clone_item',
|
||||
{'parent_location' : parent,
|
||||
'template' : template,
|
||||
'display_name': 'New Unit'
|
||||
},
|
||||
function(data) {
|
||||
// redirect to the edit page
|
||||
window.location = "/edit/" + data['id'];
|
||||
});
|
||||
{'parent_location': parent,
|
||||
'template': template,
|
||||
'display_name': 'New Unit'
|
||||
},
|
||||
function (data) {
|
||||
// redirect to the edit page
|
||||
window.location = "/edit/" + data['id'];
|
||||
});
|
||||
}
|
||||
|
||||
function deleteUnit(e) {
|
||||
@@ -375,16 +423,16 @@ function deleteSection(e) {
|
||||
}
|
||||
|
||||
function _deleteItem($el) {
|
||||
if(!confirm('Are you sure you wish to delete this item. It cannot be reversed!'))
|
||||
return;
|
||||
|
||||
if (!confirm('Are you sure you wish to delete this item. It cannot be reversed!'))
|
||||
return;
|
||||
|
||||
var id = $el.data('id');
|
||||
|
||||
$.post('/delete_item',
|
||||
{'id': id, 'delete_children' : true, 'delete_all_versions' : true},
|
||||
function(data) {
|
||||
$el.remove();
|
||||
});
|
||||
|
||||
$.post('/delete_item',
|
||||
{'id': id, 'delete_children': true, 'delete_all_versions': true},
|
||||
function (data) {
|
||||
$el.remove();
|
||||
});
|
||||
}
|
||||
|
||||
function showUploadModal(e) {
|
||||
@@ -411,7 +459,7 @@ function startUpload(e) {
|
||||
$('.upload-modal .progress-bar').removeClass('loaded').show();
|
||||
}
|
||||
|
||||
function resetUploadBar(){
|
||||
function resetUploadBar() {
|
||||
var percentVal = '0%';
|
||||
$('.upload-modal .progress-fill').width(percentVal);
|
||||
$('.upload-modal .progress-fill').html(percentVal);
|
||||
@@ -424,7 +472,7 @@ function showUploadFeedback(event, position, total, percentComplete) {
|
||||
}
|
||||
|
||||
function displayFinishedUpload(xhr) {
|
||||
if(xhr.status = 200){
|
||||
if (xhr.status = 200) {
|
||||
markAsLoaded();
|
||||
}
|
||||
|
||||
@@ -448,10 +496,10 @@ function displayFinishedUpload(xhr) {
|
||||
function markAsLoaded() {
|
||||
$('.upload-modal .copy-button').css('display', 'inline-block');
|
||||
$('.upload-modal .progress-bar').addClass('loaded');
|
||||
}
|
||||
}
|
||||
|
||||
function hideModal(e) {
|
||||
if(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
// Unit editors do not want the modal cover to hide when users click outside
|
||||
@@ -465,7 +513,7 @@ function hideModal(e) {
|
||||
}
|
||||
|
||||
function onKeyUp(e) {
|
||||
if(e.which == 87) {
|
||||
if (e.which == 87) {
|
||||
$body.toggleClass('show-wip hide-wip');
|
||||
}
|
||||
}
|
||||
@@ -515,14 +563,14 @@ function showToastMessage(message, $button, lifespan) {
|
||||
var $content = $('<div class="notification-content"></div>');
|
||||
$content.html(message);
|
||||
$toast.append($content);
|
||||
if($button) {
|
||||
if ($button) {
|
||||
$button.addClass('action-button');
|
||||
$button.bind('click', hideToastMessage);
|
||||
$content.append($button);
|
||||
}
|
||||
$closeBtn.bind('click', hideToastMessage);
|
||||
|
||||
if($('.toast-notification')[0]) {
|
||||
if ($('.toast-notification')[0]) {
|
||||
var targetY = $('.toast-notification').offset().top + $('.toast-notification').outerHeight();
|
||||
$toast.css('top', (targetY + 10) + 'px');
|
||||
}
|
||||
@@ -530,8 +578,8 @@ function showToastMessage(message, $button, lifespan) {
|
||||
$body.prepend($toast);
|
||||
$toast.fadeIn(200);
|
||||
|
||||
if(lifespan) {
|
||||
$toast.timer = setTimeout(function() {
|
||||
if (lifespan) {
|
||||
$toast.timer = setTimeout(function () {
|
||||
$toast.fadeOut(300);
|
||||
}, lifespan * 1000);
|
||||
}
|
||||
@@ -557,7 +605,7 @@ function addNewSection(e, isTemplate) {
|
||||
}
|
||||
|
||||
function checkForCancel(e) {
|
||||
if(e.which == 27) {
|
||||
if (e.which == 27) {
|
||||
$body.unbind('keyup', checkForCancel);
|
||||
e.data.$cancelButton.click();
|
||||
}
|
||||
@@ -573,11 +621,11 @@ function saveNewSection(e) {
|
||||
var display_name = $(this).find('.new-section-name').val();
|
||||
|
||||
$.post('/clone_item', {
|
||||
'parent_location' : parent,
|
||||
'template' : template,
|
||||
'parent_location': parent,
|
||||
'template': template,
|
||||
'display_name': display_name,
|
||||
},
|
||||
function(data) {
|
||||
function (data) {
|
||||
if (data.id != undefined)
|
||||
location.reload();
|
||||
}
|
||||
@@ -596,7 +644,7 @@ function addNewCourse(e) {
|
||||
$(e.target).hide();
|
||||
var $newCourse = $($('#new-course-template').html());
|
||||
var $cancelButton = $newCourse.find('.new-course-cancel');
|
||||
$('.new-course-button').after($newCourse);
|
||||
$('.inner-wrapper').prepend($newCourse);
|
||||
$newCourse.find('.new-course-name').focus().select();
|
||||
$newCourse.find('form').bind('submit', saveNewCourse);
|
||||
$cancelButton.bind('click', cancelNewCourse);
|
||||
@@ -612,18 +660,18 @@ function saveNewCourse(e) {
|
||||
var number = $newCourse.find('.new-course-number').val();
|
||||
var display_name = $newCourse.find('.new-course-name').val();
|
||||
|
||||
if (org == '' || number == '' || display_name == ''){
|
||||
if (org == '' || number == '' || display_name == '') {
|
||||
alert('You must specify all fields in order to create a new course.');
|
||||
return;
|
||||
}
|
||||
|
||||
$.post('/create_new_course', {
|
||||
'template' : template,
|
||||
'org' : org,
|
||||
'number' : number,
|
||||
'display_name': display_name
|
||||
'template': template,
|
||||
'org': org,
|
||||
'number': number,
|
||||
'display_name': display_name
|
||||
},
|
||||
function(data) {
|
||||
function (data) {
|
||||
if (data.id != undefined) {
|
||||
window.location = '/' + data.id.replace(/.*:\/\//, '');
|
||||
} else if (data.ErrMsg != undefined) {
|
||||
@@ -667,13 +715,13 @@ function saveNewSubsection(e) {
|
||||
var display_name = $(this).find('.new-subsection-name-input').val();
|
||||
|
||||
$.post('/clone_item', {
|
||||
'parent_location' : parent,
|
||||
'template' : template,
|
||||
'display_name': display_name
|
||||
'parent_location': parent,
|
||||
'template': template,
|
||||
'display_name': display_name
|
||||
},
|
||||
function(data) {
|
||||
function (data) {
|
||||
if (data.id != undefined) {
|
||||
location.reload();
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -720,21 +768,20 @@ function saveEditSectionName(e) {
|
||||
}
|
||||
|
||||
var $_this = $(this);
|
||||
// call into server to commit the new order
|
||||
// call into server to commit the new order
|
||||
$.ajax({
|
||||
url: "/save_item",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
data:JSON.stringify({ 'id' : id, 'metadata' : {'display_name' : display_name}})
|
||||
}).success(function()
|
||||
{
|
||||
$spinner.delay(250).fadeOut(250);
|
||||
$_this.closest('h3').find('.section-name-span').html(display_name).show();
|
||||
$_this.hide();
|
||||
$_this.closest('.section-name').bind('click', editSectionName);
|
||||
e.stopPropagation();
|
||||
});
|
||||
data: JSON.stringify({ 'id': id, 'metadata': {'display_name': display_name}})
|
||||
}).success(function () {
|
||||
$spinner.delay(250).fadeOut(250);
|
||||
$_this.closest('h3').find('.section-name-span').html(display_name).show();
|
||||
$_this.hide();
|
||||
$_this.closest('.section-name').bind('click', editSectionName);
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
function setSectionScheduleDate(e) {
|
||||
@@ -765,21 +812,20 @@ function saveSetSectionScheduleDate(e) {
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
data:JSON.stringify({ 'id' : id, 'metadata' : {'start' : start}})
|
||||
}).success(function()
|
||||
{
|
||||
var $thisSection = $('.courseware-section[data-id="' + id + '"]');
|
||||
$thisSection.find('.section-published-date').html('<span class="published-status"><strong>Will Release:</strong> ' + input_date + ' at ' + input_time + '</span><a href="#" class="edit-button" data-date="' + input_date + '" data-time="' + input_time + '" data-id="' + id + '">Edit</a>');
|
||||
$thisSection.find('.section-published-date').animate({
|
||||
'background-color': 'rgb(182,37,104)'
|
||||
}, 300).animate({
|
||||
'background-color': '#edf1f5'
|
||||
}, 300).animate({
|
||||
'background-color': 'rgb(182,37,104)'
|
||||
}, 300).animate({
|
||||
'background-color': '#edf1f5'
|
||||
}, 300);
|
||||
|
||||
hideModal();
|
||||
});
|
||||
}
|
||||
data: JSON.stringify({ 'id': id, 'metadata': {'start': start}})
|
||||
}).success(function () {
|
||||
var $thisSection = $('.courseware-section[data-id="' + id + '"]');
|
||||
$thisSection.find('.section-published-date').html('<span class="published-status"><strong>Will Release:</strong> ' + input_date + ' at ' + input_time + '</span><a href="#" class="edit-button" data-date="' + input_date + '" data-time="' + input_time + '" data-id="' + id + '">Edit</a>');
|
||||
$thisSection.find('.section-published-date').animate({
|
||||
'background-color': 'rgb(182,37,104)'
|
||||
}, 300).animate({
|
||||
'background-color': '#edf1f5'
|
||||
}, 300).animate({
|
||||
'background-color': 'rgb(182,37,104)'
|
||||
}, 300).animate({
|
||||
'background-color': '#edf1f5'
|
||||
}, 300);
|
||||
|
||||
hideModal();
|
||||
});
|
||||
}
|
||||
@@ -1,85 +1,83 @@
|
||||
if (!CMS.Models['Settings']) CMS.Models.Settings = new Object();
|
||||
|
||||
CMS.Models.Settings.CourseDetails = Backbone.Model.extend({
|
||||
defaults: {
|
||||
location : null, // the course's Location model, required
|
||||
start_date: null, // maps to 'start'
|
||||
end_date: null, // maps to 'end'
|
||||
enrollment_start: null,
|
||||
enrollment_end: null,
|
||||
syllabus: null,
|
||||
overview: "",
|
||||
intro_video: null,
|
||||
effort: null // an int or null
|
||||
},
|
||||
|
||||
// When init'g from html script, ensure you pass {parse: true} as an option (2nd arg to reset)
|
||||
parse: function(attributes) {
|
||||
if (attributes['course_location']) {
|
||||
attributes.location = new CMS.Models.Location(attributes.course_location, {parse:true});
|
||||
}
|
||||
if (attributes['start_date']) {
|
||||
attributes.start_date = new Date(attributes.start_date);
|
||||
}
|
||||
if (attributes['end_date']) {
|
||||
attributes.end_date = new Date(attributes.end_date);
|
||||
}
|
||||
if (attributes['enrollment_start']) {
|
||||
attributes.enrollment_start = new Date(attributes.enrollment_start);
|
||||
}
|
||||
if (attributes['enrollment_end']) {
|
||||
attributes.enrollment_end = new Date(attributes.enrollment_end);
|
||||
}
|
||||
return attributes;
|
||||
},
|
||||
|
||||
validate: function(newattrs) {
|
||||
// Returns either nothing (no return call) so that validate works or an object of {field: errorstring} pairs
|
||||
// A bit funny in that the video key validation is asynchronous; so, it won't stop the validation.
|
||||
var errors = {};
|
||||
if (newattrs.start_date && newattrs.end_date && newattrs.start_date >= newattrs.end_date) {
|
||||
errors.end_date = "The course end date cannot be before the course start date.";
|
||||
}
|
||||
if (newattrs.start_date && newattrs.enrollment_start && newattrs.start_date < newattrs.enrollment_start) {
|
||||
errors.enrollment_start = "The course start date cannot be before the enrollment start date.";
|
||||
}
|
||||
if (newattrs.enrollment_start && newattrs.enrollment_end && newattrs.enrollment_start >= newattrs.enrollment_end) {
|
||||
errors.enrollment_end = "The enrollment start date cannot be after the enrollment end date.";
|
||||
}
|
||||
if (newattrs.end_date && newattrs.enrollment_end && newattrs.end_date < newattrs.enrollment_end) {
|
||||
errors.enrollment_end = "The enrollment end date cannot be after the course end date.";
|
||||
}
|
||||
if (newattrs.intro_video && newattrs.intro_video !== this.get('intro_video')) {
|
||||
if (this._videokey_illegal_chars.exec(newattrs.intro_video)) {
|
||||
errors.intro_video = "Key should only contain letters, numbers, _, or -";
|
||||
}
|
||||
// TODO check if key points to a real video using google's youtube api
|
||||
}
|
||||
if (!_.isEmpty(errors)) return errors;
|
||||
// NOTE don't return empty errors as that will be interpreted as an error state
|
||||
},
|
||||
|
||||
url: function() {
|
||||
var location = this.get('location');
|
||||
return '/' + location.get('org') + "/" + location.get('course') + '/settings/' + location.get('name') + '/section/details';
|
||||
},
|
||||
|
||||
_videokey_illegal_chars : /[^a-zA-Z0-9_-]/g,
|
||||
save_videosource: function(newsource) {
|
||||
// newsource either is <video youtube="speed:key, *"/> or just the "speed:key, *" string
|
||||
// returns the videosource for the preview which iss the key whose speed is closest to 1
|
||||
if (_.isEmpty(newsource) && !_.isEmpty(this.get('intro_video'))) this.save({'intro_video': null},
|
||||
{ error : CMS.ServerError});
|
||||
// TODO remove all whitespace w/in string
|
||||
else {
|
||||
if (this.get('intro_video') !== newsource) this.save('intro_video', newsource,
|
||||
{ error : CMS.ServerError});
|
||||
}
|
||||
|
||||
return this.videosourceSample();
|
||||
},
|
||||
videosourceSample : function() {
|
||||
if (this.has('intro_video')) return "http://www.youtube.com/embed/" + this.get('intro_video');
|
||||
else return "";
|
||||
}
|
||||
defaults: {
|
||||
location : null, // the course's Location model, required
|
||||
start_date: null, // maps to 'start'
|
||||
end_date: null, // maps to 'end'
|
||||
enrollment_start: null,
|
||||
enrollment_end: null,
|
||||
syllabus: null,
|
||||
overview: "",
|
||||
intro_video: null,
|
||||
effort: null // an int or null
|
||||
},
|
||||
|
||||
// When init'g from html script, ensure you pass {parse: true} as an option (2nd arg to reset)
|
||||
parse: function(attributes) {
|
||||
if (attributes['course_location']) {
|
||||
attributes.location = new CMS.Models.Location(attributes.course_location, {parse:true});
|
||||
}
|
||||
if (attributes['start_date']) {
|
||||
attributes.start_date = new Date(attributes.start_date);
|
||||
}
|
||||
if (attributes['end_date']) {
|
||||
attributes.end_date = new Date(attributes.end_date);
|
||||
}
|
||||
if (attributes['enrollment_start']) {
|
||||
attributes.enrollment_start = new Date(attributes.enrollment_start);
|
||||
}
|
||||
if (attributes['enrollment_end']) {
|
||||
attributes.enrollment_end = new Date(attributes.enrollment_end);
|
||||
}
|
||||
return attributes;
|
||||
},
|
||||
|
||||
validate: function(newattrs) {
|
||||
// Returns either nothing (no return call) so that validate works or an object of {field: errorstring} pairs
|
||||
// A bit funny in that the video key validation is asynchronous; so, it won't stop the validation.
|
||||
var errors = {};
|
||||
if (newattrs.start_date && newattrs.end_date && newattrs.start_date >= newattrs.end_date) {
|
||||
errors.end_date = "The course end date cannot be before the course start date.";
|
||||
}
|
||||
if (newattrs.start_date && newattrs.enrollment_start && newattrs.start_date < newattrs.enrollment_start) {
|
||||
errors.enrollment_start = "The course start date cannot be before the enrollment start date.";
|
||||
}
|
||||
if (newattrs.enrollment_start && newattrs.enrollment_end && newattrs.enrollment_start >= newattrs.enrollment_end) {
|
||||
errors.enrollment_end = "The enrollment start date cannot be after the enrollment end date.";
|
||||
}
|
||||
if (newattrs.end_date && newattrs.enrollment_end && newattrs.end_date < newattrs.enrollment_end) {
|
||||
errors.enrollment_end = "The enrollment end date cannot be after the course end date.";
|
||||
}
|
||||
if (newattrs.intro_video && newattrs.intro_video !== this.get('intro_video')) {
|
||||
if (this._videokey_illegal_chars.exec(newattrs.intro_video)) {
|
||||
errors.intro_video = "Key should only contain letters, numbers, _, or -";
|
||||
}
|
||||
// TODO check if key points to a real video using google's youtube api
|
||||
}
|
||||
if (!_.isEmpty(errors)) return errors;
|
||||
// NOTE don't return empty errors as that will be interpreted as an error state
|
||||
},
|
||||
|
||||
url: function() {
|
||||
var location = this.get('location');
|
||||
return '/' + location.get('org') + "/" + location.get('course') + '/settings-details/' + location.get('name') + '/section/details';
|
||||
},
|
||||
|
||||
_videokey_illegal_chars : /[^a-zA-Z0-9_-]/g,
|
||||
save_videosource: function(newsource) {
|
||||
// newsource either is <video youtube="speed:key, *"/> or just the "speed:key, *" string
|
||||
// returns the videosource for the preview which iss the key whose speed is closest to 1
|
||||
if (_.isEmpty(newsource) && !_.isEmpty(this.get('intro_video'))) this.set({'intro_video': null});
|
||||
// TODO remove all whitespace w/in string
|
||||
else {
|
||||
if (this.get('intro_video') !== newsource) this.set('intro_video', newsource);
|
||||
}
|
||||
|
||||
return this.videosourceSample();
|
||||
},
|
||||
videosourceSample : function() {
|
||||
if (this.has('intro_video')) return "http://www.youtube.com/embed/" + this.get('intro_video');
|
||||
else return "";
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,55 +1,56 @@
|
||||
if (!CMS.Models['Settings']) CMS.Models.Settings = new Object();
|
||||
|
||||
CMS.Models.Settings.CourseGradingPolicy = Backbone.Model.extend({
|
||||
defaults : {
|
||||
course_location : null,
|
||||
graders : null, // CourseGraderCollection
|
||||
grade_cutoffs : null, // CourseGradeCutoff model
|
||||
defaults : {
|
||||
course_location : null,
|
||||
graders : null, // CourseGraderCollection
|
||||
grade_cutoffs : null, // CourseGradeCutoff model
|
||||
grace_period : null // either null or { hours: n, minutes: m, ...}
|
||||
},
|
||||
parse: function(attributes) {
|
||||
if (attributes['course_location']) {
|
||||
attributes.course_location = new CMS.Models.Location(attributes.course_location, {parse:true});
|
||||
}
|
||||
if (attributes['graders']) {
|
||||
var graderCollection;
|
||||
if (this.has('graders')) {
|
||||
graderCollection = this.get('graders');
|
||||
graderCollection.reset(attributes.graders);
|
||||
}
|
||||
else {
|
||||
graderCollection = new CMS.Models.Settings.CourseGraderCollection(attributes.graders);
|
||||
graderCollection.course_location = attributes['course_location'] || this.get('course_location');
|
||||
}
|
||||
attributes.graders = graderCollection;
|
||||
}
|
||||
return attributes;
|
||||
},
|
||||
url : function() {
|
||||
var location = this.get('course_location');
|
||||
return '/' + location.get('org') + "/" + location.get('course') + '/settings/' + location.get('name') + '/section/grading';
|
||||
},
|
||||
gracePeriodToDate : function() {
|
||||
var newDate = new Date();
|
||||
if (this.has('grace_period') && this.get('grace_period')['hours'])
|
||||
newDate.setHours(this.get('grace_period')['hours']);
|
||||
else newDate.setHours(0);
|
||||
if (this.has('grace_period') && this.get('grace_period')['minutes'])
|
||||
newDate.setMinutes(this.get('grace_period')['minutes']);
|
||||
else newDate.setMinutes(0);
|
||||
if (this.has('grace_period') && this.get('grace_period')['seconds'])
|
||||
newDate.setSeconds(this.get('grace_period')['seconds']);
|
||||
else newDate.setSeconds(0);
|
||||
|
||||
return newDate;
|
||||
},
|
||||
dateToGracePeriod : function(date) {
|
||||
return {hours : date.getHours(), minutes : date.getMinutes(), seconds : date.getSeconds() };
|
||||
}
|
||||
},
|
||||
parse: function(attributes) {
|
||||
if (attributes['course_location']) {
|
||||
attributes.course_location = new CMS.Models.Location(attributes.course_location, {parse:true});
|
||||
}
|
||||
if (attributes['graders']) {
|
||||
var graderCollection;
|
||||
// interesting race condition: if {parse:true} when newing, then parse called before .attributes created
|
||||
if (this.attributes && this.has('graders')) {
|
||||
graderCollection = this.get('graders');
|
||||
graderCollection.reset(attributes.graders);
|
||||
}
|
||||
else {
|
||||
graderCollection = new CMS.Models.Settings.CourseGraderCollection(attributes.graders);
|
||||
graderCollection.course_location = attributes['course_location'] || this.get('course_location');
|
||||
}
|
||||
attributes.graders = graderCollection;
|
||||
}
|
||||
return attributes;
|
||||
},
|
||||
url : function() {
|
||||
var location = this.get('course_location');
|
||||
return '/' + location.get('org') + "/" + location.get('course') + '/settings-details/' + location.get('name') + '/section/grading';
|
||||
},
|
||||
gracePeriodToDate : function() {
|
||||
var newDate = new Date();
|
||||
if (this.has('grace_period') && this.get('grace_period')['hours'])
|
||||
newDate.setHours(this.get('grace_period')['hours']);
|
||||
else newDate.setHours(0);
|
||||
if (this.has('grace_period') && this.get('grace_period')['minutes'])
|
||||
newDate.setMinutes(this.get('grace_period')['minutes']);
|
||||
else newDate.setMinutes(0);
|
||||
if (this.has('grace_period') && this.get('grace_period')['seconds'])
|
||||
newDate.setSeconds(this.get('grace_period')['seconds']);
|
||||
else newDate.setSeconds(0);
|
||||
|
||||
return newDate;
|
||||
},
|
||||
dateToGracePeriod : function(date) {
|
||||
return {hours : date.getHours(), minutes : date.getMinutes(), seconds : date.getSeconds() };
|
||||
}
|
||||
});
|
||||
|
||||
CMS.Models.Settings.CourseGrader = Backbone.Model.extend({
|
||||
defaults: {
|
||||
defaults: {
|
||||
"type" : "", // must be unique w/in collection (ie. w/in course)
|
||||
"min_count" : 1,
|
||||
"drop_count" : 0,
|
||||
@@ -57,71 +58,71 @@ CMS.Models.Settings.CourseGrader = Backbone.Model.extend({
|
||||
"weight" : 0 // int 0..100
|
||||
},
|
||||
parse : function(attrs) {
|
||||
if (attrs['weight']) {
|
||||
if (!_.isNumber(attrs.weight)) attrs.weight = parseInt(attrs.weight);
|
||||
}
|
||||
if (attrs['min_count']) {
|
||||
if (!_.isNumber(attrs.min_count)) attrs.min_count = parseInt(attrs.min_count);
|
||||
}
|
||||
if (attrs['drop_count']) {
|
||||
if (!_.isNumber(attrs.drop_count)) attrs.drop_count = parseInt(attrs.drop_count);
|
||||
}
|
||||
return attrs;
|
||||
if (attrs['weight']) {
|
||||
if (!_.isNumber(attrs.weight)) attrs.weight = parseInt(attrs.weight);
|
||||
}
|
||||
if (attrs['min_count']) {
|
||||
if (!_.isNumber(attrs.min_count)) attrs.min_count = parseInt(attrs.min_count);
|
||||
}
|
||||
if (attrs['drop_count']) {
|
||||
if (!_.isNumber(attrs.drop_count)) attrs.drop_count = parseInt(attrs.drop_count);
|
||||
}
|
||||
return attrs;
|
||||
},
|
||||
validate : function(attrs) {
|
||||
var errors = {};
|
||||
if (attrs['type']) {
|
||||
if (_.isEmpty(attrs['type'])) {
|
||||
errors.type = "The assignment type must have a name.";
|
||||
}
|
||||
else {
|
||||
// FIXME somehow this.collection is unbound sometimes. I can't track down when
|
||||
var existing = this.collection && this.collection.some(function(other) { return (other != this) && (other.get('type') == attrs['type']);}, this);
|
||||
if (existing) {
|
||||
errors.type = "There's already another assignment type with this name.";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (attrs['weight']) {
|
||||
if (!isFinite(attrs.weight) || /\D+/.test(attrs.weight)) {
|
||||
errors.weight = "Please enter an integer between 0 and 100.";
|
||||
}
|
||||
else {
|
||||
attrs.weight = parseInt(attrs.weight); // see if this ensures value saved is int
|
||||
if (this.collection && attrs.weight > 0) {
|
||||
// FIXME b/c saves don't update the models if validation fails, we should
|
||||
// either revert the field value to the one in the model and make them make room
|
||||
// or figure out a wholistic way to balance the vals across the whole
|
||||
// if ((this.collection.sumWeights() + attrs.weight - this.get('weight')) > 100)
|
||||
// errors.weight = "The weights cannot add to more than 100.";
|
||||
}
|
||||
}}
|
||||
if (attrs['min_count']) {
|
||||
if (!isFinite(attrs.min_count) || /\D+/.test(attrs.min_count)) {
|
||||
errors.min_count = "Please enter an integer.";
|
||||
}
|
||||
else attrs.min_count = parseInt(attrs.min_count);
|
||||
}
|
||||
if (attrs['drop_count']) {
|
||||
if (!isFinite(attrs.drop_count) || /\D+/.test(attrs.drop_count)) {
|
||||
errors.drop_count = "Please enter an integer.";
|
||||
}
|
||||
else attrs.drop_count = parseInt(attrs.drop_count);
|
||||
}
|
||||
if (attrs['min_count'] && attrs['drop_count'] && attrs.drop_count > attrs.min_count) {
|
||||
errors.drop_count = "Cannot drop more " + attrs.type + " than will assigned.";
|
||||
}
|
||||
if (!_.isEmpty(errors)) return errors;
|
||||
var errors = {};
|
||||
if (attrs['type']) {
|
||||
if (_.isEmpty(attrs['type'])) {
|
||||
errors.type = "The assignment type must have a name.";
|
||||
}
|
||||
else {
|
||||
// FIXME somehow this.collection is unbound sometimes. I can't track down when
|
||||
var existing = this.collection && this.collection.some(function(other) { return (other != this) && (other.get('type') == attrs['type']);}, this);
|
||||
if (existing) {
|
||||
errors.type = "There's already another assignment type with this name.";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (attrs['weight']) {
|
||||
if (!isFinite(attrs.weight) || /\D+/.test(attrs.weight)) {
|
||||
errors.weight = "Please enter an integer between 0 and 100.";
|
||||
}
|
||||
else {
|
||||
attrs.weight = parseInt(attrs.weight); // see if this ensures value saved is int
|
||||
if (this.collection && attrs.weight > 0) {
|
||||
// FIXME b/c saves don't update the models if validation fails, we should
|
||||
// either revert the field value to the one in the model and make them make room
|
||||
// or figure out a wholistic way to balance the vals across the whole
|
||||
// if ((this.collection.sumWeights() + attrs.weight - this.get('weight')) > 100)
|
||||
// errors.weight = "The weights cannot add to more than 100.";
|
||||
}
|
||||
}}
|
||||
if (attrs['min_count']) {
|
||||
if (!isFinite(attrs.min_count) || /\D+/.test(attrs.min_count)) {
|
||||
errors.min_count = "Please enter an integer.";
|
||||
}
|
||||
else attrs.min_count = parseInt(attrs.min_count);
|
||||
}
|
||||
if (attrs['drop_count']) {
|
||||
if (!isFinite(attrs.drop_count) || /\D+/.test(attrs.drop_count)) {
|
||||
errors.drop_count = "Please enter an integer.";
|
||||
}
|
||||
else attrs.drop_count = parseInt(attrs.drop_count);
|
||||
}
|
||||
if (attrs['min_count'] && attrs['drop_count'] && attrs.drop_count > attrs.min_count) {
|
||||
errors.drop_count = "Cannot drop more " + attrs.type + " than will assigned.";
|
||||
}
|
||||
if (!_.isEmpty(errors)) return errors;
|
||||
}
|
||||
});
|
||||
|
||||
CMS.Models.Settings.CourseGraderCollection = Backbone.Collection.extend({
|
||||
model : CMS.Models.Settings.CourseGrader,
|
||||
course_location : null, // must be set to a Location object
|
||||
url : function() {
|
||||
return '/' + this.course_location.get('org') + "/" + this.course_location.get('course') + '/grades/' + this.course_location.get('name') + '/';
|
||||
},
|
||||
sumWeights : function() {
|
||||
return this.reduce(function(subtotal, grader) { return subtotal + grader.get('weight'); }, 0);
|
||||
}
|
||||
model : CMS.Models.Settings.CourseGrader,
|
||||
course_location : null, // must be set to a Location object
|
||||
url : function() {
|
||||
return '/' + this.course_location.get('org') + "/" + this.course_location.get('course') + '/settings-grading/' + this.course_location.get('name') + '/';
|
||||
},
|
||||
sumWeights : function() {
|
||||
return this.reduce(function(subtotal, grader) { return subtotal + grader.get('weight'); }, 0);
|
||||
}
|
||||
});
|
||||
@@ -1,43 +1,42 @@
|
||||
if (!CMS.Models['Settings']) CMS.Models.Settings = new Object();
|
||||
CMS.Models.Settings.CourseSettings = Backbone.Model.extend({
|
||||
// a container for the models representing the n possible tabbed states
|
||||
defaults: {
|
||||
courseLocation: null,
|
||||
// NOTE: keep these sync'd w/ the data-section names in settings-page-menu
|
||||
details: null,
|
||||
faculty: null,
|
||||
grading: null,
|
||||
problems: null,
|
||||
discussions: null
|
||||
},
|
||||
// a container for the models representing the n possible tabbed states
|
||||
defaults: {
|
||||
courseLocation: null,
|
||||
details: null,
|
||||
faculty: null,
|
||||
grading: null,
|
||||
problems: null,
|
||||
discussions: null
|
||||
},
|
||||
|
||||
retrieve: function(submodel, callback) {
|
||||
if (this.get(submodel)) callback();
|
||||
else {
|
||||
var cachethis = this;
|
||||
switch (submodel) {
|
||||
case 'details':
|
||||
var details = new CMS.Models.Settings.CourseDetails({location: this.get('courseLocation')});
|
||||
details.fetch( {
|
||||
success : function(model) {
|
||||
cachethis.set('details', model);
|
||||
callback(model);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'grading':
|
||||
var grading = new CMS.Models.Settings.CourseGradingPolicy({course_location: this.get('courseLocation')});
|
||||
grading.fetch( {
|
||||
success : function(model) {
|
||||
cachethis.set('grading', model);
|
||||
callback(model);
|
||||
}
|
||||
});
|
||||
break;
|
||||
retrieve: function(submodel, callback) {
|
||||
if (this.get(submodel)) callback();
|
||||
else {
|
||||
var cachethis = this;
|
||||
switch (submodel) {
|
||||
case 'details':
|
||||
var details = new CMS.Models.Settings.CourseDetails({location: this.get('courseLocation')});
|
||||
details.fetch( {
|
||||
success : function(model) {
|
||||
cachethis.set('details', model);
|
||||
callback(model);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'grading':
|
||||
var grading = new CMS.Models.Settings.CourseGradingPolicy({course_location: this.get('courseLocation')});
|
||||
grading.fetch( {
|
||||
success : function(model) {
|
||||
cachethis.set('grading', model);
|
||||
callback(model);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -10,7 +10,7 @@ CMS.Views.CourseInfoEdit = Backbone.View.extend({
|
||||
render: function() {
|
||||
// instantiate the ClassInfoUpdateView and delegate the proper dom to it
|
||||
new CMS.Views.ClassInfoUpdateView({
|
||||
el: this.$('#course-update-view'),
|
||||
el: $('body.updates'),
|
||||
collection: this.model.get('updates')
|
||||
});
|
||||
|
||||
@@ -27,10 +27,10 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
|
||||
// collection is CourseUpdateCollection
|
||||
events: {
|
||||
"click .new-update-button" : "onNew",
|
||||
"click .save-button" : "onSave",
|
||||
"click .cancel-button" : "onCancel",
|
||||
"click .edit-button" : "onEdit",
|
||||
"click .delete-button" : "onDelete"
|
||||
"click #course-update-view .save-button" : "onSave",
|
||||
"click #course-update-view .cancel-button" : "onCancel",
|
||||
"click .post-actions > .edit-button" : "onEdit",
|
||||
"click .post-actions > .delete-button" : "onDelete"
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
|
||||
@@ -1,222 +1,91 @@
|
||||
if (!CMS.Views['Settings']) CMS.Views.Settings = {};
|
||||
|
||||
// TODO move to common place
|
||||
CMS.Views.ValidatingView = Backbone.View.extend({
|
||||
// Intended as an abstract class which catches validation errors on the model and
|
||||
// decorates the fields. Needs wiring per class, but this initialization shows how
|
||||
// either have your init call this one or copy the contents
|
||||
initialize : function() {
|
||||
this.model.on('error', this.handleValidationError, this);
|
||||
this.selectorToField = _.invert(this.fieldToSelectorMap);
|
||||
},
|
||||
|
||||
errorTemplate : _.template('<span class="message-error"><%= message %></span>'),
|
||||
|
||||
events : {
|
||||
"blur input" : "clearValidationErrors",
|
||||
"blur textarea" : "clearValidationErrors"
|
||||
},
|
||||
fieldToSelectorMap : {
|
||||
// Your subclass must populate this w/ all of the model keys and dom selectors
|
||||
// which may be the subjects of validation errors
|
||||
},
|
||||
_cacheValidationErrors : [],
|
||||
handleValidationError : function(model, error) {
|
||||
// error is object w/ fields and error strings
|
||||
for (var field in error) {
|
||||
var ele = this.$el.find('#' + this.fieldToSelectorMap[field]);
|
||||
this._cacheValidationErrors.push(ele);
|
||||
if ($(ele).is('div')) {
|
||||
// put error on the contained inputs
|
||||
$(ele).find('input, textarea').addClass('error');
|
||||
}
|
||||
else $(ele).addClass('error');
|
||||
$(ele).parent().append(this.errorTemplate({message : error[field]}));
|
||||
}
|
||||
},
|
||||
|
||||
clearValidationErrors : function() {
|
||||
// error is object w/ fields and error strings
|
||||
while (this._cacheValidationErrors.length > 0) {
|
||||
var ele = this._cacheValidationErrors.pop();
|
||||
if ($(ele).is('div')) {
|
||||
// put error on the contained inputs
|
||||
$(ele).find('input, textarea').removeClass('error');
|
||||
}
|
||||
else $(ele).removeClass('error');
|
||||
$(ele).nextAll('.message-error').remove();
|
||||
}
|
||||
},
|
||||
|
||||
saveIfChanged : function(event) {
|
||||
// returns true if the value changed and was thus sent to server
|
||||
var field = this.selectorToField[event.currentTarget.id];
|
||||
var currentVal = this.model.get(field);
|
||||
var newVal = $(event.currentTarget).val();
|
||||
if (currentVal != newVal) {
|
||||
this.clearValidationErrors();
|
||||
this.model.save(field, newVal, { error : CMS.ServerError});
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
});
|
||||
|
||||
CMS.Views.Settings.Main = Backbone.View.extend({
|
||||
// Model class is CMS.Models.Settings.CourseSettings
|
||||
// allow navigation between the tabs
|
||||
events: {
|
||||
'click .settings-page-menu a': "showSettingsTab",
|
||||
'mouseover #timezone' : "updateTime"
|
||||
},
|
||||
|
||||
currentTab: null,
|
||||
subviews: {}, // indexed by tab name
|
||||
|
||||
initialize: function() {
|
||||
// load templates
|
||||
this.currentTab = this.$el.find('.settings-page-menu .is-shown').attr('data-section');
|
||||
// create the initial subview
|
||||
this.subviews[this.currentTab] = this.createSubview();
|
||||
|
||||
// fill in fields
|
||||
this.$el.find("#course-name").val(this.model.get('courseLocation').get('name'));
|
||||
this.$el.find("#course-organization").val(this.model.get('courseLocation').get('org'));
|
||||
this.$el.find("#course-number").val(this.model.get('courseLocation').get('course'));
|
||||
this.$el.find('.set-date').datepicker({ 'dateFormat': 'm/d/yy' });
|
||||
this.$el.find(":input, textarea").focus(function() {
|
||||
$("label[for='" + this.id + "']").addClass("is-focused");
|
||||
}).blur(function() {
|
||||
$("label").removeClass("is-focused");
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
||||
// create any necessary subviews and put them onto the page
|
||||
if (!this.model.has(this.currentTab)) {
|
||||
// TODO disable screen until fetch completes?
|
||||
var cachethis = this;
|
||||
this.model.retrieve(this.currentTab, function() {
|
||||
cachethis.subviews[cachethis.currentTab] = cachethis.createSubview();
|
||||
cachethis.subviews[cachethis.currentTab].render();
|
||||
});
|
||||
}
|
||||
else this.subviews[this.currentTab].render();
|
||||
|
||||
var dateIntrospect = new Date();
|
||||
this.$el.find('#timezone').html("(" + dateIntrospect.getTimezone() + ")");
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
createSubview: function() {
|
||||
switch (this.currentTab) {
|
||||
case 'details':
|
||||
return new CMS.Views.Settings.Details({
|
||||
el: this.$el.find('.settings-' + this.currentTab),
|
||||
model: this.model.get(this.currentTab)
|
||||
});
|
||||
case 'faculty':
|
||||
break;
|
||||
case 'grading':
|
||||
return new CMS.Views.Settings.Grading({
|
||||
el: this.$el.find('.settings-' + this.currentTab),
|
||||
model: this.model.get(this.currentTab)
|
||||
});
|
||||
case 'problems':
|
||||
break;
|
||||
case 'discussions':
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
updateTime : function(e) {
|
||||
var now = new Date();
|
||||
var hours = now.getHours();
|
||||
var minutes = now.getMinutes();
|
||||
$(e.currentTarget).attr('title', (hours % 12 === 0 ? 12 : hours % 12) + ":" + (minutes < 10 ? "0" : "") +
|
||||
now.getMinutes() + (hours < 12 ? "am" : "pm") + " (current local time)");
|
||||
},
|
||||
|
||||
showSettingsTab: function(e) {
|
||||
this.currentTab = $(e.target).attr('data-section');
|
||||
$('.settings-page-section > section').hide();
|
||||
$('.settings-' + this.currentTab).show();
|
||||
$('.settings-page-menu .is-shown').removeClass('is-shown');
|
||||
$(e.target).addClass('is-shown');
|
||||
// fetch model for the tab if not loaded already
|
||||
this.render();
|
||||
}
|
||||
|
||||
});
|
||||
if (!CMS.Views['Settings']) CMS.Views.Settings = {}; // ensure the pseudo pkg exists
|
||||
|
||||
CMS.Views.Settings.Details = CMS.Views.ValidatingView.extend({
|
||||
// Model class is CMS.Models.Settings.CourseDetails
|
||||
events : {
|
||||
"blur input" : "updateModel",
|
||||
"blur textarea" : "updateModel",
|
||||
'click .remove-course-syllabus' : "removeSyllabus",
|
||||
'click .new-course-syllabus' : 'assetSyllabus',
|
||||
'click .remove-course-introduction-video' : "removeVideo",
|
||||
'focus #course-overview' : "codeMirrorize"
|
||||
},
|
||||
initialize : function() {
|
||||
// TODO move the html frag to a loaded asset
|
||||
this.fileAnchorTemplate = _.template('<a href="<%= fullpath %>"> <i class="ss-icon ss-standard">📄</i><%= filename %></a>');
|
||||
this.model.on('error', this.handleValidationError, this);
|
||||
this.selectorToField = _.invert(this.fieldToSelectorMap);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.setupDatePicker('start_date');
|
||||
this.setupDatePicker('end_date');
|
||||
this.setupDatePicker('enrollment_start');
|
||||
this.setupDatePicker('enrollment_end');
|
||||
|
||||
if (this.model.has('syllabus')) {
|
||||
this.$el.find(this.fieldToSelectorMap['syllabus']).html(
|
||||
this.fileAnchorTemplate({
|
||||
fullpath : this.model.get('syllabus'),
|
||||
filename: 'syllabus'}));
|
||||
this.$el.find('.remove-course-syllabus').show();
|
||||
}
|
||||
else {
|
||||
this.$el.find('#' + this.fieldToSelectorMap['syllabus']).html("");
|
||||
this.$el.find('.remove-course-syllabus').hide();
|
||||
}
|
||||
|
||||
this.$el.find('#' + this.fieldToSelectorMap['overview']).val(this.model.get('overview'));
|
||||
this.codeMirrorize(null, $('#course-overview')[0]);
|
||||
|
||||
this.$el.find('.current-course-introduction-video iframe').attr('src', this.model.videosourceSample());
|
||||
if (this.model.has('intro_video')) {
|
||||
this.$el.find('.remove-course-introduction-video').show();
|
||||
this.$el.find('#' + this.fieldToSelectorMap['intro_video']).val(this.model.get('intro_video'));
|
||||
}
|
||||
else this.$el.find('.remove-course-introduction-video').hide();
|
||||
|
||||
this.$el.find('#' + this.fieldToSelectorMap['effort']).val(this.model.get('effort'));
|
||||
|
||||
return this;
|
||||
},
|
||||
fieldToSelectorMap : {
|
||||
'start_date' : "course-start",
|
||||
'end_date' : 'course-end',
|
||||
'enrollment_start' : 'enrollment-start',
|
||||
'enrollment_end' : 'enrollment-end',
|
||||
'syllabus' : '.current-course-syllabus .doc-filename',
|
||||
'overview' : 'course-overview',
|
||||
'intro_video' : 'course-introduction-video',
|
||||
'effort' : "course-effort"
|
||||
},
|
||||
// Model class is CMS.Models.Settings.CourseDetails
|
||||
events : {
|
||||
"blur input" : "updateModel",
|
||||
"blur textarea" : "updateModel",
|
||||
'click .remove-course-syllabus' : "removeSyllabus",
|
||||
'click .new-course-syllabus' : 'assetSyllabus',
|
||||
'click .remove-course-introduction-video' : "removeVideo",
|
||||
'focus #course-overview' : "codeMirrorize",
|
||||
'mouseover #timezone' : "updateTime",
|
||||
// would love to move to a general superclass, but event hashes don't inherit in backbone :-(
|
||||
'focus :input' : "inputFocus",
|
||||
'blur :input' : "inputUnfocus"
|
||||
|
||||
},
|
||||
initialize : function() {
|
||||
this.fileAnchorTemplate = _.template('<a href="<%= fullpath %>"> <i class="ss-icon ss-standard">📄</i><%= filename %></a>');
|
||||
// fill in fields
|
||||
this.$el.find("#course-name").val(this.model.get('location').get('name'));
|
||||
this.$el.find("#course-organization").val(this.model.get('location').get('org'));
|
||||
this.$el.find("#course-number").val(this.model.get('location').get('course'));
|
||||
this.$el.find('.set-date').datepicker({ 'dateFormat': 'm/d/yy' });
|
||||
|
||||
var dateIntrospect = new Date();
|
||||
this.$el.find('#timezone').html("(" + dateIntrospect.getTimezone() + ")");
|
||||
|
||||
this.model.on('error', this.handleValidationError, this);
|
||||
this.selectorToField = _.invert(this.fieldToSelectorMap);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.setupDatePicker('start_date');
|
||||
this.setupDatePicker('end_date');
|
||||
this.setupDatePicker('enrollment_start');
|
||||
this.setupDatePicker('enrollment_end');
|
||||
|
||||
if (this.model.has('syllabus')) {
|
||||
this.$el.find(this.fieldToSelectorMap['syllabus']).html(
|
||||
this.fileAnchorTemplate({
|
||||
fullpath : this.model.get('syllabus'),
|
||||
filename: 'syllabus'}));
|
||||
this.$el.find('.remove-course-syllabus').show();
|
||||
}
|
||||
else {
|
||||
this.$el.find('#' + this.fieldToSelectorMap['syllabus']).html("");
|
||||
this.$el.find('.remove-course-syllabus').hide();
|
||||
}
|
||||
|
||||
this.$el.find('#' + this.fieldToSelectorMap['overview']).val(this.model.get('overview'));
|
||||
this.codeMirrorize(null, $('#course-overview')[0]);
|
||||
|
||||
this.$el.find('.current-course-introduction-video iframe').attr('src', this.model.videosourceSample());
|
||||
if (this.model.has('intro_video')) {
|
||||
this.$el.find('.remove-course-introduction-video').show();
|
||||
this.$el.find('#' + this.fieldToSelectorMap['intro_video']).val(this.model.get('intro_video'));
|
||||
}
|
||||
else this.$el.find('.remove-course-introduction-video').hide();
|
||||
|
||||
this.$el.find('#' + this.fieldToSelectorMap['effort']).val(this.model.get('effort'));
|
||||
|
||||
return this;
|
||||
},
|
||||
fieldToSelectorMap : {
|
||||
'start_date' : "course-start",
|
||||
'end_date' : 'course-end',
|
||||
'enrollment_start' : 'enrollment-start',
|
||||
'enrollment_end' : 'enrollment-end',
|
||||
'syllabus' : '.current-course-syllabus .doc-filename',
|
||||
'overview' : 'course-overview',
|
||||
'intro_video' : 'course-introduction-video',
|
||||
'effort' : "course-effort"
|
||||
},
|
||||
|
||||
updateTime : function(e) {
|
||||
var now = new Date();
|
||||
var hours = now.getHours();
|
||||
var minutes = now.getMinutes();
|
||||
$(e.currentTarget).attr('title', (hours % 12 === 0 ? 12 : hours % 12) + ":" + (minutes < 10 ? "0" : "") +
|
||||
now.getMinutes() + (hours < 12 ? "am" : "pm") + " (current local time)");
|
||||
},
|
||||
|
||||
setupDatePicker: function (fieldName) {
|
||||
var cacheModel = this.model;
|
||||
var div = this.$el.find('#' + this.fieldToSelectorMap[fieldName]);
|
||||
var datefield = $(div).find(".date");
|
||||
var timefield = $(div).find(".time");
|
||||
var datefield = $(div).find("input:.date");
|
||||
var timefield = $(div).find("input:.time");
|
||||
var cachethis = this;
|
||||
var savefield = function () {
|
||||
cachethis.clearValidationErrors();
|
||||
@@ -245,58 +114,57 @@ CMS.Views.Settings.Details = CMS.Views.ValidatingView.extend({
|
||||
datefield.datepicker('setDate', this.model.get(fieldName));
|
||||
if (this.model.has(fieldName)) timefield.timepicker('setTime', this.model.get(fieldName));
|
||||
},
|
||||
|
||||
updateModel: function(event) {
|
||||
switch (event.currentTarget.id) {
|
||||
case 'course-start-date': // handled via onSelect method
|
||||
case 'course-end-date':
|
||||
case 'course-enrollment-start-date':
|
||||
case 'course-enrollment-end-date':
|
||||
break;
|
||||
|
||||
case 'course-overview':
|
||||
// handled via code mirror
|
||||
break;
|
||||
updateModel: function(event) {
|
||||
switch (event.currentTarget.id) {
|
||||
case 'course-start-date': // handled via onSelect method
|
||||
case 'course-end-date':
|
||||
case 'course-enrollment-start-date':
|
||||
case 'course-enrollment-end-date':
|
||||
break;
|
||||
|
||||
case 'course-effort':
|
||||
this.saveIfChanged(event);
|
||||
break;
|
||||
case 'course-introduction-video':
|
||||
this.clearValidationErrors();
|
||||
var previewsource = this.model.save_videosource($(event.currentTarget).val());
|
||||
this.$el.find(".current-course-introduction-video iframe").attr("src", previewsource);
|
||||
if (this.model.has('intro_video')) {
|
||||
this.$el.find('.remove-course-introduction-video').show();
|
||||
}
|
||||
else {
|
||||
this.$el.find('.remove-course-introduction-video').hide();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
removeSyllabus: function() {
|
||||
if (this.model.has('syllabus')) this.model.save({'syllabus': null},
|
||||
{ error : CMS.ServerError});
|
||||
},
|
||||
|
||||
assetSyllabus : function() {
|
||||
// TODO implement
|
||||
},
|
||||
|
||||
removeVideo: function() {
|
||||
if (this.model.has('intro_video')) {
|
||||
this.model.save_videosource(null);
|
||||
this.$el.find(".current-course-introduction-video iframe").attr("src", "");
|
||||
this.$el.find('#' + this.fieldToSelectorMap['intro_video']).val("");
|
||||
this.$el.find('.remove-course-introduction-video').hide();
|
||||
}
|
||||
},
|
||||
codeMirrors : {},
|
||||
case 'course-overview':
|
||||
// handled via code mirror
|
||||
break;
|
||||
|
||||
case 'course-effort':
|
||||
this.saveIfChanged(event);
|
||||
break;
|
||||
case 'course-introduction-video':
|
||||
this.clearValidationErrors();
|
||||
var previewsource = this.model.save_videosource($(event.currentTarget).val());
|
||||
this.$el.find(".current-course-introduction-video iframe").attr("src", previewsource);
|
||||
if (this.model.has('intro_video')) {
|
||||
this.$el.find('.remove-course-introduction-video').show();
|
||||
}
|
||||
else {
|
||||
this.$el.find('.remove-course-introduction-video').hide();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
removeSyllabus: function() {
|
||||
if (this.model.has('syllabus')) this.model.save({'syllabus': null});
|
||||
},
|
||||
|
||||
assetSyllabus : function() {
|
||||
// TODO implement
|
||||
},
|
||||
|
||||
removeVideo: function() {
|
||||
if (this.model.has('intro_video')) {
|
||||
this.model.save_videosource(null);
|
||||
this.$el.find(".current-course-introduction-video iframe").attr("src", "");
|
||||
this.$el.find('#' + this.fieldToSelectorMap['intro_video']).val("");
|
||||
this.$el.find('.remove-course-introduction-video').hide();
|
||||
}
|
||||
},
|
||||
codeMirrors : {},
|
||||
codeMirrorize: function (e, forcedTarget) {
|
||||
var thisTarget;
|
||||
if (forcedTarget) {
|
||||
@@ -315,374 +183,11 @@ CMS.Views.Settings.Details = CMS.Views.ValidatingView.extend({
|
||||
mirror.save();
|
||||
cachethis.clearValidationErrors();
|
||||
var newVal = mirror.getValue();
|
||||
if (cachethis.model.get(field) != newVal) cachethis.model.save(field, newVal,
|
||||
{ error: CMS.ServerError});
|
||||
if (cachethis.model.get(field) != newVal) cachethis.model.save(field, newVal);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
CMS.Views.Settings.Grading = CMS.Views.ValidatingView.extend({
|
||||
// Model class is CMS.Models.Settings.CourseGradingPolicy
|
||||
events : {
|
||||
"blur input" : "updateModel",
|
||||
"blur textarea" : "updateModel",
|
||||
"blur span[contenteditable=true]" : "updateDesignation",
|
||||
"click .settings-extra header" : "showSettingsExtras",
|
||||
"click .new-grade-button" : "addNewGrade",
|
||||
"click .remove-button" : "removeGrade",
|
||||
"click .add-grading-data" : "addAssignmentType"
|
||||
},
|
||||
initialize : function() {
|
||||
// load template for grading view
|
||||
var self = this;
|
||||
this.gradeCutoffTemplate = _.template('<li class="grade-specific-bar" style="width:<%= width %>%"><span class="letter-grade" contenteditable>' +
|
||||
'<%= descriptor %>' +
|
||||
'</span><span class="range"></span>' +
|
||||
'<% if (removable) {%><a href="#" class="remove-button">remove</a><% ;} %>' +
|
||||
'</li>');
|
||||
|
||||
// Instrument grading scale
|
||||
// convert cutoffs to inversely ordered list
|
||||
var modelCutoffs = this.model.get('grade_cutoffs');
|
||||
for (var cutoff in modelCutoffs) {
|
||||
this.descendingCutoffs.push({designation: cutoff, cutoff: Math.round(modelCutoffs[cutoff] * 100)});
|
||||
}
|
||||
this.descendingCutoffs = _.sortBy(this.descendingCutoffs,
|
||||
function (gradeEle) { return -gradeEle['cutoff']; });
|
||||
|
||||
// Instrument grace period
|
||||
this.$el.find('#course-grading-graceperiod').timepicker();
|
||||
|
||||
// instantiates an editor template for each update in the collection
|
||||
// Because this calls render, put it after everything which render may depend upon to prevent race condition.
|
||||
window.templateLoader.loadRemoteTemplate("course_grade_policy",
|
||||
"/static/client_templates/course_grade_policy.html",
|
||||
function (raw_template) {
|
||||
self.template = _.template(raw_template);
|
||||
self.render();
|
||||
}
|
||||
);
|
||||
this.model.on('error', this.handleValidationError, this);
|
||||
this.model.get('graders').on('remove', this.render, this);
|
||||
this.model.get('graders').on('reset', this.render, this);
|
||||
this.model.get('graders').on('add', this.render, this);
|
||||
this.selectorToField = _.invert(this.fieldToSelectorMap);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// prevent bootstrap race condition by event dispatch
|
||||
if (!this.template) return;
|
||||
|
||||
// Create and render the grading type subs
|
||||
var self = this;
|
||||
var gradelist = this.$el.find('.course-grading-assignment-list');
|
||||
// Undo the double invocation error. At some point, fix the double invocation
|
||||
$(gradelist).empty();
|
||||
var gradeCollection = this.model.get('graders');
|
||||
gradeCollection.each(function(gradeModel) {
|
||||
$(gradelist).append(self.template({model : gradeModel }));
|
||||
var newEle = gradelist.children().last();
|
||||
var newView = new CMS.Views.Settings.GraderView({el: newEle,
|
||||
model : gradeModel, collection : gradeCollection });
|
||||
});
|
||||
|
||||
// render the grade cutoffs
|
||||
this.renderCutoffBar();
|
||||
|
||||
var graceEle = this.$el.find('#course-grading-graceperiod');
|
||||
graceEle.timepicker({'timeFormat' : 'H:i'}); // init doesn't take setTime
|
||||
if (this.model.has('grace_period')) graceEle.timepicker('setTime', this.model.gracePeriodToDate());
|
||||
// remove any existing listeners to keep them from piling on b/c render gets called frequently
|
||||
graceEle.off('change', this.setGracePeriod);
|
||||
graceEle.on('change', this, this.setGracePeriod);
|
||||
|
||||
return this;
|
||||
},
|
||||
addAssignmentType : function(e) {
|
||||
e.preventDefault();
|
||||
this.model.get('graders').push({});
|
||||
},
|
||||
fieldToSelectorMap : {
|
||||
'grace_period' : 'course-grading-graceperiod'
|
||||
},
|
||||
setGracePeriod : function(event) {
|
||||
event.data.clearValidationErrors();
|
||||
var newVal = event.data.model.dateToGracePeriod($(event.currentTarget).timepicker('getTime'));
|
||||
if (event.data.model.get('grace_period') != newVal) event.data.model.save('grace_period', newVal,
|
||||
{ error : CMS.ServerError});
|
||||
},
|
||||
updateModel : function(event) {
|
||||
if (!this.selectorToField[event.currentTarget.id]) return;
|
||||
|
||||
switch (this.selectorToField[event.currentTarget.id]) {
|
||||
case 'grace_period': // handled above
|
||||
break;
|
||||
|
||||
default:
|
||||
this.saveIfChanged(event);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// Grade sliders attributes and methods
|
||||
// Grade bars are li's ordered A -> F with A taking whole width, B overlaying it with its paint, ...
|
||||
// The actual cutoff for each grade is the width % of the next lower grade; so, the hack here
|
||||
// is to lay down a whole width bar claiming it's A and then lay down bars for each actual grade
|
||||
// starting w/ A but posting the label in the preceding li and setting the label of the last to "Fail" or "F"
|
||||
|
||||
// A does not have a drag bar (cannot change its upper limit)
|
||||
// Need to insert new bars in right place.
|
||||
GRADES : ['A', 'B', 'C', 'D'], // defaults for new grade designators
|
||||
descendingCutoffs : [], // array of { designation : , cutoff : }
|
||||
gradeBarWidth : null, // cache of value since it won't change (more certain)
|
||||
|
||||
renderCutoffBar: function() {
|
||||
var gradeBar =this.$el.find('.grade-bar');
|
||||
this.gradeBarWidth = gradeBar.width();
|
||||
var gradelist = gradeBar.children('.grades');
|
||||
// HACK fixing a duplicate call issue by undoing previous call effect. Need to figure out why called 2x
|
||||
gradelist.empty();
|
||||
var nextWidth = 100; // first width is 100%
|
||||
// Can probably be simplified to one variable now.
|
||||
var removable = false;
|
||||
var draggable = false; // first and last are not removable, first is not draggable
|
||||
_.each(this.descendingCutoffs,
|
||||
function(cutoff, index) {
|
||||
var newBar = this.gradeCutoffTemplate({
|
||||
descriptor : cutoff['designation'] ,
|
||||
width : nextWidth,
|
||||
removable : removable });
|
||||
gradelist.append(newBar);
|
||||
if (draggable) {
|
||||
newBar = gradelist.children().last(); // get the dom object not the unparsed string
|
||||
newBar.resizable({
|
||||
handles: "e",
|
||||
containment : "parent",
|
||||
start : this.startMoveClosure(),
|
||||
resize : this.moveBarClosure(),
|
||||
stop : this.stopDragClosure()
|
||||
});
|
||||
}
|
||||
// prepare for next
|
||||
nextWidth = cutoff['cutoff'];
|
||||
removable = true; // first is not removable, all others are
|
||||
draggable = true;
|
||||
},
|
||||
this);
|
||||
// add fail which is not in data
|
||||
var failBar = this.gradeCutoffTemplate({ descriptor : this.failLabel(),
|
||||
width : nextWidth, removable : false});
|
||||
$(failBar).find("span[contenteditable=true]").attr("contenteditable", false);
|
||||
gradelist.append(failBar);
|
||||
gradelist.children().last().resizable({
|
||||
handles: "e",
|
||||
containment : "parent",
|
||||
start : this.startMoveClosure(),
|
||||
resize : this.moveBarClosure(),
|
||||
stop : this.stopDragClosure()
|
||||
});
|
||||
|
||||
this.renderGradeRanges();
|
||||
},
|
||||
|
||||
showSettingsExtras : function(event) {
|
||||
$(event.currentTarget).toggleClass('active');
|
||||
$(event.currentTarget).siblings.toggleClass('is-shown');
|
||||
},
|
||||
|
||||
|
||||
startMoveClosure : function() {
|
||||
// set min/max widths
|
||||
var cachethis = this;
|
||||
var widthPerPoint = cachethis.gradeBarWidth / 100;
|
||||
return function(event, ui) {
|
||||
var barIndex = ui.element.index();
|
||||
// min and max represent limits not labels (note, can's make smaller than 3 points wide)
|
||||
var min = (barIndex < cachethis.descendingCutoffs.length ? cachethis.descendingCutoffs[barIndex]['cutoff'] + 3 : 3);
|
||||
// minus 2 b/c minus 1 is the element we're effecting. It's max is just shy of the next one above it
|
||||
var max = (barIndex >= 2 ? cachethis.descendingCutoffs[barIndex - 2]['cutoff'] - 3 : 97);
|
||||
ui.element.resizable("option",{minWidth : min * widthPerPoint, maxWidth : max * widthPerPoint});
|
||||
};
|
||||
},
|
||||
|
||||
moveBarClosure : function() {
|
||||
// 0th ele doesn't have a bar; so, will never invoke this
|
||||
var cachethis = this;
|
||||
return function(event, ui) {
|
||||
var barIndex = ui.element.index();
|
||||
// min and max represent limits not labels (note, can's make smaller than 3 points wide)
|
||||
var min = (barIndex < cachethis.descendingCutoffs.length ? cachethis.descendingCutoffs[barIndex]['cutoff'] + 3 : 3);
|
||||
// minus 2 b/c minus 1 is the element we're effecting. It's max is just shy of the next one above it
|
||||
var max = (barIndex >= 2 ? cachethis.descendingCutoffs[barIndex - 2]['cutoff'] - 3 : 100);
|
||||
var percentage = Math.min(Math.max(ui.size.width / cachethis.gradeBarWidth * 100, min), max);
|
||||
cachethis.descendingCutoffs[barIndex - 1]['cutoff'] = Math.round(percentage);
|
||||
cachethis.renderGradeRanges();
|
||||
};
|
||||
},
|
||||
|
||||
renderGradeRanges: function() {
|
||||
// the labels showing the range e.g., 71-80
|
||||
var cutoffs = this.descendingCutoffs;
|
||||
this.$el.find('.range').each(function(i) {
|
||||
var min = (i < cutoffs.length ? cutoffs[i]['cutoff'] : 0);
|
||||
var max = (i > 0 ? cutoffs[i - 1]['cutoff'] : 100);
|
||||
$(this).text(min + '-' + max);
|
||||
});
|
||||
},
|
||||
|
||||
stopDragClosure: function() {
|
||||
var cachethis = this;
|
||||
return function(event, ui) {
|
||||
// for some reason the resize is setting height to 0
|
||||
cachethis.saveCutoffs();
|
||||
};
|
||||
},
|
||||
|
||||
saveCutoffs: function() {
|
||||
this.model.save('grade_cutoffs',
|
||||
_.reduce(this.descendingCutoffs,
|
||||
function(object, cutoff) {
|
||||
object[cutoff['designation']] = cutoff['cutoff'] / 100.0;
|
||||
return object;
|
||||
},
|
||||
{}),
|
||||
{ error : CMS.ServerError});
|
||||
},
|
||||
|
||||
addNewGrade: function(e) {
|
||||
e.preventDefault();
|
||||
var gradeLength = this.descendingCutoffs.length; // cutoffs doesn't include fail/f so this is only the passing grades
|
||||
if(gradeLength > 3) {
|
||||
// TODO shouldn't we disable the button
|
||||
return;
|
||||
}
|
||||
var failBarWidth = this.descendingCutoffs[gradeLength - 1]['cutoff'];
|
||||
// going to split the grade above the insertion point in half leaving fail in same place
|
||||
var nextGradeTop = (gradeLength > 1 ? this.descendingCutoffs[gradeLength - 2]['cutoff'] : 100);
|
||||
var targetWidth = failBarWidth + ((nextGradeTop - failBarWidth) / 2);
|
||||
this.descendingCutoffs.push({designation: this.GRADES[gradeLength], cutoff: failBarWidth});
|
||||
this.descendingCutoffs[gradeLength - 1]['cutoff'] = Math.round(targetWidth);
|
||||
|
||||
var $newGradeBar = this.gradeCutoffTemplate({ descriptor : this.GRADES[gradeLength],
|
||||
width : targetWidth, removable : true });
|
||||
var gradeDom = this.$el.find('.grades');
|
||||
gradeDom.children().last().before($newGradeBar);
|
||||
var newEle = gradeDom.children()[gradeLength];
|
||||
$(newEle).resizable({
|
||||
handles: "e",
|
||||
containment : "parent",
|
||||
start : this.startMoveClosure(),
|
||||
resize : this.moveBarClosure(),
|
||||
stop : this.stopDragClosure()
|
||||
});
|
||||
|
||||
// Munge existing grade labels?
|
||||
// If going from Pass/Fail to 3 levels, change to Pass to A
|
||||
if (gradeLength === 1 && this.descendingCutoffs[0]['designation'] === 'Pass') {
|
||||
this.descendingCutoffs[0]['designation'] = this.GRADES[0];
|
||||
this.setTopGradeLabel();
|
||||
}
|
||||
this.setFailLabel();
|
||||
|
||||
this.renderGradeRanges();
|
||||
this.saveCutoffs();
|
||||
},
|
||||
|
||||
removeGrade: function(e) {
|
||||
e.preventDefault();
|
||||
var domElement = $(e.currentTarget).closest('li');
|
||||
var index = domElement.index();
|
||||
// copy the boundary up to the next higher grade then remove
|
||||
this.descendingCutoffs[index - 1]['cutoff'] = this.descendingCutoffs[index]['cutoff'];
|
||||
this.descendingCutoffs.splice(index, 1);
|
||||
domElement.remove();
|
||||
|
||||
if (this.descendingCutoffs.length === 1 && this.descendingCutoffs[0]['designation'] === this.GRADES[0]) {
|
||||
this.descendingCutoffs[0]['designation'] = 'Pass';
|
||||
this.setTopGradeLabel();
|
||||
}
|
||||
this.setFailLabel();
|
||||
this.renderGradeRanges();
|
||||
this.saveCutoffs();
|
||||
},
|
||||
|
||||
updateDesignation: function(e) {
|
||||
var index = $(e.currentTarget).closest('li').index();
|
||||
this.descendingCutoffs[index]['designation'] = $(e.currentTarget).html();
|
||||
this.saveCutoffs();
|
||||
},
|
||||
|
||||
failLabel: function() {
|
||||
if (this.descendingCutoffs.length === 1) return 'Fail';
|
||||
else return 'F';
|
||||
},
|
||||
setFailLabel: function() {
|
||||
this.$el.find('.grades .letter-grade').last().html(this.failLabel());
|
||||
},
|
||||
setTopGradeLabel: function() {
|
||||
this.$el.find('.grades .letter-grade').first().html(this.descendingCutoffs[0]['designation']);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
CMS.Views.Settings.GraderView = CMS.Views.ValidatingView.extend({
|
||||
// Model class is CMS.Models.Settings.CourseGrader
|
||||
events : {
|
||||
"blur input" : "updateModel",
|
||||
"blur textarea" : "updateModel",
|
||||
"click .remove-grading-data" : "deleteModel"
|
||||
},
|
||||
initialize : function() {
|
||||
this.model.on('error', this.handleValidationError, this);
|
||||
this.selectorToField = _.invert(this.fieldToSelectorMap);
|
||||
this.render();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return this;
|
||||
},
|
||||
fieldToSelectorMap : {
|
||||
'type' : 'course-grading-assignment-name',
|
||||
'short_label' : 'course-grading-assignment-shortname',
|
||||
'min_count' : 'course-grading-assignment-totalassignments',
|
||||
'drop_count' : 'course-grading-assignment-droppable',
|
||||
'weight' : 'course-grading-assignment-gradeweight'
|
||||
},
|
||||
updateModel : function(event) {
|
||||
// HACK to fix model sometimes losing its pointer to the collection [I think I fixed this but leaving
|
||||
// this in out of paranoia. If this error ever happens, the user will get a warning that they cannot
|
||||
// give 2 assignments the same name.]
|
||||
if (!this.model.collection) {
|
||||
this.model.collection = this.collection;
|
||||
}
|
||||
|
||||
switch (event.currentTarget.id) {
|
||||
case 'course-grading-assignment-totalassignments':
|
||||
this.$el.find('#course-grading-assignment-droppable').attr('max', $(event.currentTarget).val());
|
||||
this.saveIfChanged(event);
|
||||
break;
|
||||
case 'course-grading-assignment-name':
|
||||
var oldName = this.model.get('type');
|
||||
if (this.saveIfChanged(event) && !_.isEmpty(oldName)) {
|
||||
// overload the error display logic
|
||||
this._cacheValidationErrors.push(event.currentTarget);
|
||||
$(event.currentTarget).parent().append(
|
||||
this.errorTemplate({message : 'For grading to work, you must change all "' + oldName +
|
||||
'" subsections to "' + this.model.get('type') + '".'}));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.saveIfChanged(event);
|
||||
break;
|
||||
}
|
||||
},
|
||||
deleteModel : function(e) {
|
||||
this.model.destroy(
|
||||
{ error : CMS.ServerError});
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
});
|
||||
370
cms/static/js/views/settings/settings_grading_view.js
Normal file
@@ -0,0 +1,370 @@
|
||||
if (!CMS.Views['Settings']) CMS.Views.Settings = {}; // ensure the pseudo pkg exists
|
||||
|
||||
CMS.Views.Settings.Grading = CMS.Views.ValidatingView.extend({
|
||||
// Model class is CMS.Models.Settings.CourseGradingPolicy
|
||||
events : {
|
||||
"blur input" : "updateModel",
|
||||
"blur textarea" : "updateModel",
|
||||
"blur span[contenteditable=true]" : "updateDesignation",
|
||||
"click .settings-extra header" : "showSettingsExtras",
|
||||
"click .new-grade-button" : "addNewGrade",
|
||||
"click .remove-button" : "removeGrade",
|
||||
"click .add-grading-data" : "addAssignmentType",
|
||||
// would love to move to a general superclass, but event hashes don't inherit in backbone :-(
|
||||
'focus :input' : "inputFocus",
|
||||
'blur :input' : "inputUnfocus"
|
||||
},
|
||||
initialize : function() {
|
||||
// load template for grading view
|
||||
var self = this;
|
||||
this.gradeCutoffTemplate = _.template('<li class="grade-specific-bar" style="width:<%= width %>%"><span class="letter-grade" contenteditable>' +
|
||||
'<%= descriptor %>' +
|
||||
'</span><span class="range"></span>' +
|
||||
'<% if (removable) {%><a href="#" class="remove-button">remove</a><% ;} %>' +
|
||||
'</li>');
|
||||
|
||||
// Instrument grading scale
|
||||
// convert cutoffs to inversely ordered list
|
||||
var modelCutoffs = this.model.get('grade_cutoffs');
|
||||
for (var cutoff in modelCutoffs) {
|
||||
this.descendingCutoffs.push({designation: cutoff, cutoff: Math.round(modelCutoffs[cutoff] * 100)});
|
||||
}
|
||||
this.descendingCutoffs = _.sortBy(this.descendingCutoffs,
|
||||
function (gradeEle) { return -gradeEle['cutoff']; });
|
||||
|
||||
// Instrument grace period
|
||||
this.$el.find('#course-grading-graceperiod').timepicker();
|
||||
|
||||
// instantiates an editor template for each update in the collection
|
||||
// Because this calls render, put it after everything which render may depend upon to prevent race condition.
|
||||
window.templateLoader.loadRemoteTemplate("course_grade_policy",
|
||||
"/static/client_templates/course_grade_policy.html",
|
||||
function (raw_template) {
|
||||
self.template = _.template(raw_template);
|
||||
self.render();
|
||||
}
|
||||
);
|
||||
this.model.on('error', this.handleValidationError, this);
|
||||
this.model.get('graders').on('remove', this.render, this);
|
||||
this.model.get('graders').on('reset', this.render, this);
|
||||
this.model.get('graders').on('add', this.render, this);
|
||||
this.selectorToField = _.invert(this.fieldToSelectorMap);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// prevent bootstrap race condition by event dispatch
|
||||
if (!this.template) return;
|
||||
|
||||
// Create and render the grading type subs
|
||||
var self = this;
|
||||
var gradelist = this.$el.find('.course-grading-assignment-list');
|
||||
// Undo the double invocation error. At some point, fix the double invocation
|
||||
$(gradelist).empty();
|
||||
var gradeCollection = this.model.get('graders');
|
||||
gradeCollection.each(function(gradeModel) {
|
||||
$(gradelist).append(self.template({model : gradeModel }));
|
||||
var newEle = gradelist.children().last();
|
||||
var newView = new CMS.Views.Settings.GraderView({el: newEle,
|
||||
model : gradeModel, collection : gradeCollection });
|
||||
});
|
||||
|
||||
// render the grade cutoffs
|
||||
this.renderCutoffBar();
|
||||
|
||||
var graceEle = this.$el.find('#course-grading-graceperiod');
|
||||
graceEle.timepicker({'timeFormat' : 'H:i'}); // init doesn't take setTime
|
||||
if (this.model.has('grace_period')) graceEle.timepicker('setTime', this.model.gracePeriodToDate());
|
||||
// remove any existing listeners to keep them from piling on b/c render gets called frequently
|
||||
graceEle.off('change', this.setGracePeriod);
|
||||
graceEle.on('change', this, this.setGracePeriod);
|
||||
|
||||
return this;
|
||||
},
|
||||
addAssignmentType : function(e) {
|
||||
e.preventDefault();
|
||||
this.model.get('graders').push({});
|
||||
},
|
||||
fieldToSelectorMap : {
|
||||
'grace_period' : 'course-grading-graceperiod'
|
||||
},
|
||||
setGracePeriod : function(event) {
|
||||
event.data.clearValidationErrors();
|
||||
var newVal = event.data.model.dateToGracePeriod($(event.currentTarget).timepicker('getTime'));
|
||||
if (event.data.model.get('grace_period') != newVal) event.data.model.save('grace_period', newVal,
|
||||
{ error : CMS.ServerError});
|
||||
},
|
||||
updateModel : function(event) {
|
||||
if (!this.selectorToField[event.currentTarget.id]) return;
|
||||
|
||||
switch (this.selectorToField[event.currentTarget.id]) {
|
||||
case 'grace_period': // handled above
|
||||
break;
|
||||
|
||||
default:
|
||||
this.saveIfChanged(event);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// Grade sliders attributes and methods
|
||||
// Grade bars are li's ordered A -> F with A taking whole width, B overlaying it with its paint, ...
|
||||
// The actual cutoff for each grade is the width % of the next lower grade; so, the hack here
|
||||
// is to lay down a whole width bar claiming it's A and then lay down bars for each actual grade
|
||||
// starting w/ A but posting the label in the preceding li and setting the label of the last to "Fail" or "F"
|
||||
|
||||
// A does not have a drag bar (cannot change its upper limit)
|
||||
// Need to insert new bars in right place.
|
||||
GRADES : ['A', 'B', 'C', 'D'], // defaults for new grade designators
|
||||
descendingCutoffs : [], // array of { designation : , cutoff : }
|
||||
gradeBarWidth : null, // cache of value since it won't change (more certain)
|
||||
|
||||
renderCutoffBar: function() {
|
||||
var gradeBar =this.$el.find('.grade-bar');
|
||||
this.gradeBarWidth = gradeBar.width();
|
||||
var gradelist = gradeBar.children('.grades');
|
||||
// HACK fixing a duplicate call issue by undoing previous call effect. Need to figure out why called 2x
|
||||
gradelist.empty();
|
||||
var nextWidth = 100; // first width is 100%
|
||||
// Can probably be simplified to one variable now.
|
||||
var removable = false;
|
||||
var draggable = false; // first and last are not removable, first is not draggable
|
||||
_.each(this.descendingCutoffs,
|
||||
function(cutoff, index) {
|
||||
var newBar = this.gradeCutoffTemplate({
|
||||
descriptor : cutoff['designation'] ,
|
||||
width : nextWidth,
|
||||
removable : removable });
|
||||
gradelist.append(newBar);
|
||||
if (draggable) {
|
||||
newBar = gradelist.children().last(); // get the dom object not the unparsed string
|
||||
newBar.resizable({
|
||||
handles: "e",
|
||||
containment : "parent",
|
||||
start : this.startMoveClosure(),
|
||||
resize : this.moveBarClosure(),
|
||||
stop : this.stopDragClosure()
|
||||
});
|
||||
}
|
||||
// prepare for next
|
||||
nextWidth = cutoff['cutoff'];
|
||||
removable = true; // first is not removable, all others are
|
||||
draggable = true;
|
||||
},
|
||||
this);
|
||||
// add fail which is not in data
|
||||
var failBar = this.gradeCutoffTemplate({ descriptor : this.failLabel(),
|
||||
width : nextWidth, removable : false});
|
||||
$(failBar).find("span[contenteditable=true]").attr("contenteditable", false);
|
||||
gradelist.append(failBar);
|
||||
gradelist.children().last().resizable({
|
||||
handles: "e",
|
||||
containment : "parent",
|
||||
start : this.startMoveClosure(),
|
||||
resize : this.moveBarClosure(),
|
||||
stop : this.stopDragClosure()
|
||||
});
|
||||
|
||||
this.renderGradeRanges();
|
||||
},
|
||||
|
||||
showSettingsExtras : function(event) {
|
||||
$(event.currentTarget).toggleClass('active');
|
||||
$(event.currentTarget).siblings.toggleClass('is-shown');
|
||||
},
|
||||
|
||||
|
||||
startMoveClosure : function() {
|
||||
// set min/max widths
|
||||
var cachethis = this;
|
||||
var widthPerPoint = cachethis.gradeBarWidth / 100;
|
||||
return function(event, ui) {
|
||||
var barIndex = ui.element.index();
|
||||
// min and max represent limits not labels (note, can's make smaller than 3 points wide)
|
||||
var min = (barIndex < cachethis.descendingCutoffs.length ? cachethis.descendingCutoffs[barIndex]['cutoff'] + 3 : 3);
|
||||
// minus 2 b/c minus 1 is the element we're effecting. It's max is just shy of the next one above it
|
||||
var max = (barIndex >= 2 ? cachethis.descendingCutoffs[barIndex - 2]['cutoff'] - 3 : 97);
|
||||
ui.element.resizable("option",{minWidth : min * widthPerPoint, maxWidth : max * widthPerPoint});
|
||||
};
|
||||
},
|
||||
|
||||
moveBarClosure : function() {
|
||||
// 0th ele doesn't have a bar; so, will never invoke this
|
||||
var cachethis = this;
|
||||
return function(event, ui) {
|
||||
var barIndex = ui.element.index();
|
||||
// min and max represent limits not labels (note, can's make smaller than 3 points wide)
|
||||
var min = (barIndex < cachethis.descendingCutoffs.length ? cachethis.descendingCutoffs[barIndex]['cutoff'] + 3 : 3);
|
||||
// minus 2 b/c minus 1 is the element we're effecting. It's max is just shy of the next one above it
|
||||
var max = (barIndex >= 2 ? cachethis.descendingCutoffs[barIndex - 2]['cutoff'] - 3 : 100);
|
||||
var percentage = Math.min(Math.max(ui.size.width / cachethis.gradeBarWidth * 100, min), max);
|
||||
cachethis.descendingCutoffs[barIndex - 1]['cutoff'] = Math.round(percentage);
|
||||
cachethis.renderGradeRanges();
|
||||
};
|
||||
},
|
||||
|
||||
renderGradeRanges: function() {
|
||||
// the labels showing the range e.g., 71-80
|
||||
var cutoffs = this.descendingCutoffs;
|
||||
this.$el.find('.range').each(function(i) {
|
||||
var min = (i < cutoffs.length ? cutoffs[i]['cutoff'] : 0);
|
||||
var max = (i > 0 ? cutoffs[i - 1]['cutoff'] : 100);
|
||||
$(this).text(min + '-' + max);
|
||||
});
|
||||
},
|
||||
|
||||
stopDragClosure: function() {
|
||||
var cachethis = this;
|
||||
return function(event, ui) {
|
||||
// for some reason the resize is setting height to 0
|
||||
cachethis.saveCutoffs();
|
||||
};
|
||||
},
|
||||
|
||||
saveCutoffs: function() {
|
||||
this.model.save('grade_cutoffs',
|
||||
_.reduce(this.descendingCutoffs,
|
||||
function(object, cutoff) {
|
||||
object[cutoff['designation']] = cutoff['cutoff'] / 100.0;
|
||||
return object;
|
||||
},
|
||||
{}),
|
||||
{ error : CMS.ServerError});
|
||||
},
|
||||
|
||||
addNewGrade: function(e) {
|
||||
e.preventDefault();
|
||||
var gradeLength = this.descendingCutoffs.length; // cutoffs doesn't include fail/f so this is only the passing grades
|
||||
if(gradeLength > 3) {
|
||||
// TODO shouldn't we disable the button
|
||||
return;
|
||||
}
|
||||
var failBarWidth = this.descendingCutoffs[gradeLength - 1]['cutoff'];
|
||||
// going to split the grade above the insertion point in half leaving fail in same place
|
||||
var nextGradeTop = (gradeLength > 1 ? this.descendingCutoffs[gradeLength - 2]['cutoff'] : 100);
|
||||
var targetWidth = failBarWidth + ((nextGradeTop - failBarWidth) / 2);
|
||||
this.descendingCutoffs.push({designation: this.GRADES[gradeLength], cutoff: failBarWidth});
|
||||
this.descendingCutoffs[gradeLength - 1]['cutoff'] = Math.round(targetWidth);
|
||||
|
||||
var $newGradeBar = this.gradeCutoffTemplate({ descriptor : this.GRADES[gradeLength],
|
||||
width : targetWidth, removable : true });
|
||||
var gradeDom = this.$el.find('.grades');
|
||||
gradeDom.children().last().before($newGradeBar);
|
||||
var newEle = gradeDom.children()[gradeLength];
|
||||
$(newEle).resizable({
|
||||
handles: "e",
|
||||
containment : "parent",
|
||||
start : this.startMoveClosure(),
|
||||
resize : this.moveBarClosure(),
|
||||
stop : this.stopDragClosure()
|
||||
});
|
||||
|
||||
// Munge existing grade labels?
|
||||
// If going from Pass/Fail to 3 levels, change to Pass to A
|
||||
if (gradeLength === 1 && this.descendingCutoffs[0]['designation'] === 'Pass') {
|
||||
this.descendingCutoffs[0]['designation'] = this.GRADES[0];
|
||||
this.setTopGradeLabel();
|
||||
}
|
||||
this.setFailLabel();
|
||||
|
||||
this.renderGradeRanges();
|
||||
this.saveCutoffs();
|
||||
},
|
||||
|
||||
removeGrade: function(e) {
|
||||
e.preventDefault();
|
||||
var domElement = $(e.currentTarget).closest('li');
|
||||
var index = domElement.index();
|
||||
// copy the boundary up to the next higher grade then remove
|
||||
this.descendingCutoffs[index - 1]['cutoff'] = this.descendingCutoffs[index]['cutoff'];
|
||||
this.descendingCutoffs.splice(index, 1);
|
||||
domElement.remove();
|
||||
|
||||
if (this.descendingCutoffs.length === 1 && this.descendingCutoffs[0]['designation'] === this.GRADES[0]) {
|
||||
this.descendingCutoffs[0]['designation'] = 'Pass';
|
||||
this.setTopGradeLabel();
|
||||
}
|
||||
this.setFailLabel();
|
||||
this.renderGradeRanges();
|
||||
this.saveCutoffs();
|
||||
},
|
||||
|
||||
updateDesignation: function(e) {
|
||||
var index = $(e.currentTarget).closest('li').index();
|
||||
this.descendingCutoffs[index]['designation'] = $(e.currentTarget).html();
|
||||
this.saveCutoffs();
|
||||
},
|
||||
|
||||
failLabel: function() {
|
||||
if (this.descendingCutoffs.length === 1) return 'Fail';
|
||||
else return 'F';
|
||||
},
|
||||
setFailLabel: function() {
|
||||
this.$el.find('.grades .letter-grade').last().html(this.failLabel());
|
||||
},
|
||||
setTopGradeLabel: function() {
|
||||
this.$el.find('.grades .letter-grade').first().html(this.descendingCutoffs[0]['designation']);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
CMS.Views.Settings.GraderView = CMS.Views.ValidatingView.extend({
|
||||
// Model class is CMS.Models.Settings.CourseGrader
|
||||
events : {
|
||||
"blur input" : "updateModel",
|
||||
"blur textarea" : "updateModel",
|
||||
"click .remove-grading-data" : "deleteModel",
|
||||
// would love to move to a general superclass, but event hashes don't inherit in backbone :-(
|
||||
'focus :input' : "inputFocus",
|
||||
'blur :input' : "inputUnfocus"
|
||||
},
|
||||
initialize : function() {
|
||||
this.model.on('error', this.handleValidationError, this);
|
||||
this.selectorToField = _.invert(this.fieldToSelectorMap);
|
||||
this.render();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return this;
|
||||
},
|
||||
fieldToSelectorMap : {
|
||||
'type' : 'course-grading-assignment-name',
|
||||
'short_label' : 'course-grading-assignment-shortname',
|
||||
'min_count' : 'course-grading-assignment-totalassignments',
|
||||
'drop_count' : 'course-grading-assignment-droppable',
|
||||
'weight' : 'course-grading-assignment-gradeweight'
|
||||
},
|
||||
updateModel : function(event) {
|
||||
// HACK to fix model sometimes losing its pointer to the collection [I think I fixed this but leaving
|
||||
// this in out of paranoia. If this error ever happens, the user will get a warning that they cannot
|
||||
// give 2 assignments the same name.]
|
||||
if (!this.model.collection) {
|
||||
this.model.collection = this.collection;
|
||||
}
|
||||
|
||||
switch (event.currentTarget.id) {
|
||||
case 'course-grading-assignment-totalassignments':
|
||||
this.$el.find('#course-grading-assignment-droppable').attr('max', $(event.currentTarget).val());
|
||||
this.saveIfChanged(event);
|
||||
break;
|
||||
case 'course-grading-assignment-name':
|
||||
var oldName = this.model.get('type');
|
||||
if (this.saveIfChanged(event) && !_.isEmpty(oldName)) {
|
||||
// overload the error display logic
|
||||
this._cacheValidationErrors.push(event.currentTarget);
|
||||
$(event.currentTarget).parent().append(
|
||||
this.errorTemplate({message : 'For grading to work, you must change all "' + oldName +
|
||||
'" subsections to "' + this.model.get('type') + '".'}));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.saveIfChanged(event);
|
||||
break;
|
||||
}
|
||||
},
|
||||
deleteModel : function(e) {
|
||||
this.model.destroy(
|
||||
{ error : CMS.ServerError});
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
});
|
||||
77
cms/static/js/views/validating_view.js
Normal file
@@ -0,0 +1,77 @@
|
||||
CMS.Views.ValidatingView = Backbone.View.extend({
|
||||
// Intended as an abstract class which catches validation errors on the model and
|
||||
// decorates the fields. Needs wiring per class, but this initialization shows how
|
||||
// either have your init call this one or copy the contents
|
||||
initialize : function() {
|
||||
this.model.on('error', this.handleValidationError, this);
|
||||
this.selectorToField = _.invert(this.fieldToSelectorMap);
|
||||
},
|
||||
|
||||
errorTemplate : _.template('<span class="message-error"><%= message %></span>'),
|
||||
|
||||
events : {
|
||||
"blur input" : "clearValidationErrors",
|
||||
"blur textarea" : "clearValidationErrors"
|
||||
},
|
||||
fieldToSelectorMap : {
|
||||
// Your subclass must populate this w/ all of the model keys and dom selectors
|
||||
// which may be the subjects of validation errors
|
||||
},
|
||||
_cacheValidationErrors : [],
|
||||
handleValidationError : function(model, error) {
|
||||
// error triggered either by validation or server error
|
||||
// error is object w/ fields and error strings
|
||||
for (var field in error) {
|
||||
var ele = this.$el.find('#' + this.fieldToSelectorMap[field]);
|
||||
if (ele.length === 0) {
|
||||
// check if it might a server error: note a typo in the field name
|
||||
// or failure to put in a map may cause this to muffle validation errors
|
||||
if (_.has(error, 'error') && _.has(error, 'responseText')) {
|
||||
CMS.ServerError(model, error);
|
||||
return;
|
||||
}
|
||||
else continue;
|
||||
}
|
||||
this._cacheValidationErrors.push(ele);
|
||||
if ($(ele).is('div')) {
|
||||
// put error on the contained inputs
|
||||
$(ele).find('input, textarea').addClass('error');
|
||||
}
|
||||
else $(ele).addClass('error');
|
||||
$(ele).parent().append(this.errorTemplate({message : error[field]}));
|
||||
}
|
||||
},
|
||||
|
||||
clearValidationErrors : function() {
|
||||
// error is object w/ fields and error strings
|
||||
while (this._cacheValidationErrors.length > 0) {
|
||||
var ele = this._cacheValidationErrors.pop();
|
||||
if ($(ele).is('div')) {
|
||||
// put error on the contained inputs
|
||||
$(ele).find('input, textarea').removeClass('error');
|
||||
}
|
||||
else $(ele).removeClass('error');
|
||||
$(ele).nextAll('.message-error').remove();
|
||||
}
|
||||
},
|
||||
|
||||
saveIfChanged : function(event) {
|
||||
// returns true if the value changed and was thus sent to server
|
||||
var field = this.selectorToField[event.currentTarget.id];
|
||||
var currentVal = this.model.get(field);
|
||||
var newVal = $(event.currentTarget).val();
|
||||
this.clearValidationErrors(); // curr = new if user reverts manually
|
||||
if (currentVal != newVal) {
|
||||
this.model.save(field, newVal);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
},
|
||||
// these should perhaps go into a superclass but lack of event hash inheritance demotivates me
|
||||
inputFocus : function(event) {
|
||||
$("label[for='" + event.currentTarget.id + "']").addClass("is-focused");
|
||||
},
|
||||
inputUnfocus : function(event) {
|
||||
$("label[for='" + event.currentTarget.id + "']").removeClass("is-focused");
|
||||
}
|
||||
});
|
||||
294
cms/static/sass/_account.scss
Normal file
@@ -0,0 +1,294 @@
|
||||
// Studio - Sign In/Up
|
||||
// ====================
|
||||
body.signup, body.signin {
|
||||
|
||||
.wrapper-content {
|
||||
margin: 0;
|
||||
padding: 0 $baseline;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
@include clearfix();
|
||||
@include font-size(16);
|
||||
max-width: $fg-max-width;
|
||||
min-width: $fg-min-width;
|
||||
width: flex-grid(12);
|
||||
margin: 0 auto;
|
||||
color: $gray-d2;
|
||||
|
||||
header {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
border-bottom: 1px solid $gray-l4;
|
||||
padding-bottom: ($baseline/2);
|
||||
|
||||
h1 {
|
||||
@include font-size(32);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.action {
|
||||
@include font-size(13);
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 40%;
|
||||
}
|
||||
}
|
||||
|
||||
.introduction {
|
||||
@include font-size(14);
|
||||
margin: 0 0 $baseline 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content-primary, .content-supplementary {
|
||||
@include box-sizing(border-box);
|
||||
float: left;
|
||||
}
|
||||
|
||||
.content-primary {
|
||||
width: flex-grid(8, 12);
|
||||
margin-right: flex-gutter();
|
||||
|
||||
form {
|
||||
@include box-sizing(border-box);
|
||||
@include box-shadow(0 1px 2px $shadow-l1);
|
||||
@include border-radius(2px);
|
||||
width: 100%;
|
||||
border: 1px solid $gray-l2;
|
||||
padding: $baseline ($baseline*1.5);
|
||||
background: $white;
|
||||
|
||||
.form-actions {
|
||||
margin-top: $baseline;
|
||||
|
||||
.action-primary {
|
||||
@include blue-button;
|
||||
@include transition(all .15s);
|
||||
@include font-size(15);
|
||||
display:block;
|
||||
width: 100%;
|
||||
padding: ($baseline*0.75) ($baseline/2);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.list-input {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
.field {
|
||||
margin: 0 0 ($baseline*0.75) 0;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.required {
|
||||
|
||||
label {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
label:after {
|
||||
margin-left: ($baseline/4);
|
||||
content: "*";
|
||||
}
|
||||
}
|
||||
|
||||
label, input, textarea {
|
||||
display: block;
|
||||
}
|
||||
|
||||
label {
|
||||
@include font-size(14);
|
||||
@include transition(color, 0.15s, ease-in-out);
|
||||
margin: 0 0 ($baseline/4) 0;
|
||||
|
||||
&.is-focused {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
@include font-size(16);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: ($baseline/2);
|
||||
|
||||
&.long {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.short {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
::-webkit-input-placeholder {
|
||||
color: $gray-l4;
|
||||
}
|
||||
|
||||
:-moz-placeholder {
|
||||
color: $gray-l3;
|
||||
}
|
||||
|
||||
::-moz-placeholder {
|
||||
color: $gray-l3;
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
color: $gray-l3;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
|
||||
+ .tip {
|
||||
color: $gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea.long {
|
||||
height: ($baseline*5);
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
display: inline-block;
|
||||
margin-right: ($baseline/4);
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
||||
& + label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.tip {
|
||||
@include transition(color, 0.15s, ease-in-out);
|
||||
@include font-size(13);
|
||||
display: block;
|
||||
margin-top: ($baseline/4);
|
||||
color: $gray-l3;
|
||||
}
|
||||
}
|
||||
|
||||
.field-group {
|
||||
@include clearfix();
|
||||
margin: 0 0 ($baseline/2) 0;
|
||||
|
||||
.field {
|
||||
display: block;
|
||||
width: 47%;
|
||||
border-bottom: none;
|
||||
margin: 0 $baseline 0 0;
|
||||
padding-bottom: 0;
|
||||
|
||||
&:nth-child(odd) {
|
||||
float: left;
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-supplementary {
|
||||
width: flex-grid(4, 12);
|
||||
|
||||
.bit {
|
||||
@include font-size(13);
|
||||
margin: 0 0 $baseline 0;
|
||||
border-bottom: 1px solid $gray-l4;
|
||||
padding: 0 0 $baseline 0;
|
||||
color: $gray-l1;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
border: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@include font-size(14);
|
||||
margin: 0 0 ($baseline/4) 0;
|
||||
color: $gray-d2;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.signup {
|
||||
|
||||
}
|
||||
|
||||
.signin {
|
||||
|
||||
#field-password {
|
||||
position: relative;
|
||||
|
||||
.action-forgotpassword {
|
||||
@include font-size(13);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// messages
|
||||
.message {
|
||||
@include font-size(14);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.message-status {
|
||||
display: none;
|
||||
@include border-top-radius(2px);
|
||||
@include box-sizing(border-box);
|
||||
border-bottom: 2px solid $yellow-d2;
|
||||
margin: 0 0 $baseline 0;
|
||||
padding: ($baseline/2) $baseline;
|
||||
font-weight: 500;
|
||||
background: $yellow-d1;
|
||||
color: $white;
|
||||
|
||||
.ss-icon {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
@include font-size(16);
|
||||
display: inline-block;
|
||||
margin-right: ($baseline/2);
|
||||
}
|
||||
|
||||
.text {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&.error {
|
||||
border-color: shade($red, 50%);
|
||||
background: tint($red, 20%);
|
||||
}
|
||||
|
||||
&.is-shown {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
.assets {
|
||||
.uploads {
|
||||
input.asset-search-input {
|
||||
float: left;
|
||||
width: 260px;
|
||||
|
||||
@@ -1,180 +1,507 @@
|
||||
// -------------------------------------
|
||||
//
|
||||
// Universal
|
||||
//
|
||||
// -------------------------------------
|
||||
// studio base styling
|
||||
// ====================
|
||||
|
||||
body {
|
||||
min-width: 980px;
|
||||
background: rgb(240, 241, 245);
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
color: $baseFontColor;
|
||||
// basic reset
|
||||
html {
|
||||
font-size: 62.5%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body,
|
||||
input {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
body {
|
||||
@include font-size(16);
|
||||
min-width: 980px;
|
||||
background: $gray-l5;
|
||||
line-height: 1.6;
|
||||
color: $baseFontColor;
|
||||
}
|
||||
|
||||
body, input {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: $blue;
|
||||
@include transition(color .15s);
|
||||
text-decoration: none;
|
||||
color: $blue;
|
||||
@include transition(color .15s);
|
||||
|
||||
&:hover {
|
||||
color: #cb9c40;
|
||||
}
|
||||
&:hover {
|
||||
color: #cb9c40;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
float: left;
|
||||
font-size: 28px;
|
||||
font-weight: 300;
|
||||
margin: 24px 6px;
|
||||
@include font-size(28);
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.waiting {
|
||||
opacity: 0.1;
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.page-actions {
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.main-wrapper {
|
||||
.wrapper {
|
||||
@include clearfix();
|
||||
@include box-sizing(border-box);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// layout - basic page header
|
||||
.wrapper-mast {
|
||||
margin: 0;
|
||||
padding: 0 $baseline;
|
||||
position: relative;
|
||||
|
||||
.mast, .metadata {
|
||||
@include clearfix();
|
||||
@include font-size(16);
|
||||
position: relative;
|
||||
margin: 0 40px;
|
||||
max-width: $fg-max-width;
|
||||
min-width: $fg-min-width;
|
||||
width: flex-grid(12);
|
||||
margin: 0 auto $baseline auto;
|
||||
color: $gray-d2;
|
||||
}
|
||||
|
||||
.mast {
|
||||
border-bottom: 1px solid $gray-l4;
|
||||
padding-bottom: ($baseline/2);
|
||||
|
||||
.title-sub {
|
||||
@include font-size(14);
|
||||
position: relative;
|
||||
top: ($baseline/4);
|
||||
display: block;
|
||||
margin: 0;
|
||||
color: $gray-l2;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.title, .title-1 {
|
||||
@include font-size(32);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 600;
|
||||
color: $gray-d3;
|
||||
}
|
||||
|
||||
.nav-hierarchy {
|
||||
@include font-size(14);
|
||||
display: block;
|
||||
margin: 0;
|
||||
color: $gray-l2;
|
||||
font-weight: 400;
|
||||
|
||||
.nav-item {
|
||||
display: inline;
|
||||
vertical-align: middle;
|
||||
margin-right: ($baseline/4);
|
||||
|
||||
&:after {
|
||||
content: ">>";
|
||||
margin-left: ($baseline/4);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
|
||||
&:after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// layout with actions
|
||||
.title {
|
||||
width: flex-grid(12);
|
||||
}
|
||||
|
||||
// layout with actions
|
||||
&.has-actions {
|
||||
@include clearfix();
|
||||
|
||||
.title {
|
||||
float: left;
|
||||
width: flex-grid(6,12);
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
.nav-actions {
|
||||
position: relative;
|
||||
bottom: -($baseline*0.75);
|
||||
float: right;
|
||||
width: flex-grid(6,12);
|
||||
text-align: right;
|
||||
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-right: ($baseline/2);
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// buttons
|
||||
.button {
|
||||
padding: ($baseline/4) ($baseline/2) ($baseline/3) ($baseline/2) !important;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.new-button {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.view-button {
|
||||
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.upload-button .icon-create {
|
||||
@include font-size(18);
|
||||
margin-top: ($baseline/4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// layout with actions
|
||||
&.has-subtitle {
|
||||
|
||||
.nav-actions {
|
||||
bottom: -($baseline*1.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// page metadata/action bar
|
||||
.metadata {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// layout - basic page content
|
||||
.wrapper-content {
|
||||
margin: 0;
|
||||
padding: 0 $baseline;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.content {
|
||||
@include clearfix();
|
||||
@include font-size(16);
|
||||
max-width: $fg-max-width;
|
||||
min-width: $fg-min-width;
|
||||
width: flex-grid(12);
|
||||
margin: 0 auto;
|
||||
color: $gray-d2;
|
||||
|
||||
header {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
border-bottom: 1px solid $gray-l4;
|
||||
padding-bottom: ($baseline/2);
|
||||
|
||||
.title-sub {
|
||||
@include font-size(14);
|
||||
display: block;
|
||||
margin: 0;
|
||||
color: $gray-l2;
|
||||
}
|
||||
|
||||
.title, .title-1 {
|
||||
@include font-size(32);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 600;
|
||||
color: $gray-d3;
|
||||
}
|
||||
}
|
||||
|
||||
.introduction {
|
||||
@include box-sizing(border-box);
|
||||
@include font-size(14);
|
||||
width: flex-grid(12);
|
||||
margin: 0 0 $baseline 0;
|
||||
|
||||
.copy strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&.has-links {
|
||||
@include clearfix();
|
||||
|
||||
.copy {
|
||||
float: left;
|
||||
width: flex-grid(8,12);
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
.nav-introduction-supplementary {
|
||||
@include font-size(13);
|
||||
float: right;
|
||||
width: flex-grid(4,12);
|
||||
display: block;
|
||||
text-align: right;
|
||||
|
||||
.icon {
|
||||
@include font-size(14);
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: ($baseline/4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-primary, .content-supplementary {
|
||||
@include box-sizing(border-box);
|
||||
}
|
||||
|
||||
// layout - primary content
|
||||
.content-primary {
|
||||
|
||||
.title-1, .title-2, .title-3, .title-4, .title-5, .title-5 {
|
||||
color: $gray-d3;
|
||||
}
|
||||
|
||||
.title-1 {
|
||||
|
||||
}
|
||||
|
||||
.title-2 {
|
||||
@include font-size(24);
|
||||
margin: 0 0 ($baseline/2) 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.title-3 {
|
||||
@include font-size(16);
|
||||
margin: 0 0 ($baseline/4) 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.title-4 {
|
||||
|
||||
}
|
||||
|
||||
.title-5 {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// layout - supplemental content
|
||||
.content-supplementary {
|
||||
|
||||
.bit {
|
||||
@include font-size(13);
|
||||
margin: 0 0 $baseline 0;
|
||||
border-bottom: 1px solid $gray-l4;
|
||||
padding: 0 0 $baseline 0;
|
||||
color: $gray-l1;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
border: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@include font-size(14);
|
||||
margin: 0 0 ($baseline/4) 0;
|
||||
color: $gray-d2;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 $baseline 0;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-related {
|
||||
|
||||
.nav-item {
|
||||
margin-bottom: ($baseline/4);
|
||||
border-bottom: 1px dotted $gray-l4;
|
||||
padding-bottom: ($baseline/4);
|
||||
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
border: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// layout - grandfathered
|
||||
.main-wrapper {
|
||||
position: relative;
|
||||
margin: 0 40px;
|
||||
}
|
||||
|
||||
.inner-wrapper {
|
||||
position: relative;
|
||||
max-width: 1280px;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
max-width: 1280px;
|
||||
margin: auto;
|
||||
|
||||
> article {
|
||||
clear: both;
|
||||
}
|
||||
> article {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
float: right;
|
||||
width: 28%;
|
||||
float: right;
|
||||
width: 28%;
|
||||
}
|
||||
|
||||
.left {
|
||||
float: left;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
footer {
|
||||
clear: both;
|
||||
height: 100px;
|
||||
}
|
||||
// ====================
|
||||
|
||||
// forms
|
||||
input[type="text"],
|
||||
input[type="email"],
|
||||
input[type="password"],
|
||||
textarea.text {
|
||||
padding: 6px 8px 8px;
|
||||
@include box-sizing(border-box);
|
||||
border: 1px solid $mediumGrey;
|
||||
border-radius: 2px;
|
||||
@include linear-gradient($lightGrey, tint($lightGrey, 90%));
|
||||
background-color: $lightGrey;
|
||||
@include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset);
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size: 11px;
|
||||
color: $baseFontColor;
|
||||
padding: 6px 8px 8px;
|
||||
@include box-sizing(border-box);
|
||||
border: 1px solid $mediumGrey;
|
||||
border-radius: 2px;
|
||||
@include linear-gradient($lightGrey, tint($lightGrey, 90%));
|
||||
background-color: $lightGrey;
|
||||
@include box-shadow(0 1px 2px rgba(0, 0, 0, .1) inset);
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size: 11px;
|
||||
color: $baseFontColor;
|
||||
outline: 0;
|
||||
|
||||
&::-webkit-input-placeholder,
|
||||
&:-moz-placeholder,
|
||||
&:-ms-input-placeholder {
|
||||
color: #979faf;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include linear-gradient($paleYellow, tint($paleYellow, 90%));
|
||||
outline: 0;
|
||||
|
||||
&::-webkit-input-placeholder,
|
||||
&:-moz-placeholder,
|
||||
&:-ms-input-placeholder {
|
||||
color: #979faf;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include linear-gradient($paleYellow, tint($paleYellow, 90%));
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// forms - specific
|
||||
input.search {
|
||||
padding: 6px 15px 8px 30px;
|
||||
@include box-sizing(border-box);
|
||||
border: 1px solid $darkGrey;
|
||||
border-radius: 20px;
|
||||
background: url(../img/search-icon.png) no-repeat 8px 7px #edf1f5;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
color: $baseFontColor;
|
||||
outline: 0;
|
||||
padding: 6px 15px 8px 30px;
|
||||
@include box-sizing(border-box);
|
||||
border: 1px solid $darkGrey;
|
||||
border-radius: 20px;
|
||||
background: url(../img/search-icon.png) no-repeat 8px 7px #edf1f5;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
color: $baseFontColor;
|
||||
outline: 0;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
color: #979faf;
|
||||
}
|
||||
&::-webkit-input-placeholder {
|
||||
color: #979faf;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 0 4px;
|
||||
border-radius: 3px;
|
||||
background: #eee;
|
||||
font-family: Monaco, monospace;
|
||||
padding: 0 4px;
|
||||
border-radius: 3px;
|
||||
background: #eee;
|
||||
font-family: Monaco, monospace;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
font-size: 13px;
|
||||
border: 1px solid $darkGrey;
|
||||
background: #fff;
|
||||
font-size: 13px;
|
||||
border: 1px solid $darkGrey;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.text-editor {
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
padding: 10px;
|
||||
@include box-sizing(border-box);
|
||||
border: 1px solid $mediumGrey;
|
||||
@include linear-gradient(top, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.3));
|
||||
background-color: #edf1f5;
|
||||
@include box-shadow(0 1px 2px rgba(0, 0, 0, 0.1) inset);
|
||||
font-family: Monaco, monospace;
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
padding: 10px;
|
||||
@include box-sizing(border-box);
|
||||
border: 1px solid $mediumGrey;
|
||||
@include linear-gradient(top, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.3));
|
||||
background-color: #edf1f5;
|
||||
@include box-shadow(0 1px 2px rgba(0, 0, 0, 0.1) inset);
|
||||
font-family: Monaco, monospace;
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// UI - chrome
|
||||
.window {
|
||||
@include clearfix();
|
||||
@include border-radius(3px);
|
||||
@include box-shadow(0 1px 1px $shadow-l1);
|
||||
margin-bottom: $baseline;
|
||||
border: 1px solid $gray-l2;
|
||||
background: $white;
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// UI - actions
|
||||
.new-unit-item,
|
||||
.new-subsection-item,
|
||||
.new-policy-item {
|
||||
@include grey-button;
|
||||
margin: 5px 8px;
|
||||
padding: 3px 10px 4px 10px;
|
||||
font-size: 10px;
|
||||
@include grey-button;
|
||||
margin: 5px 8px;
|
||||
padding: 3px 10px 4px 10px;
|
||||
font-size: 10px;
|
||||
|
||||
.new-folder-icon,
|
||||
.new-policy-icon,
|
||||
.new-unit-icon {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
.new-folder-icon,
|
||||
.new-policy-icon,
|
||||
.new-unit-icon {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
|
||||
.edit-button,
|
||||
.delete-button,
|
||||
.visibility-toggle {
|
||||
float: left;
|
||||
margin-right: 13px;
|
||||
color: #a4aab7;
|
||||
}
|
||||
.edit-button,
|
||||
.delete-button,
|
||||
.visibility-toggle {
|
||||
float: left;
|
||||
margin-right: 13px;
|
||||
color: #a4aab7;
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// misc
|
||||
hr.divide {
|
||||
@include text-sr();
|
||||
}
|
||||
|
||||
.item-details {
|
||||
@@ -189,81 +516,56 @@ code {
|
||||
}
|
||||
|
||||
.window {
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid $mediumGrey;
|
||||
border-radius: 3px;
|
||||
background: #fff;
|
||||
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.1));
|
||||
// @include border-radius(3px);
|
||||
// @include box-shadow(0 1px 1px $shadow-l1);
|
||||
// margin-bottom: $baseline;
|
||||
// border: 1px solid $gray-l2;
|
||||
// background: $white;
|
||||
|
||||
.window-contents {
|
||||
padding: 20px;
|
||||
.window-contents {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 6px 14px;
|
||||
border-bottom: 1px solid $mediumGrey;
|
||||
border-radius: 2px 2px 0 0;
|
||||
@include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0));
|
||||
background-color: $lightBluishGrey;
|
||||
@include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset);
|
||||
font-size: 14px;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 700;
|
||||
|
||||
&.inline-label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 6px 14px;
|
||||
border-bottom: 1px solid $mediumGrey;
|
||||
border-radius: 2px 2px 0 0;
|
||||
@include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0));
|
||||
background-color: $lightBluishGrey;
|
||||
@include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset);
|
||||
font-size: 14px;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
.description {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
line-height: 1.3;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 700;
|
||||
|
||||
&.inline-label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.description {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
line-height: 1.3;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #cbd1db;
|
||||
}
|
||||
.row {
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #cbd1db;
|
||||
}
|
||||
}
|
||||
|
||||
body.hide-wip {
|
||||
.wip, .wip-box {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
// ====================
|
||||
|
||||
body.show-wip {
|
||||
.wip {
|
||||
outline: 1px solid #f00 !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.wip-box {
|
||||
@extend .wip;
|
||||
&:after {
|
||||
content: "WIP";
|
||||
font-size: 8px;
|
||||
padding: 2px;
|
||||
background: #f00;
|
||||
color: #fff;
|
||||
@include position(absolute, 0px 0px 0 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.waiting {
|
||||
|
||||
}
|
||||
// system notifications
|
||||
.toast-notification {
|
||||
display: none;
|
||||
position: fixed;
|
||||
@@ -323,59 +625,86 @@ body.show-wip {
|
||||
}
|
||||
|
||||
.waiting {
|
||||
position: relative;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 999998;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: rgba(255, 255, 255, .9);
|
||||
}
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 999998;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: rgba(255, 255, 255, .9);
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
@extend .spinner-icon;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -10px;
|
||||
margin-top: -10px;
|
||||
z-index: 999999;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
@extend .spinner-icon;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -10px;
|
||||
margin-top: -10px;
|
||||
z-index: 999999;
|
||||
}
|
||||
}
|
||||
|
||||
.waiting-inline {
|
||||
&:after {
|
||||
content: '';
|
||||
@extend .spinner-icon;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
@extend .spinner-icon;
|
||||
}
|
||||
}
|
||||
|
||||
.new-button {
|
||||
@include green-button;
|
||||
font-size: 13px;
|
||||
padding: 8px 20px 10px;
|
||||
text-align: center;
|
||||
@include green-button;
|
||||
@include font-size(13);
|
||||
padding: 8px 20px 10px;
|
||||
text-align: center;
|
||||
|
||||
&.big {
|
||||
display: block;
|
||||
}
|
||||
&.big {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.icon-create {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: ($baseline/4);
|
||||
margin-top: ($baseline/10);
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.view-button {
|
||||
@include blue-button;
|
||||
@include font-size(13);
|
||||
text-align: center;
|
||||
|
||||
&.big {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.icon-view {
|
||||
@include font-size(15);
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: ($baseline/2);
|
||||
margin-top: ($baseline/5);
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-button.standard,
|
||||
.delete-button.standard {
|
||||
float: left;
|
||||
@include font-size(12);
|
||||
@include white-button;
|
||||
float: left;
|
||||
padding: 3px 10px 4px;
|
||||
margin-left: 7px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
|
||||
.edit-icon,
|
||||
@@ -386,9 +715,9 @@ body.show-wip {
|
||||
|
||||
.delete-button.standard {
|
||||
|
||||
&:hover {
|
||||
background-color: tint($orange, 75%);
|
||||
}
|
||||
&:hover {
|
||||
background-color: tint($orange, 75%);
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
@@ -417,4 +746,114 @@ body.show-wip {
|
||||
font-size: 20px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// basic utility
|
||||
.sr {
|
||||
@include text-sr();
|
||||
}
|
||||
|
||||
.fake-link {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.non-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
text-wrap: wrap;
|
||||
white-space: pre-wrap;
|
||||
white-space: -moz-pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// js dependant
|
||||
body.js {
|
||||
|
||||
// lean/simple modal window
|
||||
.content-modal {
|
||||
@include border-bottom-radius(2px);
|
||||
@include box-sizing(border-box);
|
||||
@include box-shadow(0 2px 4px $shadow-d1);
|
||||
position: relative;
|
||||
display: none;
|
||||
width: 700px;
|
||||
overflow: hidden;
|
||||
border: 1px solid $gray-d1;
|
||||
padding: ($baseline);
|
||||
background: $white;
|
||||
|
||||
.action-modal-close {
|
||||
@include transition(top .25s ease-in-out);
|
||||
@include border-bottom-radius(3px);
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: $baseline;
|
||||
padding: ($baseline/4) ($baseline/2) 0 ($baseline/2);
|
||||
background: $gray-l3;
|
||||
text-align: center;
|
||||
|
||||
.label {
|
||||
@include text-sr();
|
||||
}
|
||||
|
||||
.ss-icon {
|
||||
@include font-size(18);
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $blue;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
@include box-sizing(border-box);
|
||||
width: 100%;
|
||||
overflow-y: scroll;
|
||||
padding: ($baseline/10);
|
||||
border: 1px solid $gray-l4;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include font-size(18);
|
||||
margin: 0 0 ($baseline/2) 0;
|
||||
font-weight: 600;
|
||||
color: $gray-d3;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include font-size(13);
|
||||
margin-top: ($baseline/2);
|
||||
color: $gray-l1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// works in progress
|
||||
body.hide-wip {
|
||||
|
||||
.wip-box {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// needed fudges for now
|
||||
body.dashboard {
|
||||
|
||||
.my-classes {
|
||||
margin-top: $baseline;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:hover, &.active {
|
||||
@include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset, 0 1px 1px rgba(0, 0, 0, .15));
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@
|
||||
background-color: $blue;
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
&:hover, &.active {
|
||||
background-color: #62aaf5;
|
||||
color: #fff;
|
||||
}
|
||||
@@ -285,4 +285,11 @@
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
@mixin active {
|
||||
@include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0));
|
||||
background-color: rgba(255, 255, 255, .3);
|
||||
@include box-shadow(0 -1px 0 rgba(0, 0, 0, .2) inset, 0 1px 0 #fff inset);
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
@@ -37,6 +37,11 @@
|
||||
padding: 34px 0 42px;
|
||||
border-top: 1px solid #cbd1db;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.editing {
|
||||
position: relative;
|
||||
z-index: 1001;
|
||||
|
||||
@@ -498,6 +498,7 @@ input.courseware-unit-search-input {
|
||||
}
|
||||
|
||||
&.new-section {
|
||||
|
||||
header {
|
||||
height: auto;
|
||||
@include clearfix();
|
||||
@@ -506,6 +507,15 @@ input.courseware-unit-search-input {
|
||||
.expand-collapse-icon {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.item-details {
|
||||
padding: 25px 0 0 0;
|
||||
|
||||
.section-name {
|
||||
float: none;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,27 @@
|
||||
@include box-shadow(0 1px 2px rgba(0, 0, 0, .1));
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
border-bottom: 1px solid $mediumGrey;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
padding: 20px 25px;
|
||||
line-height: 1.3;
|
||||
|
||||
&:hover {
|
||||
background: $paleYellow;
|
||||
.class-link {
|
||||
z-index: 100;
|
||||
display: block;
|
||||
padding: 20px 25px;
|
||||
line-height: 1.3;
|
||||
|
||||
&:hover {
|
||||
background: $paleYellow;
|
||||
|
||||
+ .view-live-button {
|
||||
opacity: 1.0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +42,22 @@
|
||||
margin-right: 20px;
|
||||
color: #3c3c3c;
|
||||
}
|
||||
|
||||
// view live button
|
||||
.view-live-button {
|
||||
z-index: 10000;
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: $baseline;
|
||||
padding: ($baseline/4) ($baseline/2);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new-course {
|
||||
|
||||
78
cms/static/sass/_extends.scss
Normal file
@@ -0,0 +1,78 @@
|
||||
.faded-hr-divider {
|
||||
@include background-image(linear-gradient(180deg, rgba(200,200,200, 0) 0%,
|
||||
rgba(200,200,200, 1) 50%,
|
||||
rgba(200,200,200, 0)));
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.faded-hr-divider-medium {
|
||||
@include background-image(linear-gradient(180deg, rgba(240,240,240, 0) 0%,
|
||||
rgba(240,240,240, 1) 50%,
|
||||
rgba(240,240,240, 0)));
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.faded-hr-divider-light {
|
||||
@include background-image(linear-gradient(180deg, rgba(255,255,255, 0) 0%,
|
||||
rgba(255,255,255, 0.8) 50%,
|
||||
rgba(255,255,255, 0)));
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.faded-vertical-divider {
|
||||
@include background-image(linear-gradient(90deg, rgba(200,200,200, 0) 0%,
|
||||
rgba(200,200,200, 1) 50%,
|
||||
rgba(200,200,200, 0)));
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.faded-vertical-divider-light {
|
||||
@include background-image(linear-gradient(90deg, rgba(255,255,255, 0) 0%,
|
||||
rgba(255,255,255, 0.6) 50%,
|
||||
rgba(255,255,255, 0)));
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.vertical-divider {
|
||||
@extend .faded-vertical-divider;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
@extend .faded-vertical-divider-light;
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.horizontal-divider {
|
||||
border: none;
|
||||
@extend .faded-hr-divider;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
@extend .faded-hr-divider-light;
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.fade-right-hr-divider {
|
||||
@include background-image(linear-gradient(180deg, rgba(200,200,200, 0) 0%,
|
||||
rgba(200,200,200, 1)));
|
||||
border: none;
|
||||
}
|
||||
|
||||
.fade-left-hr-divider {
|
||||
@include background-image(linear-gradient(180deg, rgba(200,200,200, 1) 0%,
|
||||
rgba(200,200,200, 0)));
|
||||
border: none;
|
||||
}
|
||||
48
cms/static/sass/_footer.scss
Normal file
@@ -0,0 +1,48 @@
|
||||
//studio global footer
|
||||
.wrapper-footer {
|
||||
margin: ($baseline*1.5) 0 $baseline 0;
|
||||
padding: $baseline;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
footer.primary {
|
||||
@include clearfix();
|
||||
@include font-size(13);
|
||||
max-width: $fg-max-width;
|
||||
min-width: $fg-min-width;
|
||||
width: flex-grid(12);
|
||||
margin: 0 auto;
|
||||
padding-top: $baseline;
|
||||
border-top: 1px solid $gray-l4;
|
||||
color: $gray-l2;
|
||||
|
||||
.colophon {
|
||||
width: flex-grid(4, 12);
|
||||
float: left;
|
||||
margin-right: flex-gutter(2);
|
||||
}
|
||||
|
||||
.nav-peripheral {
|
||||
width: flex-grid(6, 12);
|
||||
float: right;
|
||||
text-align: right;
|
||||
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
margin-right: ($baseline/2);
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $gray-l1;
|
||||
|
||||
&:hover, &:active {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +1,562 @@
|
||||
body.no-header {
|
||||
.primary-header {
|
||||
display: none;
|
||||
}
|
||||
// studio global header and navigation
|
||||
// ====================
|
||||
|
||||
.wrapper-header {
|
||||
margin: 0 0 ($baseline*1.5) 0;
|
||||
padding: $baseline;
|
||||
border-bottom: 1px solid $gray;
|
||||
@include box-shadow(0 1px 5px 0 rgba(0,0,0, 0.1));
|
||||
background: $white;
|
||||
height: 76px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
|
||||
a {
|
||||
color: $baseFontColor;
|
||||
display: block;
|
||||
|
||||
&:hover, &:active {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
header.primary {
|
||||
@include clearfix();
|
||||
max-width: $fg-max-width;
|
||||
min-width: $fg-min-width;
|
||||
width: flex-grid(12);
|
||||
margin: 0 auto;
|
||||
color: $gray-l1;
|
||||
}
|
||||
|
||||
nav .nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin active {
|
||||
@include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0));
|
||||
background-color: rgba(255, 255, 255, .3);
|
||||
@include box-shadow(0 -1px 0 rgba(0, 0, 0, .2) inset, 0 1px 0 #fff inset);
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||
// ====================
|
||||
|
||||
// basic layout
|
||||
.wrapper-left, .wrapper-right {
|
||||
@include box-sizing(border-box);
|
||||
}
|
||||
|
||||
.primary-header {
|
||||
width: 100%;
|
||||
margin-bottom: 30px;
|
||||
.wrapper-left {
|
||||
width: flex-grid(10, 12);
|
||||
float: left;
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
&.active-tab-courseware #courseware-tab {
|
||||
@include active;
|
||||
}
|
||||
.wrapper-right {
|
||||
width: flex-grid(2, 12);
|
||||
float: right;
|
||||
}
|
||||
|
||||
&.active-tab-assets #assets-tab {
|
||||
@include active;
|
||||
}
|
||||
|
||||
&.active-tab-pages #pages-tab {
|
||||
@include active;
|
||||
}
|
||||
// ====================
|
||||
|
||||
&.active-tab-users #users-tab {
|
||||
@include active;
|
||||
}
|
||||
// specific elements - branding
|
||||
.branding, .info-course, .nav-course, .nav-account, .nav-unauth, .nav-pitch {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
&.active-tab-settings #settings-tab {
|
||||
@include active;
|
||||
}
|
||||
.branding {
|
||||
position: relative;
|
||||
margin: 0 ($baseline/2) 0 0;
|
||||
padding-right: ($baseline*0.75);
|
||||
|
||||
&.active-tab-import #import-tab {
|
||||
@include active;
|
||||
}
|
||||
a {
|
||||
@include text-hide();
|
||||
display: block;
|
||||
width: 164px;
|
||||
height: 32px;
|
||||
background: transparent url('../img/logo-edx-studio.png') 0 0 no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
&.active-tab-export #export-tab {
|
||||
@include active;
|
||||
}
|
||||
// ====================
|
||||
|
||||
.drop-icon {
|
||||
margin-left: 5px;
|
||||
font-size: 11px;
|
||||
}
|
||||
// specific elements - course name/info
|
||||
.info-course {
|
||||
@include font-size(14);
|
||||
position: relative;
|
||||
margin: -3px ($baseline/2) 0 0;
|
||||
padding-right: ($baseline*0.75);
|
||||
|
||||
.settings-icon {
|
||||
font-size: 18px;
|
||||
line-height: 18px;
|
||||
}
|
||||
&:before {
|
||||
@extend .faded-vertical-divider;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
right: 1px;
|
||||
top: -8px;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.class-nav-bar {
|
||||
clear: both;
|
||||
@include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0));
|
||||
background-color: $lightBluishGrey;
|
||||
@include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset);
|
||||
}
|
||||
&:after {
|
||||
@extend .faded-vertical-divider-light;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: -12px;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.class-nav {
|
||||
@include clearfix;
|
||||
.course-org {
|
||||
margin-right: ($baseline/4);
|
||||
}
|
||||
|
||||
a {
|
||||
float: left;
|
||||
display: inline-block;
|
||||
padding: 15px 25px 17px;
|
||||
font-size: 15px;
|
||||
color: #3c3c3c;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
.course-number, .course-org {
|
||||
@include font-size(12);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@include linear-gradient(top, rgba(255, 255, 255, .2), rgba(255, 255, 255, 0));
|
||||
}
|
||||
}
|
||||
.course-title {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 220px;
|
||||
overflow: hidden;
|
||||
margin-top: -4px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
@include font-size(16);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
// ====================
|
||||
|
||||
.class {
|
||||
@include clearfix;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
color: rgb(163, 171, 184);
|
||||
@include linear-gradient(top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .1));
|
||||
background-color: rgb(47, 53, 63);
|
||||
// specific elements - course nav
|
||||
.nav-course {
|
||||
width: 335px;
|
||||
margin-top: -($baseline/4);
|
||||
@include font-size(14);
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
padding: 5px 10px 6px;
|
||||
color: rgb(163, 171, 184);
|
||||
}
|
||||
> ol > .nav-item {
|
||||
vertical-align: bottom;
|
||||
margin: 0 ($baseline/2) 0 0;
|
||||
|
||||
.home {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.log-out {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
display: block;
|
||||
padding: 5px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
color: $gray-d3;
|
||||
|
||||
.label-prefix {
|
||||
display: block;
|
||||
@include font-size(11);
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
// specific nav items
|
||||
&.nav-course-courseware {
|
||||
}
|
||||
|
||||
&.nav-course-settings {
|
||||
}
|
||||
|
||||
&.nav-course-tools {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// specific elements - account-based nav
|
||||
.nav-account {
|
||||
width: 100%;
|
||||
margin-top: ($baseline*0.75);
|
||||
@include font-size(14);
|
||||
text-align: right;
|
||||
|
||||
.nav-account-username {
|
||||
width: 100%;
|
||||
|
||||
.icon-user {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 3px;
|
||||
@include font-size(12);
|
||||
}
|
||||
|
||||
.account-username {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 80%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.icon-expand {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// UI - dropdown
|
||||
.nav-dropdown {
|
||||
|
||||
.nav-item {
|
||||
position: relative;
|
||||
|
||||
.icon-expand {
|
||||
@include font-size(14);
|
||||
@include transition (color 0.5s ease-in-out, opacity 0.5s ease-in-out);
|
||||
display: inline-block;
|
||||
margin-left: 2px;
|
||||
opacity: 0.5;
|
||||
color: $gray-l2;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
.icon-expand {
|
||||
color: $blue;
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper-nav-sub {
|
||||
position: absolute;
|
||||
left: -7px;
|
||||
top: 47px;
|
||||
width: 140px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-sub {
|
||||
@include border-radius(2px);
|
||||
@include box-sizing(border-box);
|
||||
@include box-shadow(0 1px 5px 0 rgba(0,0,0, 0.1));
|
||||
position: relative;
|
||||
width: 100%;
|
||||
border: 1px solid $gray-l2;
|
||||
padding: ($baseline/4) ($baseline/2);
|
||||
background: $white;
|
||||
|
||||
&:after, &:before {
|
||||
bottom: 100%;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:after {
|
||||
border-color: rgba(255, 255, 255, 0);
|
||||
border-bottom-color: #fff;
|
||||
border-width: 5px;
|
||||
right: 3px;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
border-color: rgba(178, 178, 178, 0);
|
||||
border-bottom-color: $gray-l2;
|
||||
border-width: 6px;
|
||||
right: 3px;
|
||||
margin-left: -6px;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: block;
|
||||
margin: 0 0 ($baseline/4) 0;
|
||||
border-bottom: 1px solid $gray-l5;
|
||||
padding: 0 0($baseline/4) 0;
|
||||
@include font-size(13);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UI - dropdown - specific navs
|
||||
&.nav-account {
|
||||
|
||||
.wrapper-nav-sub {
|
||||
top: 27px;
|
||||
left: auto;
|
||||
right: -13px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.nav-sub {
|
||||
text-align: left;
|
||||
|
||||
.icon-expand {
|
||||
top: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-sub:after {
|
||||
left: auto;
|
||||
right: 11px;
|
||||
}
|
||||
|
||||
.nav-sub:before {
|
||||
left: auto;
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&.nav-course {
|
||||
|
||||
.nav-course-courseware {
|
||||
|
||||
.nav-sub:after {
|
||||
left: 88px;
|
||||
}
|
||||
|
||||
.nav-sub:before {
|
||||
left: 88px;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-course-settings {
|
||||
|
||||
.nav-sub:after {
|
||||
left: 88px;
|
||||
}
|
||||
|
||||
.nav-sub:before {
|
||||
left: 88px;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-course-tools {
|
||||
|
||||
.wrapper-nav-sub {
|
||||
top: ($baseline*1.5);
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.nav-sub:after {
|
||||
left: 68px;
|
||||
}
|
||||
|
||||
.nav-sub:before {
|
||||
left: 68px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// STATE: is-signed in
|
||||
.is-signedin {
|
||||
|
||||
&.course .branding {
|
||||
|
||||
&:before {
|
||||
@extend .faded-vertical-divider;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
right: 1px;
|
||||
top: -8px;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
@extend .faded-vertical-divider-light;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: -12px;
|
||||
width: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// STATE: not signed in
|
||||
.not-signedin {
|
||||
|
||||
.wrapper-left {
|
||||
width: flex-grid(4, 12);
|
||||
}
|
||||
|
||||
.wrapper-right {
|
||||
width: flex-grid(8, 12);
|
||||
}
|
||||
|
||||
// STATE: not signed in - unauthenticated nav
|
||||
.nav-not-signedin {
|
||||
float: right;
|
||||
margin-top: ($baseline/4);
|
||||
|
||||
.nav-item {
|
||||
@include font-size(16);
|
||||
vertical-align: middle;
|
||||
margin: 0 $baseline 0 0;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-top: -($baseline/4);
|
||||
display: inline-block;
|
||||
padding: ($baseline/4) ($baseline/2);
|
||||
}
|
||||
}
|
||||
|
||||
// STATE: not signed in - specific items
|
||||
.nav-not-signedin-help {
|
||||
|
||||
}
|
||||
|
||||
.nav-not-signedin-signup {
|
||||
margin-right: ($baseline/2);
|
||||
|
||||
.action-signup {
|
||||
@include blue-button;
|
||||
@include transition(all .15s);
|
||||
@include font-size(14);
|
||||
padding: ($baseline/4) ($baseline/2);
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-not-signedin-signin {
|
||||
|
||||
.action-signin {
|
||||
@include white-button;
|
||||
@include transition(all .15s);
|
||||
@include font-size(14);
|
||||
padding: ($baseline/4) ($baseline/2);
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// STATE: active/current nav states
|
||||
|
||||
.nav-item.is-current,
|
||||
body.howitworks .nav-not-signedin-hiw,
|
||||
|
||||
// dashboard
|
||||
body.dashboard .nav-account-dashboard,
|
||||
|
||||
// course content
|
||||
body.course.outline .nav-course-courseware .title,
|
||||
body.course.updates .nav-course-courseware .title,
|
||||
body.course.pages .nav-course-courseware .title,
|
||||
body.course.uploads .nav-course-courseware .title,
|
||||
|
||||
body.course.outline .nav-course-courseware-outline,
|
||||
body.course.updates .nav-course-courseware-updates,
|
||||
body.course.pages .nav-course-courseware-pages,
|
||||
body.course.uploads .nav-course-courseware-uploads,
|
||||
|
||||
// course settings
|
||||
body.course.schedule .nav-course-settings .title,
|
||||
body.course.grading .nav-course-settings .title,
|
||||
body.course.team .nav-course-settings .title,
|
||||
body.course.advanced .nav-course-settings .title,
|
||||
|
||||
body.course.schedule .nav-course-settings-schedule,
|
||||
body.course.grading .nav-course-settings-grading,
|
||||
body.course.team .nav-course-settings-team,
|
||||
body.course.advanced .nav-course-settings-advanced,
|
||||
|
||||
// course tools
|
||||
body.course.import .nav-course-tools .title,
|
||||
body.course.export .nav-course-tools .title,
|
||||
|
||||
body.course.import .nav-course-tools-import,
|
||||
body.course.export .nav-course-tools-export,
|
||||
|
||||
{
|
||||
|
||||
color: $blue;
|
||||
|
||||
a {
|
||||
color: $blue;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
body.signup .nav-not-signedin-signin {
|
||||
|
||||
a {
|
||||
background-color: #d9e3ee;
|
||||
color: #6d788b;
|
||||
}
|
||||
}
|
||||
|
||||
body.signin .nav-not-signedin-signup {
|
||||
|
||||
a {
|
||||
background-color: #62aaf5;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// STATE: js enabled
|
||||
.js {
|
||||
|
||||
.nav-dropdown {
|
||||
|
||||
.nav-item .title {
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover, &:active, &.is-selected {
|
||||
color: $blue;
|
||||
|
||||
.icon-expand {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper-nav-sub {
|
||||
@include transition (opacity 1.0s ease-in-out 0s);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
|
||||
&.is-shown {
|
||||
opacity: 1.0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +1,353 @@
|
||||
body.index {
|
||||
> header {
|
||||
display: none;
|
||||
}
|
||||
// how it works/not signed in index
|
||||
.index {
|
||||
|
||||
> h1 {
|
||||
font-weight: 300;
|
||||
color: lighten($dark-blue, 40%);
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
margin: 80px auto 30px;
|
||||
}
|
||||
&.not-signedin {
|
||||
|
||||
section.main-container {
|
||||
border-right: 3px;
|
||||
background: #FFF;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
@include box-sizing(border-box);
|
||||
border: 1px solid lighten( $dark-blue , 30% );
|
||||
@include border-radius(3px);
|
||||
overflow: hidden;
|
||||
@include bounce-in-animation(.8s);
|
||||
.wrapper-header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 1px solid lighten($dark-blue, 50%);
|
||||
@include linear-gradient(#fff, lighten($dark-blue, 62%));
|
||||
@include clearfix();
|
||||
@include box-shadow( 0 2px 0 $light-blue, inset 0 -1px 0 #fff);
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
.wrapper-footer {
|
||||
margin: 0;
|
||||
border-top: 2px solid $gray-l3;
|
||||
|
||||
h1 {
|
||||
font-size: 14px;
|
||||
padding: 8px 20px;
|
||||
float: left;
|
||||
color: $dark-blue;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
float: right;
|
||||
padding: 8px 20px;
|
||||
border-left: 1px solid lighten($dark-blue, 50%);
|
||||
@include box-shadow( inset -1px 0 0 #fff);
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
line-height: 1;
|
||||
color: $dark-blue;
|
||||
footer.primary {
|
||||
border: none;
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: none;
|
||||
.wrapper-content-header, .wrapper-content-features, .wrapper-content-cta {
|
||||
@include box-sizing(border-box);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding: 0 $baseline;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
li {
|
||||
border-bottom: 1px solid lighten($dark-blue, 50%);
|
||||
.content {
|
||||
@include clearfix();
|
||||
@include font-size(16);
|
||||
max-width: $fg-max-width;
|
||||
min-width: $fg-min-width;
|
||||
width: flex-grid(12);
|
||||
margin: 0 auto;
|
||||
color: $gray-d2;
|
||||
|
||||
header {
|
||||
border: none;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: 10px 20px;
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: $gray-d3;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $dark-blue;
|
||||
background: lighten($yellow, 10%);
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
h2 {
|
||||
|
||||
}
|
||||
|
||||
h3 {
|
||||
|
||||
}
|
||||
|
||||
h4 {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// welcome content
|
||||
.wrapper-content-header {
|
||||
@include linear-gradient($blue-l1,$blue,$blue-d1);
|
||||
padding-bottom: ($baseline*4);
|
||||
padding-top: ($baseline*4);
|
||||
}
|
||||
|
||||
.content-header {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
color: $white;
|
||||
|
||||
h1 {
|
||||
@include font-size(52);
|
||||
float: none;
|
||||
margin: 0 0 ($baseline/2) 0;
|
||||
border-bottom: 1px solid $blue-l1;
|
||||
padding: 0;
|
||||
font-weight: 500;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.logo {
|
||||
@include text-hide();
|
||||
position: relative;
|
||||
top: 3px;
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
width: 282px;
|
||||
height: 57px;
|
||||
background: transparent url('../img/logo-edx-studio-white.png') 0 0 no-repeat;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
@include font-size(24);
|
||||
margin: 0;
|
||||
color: $blue-l3;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow_box {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
border: 4px solid #000;
|
||||
}
|
||||
.arrow_box:after, .arrow_box:before {
|
||||
top: 100%;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.arrow_box:after {
|
||||
border-color: rgba(255, 255, 255, 0);
|
||||
border-top-color: #fff;
|
||||
border-width: 30px;
|
||||
left: 50%;
|
||||
margin-left: -30px;
|
||||
}
|
||||
.arrow_box:before {
|
||||
border-color: rgba(0, 0, 0, 0);
|
||||
border-top-color: #000;
|
||||
border-width: 36px;
|
||||
left: 50%;
|
||||
margin-left: -36px;
|
||||
}
|
||||
|
||||
// feature content
|
||||
.wrapper-content-features {
|
||||
@include box-shadow(0 -1px ($baseline/4) $shadow);
|
||||
padding-bottom: ($baseline*2);
|
||||
padding-top: ($baseline*3);
|
||||
background: $white;
|
||||
}
|
||||
|
||||
.content-features {
|
||||
|
||||
.list-features {
|
||||
|
||||
}
|
||||
|
||||
// indiv features
|
||||
.feature {
|
||||
@include clearfix();
|
||||
margin: 0 0 ($baseline*2) 0;
|
||||
border-bottom: 1px solid $gray-l4;
|
||||
padding: 0 0 ($baseline*2) 0;
|
||||
|
||||
.img {
|
||||
@include box-sizing(border-box);
|
||||
float: left;
|
||||
width: flex-grid(3, 12);
|
||||
margin-right: flex-gutter();
|
||||
|
||||
a {
|
||||
@include box-sizing(border-box);
|
||||
@include box-shadow(0 1px ($baseline/10) $shadow-l1);
|
||||
position: relative;
|
||||
top: 0;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
border: 1px solid $gray-l3;
|
||||
padding: ($baseline/4);
|
||||
background: $white;
|
||||
|
||||
.action-zoom {
|
||||
@include transition(bottom .50s ease-in-out);
|
||||
position: absolute;
|
||||
bottom: -30px;
|
||||
right: ($baseline/2);
|
||||
opacity: 0;
|
||||
|
||||
.ss-icon {
|
||||
@include font-size(18);
|
||||
@include border-top-radius(3px);
|
||||
display: inline-block;
|
||||
padding: ($baseline/4) ($baseline/2);
|
||||
background: $blue;
|
||||
color: $white;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $blue;
|
||||
|
||||
.action-zoom {
|
||||
opacity: 1.0;
|
||||
bottom: -2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.copy {
|
||||
float: left;
|
||||
width: flex-grid(9, 12);
|
||||
margin-top: -($baseline/4);
|
||||
|
||||
h3 {
|
||||
margin: 0 0 ($baseline/2) 0;
|
||||
@include font-size(24);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
> p {
|
||||
@include font-size(18);
|
||||
color: $gray-d1;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: $gray-d2;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.list-proofpoints {
|
||||
@include clearfix();
|
||||
@include font-size(14);
|
||||
width: flex-grid(9, 9);
|
||||
margin: ($baseline*1.5) 0 0 0;
|
||||
|
||||
.proofpoint {
|
||||
@include box-sizing(border-box);
|
||||
@include border-radius(($baseline/4));
|
||||
@include transition(color .50s ease-in-out);
|
||||
position: relative;
|
||||
top: 0;
|
||||
float: left;
|
||||
width: flex-grid(3, 9);
|
||||
min-height: ($baseline*8);
|
||||
margin-right: flex-gutter();
|
||||
padding: ($baseline*0.75) $baseline;
|
||||
color: $gray-l1;
|
||||
|
||||
.title {
|
||||
@include font-size(16);
|
||||
margin: 0 0 ($baseline/4) 0;
|
||||
font-weight: 500;
|
||||
color: $gray-d3;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@include box-shadow(0 1px ($baseline/10) $shadow-l1);
|
||||
background: $blue-l5;
|
||||
top: -($baseline/5);
|
||||
|
||||
.title {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
border: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
|
||||
.img {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: flex-gutter();
|
||||
}
|
||||
|
||||
.copy {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.list-proofpoints {
|
||||
|
||||
.proofpoint {
|
||||
float: right;
|
||||
width: flex-grid(3, 9);
|
||||
margin-left: flex-gutter();
|
||||
margin-right: 0;
|
||||
|
||||
&:last-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call to action content
|
||||
.wrapper-content-cta {
|
||||
padding-bottom: ($baseline*2);
|
||||
padding-top: ($baseline*2);
|
||||
background: $white;
|
||||
}
|
||||
|
||||
.content-cta {
|
||||
border-top: 1px solid $gray-l4;
|
||||
|
||||
header {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.list-actions {
|
||||
position: relative;
|
||||
margin-top: -($baseline*1.5);
|
||||
|
||||
li {
|
||||
width: flex-grid(6, 12);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.action-primary {
|
||||
@include blue-button;
|
||||
@include transition(all .15s);
|
||||
@include font-size(18);
|
||||
padding: ($baseline*0.75) ($baseline/2);
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.action-secondary {
|
||||
@include font-size(14);
|
||||
margin-top: ($baseline/2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,4 +54,16 @@
|
||||
@include white-button;
|
||||
margin-top: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
// lean modal alternative
|
||||
#lean_overlay {
|
||||
position: fixed;
|
||||
z-index: 10000;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
display: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: $black;
|
||||
}
|
||||
@@ -54,4 +54,118 @@ del {
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
/* Reset styles to remove ui-lightness jquery ui theme
|
||||
from the tabs component (used in the add component problem tab menu)
|
||||
*/
|
||||
|
||||
.ui-tabs {
|
||||
padding: 0;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.ui-corner-all, .ui-corner-bottom, .ui-corner-left, ui-corner-top, .ui-corner-br, .ui-corner-right {
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
|
||||
.ui-widget-content {
|
||||
border: 0;
|
||||
background: none;
|
||||
}
|
||||
.ui-widget {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.ui-widget-header {
|
||||
border:none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.ui-tabs .ui-tabs-nav {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ui-tabs .ui-tabs-nav li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
top: 0;
|
||||
margin: 0;
|
||||
float: none;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.ui-tabs-nav {
|
||||
|
||||
li {
|
||||
top: 0;
|
||||
margin: 0;
|
||||
}
|
||||
a {
|
||||
float: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-tabs .ui-tabs-panel {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* reapplying the tab styles from unit.scss after
|
||||
removing jquery ui ui-lightness styling
|
||||
*/
|
||||
|
||||
.problem-type-tabs {
|
||||
border:none;
|
||||
list-style-type: none;
|
||||
width: 100%;
|
||||
@include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0));
|
||||
//background-color: $lightBluishGrey;
|
||||
@include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset);
|
||||
|
||||
li:first-child {
|
||||
margin-left: 20px;
|
||||
}
|
||||
li {
|
||||
opacity: .8;
|
||||
|
||||
&:ui-state-active {
|
||||
background-color: rgba(255, 255, 255, .3);
|
||||
opacity: 1;
|
||||
font-weight: 400;
|
||||
}
|
||||
a:focus {
|
||||
outline: none;
|
||||
border: 0px;
|
||||
}
|
||||
}
|
||||
/*
|
||||
li {
|
||||
float:left;
|
||||
display:inline-block;
|
||||
text-align:center;
|
||||
width: auto;
|
||||
//@include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0));
|
||||
//background-color: tint($lightBluishGrey, 20%);
|
||||
//@include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset);
|
||||
opacity:.8;
|
||||
|
||||
&:hover {
|
||||
opacity:1;
|
||||
}
|
||||
|
||||
&.current {
|
||||
border: 0px;
|
||||
//@include active;
|
||||
opacity:1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -1,800 +1,584 @@
|
||||
.settings {
|
||||
.settings-overview {
|
||||
// Studio - Course Settings
|
||||
// ====================
|
||||
body.course.settings {
|
||||
|
||||
.content-primary, .content-supplementary {
|
||||
@include box-sizing(border-box);
|
||||
float: left;
|
||||
}
|
||||
|
||||
.content-primary {
|
||||
@extend .window;
|
||||
@include clearfix;
|
||||
display: table;
|
||||
width: 100%;
|
||||
width: flex-grid(9, 12);
|
||||
margin-right: flex-gutter();
|
||||
padding: $baseline ($baseline*1.5);
|
||||
}
|
||||
|
||||
// layout
|
||||
.sidebar {
|
||||
display: table-cell;
|
||||
float: none;
|
||||
width: 20%;
|
||||
padding: 30px 0 30px 20px;
|
||||
@include border-radius(3px 0 0 3px);
|
||||
background: $lightGrey;
|
||||
}
|
||||
.group-settings {
|
||||
margin: 0 0 ($baseline*2) 0;
|
||||
|
||||
.main-column {
|
||||
display: table-cell;
|
||||
float: none;
|
||||
width: 80%;
|
||||
padding: 30px 40px 30px 60px;
|
||||
}
|
||||
header {
|
||||
@include clearfix();
|
||||
|
||||
.settings-page-menu {
|
||||
a {
|
||||
display: block;
|
||||
padding-left: 20px;
|
||||
line-height: 52px;
|
||||
.title-2 {
|
||||
width: flex-grid(4, 9);
|
||||
margin: 0 flex-gutter() 0 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
&.is-shown {
|
||||
background: #fff;
|
||||
@include border-radius(5px 0 0 5px);
|
||||
}
|
||||
.tip {
|
||||
@include font-size(13);
|
||||
width: flex-grid(5, 9);
|
||||
float: right;
|
||||
margin-top: ($baseline/2);
|
||||
text-align: right;
|
||||
color: $gray-l2;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-page-section {
|
||||
> .alert {
|
||||
display: none;
|
||||
|
||||
&.is-shown {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
> section {
|
||||
display: none;
|
||||
margin-bottom: 40px;
|
||||
|
||||
&.is-shown {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
> .title {
|
||||
margin-bottom: 30px;
|
||||
font-size: 28px;
|
||||
font-weight: 300;
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
> section {
|
||||
margin-bottom: 100px;
|
||||
@include clearfix;
|
||||
|
||||
header {
|
||||
@include clearfix;
|
||||
border-bottom: 1px solid $mediumGrey;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
h3 {
|
||||
color: $darkGrey;
|
||||
float: left;
|
||||
|
||||
margin: 0 40px 0 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.detail {
|
||||
float: right;
|
||||
margin-top: 3px;
|
||||
color: $mediumGrey;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// form basics
|
||||
label, .label {
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
|
||||
&.check-label {
|
||||
display: inline;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
&.ranges {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
@include transition(all 1s ease-in-out);
|
||||
@include box-sizing(border-box);
|
||||
font-size: 15px;
|
||||
|
||||
&.long {
|
||||
width: 100%;
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
&.tall {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
&.short {
|
||||
min-width: 100px;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
&.date {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
&.time {
|
||||
width: 85px !important;
|
||||
min-width: 85px !important;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
border: none;
|
||||
@include box-shadow(none);
|
||||
padding: 0;
|
||||
color: $darkGrey !important;
|
||||
font-weight: bold;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
textarea.tinymce {
|
||||
border: 1px solid $darkGrey;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
input[type="checkbox"], input[type="radio"] {
|
||||
// basic layout/elements
|
||||
.title-2 {
|
||||
|
||||
}
|
||||
|
||||
input:disabled + .copy > label, input:disabled + .label {
|
||||
color: $mediumGrey;
|
||||
}
|
||||
|
||||
|
||||
.input-default input, .input-default textarea {
|
||||
color: $mediumGrey;
|
||||
background: $lightGrey;
|
||||
}
|
||||
|
||||
::-webkit-input-placeholder {
|
||||
color: $mediumGrey;
|
||||
font-size: 13px;
|
||||
}
|
||||
:-moz-placeholder {
|
||||
color: $mediumGrey;
|
||||
font-size: 13px;
|
||||
.title-3 {
|
||||
|
||||
}
|
||||
|
||||
// UI hints/tips/messages
|
||||
.tip {
|
||||
color: $mediumGrey;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
|
||||
// form layouts
|
||||
.row {
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 30px;
|
||||
border-bottom: 1px solid $lightGrey;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
// structural labels, not semantic labels per se
|
||||
> label, .label {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
// tips
|
||||
.tip-inline {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.tip-stacked {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
// structural field, not semantic fields per se
|
||||
.field {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
> input, > textarea, .input {
|
||||
display: inline-block;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.group {
|
||||
input, textarea {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.label, label {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
// multi-field
|
||||
&.multi {
|
||||
display: block;
|
||||
background: tint($lightGrey, 50%);
|
||||
padding: 20px;
|
||||
@include border-radius(4px);
|
||||
@include box-sizing(border-box);
|
||||
|
||||
.group {
|
||||
margin-bottom: 10px;
|
||||
max-width: 175px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input, .input, textarea {
|
||||
|
||||
}
|
||||
|
||||
.tip-stacked {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// multi stacked
|
||||
&.multi-stacked {
|
||||
|
||||
.group {
|
||||
input, .input, textarea {
|
||||
min-width: 370px;
|
||||
width: 370px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// multi-field inline
|
||||
&.multi-inline {
|
||||
@include clearfix;
|
||||
|
||||
.group {
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
|
||||
&:nth-child(2) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.input, input, textarea {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// input-list
|
||||
.input-list {
|
||||
|
||||
.input {
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px dotted $lightGrey;
|
||||
@include clearfix();
|
||||
|
||||
&:last-child {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//radio buttons and checkboxes
|
||||
.input-radio {
|
||||
@include clearfix();
|
||||
|
||||
input {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.copy {
|
||||
position: relative;
|
||||
top: -5px;
|
||||
float: left;
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tip {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.message-error {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.input-checkbox {
|
||||
|
||||
}
|
||||
|
||||
// enumerated inputs
|
||||
&.enum {
|
||||
}
|
||||
}
|
||||
|
||||
// layout - aligned label/field pairs
|
||||
&.row-col2 {
|
||||
|
||||
> label, .label {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.field {
|
||||
width: 400px ! important;
|
||||
}
|
||||
|
||||
&.multi-inline {
|
||||
@include clearfix();
|
||||
|
||||
.group {
|
||||
width: 170px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.field-additional {
|
||||
margin-left: 204px;
|
||||
}
|
||||
}
|
||||
|
||||
// editing controls - adding
|
||||
.new-item, .replace-item {
|
||||
clear: both;
|
||||
@include transition(color, 0.15s, ease-in-out);
|
||||
@include font-size(13);
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
@include grey-button;
|
||||
@include box-sizing(border-box);
|
||||
}
|
||||
|
||||
|
||||
// editing controls - removing
|
||||
.delete-button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
// editing controls - preview
|
||||
.input-existing {
|
||||
display: block !important;
|
||||
|
||||
.current {
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
@include box-sizing(border-box);
|
||||
@include border-radius(5px);
|
||||
font-size: 14px;
|
||||
background: tint($lightGrey, 50%);
|
||||
@include clearfix();
|
||||
|
||||
.doc-filename {
|
||||
display: inline-block;
|
||||
width: 220px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.remove-doc-data {
|
||||
display: inline-block;
|
||||
margin-top: 0;
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// specific sections
|
||||
.settings-details {
|
||||
|
||||
}
|
||||
|
||||
.settings-faculty {
|
||||
|
||||
.settings-faculty-members {
|
||||
|
||||
> header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.field .multi {
|
||||
display: block;
|
||||
margin-bottom: 40px;
|
||||
padding: 20px;
|
||||
background: tint($lightGrey, 50%);
|
||||
@include border-radius(4px);
|
||||
@include box-sizing(border-box);
|
||||
}
|
||||
|
||||
.course-faculty-list-item {
|
||||
|
||||
.row {
|
||||
|
||||
&:nth-child(4) {
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.remove-faculty-photo {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
#course-faculty-bio-input {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.new-course-faculty-item {
|
||||
}
|
||||
|
||||
.current-faculty-photo {
|
||||
padding: 0;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
@include box-shadow(0 1px 3px rgba(0,0,0,0.1));
|
||||
padding: 10px;
|
||||
border: 2px solid $mediumGrey;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-grading {
|
||||
|
||||
.setting-grading-assignment-types {
|
||||
|
||||
.row .field.enum {
|
||||
width: 684px;
|
||||
}
|
||||
}
|
||||
|
||||
.course-grading-assignment-list-item {
|
||||
|
||||
}
|
||||
|
||||
.input-list {
|
||||
.row {
|
||||
|
||||
.input {
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-handouts {
|
||||
|
||||
}
|
||||
|
||||
.settings-problems {
|
||||
|
||||
> section {
|
||||
|
||||
&.is-shown {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-discussions {
|
||||
|
||||
.course-discussions-categories-list-item {
|
||||
|
||||
label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.remove-item {
|
||||
display: inline-block !important;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// states
|
||||
label.is-focused {
|
||||
color: $blue;
|
||||
@include transition(color 1s ease-in-out);
|
||||
}
|
||||
|
||||
// extras/abbreviations
|
||||
// .settings-extras {
|
||||
|
||||
// > header {
|
||||
// cursor: pointer;
|
||||
|
||||
// &.active {
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
// > div {
|
||||
// display: none;
|
||||
// @include transition(display 0.25s ease-in-out);
|
||||
|
||||
// &.is-shown {
|
||||
// display: block;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
input.error, textarea.error {
|
||||
border-color: $red;
|
||||
margin-top: ($baseline/4);
|
||||
color: $gray-l3;
|
||||
}
|
||||
|
||||
.message-error {
|
||||
@include font-size(13);
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
margin-top: ($baseline/4);
|
||||
margin-bottom: ($baseline/2);
|
||||
color: $red;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
// misc
|
||||
.divide {
|
||||
display: none;
|
||||
// buttons
|
||||
.remove-item {
|
||||
@include white-button;
|
||||
@include font-size(13);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
i.ss-icon {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin-right: 5px;
|
||||
.new-button {
|
||||
@include font-size(13);
|
||||
}
|
||||
|
||||
.well {
|
||||
padding: 20px;
|
||||
background: $lightGrey;
|
||||
border: 1px solid $mediumGrey;
|
||||
@include border-radius(4px);
|
||||
@include box-shadow(0 1px 1px rgba(0,0,0,0.05) inset)
|
||||
}
|
||||
}
|
||||
// form basics
|
||||
.list-input {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
.field {
|
||||
margin: 0 0 $baseline 0;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 30px;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: $blue;
|
||||
}
|
||||
&.required {
|
||||
|
||||
.grade-controls {
|
||||
@include clearfix;
|
||||
width: 642px;
|
||||
}
|
||||
|
||||
.new-grade-button {
|
||||
position: relative;
|
||||
float: left;
|
||||
display: block;
|
||||
width: 29px;
|
||||
height: 29px;
|
||||
margin: 10px 20px 0 0;
|
||||
border-radius: 20px;
|
||||
border: 1px solid $darkGrey;
|
||||
@include linear-gradient(top, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
|
||||
background-color: #d1dae3;
|
||||
@include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset);
|
||||
color: #6d788b;
|
||||
|
||||
.plus-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
}
|
||||
|
||||
.grade-slider {
|
||||
float: left;
|
||||
width: 580px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.grade-bar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
background: $lightGrey;
|
||||
|
||||
.increments {
|
||||
position: relative;
|
||||
|
||||
li {
|
||||
position: absolute;
|
||||
top: 52px;
|
||||
width: 30px;
|
||||
margin-left: -15px;
|
||||
font-size: 9px;
|
||||
text-align: center;
|
||||
|
||||
&.increment-0 {
|
||||
left: 0;
|
||||
label {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&.increment-10 {
|
||||
left: 10%;
|
||||
label:after {
|
||||
margin-left: ($baseline/4);
|
||||
content: "*";
|
||||
}
|
||||
}
|
||||
|
||||
label, input, textarea {
|
||||
display: block;
|
||||
}
|
||||
|
||||
label {
|
||||
@include font-size(14);
|
||||
@include transition(color, 0.15s, ease-in-out);
|
||||
margin: 0 0 ($baseline/4) 0;
|
||||
font-weight: 400;
|
||||
|
||||
&.is-focused {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
@include placeholder($gray-l4);
|
||||
@include font-size(16);
|
||||
@include size(100%,100%);
|
||||
padding: ($baseline/2);
|
||||
|
||||
&.long {
|
||||
}
|
||||
|
||||
&.increment-20 {
|
||||
left: 20%;
|
||||
&.short {
|
||||
}
|
||||
|
||||
&.increment-30 {
|
||||
left: 30%;
|
||||
&.error {
|
||||
border-color: $red;
|
||||
}
|
||||
|
||||
&.increment-40 {
|
||||
left: 40%;
|
||||
}
|
||||
&:focus {
|
||||
|
||||
&.increment-50 {
|
||||
left: 50%;
|
||||
+ .tip {
|
||||
color: $gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.increment-60 {
|
||||
left: 60%;
|
||||
}
|
||||
textarea.long {
|
||||
height: ($baseline*5);
|
||||
}
|
||||
|
||||
&.increment-70 {
|
||||
left: 70%;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
display: inline-block;
|
||||
margin-right: ($baseline/4);
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
||||
&.increment-80 {
|
||||
left: 80%;
|
||||
}
|
||||
|
||||
&.increment-90 {
|
||||
left: 90%;
|
||||
}
|
||||
|
||||
&.increment-100 {
|
||||
left: 100%;
|
||||
& + label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grade-specific-bar {
|
||||
height: 50px !important;
|
||||
|
||||
.field-group {
|
||||
@include clearfix();
|
||||
margin: 0 0 ($baseline/2) 0;
|
||||
}
|
||||
|
||||
.grades {
|
||||
position: relative;
|
||||
// enumerated/grouped lists
|
||||
&.enum {
|
||||
|
||||
li {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 50px;
|
||||
text-align: right;
|
||||
@include border-radius(2px);
|
||||
.field-group {
|
||||
@include box-sizing(border-box);
|
||||
@include border-radius(3px);
|
||||
background: $gray-l5;
|
||||
padding: $baseline;
|
||||
|
||||
&:hover,
|
||||
&.is-dragging {
|
||||
.remove-button {
|
||||
display: block;
|
||||
&:last-child {
|
||||
padding-bottom: $baseline;
|
||||
}
|
||||
|
||||
.actions {
|
||||
@include clearfix();
|
||||
margin-top: ($baseline/2);
|
||||
border-top: 1px solid $gray-l4;
|
||||
padding-top: ($baseline/2);
|
||||
|
||||
.remove-item {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-dragging {
|
||||
// existing inputs
|
||||
.input-existing {
|
||||
margin: 0 0 $baseline 0;
|
||||
|
||||
.actions {
|
||||
margin: ($baseline/4) 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// not editable fields
|
||||
.field.is-not-editable {
|
||||
|
||||
.remove-button {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -17px;
|
||||
right: 1px;
|
||||
height: 17px;
|
||||
font-size: 10px;
|
||||
}
|
||||
label, .label {
|
||||
color: $gray-l3;
|
||||
}
|
||||
|
||||
&:nth-child(1) {
|
||||
background: #4fe696;
|
||||
}
|
||||
input {
|
||||
opacity: 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
background: #ffdf7e;
|
||||
}
|
||||
// field with error
|
||||
.field.error {
|
||||
|
||||
&:nth-child(3) {
|
||||
background: #ffb657;
|
||||
}
|
||||
input, textarea {
|
||||
border-color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
// specific fields - basic
|
||||
&.basic {
|
||||
|
||||
&:nth-child(4) {
|
||||
background: #ef54a1;
|
||||
}
|
||||
.list-input {
|
||||
@include clearfix();
|
||||
|
||||
&:nth-child(5),
|
||||
&.bar-fail {
|
||||
background: #fb336c;
|
||||
}
|
||||
.field {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.letter-grade {
|
||||
display: block;
|
||||
margin: 10px 15px 0 0;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 14px;
|
||||
}
|
||||
#field-course-organization {
|
||||
float: left;
|
||||
width: flex-grid(2, 9);
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
.range {
|
||||
display: block;
|
||||
margin-right: 15px;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
}
|
||||
#field-course-number {
|
||||
float: left;
|
||||
width: flex-grid(2, 9);
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
.drag-bar {
|
||||
#field-course-name {
|
||||
float: left;
|
||||
width: flex-grid(5, 9);
|
||||
}
|
||||
}
|
||||
|
||||
// specific fields - schedule
|
||||
&.schedule {
|
||||
|
||||
.list-input {
|
||||
margin-bottom: ($baseline*1.5);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.field-group {
|
||||
@include clearfix();
|
||||
border-bottom: 1px solid $gray-l5;
|
||||
padding-bottom: ($baseline/2);
|
||||
|
||||
&:last-child {
|
||||
border: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.field {
|
||||
float: left;
|
||||
width: flex-grid(3, 9);
|
||||
margin-bottom: ($baseline/4);
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
.field.time {
|
||||
position: relative;
|
||||
|
||||
.tip {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -1px;
|
||||
height: 50px;
|
||||
width: 2px;
|
||||
background-color: #fff;
|
||||
@include box-shadow(-1px 0 3px rgba(0,0,0,0.1));
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// specific fields - overview
|
||||
#field-course-overview {
|
||||
|
||||
cursor: ew-resize;
|
||||
@include transition(none);
|
||||
#course-overview {
|
||||
height: ($baseline*20);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
width: 6px;
|
||||
right: -2px;
|
||||
// specific fields - video
|
||||
#field-course-introduction-video {
|
||||
|
||||
.input-existing {
|
||||
@include box-sizing(border-box);
|
||||
@include border-radius(3px);
|
||||
background: $gray-l5;
|
||||
padding: ($baseline/2);
|
||||
|
||||
.actions {
|
||||
@include clearfix();
|
||||
margin-top: ($baseline/2);
|
||||
border-top: 1px solid $gray-l4;
|
||||
padding-top: ($baseline/2);
|
||||
|
||||
.remove-item {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: ($baseline/2);
|
||||
border-top: 1px solid $gray-l5;
|
||||
padding-top: ($baseline/2);
|
||||
}
|
||||
}
|
||||
|
||||
// specific fields - requirements
|
||||
&.requirements {
|
||||
|
||||
#field-course-effort {
|
||||
width: flex-grid(3, 9);
|
||||
}
|
||||
}
|
||||
|
||||
// specific fields - grading range (artifact styling)
|
||||
&.grade-range {
|
||||
margin-bottom: ($baseline*3);
|
||||
|
||||
.grade-controls {
|
||||
@include clearfix;
|
||||
width: flex-grid(9,9);
|
||||
}
|
||||
|
||||
.new-grade-button {
|
||||
@include box-sizing(border-box);
|
||||
@include linear-gradient(top, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
|
||||
@include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset);
|
||||
width: flex-grid(1,9);
|
||||
height: ($baseline*2);
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: flex-gutter();
|
||||
border-radius: 20px;
|
||||
border: 1px solid $darkGrey;
|
||||
background-color: #d1dae3;
|
||||
color: #6d788b;
|
||||
|
||||
.plus-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
}
|
||||
|
||||
.grade-slider {
|
||||
@include box-sizing(border-box);
|
||||
width: flex-grid(8,9);
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
.grade-bar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: ($baseline*2.5);
|
||||
background: $lightGrey;
|
||||
|
||||
.increments {
|
||||
position: relative;
|
||||
|
||||
li {
|
||||
position: absolute;
|
||||
top: 52px;
|
||||
width: 30px;
|
||||
margin-left: -15px;
|
||||
font-size: 9px;
|
||||
text-align: center;
|
||||
|
||||
&.increment-0 {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&.increment-10 {
|
||||
left: 10%;
|
||||
}
|
||||
|
||||
&.increment-20 {
|
||||
left: 20%;
|
||||
}
|
||||
|
||||
&.increment-30 {
|
||||
left: 30%;
|
||||
}
|
||||
|
||||
&.increment-40 {
|
||||
left: 40%;
|
||||
}
|
||||
|
||||
&.increment-50 {
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
&.increment-60 {
|
||||
left: 60%;
|
||||
}
|
||||
|
||||
&.increment-70 {
|
||||
left: 70%;
|
||||
}
|
||||
|
||||
&.increment-80 {
|
||||
left: 80%;
|
||||
}
|
||||
|
||||
&.increment-90 {
|
||||
left: 90%;
|
||||
}
|
||||
|
||||
&.increment-100 {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grade-specific-bar {
|
||||
height: 50px !important;
|
||||
}
|
||||
|
||||
.grades {
|
||||
position: relative;
|
||||
|
||||
li {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 50px;
|
||||
text-align: right;
|
||||
@include border-radius(2px);
|
||||
|
||||
&:hover,
|
||||
&.is-dragging {
|
||||
.remove-button {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-dragging {
|
||||
|
||||
}
|
||||
|
||||
.remove-button {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -17px;
|
||||
right: 1px;
|
||||
height: 17px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
&:nth-child(1) {
|
||||
background: #4fe696;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
background: #ffdf7e;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
background: #ffb657;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
background: #ef54a1;
|
||||
}
|
||||
|
||||
&:nth-child(5),
|
||||
&.bar-fail {
|
||||
background: #fb336c;
|
||||
}
|
||||
|
||||
.letter-grade {
|
||||
display: block;
|
||||
margin: 10px 15px 0 0;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.range {
|
||||
display: block;
|
||||
margin-right: 15px;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.drag-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -1px;
|
||||
height: 50px;
|
||||
width: 2px;
|
||||
background-color: #fff;
|
||||
@include box-shadow(-1px 0 3px rgba(0,0,0,0.1));
|
||||
|
||||
cursor: ew-resize;
|
||||
@include transition(none);
|
||||
|
||||
&:hover {
|
||||
width: 6px;
|
||||
right: -2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// specific fields - grading rules
|
||||
&.grade-rules {
|
||||
|
||||
#field-course-grading-graceperiod {
|
||||
width: flex-grid(3, 9);
|
||||
}
|
||||
}
|
||||
|
||||
&.assignment-types {
|
||||
|
||||
.list-input {
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.field-group {
|
||||
@include clearfix();
|
||||
width: flex-grid(9, 9);
|
||||
margin-bottom: ($baseline*1.5);
|
||||
border-bottom: 1px solid $gray-l5;
|
||||
padding-bottom: ($baseline*1.5);
|
||||
|
||||
&:last-child {
|
||||
border: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.field {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: flex-grid(3, 6);
|
||||
margin-bottom: ($baseline/2);
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
#field-course-grading-assignment-shortname,
|
||||
#field-course-grading-assignment-totalassignments,
|
||||
#field-course-grading-assignment-gradeweight,
|
||||
#field-course-grading-assignment-droppable {
|
||||
width: flex-grid(2, 6);
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
float: left;
|
||||
width: flex-grid(9, 9);
|
||||
|
||||
.delete-button {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-supplementary {
|
||||
width: flex-grid(3, 12);
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,9 @@
|
||||
border-radius: 0;
|
||||
|
||||
&.new-component-item {
|
||||
margin-top: 20px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
@include box-shadow(none);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
.subsection .main-wrapper {
|
||||
margin: 40px;
|
||||
}
|
||||
|
||||
.subsection .inner-wrapper {
|
||||
@include clearfix();
|
||||
}
|
||||
|
||||
.subsection-body {
|
||||
padding: 32px 40px;
|
||||
@include clearfix;
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
.unit .main-wrapper,
|
||||
.subsection .main-wrapper {
|
||||
.unit .main-wrapper {
|
||||
@include clearfix();
|
||||
margin: 40px;
|
||||
}
|
||||
|
||||
//Problem Selector tab menu requirements
|
||||
.js .tabs .tab {
|
||||
display: none;
|
||||
}
|
||||
//end problem selector reqs
|
||||
|
||||
.main-column {
|
||||
clear: both;
|
||||
float: left;
|
||||
@@ -58,6 +64,7 @@
|
||||
margin: 20px 40px;
|
||||
|
||||
|
||||
|
||||
.title {
|
||||
margin: 0 0 15px 0;
|
||||
color: $mediumGrey;
|
||||
@@ -67,22 +74,25 @@
|
||||
}
|
||||
|
||||
&.new-component-item {
|
||||
padding: 20px;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
background: $lightGrey;
|
||||
margin: 20px 0px;
|
||||
border-top: 1px solid $mediumGrey;
|
||||
box-shadow: 0 2px 1px rgba(182, 182, 182, 0.75) inset;
|
||||
background-color: $lightGrey;
|
||||
margin-bottom: 0px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.new-component-button {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #6d788b;
|
||||
color: #edf1f5;
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin-bottom: 8px;
|
||||
margin: 20px 0px;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.rendered-component {
|
||||
@@ -92,18 +102,21 @@
|
||||
}
|
||||
|
||||
.new-component-type {
|
||||
|
||||
a,
|
||||
li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
a {
|
||||
border: 1px solid $mediumGrey;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
color: #fff;
|
||||
margin-right: 15px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
font-size: 15px;
|
||||
line-height: 14px;
|
||||
text-align: center;
|
||||
@include box-shadow(0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset);
|
||||
@@ -115,25 +128,40 @@
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
@include box-sizing(border-box);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new-component-templates {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
margin: 20px 40px 20px 40px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid $mediumGrey;
|
||||
background-color: #fff;
|
||||
@include box-shadow(0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset);
|
||||
@include clearfix;
|
||||
|
||||
.cancel-button {
|
||||
margin: 20px 0px 10px 10px;
|
||||
@include white-button;
|
||||
}
|
||||
|
||||
.problem-type-tabs {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// specific menu types
|
||||
&.new-component-problem {
|
||||
padding-bottom:10px;
|
||||
|
||||
.ss-icon, .editor-indicator {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.problem-type-tabs {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +174,6 @@
|
||||
border: 1px solid $darkGreen;
|
||||
background: tint($green,20%);
|
||||
color: #fff;
|
||||
@include transition(background-color .15s);
|
||||
|
||||
&:hover {
|
||||
background: $brightGreen;
|
||||
@@ -154,19 +181,81 @@
|
||||
}
|
||||
}
|
||||
|
||||
.new-component-template {
|
||||
margin-bottom: 20px;
|
||||
.problem-type-tabs {
|
||||
list-style-type: none;
|
||||
border-radius: 0;
|
||||
width: 100%;
|
||||
@include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0));
|
||||
background-color: $lightBluishGrey;
|
||||
@include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset);
|
||||
|
||||
li:last-child {
|
||||
li:first-child {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
float:left;
|
||||
display:inline-block;
|
||||
text-align:center;
|
||||
width: auto;
|
||||
@include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0));
|
||||
background-color: tint($lightBluishGrey, 10%);
|
||||
@include box-shadow(0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset);
|
||||
opacity:.8;
|
||||
|
||||
&:hover {
|
||||
opacity:1;
|
||||
background-color: tint($lightBluishGrey, 20%);
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
border: 0px;
|
||||
@include active;
|
||||
opacity:1;
|
||||
}
|
||||
}
|
||||
|
||||
a{
|
||||
display: block;
|
||||
padding: 15px 25px;
|
||||
font-size: 15px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
color: #3c3c3c;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.new-component-template {
|
||||
|
||||
a {
|
||||
background: #fff;
|
||||
border: 0px;
|
||||
color: #3c3c3c;
|
||||
@include transition (none);
|
||||
|
||||
&:hover {
|
||||
background: tint($green,30%);
|
||||
color: #fff;
|
||||
@include transition(background-color .15s);
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
border:none;
|
||||
border-bottom: 1px dashed $lightGrey;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
li:first-child {
|
||||
a {
|
||||
border-radius: 0 0 3px 3px;
|
||||
border-bottom: 1px solid $darkGreen;
|
||||
border-top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
li:nth-child(2) {
|
||||
a {
|
||||
border-radius: 3px 3px 0 0;
|
||||
border-radius: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,18 +264,20 @@
|
||||
display: block;
|
||||
padding: 7px 20px;
|
||||
border-bottom: none;
|
||||
font-weight: 300;
|
||||
font-weight: 500;
|
||||
|
||||
.name {
|
||||
float: left;
|
||||
|
||||
.ss-icon {
|
||||
@include transition(opacity .15s);
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
top: 1px;
|
||||
font-size: 13px;
|
||||
margin-right: 5px;
|
||||
opacity: 0.5;
|
||||
width: 17;
|
||||
height: 21px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +295,7 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
|
||||
.ss-icon {
|
||||
opacity: 1.0;
|
||||
@@ -217,14 +309,18 @@
|
||||
|
||||
// specific editor types
|
||||
.empty {
|
||||
@include box-shadow(0 1px 3px rgba(0,0,0,0.2));
|
||||
margin-bottom: 10px;
|
||||
|
||||
a {
|
||||
border-bottom: 1px solid $darkGreen;
|
||||
border-radius: 3px;
|
||||
font-weight: 500;
|
||||
background: $green;
|
||||
line-height: 1.4;
|
||||
font-weight: 400;
|
||||
background: #fff;
|
||||
color: #3c3c3c;
|
||||
|
||||
|
||||
&:hover {
|
||||
background: tint($green,30%);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,7 +329,7 @@
|
||||
text-align: center;
|
||||
|
||||
h5 {
|
||||
color: $green;
|
||||
color: $darkGreen;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -507,6 +603,7 @@
|
||||
|
||||
.edit-state-draft {
|
||||
.visibility,
|
||||
|
||||
.edit-draft-message,
|
||||
.view-button {
|
||||
display: none;
|
||||
|
||||
@@ -1,25 +1,85 @@
|
||||
$gw-column: 80px;
|
||||
$gw-gutter: 20px;
|
||||
$baseline: 20px;
|
||||
|
||||
// grid
|
||||
$gw-column: ($baseline*3);
|
||||
$gw-gutter: $baseline;
|
||||
$fg-column: $gw-column;
|
||||
$fg-gutter: $gw-gutter;
|
||||
$fg-max-columns: 12;
|
||||
$fg-max-width: 1400px;
|
||||
$fg-min-width: 810px;
|
||||
$fg-max-width: 1280px;
|
||||
$fg-min-width: 900px;
|
||||
|
||||
// type
|
||||
$sans-serif: 'Open Sans', $verdana;
|
||||
$body-line-height: golden-ratio(.875em, 1);
|
||||
|
||||
$white: rgb(255,255,255);
|
||||
$black: rgb(0,0,0);
|
||||
$pink: rgb(182,37,104);
|
||||
$error-red: rgb(253, 87, 87);
|
||||
|
||||
$baseFontColor: #3c3c3c;
|
||||
$offBlack: #3c3c3c;
|
||||
// colors - new for re-org
|
||||
$black: rgb(0,0,0);
|
||||
$white: rgb(255,255,255);
|
||||
$blue: #5597dd;
|
||||
|
||||
$gray: rgb(127,127,127);
|
||||
$gray-l1: tint($gray,20%);
|
||||
$gray-l2: tint($gray,40%);
|
||||
$gray-l3: tint($gray,60%);
|
||||
$gray-l4: tint($gray,80%);
|
||||
$gray-l5: tint($gray,90%);
|
||||
$gray-d1: shade($gray,20%);
|
||||
$gray-d2: shade($gray,40%);
|
||||
$gray-d3: shade($gray,60%);
|
||||
$gray-d4: shade($gray,80%);
|
||||
|
||||
$blue: rgb(85, 151, 221);
|
||||
$blue-l1: tint($blue,20%);
|
||||
$blue-l2: tint($blue,40%);
|
||||
$blue-l3: tint($blue,60%);
|
||||
$blue-l4: tint($blue,80%);
|
||||
$blue-l5: tint($blue,90%);
|
||||
$blue-d1: shade($blue,20%);
|
||||
$blue-d2: shade($blue,40%);
|
||||
$blue-d3: shade($blue,60%);
|
||||
$blue-d4: shade($blue,80%);
|
||||
|
||||
$pink: rgb(183, 37, 103);
|
||||
$pink-l1: tint($pink,20%);
|
||||
$pink-l2: tint($pink,40%);
|
||||
$pink-l3: tint($pink,60%);
|
||||
$pink-l4: tint($pink,80%);
|
||||
$pink-l5: tint($pink,90%);
|
||||
$pink-d1: shade($pink,20%);
|
||||
$pink-d2: shade($pink,40%);
|
||||
$pink-d3: shade($pink,60%);
|
||||
$pink-d4: shade($pink,80%);
|
||||
|
||||
$green: rgb(37, 184, 90);
|
||||
$green-l1: tint($green,20%);
|
||||
$green-l2: tint($green,40%);
|
||||
$green-l3: tint($green,60%);
|
||||
$green-l4: tint($green,80%);
|
||||
$green-l5: tint($green,90%);
|
||||
$green-d1: shade($green,20%);
|
||||
$green-d2: shade($green,40%);
|
||||
$green-d3: shade($green,60%);
|
||||
$green-d4: shade($green,80%);
|
||||
|
||||
$yellow: rgb(231, 214, 143);
|
||||
$yellow-l1: tint($yellow,20%);
|
||||
$yellow-l2: tint($yellow,40%);
|
||||
$yellow-l3: tint($yellow,60%);
|
||||
$yellow-l4: tint($yellow,80%);
|
||||
$yellow-l5: tint($yellow,90%);
|
||||
$yellow-d1: shade($yellow,20%);
|
||||
$yellow-d2: shade($yellow,40%);
|
||||
$yellow-d3: shade($yellow,60%);
|
||||
$yellow-d4: shade($yellow,80%);
|
||||
|
||||
$shadow: rgba(0,0,0,0.2);
|
||||
$shadow-l1: rgba(0,0,0,0.1);
|
||||
$shadow-d1: rgba(0,0,0,0.4);
|
||||
|
||||
// colors - inherited
|
||||
$baseFontColor: #3c3c3c;
|
||||
$offBlack: #3c3c3c;
|
||||
$orange: #edbd3c;
|
||||
$red: #b20610;
|
||||
$green: #108614;
|
||||
@@ -34,4 +94,4 @@ $brightGreen: rgb(22, 202, 87);
|
||||
$disabledGreen: rgb(124, 206, 153);
|
||||
$darkGreen: rgb(52, 133, 76);
|
||||
$lightBluishGrey: rgb(197, 207, 223);
|
||||
$lightBluishGrey2: rgb(213, 220, 228);
|
||||
$lightBluishGrey2: rgb(213, 220, 228);
|
||||
@@ -1,4 +1,5 @@
|
||||
@import 'bourbon/bourbon';
|
||||
@import 'bourbon/addons/button';
|
||||
@import 'vendor/normalize';
|
||||
@import 'keyframes';
|
||||
|
||||
@@ -8,8 +9,10 @@
|
||||
@import "fonts";
|
||||
@import "variables";
|
||||
@import "cms_mixins";
|
||||
@import "extends";
|
||||
@import "base";
|
||||
@import "header";
|
||||
@import "footer";
|
||||
@import "dashboard";
|
||||
@import "courseware";
|
||||
@import "subsection";
|
||||
@@ -26,6 +29,8 @@
|
||||
@import "modal";
|
||||
@import "alerts";
|
||||
@import "login";
|
||||
@import "account";
|
||||
@import "index";
|
||||
@import 'jquery-ui-calendar';
|
||||
|
||||
@import 'content-types';
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<section class="activation">
|
||||
<h1>Account already active!</h1>
|
||||
<p> This account has already been activated. <a href="/login">Log in here</a>.</p>
|
||||
<p> This account has already been activated. <a href="/signin">Log in here</a>.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<section class="tos">
|
||||
<div>
|
||||
<h1>Activation Complete!</h1>
|
||||
<p>Thanks for activating your account. <a href="/login">Log in here</a>.</p>
|
||||
<p>Thanks for activating your account. <a href="/signin">Log in here</a>.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<%inherit file="base.html" />
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<%block name="bodyclass">assets</%block>
|
||||
<%block name="title">Courseware Assets</%block>
|
||||
<%block name="bodyclass">is-signedin course uploads</%block>
|
||||
<%block name="title">Uploads & Files</%block>
|
||||
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
|
||||
@@ -33,12 +33,27 @@
|
||||
</tr>
|
||||
</script>
|
||||
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast has-actions has-subtitle">
|
||||
<div class="title">
|
||||
<span class="title-sub">Course Content</span>
|
||||
<h1 class="title-1">Files & Uploads</h1>
|
||||
</div>
|
||||
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Page Actions</h3>
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button upload-button new-button"><i class="ss-icon ss-symbolicons-standard icon icon-create"></i> Upload New File</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<div class="page-actions">
|
||||
<a href="#" class="upload-button new-button">
|
||||
<span class="upload-icon"></span>Upload New Asset
|
||||
</a>
|
||||
<input type="text" class="asset-search-input search wip-box" placeholder="search assets" style="display:none"/>
|
||||
</div>
|
||||
<article class="asset-library">
|
||||
|
||||
@@ -5,23 +5,29 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>
|
||||
<%block name="title"></%block> |
|
||||
% if context_course:
|
||||
<% ctx_loc = context_course.location %>
|
||||
${context_course.display_name} |
|
||||
% endif
|
||||
edX Studio
|
||||
</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<meta name="path_prefix" content="${MITX_ROOT_URL}">
|
||||
|
||||
<%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-standard.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')}" />
|
||||
|
||||
<title><%block name="title"></%block></title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="path_prefix" content="${MITX_ROOT_URL}">
|
||||
|
||||
<%block name="header_extras"></%block>
|
||||
</head>
|
||||
|
||||
<body class="<%block name='bodyclass'></%block> hide-wip">
|
||||
<%include file="widgets/header.html" args="active_tab=active_tab"/>
|
||||
<%include file="widgets/header.html" />
|
||||
<%include file="courseware_vendor_js.html"/>
|
||||
|
||||
<script type="text/javascript" src="${static.url('js/vendor/json2.js')}"></script>
|
||||
@@ -47,9 +53,9 @@
|
||||
</script>
|
||||
|
||||
<%block name="content"></%block>
|
||||
<%include file="widgets/footer.html" />
|
||||
|
||||
<%block name="jsextra"></%block>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<%inherit file="base.html" />
|
||||
<%block name="title">Course Manager</%block>
|
||||
|
||||
<%include file="widgets/header.html"/>
|
||||
|
||||
<%block name="content">
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
|
||||
<!-- TODO decode course # from context_course into title -->
|
||||
<%block name="title">Course Info</%block>
|
||||
<%block name="bodyclass">course-info</%block>
|
||||
<%block name="title">Updates</%block>
|
||||
<%block name="bodyclass">is-signedin course course-info updates</%block>
|
||||
|
||||
|
||||
<%block name="jsextra">
|
||||
<script type="text/javascript" src="${static.url('js/template_loader.js')}"></script>
|
||||
@@ -41,16 +42,38 @@
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast has-actions has-subtitle">
|
||||
<div class="title">
|
||||
<span class="title-sub">Course Content</span>
|
||||
<h1 class="title-1">Course Updates</h1>
|
||||
</div>
|
||||
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Page Actions</h3>
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<a href="#" class=" button new-button new-update-button"><i class="ss-icon ss-symbolicons-standard icon icon-create">+</i> New Update</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-content wrapper">
|
||||
<section class="content">
|
||||
<div class="introduction">
|
||||
<p clas="copy">Course updates are announcements or notifications you want to share with your class. Other course authors have used them for important exam/date reminders, change in schedules, and to call out any important steps students need to be aware of.</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<h1>Course Info</h1>
|
||||
<div class="course-info-wrapper">
|
||||
<div class="main-column window">
|
||||
<article class="course-updates" id="course-update-view">
|
||||
<h2>Course Updates & News</h2>
|
||||
<a href="#" class="new-update-button">New Update</a>
|
||||
<ol class="update-list" id="course-update-list"></ol>
|
||||
<!-- probably replace w/ a vertical where each element of the vertical is a separate update w/ a date and html field -->
|
||||
</article>
|
||||
</div>
|
||||
<div class="sidebar window course-handouts" id="course-handouts-view"></div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<%inherit file="base.html" />
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<%block name="title">Edit Static Page</%block>
|
||||
<%block name="bodyclass">edit-static-page</%block>
|
||||
<%block name="title">Editing Static Page</%block>
|
||||
<%block name="bodyclass">is-signedin course pages edit-static-page</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div class="main-wrapper">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<%inherit file="base.html" />
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<%block name="title">Tabs</%block>
|
||||
<%block name="bodyclass">static-pages</%block>
|
||||
<%block name="title">Static Pages</%block>
|
||||
<%block name="bodyclass">is-signedin course pages static-pages</%block>
|
||||
|
||||
<%block name="jsextra">
|
||||
<script type='text/javascript'>
|
||||
@@ -9,25 +9,49 @@
|
||||
el: $('.main-wrapper'),
|
||||
model: new CMS.Models.Module({
|
||||
id: '${context_course.location}'
|
||||
})
|
||||
}),
|
||||
mast: $('.wrapper-mast')
|
||||
});
|
||||
</script>
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast has-actions has-subtitle">
|
||||
<div class="title">
|
||||
<span class="title-sub">Course Content</span>
|
||||
<h1 class="title-1">Static Pages</h1>
|
||||
</div>
|
||||
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Page Actions</h3>
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button new-button new-tab"><i class="ss-icon ss-symbolicons-standard icon icon-create">+</i> New Page</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-content wrapper">
|
||||
<section class="content">
|
||||
<div class="introduction has-links">
|
||||
<p class="copy">Static Pages are additional pages that supplement your Courseware. Other course authors have used them to share a syllabus, calendar, handouts, and more.</p>
|
||||
<nav class="nav-introduction-supplementary">
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<a rel="modal" href="#preview-lms-staticpages"><i class="ss-icon ss-symbolicons-block icon icon-information">❓</i>How do Static Pages look to students in my course?</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<article class="unit-body">
|
||||
<div class="details">
|
||||
<h2>Here you can add and manage additional pages for your course</h2>
|
||||
<p>These pages will be added to the primary navigation menu alongside Courseware, Course Info, Discussion, etc.</p>
|
||||
</div>
|
||||
|
||||
<div class="page-actions">
|
||||
<a href="#" class="new-button new-tab">
|
||||
<span class="plus-icon white"></span>New Page
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="tab-list">
|
||||
<ol class='components'>
|
||||
@@ -43,4 +67,17 @@
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-modal" id="preview-lms-staticpages">
|
||||
<h3 class="title">How Static Pages are Used in Your Course</h3>
|
||||
<figure>
|
||||
<img src="/static/img/preview-lms-staticpages.png" alt="Preview of how Static Pages are used in your course" />
|
||||
<figcaption class="description">These pages will be presented in your course's main navigation alongside Courseware, Course Info, Discussion, etc.</figcaption>
|
||||
</figure>
|
||||
|
||||
<a href="#" rel="view" class="action action-modal-close">
|
||||
<i class="ss-icon ss-symbolicons-block icon-close icon">␡</i>
|
||||
<span class="label">close modal</span>
|
||||
</a>
|
||||
</div>
|
||||
</%block>
|
||||
@@ -7,8 +7,9 @@
|
||||
%>
|
||||
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<%block name="bodyclass">subsection</%block>
|
||||
<%block name="title">CMS Subsection</%block>
|
||||
<%block name="bodyclass">is-signedin course subsection</%block>
|
||||
|
||||
|
||||
<%namespace name="units" file="widgets/units.html" />
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
@@ -97,6 +98,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%block>
|
||||
|
||||
<%block name="jsextra">
|
||||
|
||||
@@ -2,10 +2,19 @@
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<%block name="title">Export</%block>
|
||||
<%block name="bodyclass">export</%block>
|
||||
<%block name="title">Export Course</%block>
|
||||
<%block name="bodyclass">is-signedin course tools export</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast has-subtitle">
|
||||
<div class="title">
|
||||
<span class="title-sub">Tools</span>
|
||||
<h1 class="title-1">Course Export</h1>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<article class="export-overview">
|
||||
|
||||
185
cms/templates/howitworks.html
Normal file
@@ -0,0 +1,185 @@
|
||||
<%inherit file="base.html" />
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
|
||||
<%block name="title">Welcome</%block>
|
||||
<%block name="bodyclass">not-signedin index howitworks</%block>
|
||||
|
||||
<%block name="content">
|
||||
|
||||
<div class="wrapper-content-header wrapper">
|
||||
<section class="content content-header">
|
||||
<header>
|
||||
<h1>Welcome to <span class="logo">edX Studio</span></h1>
|
||||
<p class="tagline">Studio helps manage your courses online, so you can focus on teaching them</p>
|
||||
</header>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-content-features wrapper">
|
||||
<section class="content content-features">
|
||||
<header>
|
||||
<h2 class="sr">Studio's Many Features</h2>
|
||||
</header>
|
||||
|
||||
<ol class="list-features">
|
||||
<li class="feature">
|
||||
<figure class="img zoom">
|
||||
<a rel="modal" href="#hiw-feature1">
|
||||
<img src="/static/img/thumb-hiw-feature1.png" alt="Studio Helps You Keep Your Courses Organized" />
|
||||
<figcaption class="sr">Studio Helps You Keep Your Courses Organized</figcaption>
|
||||
<span class="action-zoom">
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-zoom"></i>
|
||||
</span>
|
||||
</a>
|
||||
</figure>
|
||||
|
||||
<div class="copy">
|
||||
<h3>Keeping Your Course Organized</h3>
|
||||
<p>The backbone of your course is how it is organized. Studio offers an <strong>Outline</strong> editor, providing a simple hierarchy and easy drag and drop to help you and your students stay organized.</p>
|
||||
|
||||
<ul class="list-proofpoints">
|
||||
<li class="proofpoint">
|
||||
<h4 class="title">Simple Organization For Content</h4>
|
||||
<p>Studio uses a simple hierarchy of <strong>sections</strong> and <strong>subsections</strong> to organize your content.</p>
|
||||
</li>
|
||||
|
||||
<li class="proofpoint">
|
||||
<h4 class="title">Change Your Mind Anytime</h4>
|
||||
<p>Draft your outline and build content anywhere. Simple drag and drop tools let your reorganize quickly.</p>
|
||||
</li>
|
||||
|
||||
<li class="proofpoint">
|
||||
<h4 class="title">Go A Week Or A Semester At A Time</h4>
|
||||
<p>Build and release <strong>sections</strong> to your students incrementally. You don't have to have it all done at once.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="feature">
|
||||
<figure class="img zoom">
|
||||
<a rel="modal" href="#hiw-feature2">
|
||||
<img src="/static/img/thumb-hiw-feature2.png" alt="Learning is More than Just Lectures" />
|
||||
<figcaption class="sr">Learning is More than Just Lectures</figcaption>
|
||||
<span class="action-zoom">
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-zoom"></i>
|
||||
</span>
|
||||
</a>
|
||||
</figure>
|
||||
|
||||
<div class="copy">
|
||||
<h3>Learning is More than Just Lectures</h3>
|
||||
<p>Studio lets you weave your content together in a way that reinforces learning — short video lectures interleaved with exercises and more. Insert videos and author a wide variety of exercise types with just a few clicks. </p>
|
||||
|
||||
<ul class="list-proofpoints">
|
||||
<li class="proofpoint">
|
||||
<h4 class="title">Create Learning Pathways</h4>
|
||||
<p>Help your students understand a small interactive piece at a time with multimedia, HTML, and exercises.</p>
|
||||
</li>
|
||||
|
||||
<li class="proofpoint">
|
||||
<h4 class="title">Work Visually, Organize Quickly</h4>
|
||||
<p>Work visually and see exactly what your students will see. Reorganize all your content with drag and drop.</p>
|
||||
</li>
|
||||
|
||||
<li class="proofpoint">
|
||||
<h4 class="title">A Broad Library of Problem Types</h4>
|
||||
<p>It's more than just multiple choice. Studio has nearly a dozen types of problems to challenge your learners.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="feature">
|
||||
<figure class="img zoom">
|
||||
<a rel="modal" href="#hiw-feature3">
|
||||
<img src="/static/img/thumb-hiw-feature3.png" alt="Studio Gives You Simple, Fast, and Incremental Publishing. With Friends." />
|
||||
<figcaption class="sr">Studio Gives You Simple, Fast, and Incremental Publishing. With Friends.</figcaption>
|
||||
<span class="action-zoom">
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-zoom"></i>
|
||||
</span>
|
||||
</a>
|
||||
</figure>
|
||||
|
||||
<div class="copy">
|
||||
<h3>Simple, Fast, and Incremental Publishing. With Friends.</h3>
|
||||
<p>Studio works like web applications you already know, yet understands how you build curriculum. Instant publishing to the web when you want it, incremental release when it makes sense. And with co-authors, you can have a whole team building a course, together.</p>
|
||||
|
||||
<ul class="list-proofpoints">
|
||||
<li class="proofpoint">
|
||||
<h4 class="title">Instant Changes</h4>
|
||||
<p>Caught a bug? No problem. When you want, your changes to live when you hit Save.</p>
|
||||
</li>
|
||||
|
||||
<li class="proofpoint">
|
||||
<h4 class="title">Release-On Date Publishing</h4>
|
||||
<p>When you've finished a <strong>section</strong>, pick when you want it to go live and Studio takes care of the rest. Build your course incrementally.</p>
|
||||
</li>
|
||||
|
||||
<li class="proofpoint">
|
||||
<h4 class="title">Work in Teams</h4>
|
||||
<p>Co-authors have full access to all the same authoring tools. Make your course better through a team effort.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-content-cta wrapper">
|
||||
<section class="content content-cta">
|
||||
<header>
|
||||
<h2 class="sr">Sign Up for Studio Today!</h2>
|
||||
</header>
|
||||
|
||||
<ul class="list-actions">
|
||||
<li>
|
||||
<a href="${reverse('signup')}" class="action action-primary">Sign Up & Start Making an edX Course</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="${reverse('login')}" class="action action-secondary">Already have a Studio Account? Sign In</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="content-modal" id="hiw-feature1">
|
||||
<h3 class="title">Outlining Your Course</h3>
|
||||
<figure>
|
||||
<img src="/static/img/hiw-feature1.png" alt="" />
|
||||
<figcaption class="description">Simple two-level outline to organize your couse. Drag and drop, and see your course at a glance.</figcaption>
|
||||
</figure>
|
||||
|
||||
<a href="#" rel="view" class="action action-modal-close">
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-close">␡</i>
|
||||
<span class="label">close modal</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="content-modal" id="hiw-feature2">
|
||||
<h3 class="title">More than Just Lectures</h3>
|
||||
<figure>
|
||||
<img src="/static/img/hiw-feature2.png" alt="" />
|
||||
<figcaption class="description">Quickly create videos, text snippets, inline discussions, and a variety of problem types.</figcaption>
|
||||
</figure>
|
||||
|
||||
<a href="#" rel="view" class="action action-modal-close">
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-close">␡</i>
|
||||
<span class="label">close modal</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="content-modal" id="hiw-feature3">
|
||||
<h3 class="title">Publishing on Date</h3>
|
||||
<figure>
|
||||
<img src="/static/img/hiw-feature3.png" alt="" />
|
||||
<figcaption class="description">Simply set the date of a section or subsection, and Studio will publish it to your students for you.</figcaption>
|
||||
</figure>
|
||||
|
||||
<a href="#" rel="view" class="action action-modal-close">
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-close">␡</i>
|
||||
<span class="label">close modal</span>
|
||||
</a>
|
||||
</div>
|
||||
</%block>
|
||||
@@ -2,10 +2,19 @@
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<%block name="title">Import</%block>
|
||||
<%block name="bodyclass">import</%block>
|
||||
<%block name="title">Import Course</%block>
|
||||
<%block name="bodyclass">is-signedin course tools import</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast has-subtitle">
|
||||
<div class="title">
|
||||
<span class="title-sub">Tools</span>
|
||||
<h1 class="title-1">Course Import</h1>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<article class="import-overview">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<%inherit file="base.html" />
|
||||
<%block name="bodyclass">index</%block>
|
||||
|
||||
<%block name="title">Courses</%block>
|
||||
<%block name="bodyclass">is-signedin index dashboard</%block>
|
||||
|
||||
<%block name="header_extras">
|
||||
<script type="text/template" id="new-course-template">
|
||||
@@ -32,35 +33,57 @@
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<h1>My Courses</h1>
|
||||
<article class="my-classes">
|
||||
% if user.is_active:
|
||||
% if not disable_course_creation:
|
||||
<a href="#" class="new-button new-course-button"><span class="plus-icon white"></span> New Course</a>
|
||||
%endif
|
||||
<ul class="class-list">
|
||||
%for course, url in courses:
|
||||
<li>
|
||||
<a href="${url}" class="class-name">
|
||||
<span class="class-name">${course}</span>
|
||||
<!--
|
||||
<span class="detail">Started: 9/21/2012</span>
|
||||
<span class="detail">Ends: 10/21/2012</span>
|
||||
-->
|
||||
</a>
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
% else:
|
||||
<div class='warn-msg'>
|
||||
<p>
|
||||
In order to start authoring courses using edX studio, please click on the activation link in your email.
|
||||
</p>
|
||||
</div>
|
||||
% endif
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</%block>
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast has-actions">
|
||||
<div class="title">
|
||||
<h1 class="title-1">My Courses</h1>
|
||||
</div>
|
||||
|
||||
% if user.is_active:
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Page Actions</h3>
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
% if not disable_course_creation:
|
||||
<a href="#" class="button new-button new-course-button"><i class="ss-icon ss-symbolicons-standard icon icon-create">+</i> New Course</a>
|
||||
% endif
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
% endif
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-content wrapper">
|
||||
<section class="content">
|
||||
<div class="introduction">
|
||||
<p class="copy"><strong>Welcome, ${ user.username }</strong>. Here are all of the courses you are currently authoring in Studio:</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<article class="my-classes">
|
||||
% if user.is_active:
|
||||
<ul class="class-list">
|
||||
%for course, url, lms_link in courses:
|
||||
<li>
|
||||
<a class="class-link" href="${url}" class="class-name">
|
||||
<span class="class-name">${course}</span>
|
||||
</a>
|
||||
<a href="${lms_link}" rel="external" class="button view-button view-live-button"><i class="ss-icon ss-symbolicons-block icon icon-view"></i>View Live</a>
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
% else:
|
||||
<div class='warn-msg'>
|
||||
<p>
|
||||
In order to start authoring courses using edX Studio, please click on the activation link in your email.
|
||||
</p>
|
||||
</div>
|
||||
% endif
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</%block>
|
||||
@@ -1,33 +1,59 @@
|
||||
<%inherit file="base.html" />
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<%block name="title">Log in</%block>
|
||||
<%block name="bodyclass">no-header</%block>
|
||||
<%block name="title">Sign In</%block>
|
||||
<%block name="bodyclass">not-signedin signin</%block>
|
||||
|
||||
<%block name="content">
|
||||
|
||||
<div class="edx-studio-logo-large"></div>
|
||||
|
||||
<article class="log-in-box">
|
||||
<div class="wrapper-content wrapper">
|
||||
<section class="content">
|
||||
<header>
|
||||
<h1>Log in to edX studio</h1>
|
||||
<h1 class="title title-1">Sign In to edX Studio</h1>
|
||||
<a href="${reverse('signup')}" class="action action-signin">Don't have a Studio Account? Sign up!</a>
|
||||
</header>
|
||||
<form class="log-in-form" id="login_form" action="login_post" method="post">
|
||||
<div class="row">
|
||||
<label>Email</label>
|
||||
<input name="email" type="email" class="email-field" tabindex="1">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Password <a href="${forgot_password_link}" class="forgot-button">Forgot password?</a></label>
|
||||
<input name="password" type="password" class="password-field" tabindex="2">
|
||||
</div>
|
||||
<div class="row form-actions">
|
||||
<input name="submit" type="submit" value="Log In" class="log-in-button" tabindex="3">
|
||||
<span class="or">or</span>
|
||||
<a href="${reverse('signup')}" class="sign-up-button" tabindex="4">Sign up</a>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<article class="content-primary" role="main">
|
||||
<form id="login_form" method="post" action="login_post">
|
||||
|
||||
<fieldset>
|
||||
<legend class="sr">Required Information to Sign In to edX Studio</legend>
|
||||
|
||||
<ol class="list-input">
|
||||
<li class="field text required" id="field-email">
|
||||
<label for="email">Email Address</label>
|
||||
<input id="email" type="email" name="email" placeholder="e.g. jane.doe@gmail.com" />
|
||||
</li>
|
||||
|
||||
<li class="field text required" id="field-password">
|
||||
<a href="${forgot_password_link}" class="action action-forgotpassword" tabindex="-1">Forgot password?</a>
|
||||
<label for="password">Password</label>
|
||||
<input id="password" type="password" name="password" />
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" id="submit" name="submit" class="action action-primary">Sign In to edX Studio</button>
|
||||
</div>
|
||||
|
||||
<!-- no honor code for CMS, but need it because we're using the lms student object -->
|
||||
<input name="honor_code" type="checkbox" value="true" checked="true" hidden="true">
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<aside class="content-supplementary" role="complimentary">
|
||||
<h2 class="sr">Studio Support</h2>
|
||||
|
||||
<div class="bit">
|
||||
<h3 class="title-3">Need Help?</h3>
|
||||
<p>Having trouble with your account? Use <a href="http://help.edge.edx.org" rel="external">our support center</a> to look over self help steps, find solutions others have found to the same problem, or let us know of your issue.</p>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
</div>
|
||||
</%block>
|
||||
|
||||
<%block name="jsextra">
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
function getCookie(name) {
|
||||
@@ -51,12 +77,16 @@
|
||||
submit_data,
|
||||
function(json) {
|
||||
if(json.success) {
|
||||
location.href = "${reverse('index')}";
|
||||
var next = /next=([^&]*)/g.exec(decodeURIComponent(window.location.search));
|
||||
if (next && next.length > 1) {
|
||||
location.href = next[1];
|
||||
}
|
||||
else location.href = "${reverse('homepage')}";
|
||||
} else if($('#login_error').length == 0) {
|
||||
$('#login_form').prepend('<div id="login_error">' + json.value + '</div>');
|
||||
$('#login_error').slideDown(150);
|
||||
$('#login_form').prepend('<div id="login_error" class="message message-status error">' + json.value + '</span></div>');
|
||||
$('#login_error').addClass('is-shown');
|
||||
} else {
|
||||
$('#login_error').stop().slideDown(150);
|
||||
$('#login_error').stop().addClass('is-shown');
|
||||
$('#login_error').html(json.value);
|
||||
}
|
||||
}
|
||||
@@ -64,5 +94,4 @@
|
||||
});
|
||||
})(this)
|
||||
</script>
|
||||
|
||||
</%block>
|
||||
</%block>
|
||||
@@ -1,17 +1,31 @@
|
||||
<%inherit file="base.html" />
|
||||
<%block name="title">Course Staff Manager</%block>
|
||||
<%block name="bodyclass">users</%block>
|
||||
<%block name="bodyclass">is-signedin course users settings team</%block>
|
||||
|
||||
|
||||
<%block name="content">
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast has-actions has-subtitle">
|
||||
<div class="title">
|
||||
<span class="title-sub">Course Settings</span>
|
||||
<h1 class="title-1">Course Team</h1>
|
||||
</div>
|
||||
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Page Actions</h3>
|
||||
<ul>
|
||||
%if allow_actions:
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button new-button new-user-button"><i class="ss-icon ss-symbolicons-standard icon icon-create">+</i> New User</a>
|
||||
</li>
|
||||
%endif
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<div class="page-actions">
|
||||
%if allow_actions:
|
||||
<a href="#" class="new-button new-user-button">
|
||||
<span class="plus-icon white"></span>New User
|
||||
</a>
|
||||
%endif
|
||||
</div>
|
||||
|
||||
<div class="details">
|
||||
<p>The following list of users have been designated as course staff. This means that these users will have permissions to modify course content. You may add additional course staff below, if you are the course instructor. Please note that they must have already registered and verified their account.</p>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
from datetime import datetime
|
||||
%>
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<%block name="title">CMS Courseware Overview</%block>
|
||||
<%block name="title">Course Outline</%block>
|
||||
<%block name="bodyclass">is-signedin course outline</%block>
|
||||
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
<%namespace name="units" file="widgets/units.html" />
|
||||
@@ -119,12 +120,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast has-actions has-subtitle">
|
||||
<div class="title">
|
||||
<span class="title-sub">Course Content</span>
|
||||
<h1 class="title-1">Course Outline</h1>
|
||||
</div>
|
||||
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Page Actions</h3>
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="toggle-button toggle-button-sections"><i class="ss-icon ss-symbolicons-block icon">up</i> <span class="label">Collapse All Sections</span></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button new-button new-courseware-section-button"><i class="ss-icon ss-symbolicons-standard icon icon-create">+</i> New Section</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="${lms_link}" rel="external" class="button view-button view-live-button"><i class="ss-icon ss-symbolicons-block icon icon-view"></i>View Live</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<div class="page-actions">
|
||||
<a href="#" class="new-button new-courseware-section-button"><span class="plus-icon white"></span> New Section</a>
|
||||
<a href="#" class="toggle-button toggle-button-sections"><i class="ss-icon ss-symbolicons-block">up</i> <span class="label">Collapse All Sections</span></a>
|
||||
</div>
|
||||
<article class="courseware-overview" data-course-id="${context_course.location.url()}">
|
||||
% for section in sections:
|
||||
<section class="courseware-section branch" data-id="${section.location}">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<%inherit file="base.html" />
|
||||
<%block name="bodyclass">settings</%block>
|
||||
<%block name="title">Settings</%block>
|
||||
<%block name="title">Schedule & Details</%block>
|
||||
<%block name="bodyclass">is-signedin course schedule settings</%block>
|
||||
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
<%!
|
||||
@@ -15,24 +15,24 @@ from contentstore import utils
|
||||
<script src="${static.url('js/vendor/date.js')}"></script>
|
||||
|
||||
<script type="text/javascript" src="${static.url('js/template_loader.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/models/course_relative.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/models/settings/course_details.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/models/settings/course_settings.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/models/settings/course_grading_policy.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/views/settings/main_settings_view.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/views/server_error.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/models/course_relative.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/views/validating_view.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/views/settings/main_settings_view.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/models/settings/course_details.js')}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
|
||||
var settingsModel = new CMS.Models.Settings.CourseSettings({
|
||||
courseLocation: new CMS.Models.Location('${context_course.location}',{parse:true}),
|
||||
details: new CMS.Models.Settings.CourseDetails(${course_details|n},{parse:true})
|
||||
$("form :input").focus(function() {
|
||||
$("label[for='" + this.id + "']").addClass("is-focused");
|
||||
}).blur(function() {
|
||||
$("label").removeClass("is-focused");
|
||||
});
|
||||
|
||||
var editor = new CMS.Views.Settings.Main({
|
||||
el: $('.main-wrapper'),
|
||||
model : settingsModel
|
||||
var editor = new CMS.Views.Settings.Details({
|
||||
el: $('.settings-details'),
|
||||
model: new CMS.Models.Settings.CourseDetails(${course_details|n},{parse:true})
|
||||
});
|
||||
|
||||
editor.render();
|
||||
@@ -42,689 +42,189 @@ from contentstore import utils
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
<!-- -->
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<h1>Settings</h1>
|
||||
<article class="settings-overview">
|
||||
<div class="sidebar">
|
||||
<nav class="settings-page-menu">
|
||||
<ul>
|
||||
<li><a href="#" class="is-shown" data-section="details">Course Details</a></li>
|
||||
<!-- <li><a href="#" data-section="faculty">Faculty</a></li> -->
|
||||
<li><a href="#" data-section="grading">Grading</a></li>
|
||||
<!-- <li><a href="#" data-section="problems">Problems</a></li> -->
|
||||
<!-- <li><a href="#" data-section="discussions">Discussions</a></li> -->
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="settings-page-section main-column">
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast has-subtitle">
|
||||
<div class="title">
|
||||
<span class="title-sub">Settings</span>
|
||||
<h1 class="title-1">Schedule & Details</h1>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<section class="settings-details is-shown">
|
||||
<h2 class="title">Course Details</h2>
|
||||
|
||||
<section class="settings-details-basic">
|
||||
<div class="wrapper-content wrapper">
|
||||
<section class="content">
|
||||
<article class="content-primary" role="main">
|
||||
<form id="settings_details" class="settings-details" method="post" action="">
|
||||
<section class="group-settings basic">
|
||||
<header>
|
||||
<h3>Basic Information</h3>
|
||||
<span class="detail">The nuts and bolts of your course</span>
|
||||
<h2 class="title-2">Basic Information</h2>
|
||||
<span class="tip">The nuts and bolts of your course</span>
|
||||
</header>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-name">Course Name:</label>
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<input type="text" class="long" id="course-name" value="[Course Name]" disabled="disabled">
|
||||
<span class="tip tip-stacked">This is used in <a href="${utils.get_lms_link_for_about_page(context_course.location)}">your course URL</a>, and cannot be changed</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="list-input">
|
||||
<li class="field text is-not-editable" id="field-course-organization">
|
||||
<label for="course-organization">Organization</label>
|
||||
<input type="text" class="long" id="course-organization" value="[Course Organization]" disabled="disabled" />
|
||||
</li>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-organization">Organization:</label>
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<input type="text" class="long" id="course-organization" value="[Course Organization]" disabled="disabled">
|
||||
<span class="tip tip-stacked">This is used in <a href="${utils.get_lms_link_for_about_page(context_course.location)}">your course URL</a>, and cannot be changed</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<li class="field text is-not-editable" id="field-course-number">
|
||||
<label for="course-number">Course Number</label>
|
||||
<input type="text" class="short" id="course-number" value="[Course No.]" disabled="disabled">
|
||||
</li>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-number">Course Number:</label>
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<input type="text" class="short" id="course-number" value="[Course No.]" disabled="disabled">
|
||||
<span class="tip tip-stacked">This is used in <a href="${utils.get_lms_link_for_about_page(context_course.location)}">your course URL</a>, and cannot be changed</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section><!-- .settings-details-basic -->
|
||||
<li class="field text is-not-editable" id="field-course-name">
|
||||
<label for="course-name">Course Name</label>
|
||||
<input type="text" class="long" id="course-name" value="[Course Name]" disabled="disabled" />
|
||||
</li>
|
||||
</ol>
|
||||
<span class="tip tip-stacked">These are used in <a rel="external" href="${utils.get_lms_link_for_about_page(course_location)}" />your course URL</a>, and cannot be changed</span>
|
||||
</section>
|
||||
|
||||
<hr class="divide" />
|
||||
|
||||
<section class="settings-details-schedule">
|
||||
<section class="group-settings schedule">
|
||||
<header>
|
||||
<h3>Course Schedule</h3>
|
||||
<span class="detail">Important steps and segments of your course</span>
|
||||
<h2 class="title-2">Course Schedule</h2>
|
||||
<span class="tip">Important steps and segments of your course</span>
|
||||
</header>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Course Dates:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input multi multi-inline" id="course-start">
|
||||
<div class="group">
|
||||
<label for="course-start-date">Start Date</label>
|
||||
<input type="text" class="start-date date start datepicker" id="course-start-date" placeholder="MM/DD/YYYY" autocomplete="off">
|
||||
<ol class="list-input">
|
||||
<li class="field-group field-group-course-start" id="course-start">
|
||||
<div class="field date" id="field-course-start-date">
|
||||
<label for="course-start-date">Course Start Date</label>
|
||||
<input type="text" class="start-date date start datepicker" id="course-start-date" placeholder="MM/DD/YYYY" autocomplete="off" />
|
||||
<span class="tip tip-stacked">First day the course begins</span>
|
||||
</div>
|
||||
|
||||
<div class="group">
|
||||
<label for="course-start-time">Start Time</label>
|
||||
<input type="text" class="time start timepicker" id="course-start-time" value="" placeholder="HH:MM" autocomplete="off">
|
||||
<div class="field time" id="field-course-start-time">
|
||||
<label for="course-start-time">Course Start Time</label>
|
||||
<input type="text" class="time start timepicker" id="course-start-time" value="" placeholder="HH:MM" autocomplete="off" />
|
||||
<span class="tip tip-stacked" id="timezone"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<div class="field field-additional">
|
||||
<div class="input multi multi-inline" id="course-end">
|
||||
<div class="group">
|
||||
<label for="course-end-date">End Date</label>
|
||||
<input type="text" class="end-date date end" id="course-end-date" placeholder="MM/DD/YYYY" autocomplete="off">
|
||||
<span class="tip tip-stacked">Last day the course is active</span>
|
||||
<li class="field-group field-group-course-end" id="course-end">
|
||||
<div class="field date" id="field-course-end-date">
|
||||
<label for="course-end-date">Course End Date</label>
|
||||
<input type="text" class="end-date date end" id="course-end-date" placeholder="MM/DD/YYYY" autocomplete="off" />
|
||||
<span class="tip tip-stacked">Last day your course is active</span>
|
||||
</div>
|
||||
|
||||
<div class="group">
|
||||
<label for="course-end-time">End Time</label>
|
||||
<input type="text" class="time end" id="course-end-time" value="" placeholder="HH:MM" autocomplete="off">
|
||||
<div class="field time" id="field-course-end-time">
|
||||
<label for="course-end-time">Course End Time</label>
|
||||
<input type="text" class="time end" id="course-end-time" value="" placeholder="HH:MM" autocomplete="off" />
|
||||
<span class="tip tip-stacked" id="timezone"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Enrollment Dates:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input multi multi-inline" id="enrollment-start">
|
||||
<div class="group">
|
||||
<label for="course-enrollment-start-date">Start Date</label>
|
||||
<input type="text" class="start-date date start" id="course-enrollment-start-date" placeholder="MM/DD/YYYY" autocomplete="off">
|
||||
<ol class="list-input">
|
||||
<li class="field-group field-group-enrollment-start" id="enrollment-start">
|
||||
<div class="field date" id="field-enrollment-start-date">
|
||||
<label for="course-enrollment-start-date">Enrollment Start Date</label>
|
||||
<input type="text" class="start-date date start" id="course-enrollment-start-date" placeholder="MM/DD/YYYY" autocomplete="off" />
|
||||
<span class="tip tip-stacked">First day students can enroll</span>
|
||||
</div>
|
||||
|
||||
<div class="group">
|
||||
<label for="course-enrollment-start-time">Start Time</label>
|
||||
<input type="text" class="time start" id="course-enrollment-start-time" value="" placeholder="HH:MM" autocomplete="off">
|
||||
<div class="field time" id="field-enrollment-start-time">
|
||||
<label for="course-enrollment-start-time">Enrollment Start Time</label>
|
||||
<input type="text" class="time start" id="course-enrollment-start-time" value="" placeholder="HH:MM" autocomplete="off" />
|
||||
<span class="tip tip-stacked" id="timezone"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<div class="field field-additional">
|
||||
<div class="input multi multi-inline" id="enrollment-end">
|
||||
<div class="group">
|
||||
<label for="course-enrollment-end-date">End Date</label>
|
||||
<input type="text" class="end-date date end" id="course-enrollment-end-date" placeholder="MM/DD/YYYY" autocomplete="off">
|
||||
<li class="field-group field-group-enrollment-end" id="enrollment-end">
|
||||
<div class="field date" id="field-enrollment-end-date">
|
||||
<label for="course-enrollment-end-date">Enrollment End Date</label>
|
||||
<input type="text" class="end-date date end" id="course-enrollment-end-date" placeholder="MM/DD/YYYY" autocomplete="off" />
|
||||
<span class="tip tip-stacked">Last day students can enroll</span>
|
||||
</div>
|
||||
|
||||
<div class="group">
|
||||
<label for="course-enrollment-end-time">End Time</label>
|
||||
<input type="text" class="time end" id="course-enrollment-end-time" value="" placeholder="HH:MM" autocomplete="off">
|
||||
<div class="field time" id="field-enrollment-end-time">
|
||||
<label for="course-enrollment-end-time">Enrollment End Time</label>
|
||||
<input type="text" class="time end" id="course-enrollment-end-time" value="" placeholder="HH:MM" autocomplete="off" />
|
||||
<span class="tip tip-stacked" id="timezone"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="row row-col2">
|
||||
<label for="course-syllabus">Course Syllabus</label>
|
||||
<div class="field">
|
||||
<div class="input input-existing">
|
||||
<div class="current current-course-syllabus">
|
||||
<span class="doc-filename"></span>
|
||||
|
||||
<a href="#" class="remove-item remove-course-syllabus remove-doc-data" id="course-syllabus"><span class="delete-icon"></span> Delete Syllabus</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input">
|
||||
<a href="#" class="new-item new-course-syllabus add-syllabus-data" id="course-syllabus">
|
||||
<span class="upload-icon"></span>Upload Syllabus
|
||||
</a>
|
||||
<span class="tip tip-inline">PDF formatting preferred</span>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</section><!-- .settings-details-schedule -->
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<hr class="divide" />
|
||||
|
||||
<section class="setting-details-marketing">
|
||||
<header>
|
||||
<h3>Introducing Your Course</h3>
|
||||
<span class="detail">Information for prospective students</span>
|
||||
</header>
|
||||
<section class="group-settings marketing">
|
||||
<header>
|
||||
<h2 class="title-2">Introducing Your Course</h2>
|
||||
<span class="tip">Information for prospective students</span>
|
||||
</header>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-overview">Course Overview:</label>
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<textarea class="long tall tinymce text-editor" id="course-overview"></textarea>
|
||||
<span class="tip tip-stacked">Introductions, prerequisites, FAQs that are used on <a href="${utils.get_lms_link_for_about_page(context_course.location)}">your course summary page</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="list-input">
|
||||
<li class="field text" id="field-course-overview">
|
||||
<label for="course-overview">Course Overview</label>
|
||||
<textarea class="tinymce text-editor" id="course-overview"></textarea>
|
||||
<span class="tip tip-stacked">Introductions, prerequisites, FAQs that are used on <a href="${utils.get_lms_link_for_about_page(course_location)}">your course summary page</a></span>
|
||||
</li>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-introduction-video">Introduction Video:</label>
|
||||
<div class="field">
|
||||
<li class="field video" id="field-course-introduction-video">
|
||||
<label for="course-overview">Course Introduction Video</label>
|
||||
<div class="input input-existing">
|
||||
<div class="current current-course-introduction-video">
|
||||
<iframe width="380" height="215" src="" frameborder="0" allowfullscreen></iframe>
|
||||
<iframe width="618" height="350" src="" frameborder="0" allowfullscreen></iframe>
|
||||
|
||||
<a href="#" class="remove-item remove-course-introduction-video remove-video-data"><span class="delete-icon"></span> Delete Video</a>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a href="#" class="remove-item remove-course-introduction-video remove-video-data"><span class="delete-icon"></span> Delete Current Video</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input">
|
||||
<input type="text" class="long new-course-introduction-video add-video-data" id="course-introduction-video" value="" placeholder="id" autocomplete="off">
|
||||
<span class="tip tip-stacked">Video restrictions go here</span>
|
||||
<input type="text" class="long new-course-introduction-video add-video-data" id="course-introduction-video" value="" placeholder="your YouTube video's ID" autocomplete="off" />
|
||||
<span class="tip tip-stacked">Enter your YouTube video's ID (along with any restriction parameters)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section><!-- .settings-details-marketing -->
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<hr class="divide" />
|
||||
|
||||
<section class="settings-details-requirements">
|
||||
<section class="group-settings requirements">
|
||||
<header>
|
||||
<h3>Requirements</h3>
|
||||
<span class="detail">Expectations of the students taking this course</span>
|
||||
<h2 class="title-2">Requirements</h2>
|
||||
<span class="tip">Expectations of the students taking this course</span>
|
||||
</header>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-effort">Hours of Effort per Week:</label>
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<input type="text" class="short time" id="course-effort" placeholder="HH:MM">
|
||||
<ol class="list-input">
|
||||
<li class="field text" id="field-course-effort">
|
||||
<label for="course-effort">Hours of Effort per Week</label>
|
||||
<input type="text" class="short time" id="course-effort" placeholder="HH:MM" />
|
||||
<span class="tip tip-inline">Time spent on all course work</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section><!-- .settings-details -->
|
||||
|
||||
<section class="settings-faculty">
|
||||
<h2 class="title">Faculty</h2>
|
||||
|
||||
<section class="settings-faculty-members">
|
||||
<header>
|
||||
<h3>Faculty Members</h3>
|
||||
<span class="detail">Individuals instructing and help with this course</span>
|
||||
</header>
|
||||
|
||||
<div class="row">
|
||||
<div class="field enum">
|
||||
<ul class="input-list course-faculty-list">
|
||||
<li class="input input-existing multi course-faculty-list-item">
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-1-firstname">Faculty First Name:</label>
|
||||
<div class="field">
|
||||
<input type="text" class="long" id="course-faculty-1-firstname">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-1-lastname">Faculty Last Name:</label>
|
||||
<div class="field">
|
||||
<input type="text" class="long" id="course-faculty-1-lastname">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-1-photo">Faculty Photo</label>
|
||||
<div class="field">
|
||||
<div class="input input-existing">
|
||||
<div class="current current-faculty-1-photo">
|
||||
<a href="#" class="remove-item remove-faculty-photo remove-video-data"><span class="delete-icon"></span> Delete Faculty Photo</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="course-faculty-1-bio">Faculty Bio:</label>
|
||||
<div class="field">
|
||||
<textarea class="long tall edit-box tinymce" id="course-faculty-1-bio"></textarea>
|
||||
<span class="tip tip-stacked">A brief description of your education, experience, and expertise</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="#" class="remove-item remove-faculty-data"><span class="delete-icon"></span> Delete Faculty Member</a>
|
||||
</li>
|
||||
|
||||
<li class="input multi course-faculty-list-item">
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-2-firstname">Faculty First Name:</label>
|
||||
<div class="field">
|
||||
<input type="text" class="long" id="course-faculty-2-firstname">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-2-lastname">Faculty Last Name:</label>
|
||||
<div class="field">
|
||||
<input type="text" class="long" id="course-faculty-2-lastname">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-2-photo">Faculty Photo</label>
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<a href="#" class="new-item new-faculty-photo add-faculty-photo-data" id="course-faculty-2-photo">
|
||||
<span class="upload-icon"></span>Upload Faculty Photo
|
||||
</a>
|
||||
<span class="tip tip-inline">Max size: 30KB</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="course-faculty-2-bio">Faculty Bio:</label>
|
||||
<div class="field">
|
||||
<div clas="input">
|
||||
<textarea class="long tall edit-box tinymce" id="course-faculty-2-bio"></textarea>
|
||||
<span class="tip tip-stacked">A brief description of your education, experience, and expertise</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<a href="#" class="new-item new-course-faculty-item add-faculty-data">
|
||||
<span class="plus-icon"></span>New Faculty Member
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</section><!-- .settings-staff -->
|
||||
|
||||
<section class="settings-grading">
|
||||
<h2 class="title">Grading</h2>
|
||||
|
||||
<section class="settings-grading-range">
|
||||
<header>
|
||||
<h3>Overall Grade Range</h3>
|
||||
<span class="detail">Course grade ranges and their values</span>
|
||||
</header>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="grade-controls course-grading-range well">
|
||||
<a href="#" class="new-grade-button"><span class="plus-icon"></span></a>
|
||||
<div class="grade-slider">
|
||||
<div class="grade-bar">
|
||||
<ol class="increments">
|
||||
<li class="increment-0">0</li>
|
||||
<li class="increment-10">10</li>
|
||||
<li class="increment-20">20</li>
|
||||
<li class="increment-30">30</li>
|
||||
<li class="increment-40">40</li>
|
||||
<li class="increment-50">50</li>
|
||||
<li class="increment-60">60</li>
|
||||
<li class="increment-70">70</li>
|
||||
<li class="increment-80">80</li>
|
||||
<li class="increment-90">90</li>
|
||||
<li class="increment-100">100</li>
|
||||
</ol>
|
||||
<ol class="grades">
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<section class="settings-grading-general">
|
||||
<header>
|
||||
<h3>General Grading</h3>
|
||||
<span class="detail">Deadlines and Requirements</span>
|
||||
</header>
|
||||
<aside class="content-supplementary" role="complimentary">
|
||||
<div class="bit">
|
||||
<h3 class="title-3">How will these settings be used</h3>
|
||||
<p>Your course's schedule settings determine when students can enroll in and begin a course as well as when the course.</p>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-grading-graceperiod">Grace Period on Deadline:</label>
|
||||
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<input type="text" class="short time" id="course-grading-graceperiod" value="0:00" placeholder="e.g. 10 minutes">
|
||||
<span class="tip tip-inline">leeway on due dates</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="setting-grading-assignment-types">
|
||||
<header>
|
||||
<h3>Assignment Types</h3>
|
||||
</header>
|
||||
|
||||
<div class="row">
|
||||
<div class="field enum">
|
||||
<ul class="input-list course-grading-assignment-list">
|
||||
</ul>
|
||||
|
||||
<a href="#" class="new-button new-course-grading-item add-grading-data">
|
||||
<span class="plus-icon white"></span>New Assignment Type
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section><!-- .settings-grading -->
|
||||
|
||||
<section class="settings-problems">
|
||||
<h2 class="title">Problems</h2>
|
||||
|
||||
<section class="settings-problems-general">
|
||||
<header>
|
||||
<h3>General Settings</h3>
|
||||
<span class="detail">Course-wide settings for all problems</span>
|
||||
</header>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Problem Randomization:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-problems-general-randomization" id="course-problems-general-randomization-always" value="Always">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-general-randomization-always">Always</label>
|
||||
<span class="tip tip-stacked"><strong>randomize all</strong> problems</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-general-randomization" id="course-problems-general-randomization-never" value="Never">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-general-randomization-never">Never</label>
|
||||
<span class="tip tip-stacked"><strong>do not randomize</strong> problems</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-general-randomization" id="course-problems-general-randomization-perstudent" value="Per Student">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-general-randomization-perstudent">Per Student</label>
|
||||
<span class="tip tip-stacked">randomize problems <strong>per student</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Show Answers:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-problems-general-showanswer" id="course-problems-general-showanswer-always" value="Always">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-general-showanswer-always">Always</label>
|
||||
<span class="tip tip-stacked">Answers will be shown after the number of attempts has been met</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-general-showanswer" id="course-problems-general-showanswer-never" value="Never">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-general-showanswer-never">Never</label>
|
||||
<span class="tip tip-stacked">Answers will never be shown, regardless of attempts</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="pcourse-roblems-general-attempts">Number of Attempts <br /> Allowed on Problems: </label>
|
||||
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<input type="text" class="short" id="course-problems-general-attempts" placeholder="0 or higher" value="0">
|
||||
<span class="tip tip-stacked">Students will this have this number of chances to answer a problem. To set infinite atttempts, use "0"</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section><!-- .settings-problems-general -->
|
||||
|
||||
<section class="settings-problems-assignment-1 settings-extras">
|
||||
<header>
|
||||
<h3>[Assignment Type Name]</h3>
|
||||
</header>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Problem Randomization:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-problems-assignment-1-randomization" id="course-problems-assignment-1-randomization-always" value="Always">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-assignment-1-randomization-always">Always</label>
|
||||
<span class="tip tip-stacked"><strong>randomize all</strong> problems</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-assignment-1-randomization" id="course-problems-assignment-1-randomization-never" value="Never">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-assignment-1-randomization-never">Never</label>
|
||||
<span class="tip tip-stacked"><strong>do not randomize</strong> problems</span>
|
||||
</div>
|
||||
<p>Additionally, details provided on this page are also used in edX's catalog of courses, which new and returning students use to choose new courses to study.</p>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-assignment-1-randomization" id="course-problems-assignment-1-randomization-perstudent" value="Per Student">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-assignment-1-randomization-perstudent">Per Student</label>
|
||||
<span class="tip tip-stacked">randomize problems <strong>per student</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Show Answers:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-problems-assignment-1-showanswer" id="course-problems-assignment-1-showanswer-always" value="Always">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-assignment-1-showanswer-always">Always</label>
|
||||
<span class="tip tip-stacked">Answers will be shown after the number of attempts has been met</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-assignment-1-showanswer" id="course-problems-assignment-1-showanswer-never" value="Never">
|
||||
|
||||
<div class="copy">
|
||||
<label for="pcourse-roblems-assignment-1-showanswer-never">Never</label>
|
||||
<span class="tip tip-stacked">Answers will never be shown, regardless of attempts</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-problems-assignment-1-attempts">Number of Attempts <br /> Allowed on Problems: </label>
|
||||
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<input type="text" class="short" id="course-problems-assignment-1-attempts" placeholder="0 or higher" value="0">
|
||||
<span class="tip tip-stacked">Students will this have this number of chances to answer a problem. To set infinite atttempts, use "0"</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section><!-- .settings-problems-assignment-1 -->
|
||||
</section><!-- .settings-problems -->
|
||||
|
||||
<section class="settings-discussions">
|
||||
<h2 class="title">Discussions</h2>
|
||||
|
||||
<section class="settings-discussions-general">
|
||||
<header>
|
||||
<h3>General Settings</h3>
|
||||
<span class="detail">Course-wide settings for online discussion</span>
|
||||
</header>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Anonymous Discussions:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-discussions-anonymous" id="course-discussions-anonymous-allow" value="Allow">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-discussions-anonymous-allow">Allow</label>
|
||||
<span class="tip tip-stacked">Students and faculty <strong>will be able to post anonymously</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-discussions-anonymous" id="course-discussions-anonymous-dontallow" value="Do Not Allow">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-discussions-anonymous-dontallow">Do not allow</label>
|
||||
<span class="tip tip-stacked"><strong>Posting anonymously is not allowed</strong>. Any previous anonymous posts <strong>will be reverted to non-anonymous</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Anonymous Discussions:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-discussions-anonymous" id="course-discussions-anonymous-allow" value="Allow">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-discussions-anonymous-allow">Allow</label>
|
||||
<span class="tip tip-stacked">Students and faculty <strong>will be able to post anonymously</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input disabled="disabled" type="radio" name="course-discussions-anonymous" id="course-discussions-anonymous-dontallow" value="Do Not Allow">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-discussions-anonymous-dontallow">Do not allow</label>
|
||||
<span class="tip tip-stacked">This option is disabled since there are previous discussions that are anonymous.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Discussion Categories</h4>
|
||||
|
||||
<div class="field enum">
|
||||
<ul class="input-list course-discussions-categories-list sortable">
|
||||
<li class="input input-existing input-default course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-1-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-1-name" placeholder="" value="General" disabled="disabled">
|
||||
</div>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing input-default course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-2-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-2-name" placeholder="" value="Feedback" disabled="disabled">
|
||||
</div>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing input-default course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-3-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-3-name" placeholder="" value="Troubleshooting" disabled="disabled">
|
||||
</div>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-4-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-4-name" placeholder="" value="Study Groups">
|
||||
|
||||
<a href="#" class="remove-item remove-course-discussions-categories-data"><span class="delete-icon"></span> Delete Category</a>
|
||||
</div>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-5-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-5-name" placeholder="" value="Lectures">
|
||||
</div>
|
||||
|
||||
<a href="#" class="remove-item remove-course-discussions-categories-data"><span class="delete-icon"></span> Delete Category</a>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-6-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-6-name" placeholder="" value="Labs">
|
||||
</div>
|
||||
|
||||
<a href="#" class="remove-item remove-course-discussions-categories-data"><span class="delete-icon"></span> Delete Category</a>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-6-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-6-name" placeholder="" value="">
|
||||
</div>
|
||||
|
||||
<a href="#" class="remove-item remove-course-discussions-categories-data"><span class="delete-icon"></span> Delete Category</a>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<a href="#" class="new-item new-course-discussions-categories-item add-categories-data">
|
||||
<span class="plus-icon"></span>New Discussion Category
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section><!-- .settings-discussions-general -->
|
||||
</section><!-- .settings-discussions -->
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<footer></footer>
|
||||
</%block>
|
||||
<div class="bit">
|
||||
% if context_course:
|
||||
<% ctx_loc = context_course.location %>
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<h3 class="title-3">Other Course Settings</h3>
|
||||
<nav class="nav-related">
|
||||
<ul>
|
||||
<li class="nav-item"><a href="${reverse('contentstore.views.course_config_graders_page', kwargs={'org' : ctx_loc.org, 'course' : ctx_loc.course, 'name': ctx_loc.name})}">Grading</a></li>
|
||||
<li class="nav-item"><a href="${reverse('manage_users', kwargs=dict(location=ctx_loc))}">Course Team</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
% endif
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
</div>
|
||||
</%block>
|
||||
430
cms/templates/settings_discussions_faculty.html
Normal file
@@ -0,0 +1,430 @@
|
||||
<!-- NOTE not used currently but retained b/c it's yet-to-be-wired functionality -->
|
||||
<%inherit file="base.html" />
|
||||
<%block name="title">Schedule and details</%block>
|
||||
<%block name="bodyclass">is-signedin course settings</%block>
|
||||
|
||||
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
<%!
|
||||
from contentstore import utils
|
||||
%>
|
||||
|
||||
|
||||
<%block name="jsextra">
|
||||
<script type="text/javascript" src="${static.url('js/template_loader.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/views/server_error.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/models/course_relative.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/views/validating_view.js')}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
});
|
||||
|
||||
</script>
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
<!-- -->
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<h1>Settings</h1>
|
||||
<article class="settings-overview">
|
||||
<div class="settings-page-section main-column">
|
||||
|
||||
<section class="settings-faculty">
|
||||
<h2 class="title">Faculty</h2>
|
||||
|
||||
<section class="settings-faculty-members">
|
||||
<header>
|
||||
<h3>Faculty Members</h3>
|
||||
<span class="detail">Individuals instructing and help with this course</span>
|
||||
</header>
|
||||
|
||||
<div class="row">
|
||||
<div class="field enum">
|
||||
<ul class="input-list course-faculty-list">
|
||||
<li class="input input-existing multi course-faculty-list-item">
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-1-firstname">Faculty First Name:</label>
|
||||
<div class="field">
|
||||
<input type="text" class="long" id="course-faculty-1-firstname">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-1-lastname">Faculty Last Name:</label>
|
||||
<div class="field">
|
||||
<input type="text" class="long" id="course-faculty-1-lastname">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-1-photo">Faculty Photo</label>
|
||||
<div class="field">
|
||||
<div class="input input-existing">
|
||||
<div class="current current-faculty-1-photo">
|
||||
<a href="#" class="remove-item remove-faculty-photo remove-video-data"><span class="delete-icon"></span> Delete Faculty Photo</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="course-faculty-1-bio">Faculty Bio:</label>
|
||||
<div class="field">
|
||||
<textarea class="long tall edit-box tinymce" id="course-faculty-1-bio"></textarea>
|
||||
<span class="tip tip-stacked">A brief description of your education, experience, and expertise</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="#" class="remove-item remove-faculty-data"><span class="delete-icon"></span> Delete Faculty Member</a>
|
||||
</li>
|
||||
|
||||
<li class="input multi course-faculty-list-item">
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-2-firstname">Faculty First Name:</label>
|
||||
<div class="field">
|
||||
<input type="text" class="long" id="course-faculty-2-firstname">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-2-lastname">Faculty Last Name:</label>
|
||||
<div class="field">
|
||||
<input type="text" class="long" id="course-faculty-2-lastname">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-faculty-2-photo">Faculty Photo</label>
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<a href="#" class="new-item new-faculty-photo add-faculty-photo-data" id="course-faculty-2-photo">
|
||||
<span class="upload-icon"></span>Upload Faculty Photo
|
||||
</a>
|
||||
<span class="tip tip-inline">Max size: 30KB</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="course-faculty-2-bio">Faculty Bio:</label>
|
||||
<div class="field">
|
||||
<div clas="input">
|
||||
<textarea class="long tall edit-box tinymce" id="course-faculty-2-bio"></textarea>
|
||||
<span class="tip tip-stacked">A brief description of your education, experience, and expertise</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<a href="#" class="new-item new-course-faculty-item add-faculty-data">
|
||||
<span class="plus-icon"></span>New Faculty Member
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</section><!-- .settings-staff -->
|
||||
|
||||
<section class="settings-problems">
|
||||
<h2 class="title">Problems</h2>
|
||||
|
||||
<section class="settings-problems-general">
|
||||
<header>
|
||||
<h3>General Settings</h3>
|
||||
<span class="detail">Course-wide settings for all problems</span>
|
||||
</header>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Problem Randomization:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-problems-general-randomization" id="course-problems-general-randomization-always" value="Always">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-general-randomization-always">Always</label>
|
||||
<span class="tip tip-stacked"><strong>randomize all</strong> problems</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-general-randomization" id="course-problems-general-randomization-never" value="Never">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-general-randomization-never">Never</label>
|
||||
<span class="tip tip-stacked"><strong>do not randomize</strong> problems</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-general-randomization" id="course-problems-general-randomization-perstudent" value="Per Student">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-general-randomization-perstudent">Per Student</label>
|
||||
<span class="tip tip-stacked">randomize problems <strong>per student</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Show Answers:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-problems-general-showanswer" id="course-problems-general-showanswer-always" value="Always">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-general-showanswer-always">Always</label>
|
||||
<span class="tip tip-stacked">Answers will be shown after the number of attempts has been met</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-general-showanswer" id="course-problems-general-showanswer-never" value="Never">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-general-showanswer-never">Never</label>
|
||||
<span class="tip tip-stacked">Answers will never be shown, regardless of attempts</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="pcourse-roblems-general-attempts">Number of Attempts <br /> Allowed on Problems: </label>
|
||||
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<input type="text" class="short" id="course-problems-general-attempts" placeholder="0 or higher" value="0">
|
||||
<span class="tip tip-stacked">Students will this have this number of chances to answer a problem. To set infinite atttempts, use "0"</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section><!-- .settings-problems-general -->
|
||||
|
||||
<section class="settings-problems-assignment-1 settings-extras">
|
||||
<header>
|
||||
<h3>[Assignment Type Name]</h3>
|
||||
</header>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Problem Randomization:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-problems-assignment-1-randomization" id="course-problems-assignment-1-randomization-always" value="Always">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-assignment-1-randomization-always">Always</label>
|
||||
<span class="tip tip-stacked"><strong>randomize all</strong> problems</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-assignment-1-randomization" id="course-problems-assignment-1-randomization-never" value="Never">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-assignment-1-randomization-never">Never</label>
|
||||
<span class="tip tip-stacked"><strong>do not randomize</strong> problems</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-assignment-1-randomization" id="course-problems-assignment-1-randomization-perstudent" value="Per Student">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-assignment-1-randomization-perstudent">Per Student</label>
|
||||
<span class="tip tip-stacked">randomize problems <strong>per student</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Show Answers:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-problems-assignment-1-showanswer" id="course-problems-assignment-1-showanswer-always" value="Always">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-problems-assignment-1-showanswer-always">Always</label>
|
||||
<span class="tip tip-stacked">Answers will be shown after the number of attempts has been met</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-problems-assignment-1-showanswer" id="course-problems-assignment-1-showanswer-never" value="Never">
|
||||
|
||||
<div class="copy">
|
||||
<label for="pcourse-roblems-assignment-1-showanswer-never">Never</label>
|
||||
<span class="tip tip-stacked">Answers will never be shown, regardless of attempts</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<label for="course-problems-assignment-1-attempts">Number of Attempts <br /> Allowed on Problems: </label>
|
||||
|
||||
<div class="field">
|
||||
<div class="input">
|
||||
<input type="text" class="short" id="course-problems-assignment-1-attempts" placeholder="0 or higher" value="0">
|
||||
<span class="tip tip-stacked">Students will this have this number of chances to answer a problem. To set infinite atttempts, use "0"</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section><!-- .settings-problems-assignment-1 -->
|
||||
</section><!-- .settings-problems -->
|
||||
|
||||
<section class="settings-discussions">
|
||||
<h2 class="title">Discussions</h2>
|
||||
|
||||
<section class="settings-discussions-general">
|
||||
<header>
|
||||
<h3>General Settings</h3>
|
||||
<span class="detail">Course-wide settings for online discussion</span>
|
||||
</header>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Anonymous Discussions:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input type="radio" name="course-discussions-anonymous" id="course-discussions-anonymous-allow" value="Allow">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-discussions-anonymous-allow">Allow</label>
|
||||
<span class="tip tip-stacked">Students and faculty <strong>will be able to post anonymously</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-discussions-anonymous" id="course-discussions-anonymous-dontallow" value="Do Not Allow">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-discussions-anonymous-dontallow">Do not allow</label>
|
||||
<span class="tip tip-stacked"><strong>Posting anonymously is not allowed</strong>. Any previous anonymous posts <strong>will be reverted to non-anonymous</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Anonymous Discussions:</h4>
|
||||
|
||||
<div class="field">
|
||||
<div class="input input-radio">
|
||||
<input checked="checked" type="radio" name="course-discussions-anonymous" id="course-discussions-anonymous-allow" value="Allow">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-discussions-anonymous-allow">Allow</label>
|
||||
<span class="tip tip-stacked">Students and faculty <strong>will be able to post anonymously</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input input-radio">
|
||||
<input disabled="disabled" type="radio" name="course-discussions-anonymous" id="course-discussions-anonymous-dontallow" value="Do Not Allow">
|
||||
|
||||
<div class="copy">
|
||||
<label for="course-discussions-anonymous-dontallow">Do not allow</label>
|
||||
<span class="tip tip-stacked">This option is disabled since there are previous discussions that are anonymous.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-col2">
|
||||
<h4 class="label">Discussion Categories</h4>
|
||||
|
||||
<div class="field enum">
|
||||
<ul class="input-list course-discussions-categories-list sortable">
|
||||
<li class="input input-existing input-default course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-1-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-1-name" placeholder="" value="General" disabled="disabled">
|
||||
</div>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing input-default course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-2-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-2-name" placeholder="" value="Feedback" disabled="disabled">
|
||||
</div>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing input-default course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-3-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-3-name" placeholder="" value="Troubleshooting" disabled="disabled">
|
||||
</div>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-4-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-4-name" placeholder="" value="Study Groups">
|
||||
|
||||
<a href="#" class="remove-item remove-course-discussions-categories-data"><span class="delete-icon"></span> Delete Category</a>
|
||||
</div>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-5-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-5-name" placeholder="" value="Lectures">
|
||||
</div>
|
||||
|
||||
<a href="#" class="remove-item remove-course-discussions-categories-data"><span class="delete-icon"></span> Delete Category</a>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-6-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-6-name" placeholder="" value="Labs">
|
||||
</div>
|
||||
|
||||
<a href="#" class="remove-item remove-course-discussions-categories-data"><span class="delete-icon"></span> Delete Category</a>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
|
||||
<li class="input input-existing course-discussions-categories-list-item sortable-item">
|
||||
<div class="group">
|
||||
<label for="course-discussions-categories-6-name">Category Name: </label>
|
||||
<input type="text" class="course-discussions-categories-name" id="course-discussions-categories-6-name" placeholder="" value="">
|
||||
</div>
|
||||
|
||||
<a href="#" class="remove-item remove-course-discussions-categories-data"><span class="delete-icon"></span> Delete Category</a>
|
||||
|
||||
<a href="#" class="drag-handle"></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<a href="#" class="new-item new-course-discussions-categories-item add-categories-data">
|
||||
<span class="plus-icon"></span>New Discussion Category
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section><!-- .settings-discussions-general -->
|
||||
</section><!-- .settings-discussions -->
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<footer></footer>
|
||||
</%block>
|
||||
151
cms/templates/settings_graders.html
Normal file
@@ -0,0 +1,151 @@
|
||||
<%inherit file="base.html" />
|
||||
<%block name="title">Grading</%block>
|
||||
<%block name="bodyclass">is-signedin course grading settings</%block>
|
||||
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
<%!
|
||||
from contentstore import utils
|
||||
%>
|
||||
|
||||
<%block name="jsextra">
|
||||
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" />
|
||||
<script src="${static.url('js/vendor/timepicker/jquery.timepicker.js')}"></script>
|
||||
|
||||
<script type="text/javascript" src="${static.url('js/template_loader.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/views/server_error.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/models/course_relative.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/views/validating_view.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/models/settings/course_grading_policy.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/views/settings/settings_grading_view.js')}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
|
||||
$("form :input").focus(function() {
|
||||
$("label[for='" + this.id + "']").addClass("is-focused");
|
||||
}).blur(function() {
|
||||
$("label").removeClass("is-focused");
|
||||
});
|
||||
|
||||
var editor = new CMS.Views.Settings.Grading({
|
||||
el: $('.settings-grading'),
|
||||
model : new CMS.Models.Settings.CourseGradingPolicy(${course_details|n},{parse:true})
|
||||
});
|
||||
|
||||
editor.render();
|
||||
});
|
||||
|
||||
</script>
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast has-subtitle">
|
||||
<div class="title">
|
||||
<span class="title-sub">Settings</span>
|
||||
<h1 class="title-1">Grading</h1>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-content wrapper">
|
||||
<section class="content">
|
||||
<article class="content-primary" role="main">
|
||||
<form id="settings_details" class="settings-grading" method="post" action="">
|
||||
<section class="group-settings grade-range">
|
||||
<header>
|
||||
<h2 class="title-2">Overall Grade Range</h2>
|
||||
<span class="tip">Your overall grading scale for student final grades</span>
|
||||
</header>
|
||||
|
||||
<ol class="list-input">
|
||||
<li class="field" id="field-course-grading-range">
|
||||
<div class="grade-controls course-grading-range well">
|
||||
<a href="#" class="new-grade-button"><span class="plus-icon"></span></a>
|
||||
<div class="grade-slider">
|
||||
<div class="grade-bar">
|
||||
<ol class="increments">
|
||||
<li class="increment-0">0</li>
|
||||
<li class="increment-10">10</li>
|
||||
<li class="increment-20">20</li>
|
||||
<li class="increment-30">30</li>
|
||||
<li class="increment-40">40</li>
|
||||
<li class="increment-50">50</li>
|
||||
<li class="increment-60">60</li>
|
||||
<li class="increment-70">70</li>
|
||||
<li class="increment-80">80</li>
|
||||
<li class="increment-90">90</li>
|
||||
<li class="increment-100">100</li>
|
||||
</ol>
|
||||
<ol class="grades">
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<hr class="divide" />
|
||||
|
||||
<section class="group-settings grade-rules">
|
||||
<header>
|
||||
<h2 class="title-2">Grading Rules & Policies</h2>
|
||||
<span class="tip">Deadlines, requirements, and logistics around grading student work</span>
|
||||
</header>
|
||||
|
||||
<ol class="list-input">
|
||||
<li class="field text" id="field-course-grading-graceperiod">
|
||||
<label for="course-grading-graceperiod">Grace Period on Deadline:</label>
|
||||
<input type="text" class="short time" id="course-grading-graceperiod" value="0:00" placeholder="e.g. 10 minutes">
|
||||
<span class="tip tip-inline">Leeway on due dates</span>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<hr class="divide" />
|
||||
|
||||
<section class="group-settings assignment-types">
|
||||
<header>
|
||||
<h2 class="title-2">Assignment Types</h2>
|
||||
<span class="tip">Categories and labels for any exercises that are gradable</span>
|
||||
</header>
|
||||
|
||||
<ol class="list-input course-grading-assignment-list enum">
|
||||
|
||||
</ol>
|
||||
|
||||
<div class="actions">
|
||||
<a href="#" class="new-button new-course-grading-item add-grading-data">
|
||||
<span class="plus-icon white"></span>New Assignment Type
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<aside class="content-supplementary" role="complimentary">
|
||||
<div class="bit">
|
||||
<h3 class="title-3">How will these settings be used</h3>
|
||||
<p>Your grading settings will be used to calculate students grades and performance.</p>
|
||||
|
||||
<p>Overall grade range will be used in students' final grades, which are calculated by the weighting you determine for each custom assignment type.</p>
|
||||
</div>
|
||||
|
||||
<div class="bit">
|
||||
% if context_course:
|
||||
<% ctx_loc = context_course.location %>
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<h3 class="title-3">Other Course Settings</h3>
|
||||
<nav class="nav-related">
|
||||
<ul>
|
||||
<li class="nav-item"><a href="${reverse('contentstore.views.get_course_settings', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}">Details & Schedule</a></li>
|
||||
<li class="nav-item"><a href="${reverse('manage_users', kwargs=dict(location=ctx_loc))}">Course Team</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
% endif
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
</div>
|
||||
</%block>
|
||||
@@ -1,94 +1,141 @@
|
||||
<%inherit file="base.html" />
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
|
||||
<%block name="title">Sign up</%block>
|
||||
<%block name="bodyclass">no-header</%block>
|
||||
<%block name="title">Sign Up</%block>
|
||||
<%block name="bodyclass">not-signedin signup</%block>
|
||||
|
||||
<%block name="content">
|
||||
|
||||
<div class="edx-studio-logo-large"></div>
|
||||
<div class="wrapper-content wrapper">
|
||||
<section class="content">
|
||||
<header>
|
||||
<h1 class="title title-1">Sign Up for edX Studio</h1>
|
||||
<a href="${reverse('login')}" class="action action-signin">Already have a Studio Account? Sign in</a>
|
||||
</header>
|
||||
|
||||
<article class="sign-up-box">
|
||||
<header>
|
||||
<h1>Register for edX studio</h1>
|
||||
</header>
|
||||
<form id="register_form" method="post">
|
||||
<div id="register_error" name="register_error"></div>
|
||||
<div class="row">
|
||||
<label>Email</label>
|
||||
<input name="email" type="email">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Password</label>
|
||||
<input name="password" type="password">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Public Username</label>
|
||||
<input name="username" type="text">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Full Name</label>
|
||||
<input name="name" type="text">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="split">
|
||||
<label>Your Location</label>
|
||||
<input name="location" type="text">
|
||||
<p class="introduction">Ready to start creating online courses? Sign up below and start creating your first edX course today.</p>
|
||||
|
||||
<article class="content-primary" role="main">
|
||||
<form id="register_form" method="post" action="register_post">
|
||||
<div id="register_error" name="register_error" class="message message-status message-status error">
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<legend class="sr">Required Information to Sign Up for edX Studio</legend>
|
||||
|
||||
<ol class="list-input">
|
||||
<li class="field text required" id="field-email">
|
||||
<label for="email">Email Address</label>
|
||||
<input id="email" type="email" name="email" placeholder="e.g. jane.doe@gmail.com" />
|
||||
</li>
|
||||
|
||||
<li class="field text required" id="field-password">
|
||||
<label for="password">Password</label>
|
||||
<input id="password" type="password" name="password" />
|
||||
</li>
|
||||
|
||||
<li class="field text required" id="field-username">
|
||||
<label for="username">Public Username</label>
|
||||
<input id="username" type="text" name="username" placeholder="e.g. janedoe" />
|
||||
<span class="tip tip-stacked">This will be used in public discussions with your courses and in our edX101 support forums</span>
|
||||
</li>
|
||||
|
||||
<li class="field text required" id="field-name">
|
||||
<label for="name">Full Name</label>
|
||||
<input id="name" type="text" name="name" placeholder="e.g. Jane Doe" />
|
||||
</li>
|
||||
|
||||
<li class="field-group">
|
||||
<div class="field text" id="field-location">
|
||||
<label for="location">Your Location</label>
|
||||
<input class="short" id="location" type="text" name="location" />
|
||||
</div>
|
||||
|
||||
<div class="field text" id="field-language">
|
||||
<label for="language">Preferred Language</label>
|
||||
<input class="short" id="language" type="text" name="language" />
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="field checkbox required" id="field-tos">
|
||||
<input id="tos" name="terms_of_service" type="checkbox" value="true" />
|
||||
<label for="tos">I agree to the Terms of Service</label>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" id="submit" name="submit" class="action action-primary">Create My Account & Start Authoring Courses</button>
|
||||
</div>
|
||||
|
||||
<!-- no honor code for CMS, but need it because we're using the lms student object -->
|
||||
<input name="honor_code" type="checkbox" value="true" checked="true" hidden="true">
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<aside class="content-supplementary" role="complimentary">
|
||||
<h2 class="sr">Common Studio Questions</h2>
|
||||
|
||||
<div class="bit">
|
||||
<h3 class="title-3">Who is Studio for?</h3>
|
||||
<p>Studio is for anyone that wants to create online courses that leverage the global edX platform. Our users are often faculty members, teaching assistants and course staff, and members of instructional technology groups.</p>
|
||||
</div>
|
||||
<div class="split">
|
||||
<label>Preferred Language</label>
|
||||
<input name="language" type="text">
|
||||
|
||||
<div class="bit">
|
||||
<h3 class="title-3">How technically savvy do I need to be to create courses in Studio?</h3>
|
||||
<p>Studio is designed to be easy to use by almost anyone familiar with common web-based authoring environments (Wordpress, Moodle, etc.). No programming knowledge is required, but for some of the more advanced features, a technical background would be helpful. As always, we are here to help, so don't hesitate to dive right in.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="terms-of-service">
|
||||
<input name="terms_of_service" type="checkbox" value="true">
|
||||
I agree to the
|
||||
<a href="#">Terms of Service</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- no honor code for CMS, but need it because we're using the lms student object -->
|
||||
<input name="honor_code" type="checkbox" value="true" checked="true" hidden="true">
|
||||
|
||||
<div class="row form-actions submit">
|
||||
<input name="submit" type="submit" value="Create My Account" class="create-account-button">
|
||||
<p class="enrolled">Already enrolled? <a href="/">Log In.</a></p>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
<div class="bit">
|
||||
<h3 class="title-3">I've never authored a course online before. Is there help?</h3>
|
||||
<p>Absolutely. We have created an online course, edX101, that describes some best practices: from filming video, creating exercises, to the basics of running an online course. Additionally, we're always here to help, just drop us a note.</p>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
</div>
|
||||
</%block>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
function getCookie(name) {
|
||||
return $.cookie(name);
|
||||
}
|
||||
<%block name="jsextra">
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
|
||||
function postJSON(url, data, callback) {
|
||||
$.ajax({type:'POST',
|
||||
url: url,
|
||||
dataType: 'json',
|
||||
data: data,
|
||||
success: callback,
|
||||
headers : {'X-CSRFToken':getCookie('csrftoken')}
|
||||
$("form :input").focus(function() {
|
||||
$("label[for='" + this.id + "']").addClass("is-focused");
|
||||
}).blur(function() {
|
||||
$("label").removeClass("is-focused");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$('form#register_form').submit(function(e) {
|
||||
e.preventDefault();
|
||||
var submit_data = $('#register_form').serialize();
|
||||
function getCookie(name) {
|
||||
return $.cookie(name);
|
||||
}
|
||||
|
||||
postJSON('/create_account',
|
||||
submit_data,
|
||||
function(json) {
|
||||
if(json.success) {
|
||||
location.href = "${reverse('index')}";
|
||||
} else {
|
||||
$('#register_error').html(json.value).stop().slideDown(150);
|
||||
// form validation
|
||||
function postJSON(url, data, callback) {
|
||||
$.ajax({type:'POST',
|
||||
url: url,
|
||||
dataType: 'json',
|
||||
data: data,
|
||||
success: callback,
|
||||
headers : {'X-CSRFToken':getCookie('csrftoken')}
|
||||
});
|
||||
}
|
||||
|
||||
$('form#register_form').submit(function(e) {
|
||||
e.preventDefault();
|
||||
var submit_data = $('#register_form').serialize();
|
||||
|
||||
postJSON('/create_account',
|
||||
submit_data,
|
||||
function(json) {
|
||||
if(json.success) {
|
||||
location.href = "${reverse('index')}";
|
||||
} else {
|
||||
$('#register_error').html(json.value).stop().addClass('is-shown');
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
})(this)
|
||||
</script>
|
||||
);
|
||||
});
|
||||
})(this)
|
||||
</script>
|
||||
</%block>
|
||||
@@ -1,8 +1,9 @@
|
||||
<%inherit file="base.html" />
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<%namespace name="units" file="widgets/units.html" />
|
||||
<%block name="bodyclass">unit</%block>
|
||||
<%block name="title">CMS Unit</%block>
|
||||
<%block name="title">Individual Unit</%block>
|
||||
<%block name="bodyclass">is-signedin course unit</%block>
|
||||
|
||||
<%block name="jsextra">
|
||||
<script type='text/javascript'>
|
||||
$(document).ready(function() {
|
||||
@@ -13,12 +14,20 @@
|
||||
state: '${unit_state}'
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
$('body').addClass('js');
|
||||
|
||||
// tabs
|
||||
$('.tab-group').tabs();
|
||||
});
|
||||
|
||||
$('.new-component-template').each(function(){
|
||||
$emptyEditor = $(this).find('.empty');
|
||||
$(this).prepend($emptyEditor);
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</%block>
|
||||
@@ -56,38 +65,66 @@
|
||||
</div>
|
||||
% for type, templates in sorted(component_templates.items()):
|
||||
<div class="new-component-templates new-component-${type}">
|
||||
<h3 class="title">Select <span class="type">${type}</span> component type:</h3>
|
||||
|
||||
<ul class="new-component-template">
|
||||
% for name, location, has_markdown, is_empty in templates:
|
||||
|
||||
% if is_empty:
|
||||
<li class="editor-md empty">
|
||||
<a href="#" data-location="${location}">
|
||||
<span class="name"><i class="ss-icon ss-symbolicons-block"></i> ${name}</span>
|
||||
<span class="editor-indicator">Simple <span class="sr">Editor</span></span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
% elif has_markdown:
|
||||
<li class="editor-md">
|
||||
<a href="#" data-location="${location}">
|
||||
<span class="name"><i class="ss-icon ss-symbolicons-block"></i> ${name}</span>
|
||||
<span class="editor-indicator">Simple <span class="sr">Editor</span></span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
% else:
|
||||
<li class="editor-manual">
|
||||
<a href="#" data-location="${location}">
|
||||
<span class="name"><i class="ss-icon ss-symbolicons-block">🔧</i> ${name}</span>
|
||||
<span class="editor-indicator">Advanced <span class="sr">Editor</span></span>
|
||||
</a>
|
||||
</li>
|
||||
% endif
|
||||
|
||||
%endfor
|
||||
</ul>
|
||||
% if type == "problem":
|
||||
<div class="tab-group tabs">
|
||||
<ul class="problem-type-tabs nav-tabs">
|
||||
<li class="current">
|
||||
<a class="link-tab" href="#tab1">Common Problem Types</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="link-tab" href="#tab2">Advanced</a>
|
||||
</li>
|
||||
</ul>
|
||||
% endif
|
||||
<div class="tab current" id="tab1">
|
||||
<ul class="new-component-template">
|
||||
% for name, location, has_markdown, is_empty in templates:
|
||||
% if has_markdown or type != "problem":
|
||||
% if is_empty:
|
||||
<li class="editor-md empty">
|
||||
<a href="#" data-location="${location}">
|
||||
<span class="name"> ${name}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
% else:
|
||||
<li class="editor-md">
|
||||
<a href="#" data-location="${location}">
|
||||
<span class="name"> ${name}</span>
|
||||
</a>
|
||||
</li>
|
||||
% endif
|
||||
% endif
|
||||
|
||||
%endfor
|
||||
</ul>
|
||||
</div>
|
||||
% if type == "problem":
|
||||
<div class="tab" id="tab2">
|
||||
<ul class="new-component-template">
|
||||
% for name, location, has_markdown, is_empty in templates:
|
||||
% if not has_markdown:
|
||||
% if is_empty:
|
||||
<li class="editor-manual empty">
|
||||
<a href="#" data-location="${location}">
|
||||
<span class="name">${name}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
% else:
|
||||
<li class="editor-manual">
|
||||
<a href="#" data-location="${location}">
|
||||
<span class="name"> ${name}</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
% endif
|
||||
% endif
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
<a href="#" class="cancel-button">Cancel</a>
|
||||
</div>
|
||||
% endfor
|
||||
|
||||
30
cms/templates/widgets/footer.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
|
||||
<div class="wrapper-footer wrapper">
|
||||
<footer class="primary" role="contentinfo">
|
||||
<div class="colophon">
|
||||
<p>© 2013 <a href="http://www.edx.org" rel="external">edX</a>. All rights reserved.</p>
|
||||
</div>
|
||||
|
||||
<nav class="nav-peripheral">
|
||||
<ol>
|
||||
<!-- <li class="nav-item nav-peripheral-tos">
|
||||
<a href="#">Terms of Service</a>
|
||||
</li>
|
||||
<li class="nav-item nav-peripheral-pp">
|
||||
<a href="#">Privacy Policy</a>
|
||||
</li> -->
|
||||
<li class="nav-item nav-peripheral-help">
|
||||
<a href="http://help.edge.edx.org/" rel="external">edX Studio Help</a>
|
||||
</li>
|
||||
<li class="nav-item nav-peripheral-contact">
|
||||
<a href="https://www.edx.org/contact" rel="external">Contact edX</a>
|
||||
</li>
|
||||
|
||||
% if user.is_authenticated():
|
||||
<!-- add in zendesk/tender feedback form UI -->
|
||||
% endif
|
||||
</ol>
|
||||
</nav>
|
||||
</footer>
|
||||
</div>
|
||||
@@ -1,40 +1,117 @@
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
|
||||
<% active_tab_class = 'active-tab-' + active_tab if active_tab else '' %>
|
||||
<header class="primary-header ${active_tab_class}">
|
||||
<div class="class">
|
||||
<div class="inner-wrapper">
|
||||
<div class="left">
|
||||
% if context_course:
|
||||
<% ctx_loc = context_course.location %>
|
||||
<a href="/" class="home"><span class="small-home-icon"></span></a> ›
|
||||
<a href="${reverse('course_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}" class="class-name">${context_course.display_name}</a> ›
|
||||
% endif
|
||||
</div>
|
||||
<div class="wrapper-header wrapper">
|
||||
<header class="primary" role="banner">
|
||||
|
||||
<div class="right">
|
||||
<span class="username">${ user.username }</span>
|
||||
% if user.is_authenticated():
|
||||
<a href="${reverse('logout')}" class="log-out"><span class="log-out-icon"></span></a>
|
||||
% else:
|
||||
<a href="${reverse('login')}">Log in</a>
|
||||
% endif
|
||||
<div class="wrapper wrapper-left ">
|
||||
<h1 class="branding"><a href="/">edX Studio</a></h1>
|
||||
|
||||
% if context_course:
|
||||
<% ctx_loc = context_course.location %>
|
||||
<div class="info-course">
|
||||
<h2 class="sr">Current Course:</h2>
|
||||
<a href="${reverse('course_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}">
|
||||
<span class="course-org">${ctx_loc.org}</span><span class="course-number">${ctx_loc.course}</span>
|
||||
<span class="course-title" title="${context_course.display_name}">${context_course.display_name}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<nav class="nav-course primary nav-dropdown" role="navigation">
|
||||
<h2 class="sr">${context_course.display_name}'s Navigation:</h2>
|
||||
|
||||
<ol>
|
||||
<li class="nav-item nav-course-courseware">
|
||||
<h3 class="title"><span class="label-prefix">Course </span>Content <i class="ss-icon ss-symbolicons-block icon-expand">▾</i></h3>
|
||||
|
||||
<div class="wrapper wrapper-nav-sub">
|
||||
<div class="nav-sub">
|
||||
<ul>
|
||||
<li class="nav-item nav-course-courseware-outline"><a href="${reverse('course_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}">Outline</a></li>
|
||||
<li class="nav-item nav-course-courseware-updates"><a href="${reverse('course_info', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}">Updates</a></li>
|
||||
<li class="nav-item nav-course-courseware-pages"><a href="${reverse('edit_tabs', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, coursename=ctx_loc.name))}">Static Pages</a></li>
|
||||
<li class="nav-item nav-course-courseware-uploads"><a href="${reverse('asset_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}">Files & Uploads</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="nav-item nav-course-settings">
|
||||
<h3 class="title"><span class="label-prefix">Course </span>Settings <i class="ss-icon ss-symbolicons-block icon-expand">▾</i></h3>
|
||||
|
||||
<div class="wrapper wrapper-nav-sub">
|
||||
<div class="nav-sub">
|
||||
<ul>
|
||||
<li class="nav-item nav-course-settings-schedule"><a href="${reverse('contentstore.views.get_course_settings', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}">Schedule & Details</a></li>
|
||||
<li class="nav-item nav-course-settings-grading"><a href="${reverse('contentstore.views.course_config_graders_page', kwargs={'org' : ctx_loc.org, 'course' : ctx_loc.course, 'name': ctx_loc.name})}">Grading</a></li>
|
||||
<li class="nav-item nav-course-settings-team"><a href="${reverse('manage_users', kwargs=dict(location=ctx_loc))}">Course Team</a></li>
|
||||
<!-- <li class="nav-item nav-course-settings-advanced"><a href="${reverse('course_settings', kwargs={'org' : ctx_loc.org, 'course' : ctx_loc.course, 'name': ctx_loc.name})}">Advanced Settings</a></li> -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="nav-item nav-course-tools">
|
||||
<h3 class="title">Tools <i class="ss-icon ss-symbolicons-block icon-expand">▾</i></h3>
|
||||
|
||||
<div class="wrapper wrapper-nav-sub">
|
||||
<div class="nav-sub">
|
||||
<ul>
|
||||
<li class="nav-item nav-course-tools-import"><a href="${reverse('import_course', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}">Import</a></li>
|
||||
<li class="nav-item nav-course-tools-export"><a href="${reverse('export_course', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}">Export</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<nav class="class-nav-bar">
|
||||
% if context_course:
|
||||
<% ctx_loc = context_course.location %>
|
||||
<ul class="class-nav inner-wrapper">
|
||||
<li><a href="${reverse('course_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}" id='courseware-tab'>Courseware</a></li>
|
||||
<li><a href="${reverse('course_info', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}" id='courseinfo-tab'>Course Info</a></li>
|
||||
<li><a href="${reverse('edit_tabs', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, coursename=ctx_loc.name))}" id='pages-tab'>Pages</a></li>
|
||||
<li><a href="${reverse('manage_users', kwargs=dict(location=ctx_loc))}" id='users-tab'>Users</a></li>
|
||||
<li><a href="${reverse('asset_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}" id='assets-tab'>Assets</a></li>
|
||||
<li><a href="${reverse('course_settings', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}" id='settings-tab'>Settings</a></li>
|
||||
<li><a href="${reverse('import_course', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}" id='import-tab'>Import</a></li>
|
||||
<li><a href="${reverse('export_course', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}" id='export-tab'>Export</a></li>
|
||||
</ul>
|
||||
% endif
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="wrapper wrapper-right">
|
||||
% if user.is_authenticated():
|
||||
<nav class="nav-account nav-is-signedin nav-dropdown">
|
||||
<h2 class="sr">Currently logged in as:</h2>
|
||||
<ol>
|
||||
<li class="nav-item nav-account-username">
|
||||
<a href="#" class="title">
|
||||
<span class="account-username">
|
||||
<i class="ss-icon ss-symbolicons-standard icon-user">👤</i>
|
||||
${ user.username }
|
||||
</span>
|
||||
<i class="ss-icon ss-symbolicons-block icon-expand">▾</i>
|
||||
</a>
|
||||
|
||||
<div class="wrapper wrapper-nav-sub">
|
||||
<div class="nav-sub">
|
||||
<ul>
|
||||
<li class="nav-item nav-account-dashboard"><a href="/">My Courses</a></li>
|
||||
<li class="nav-item nav-account-help"><a href="http://help.edge.edx.org/" rel="external">Studio Help</a></li>
|
||||
<li class="nav-item nav-account-signout"><a class="action action-signout" href="${reverse('logout')}">Sign Out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
% else:
|
||||
<nav class="nav-not-signedin">
|
||||
<h2 class="sr">You're not currently signed in</h2>
|
||||
<ol>
|
||||
<li class="nav-item nav-not-signedin-hiw">
|
||||
<a href="/">How Studio Works</a>
|
||||
</li>
|
||||
<li class="nav-item nav-not-signedin-help">
|
||||
<a href="http://help.edge.edx.org/" rel="external">Studio Help</a>
|
||||
</li>
|
||||
<li class="nav-item nav-not-signedin-signup">
|
||||
<a class="action action-signup" href="${reverse('signup')}">Sign Up</a>
|
||||
</li>
|
||||
<li class="nav-item nav-not-signedin-signin">
|
||||
<a class="action action-signin" href="${reverse('login')}">Sign In</a>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
% endif
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
@@ -1,20 +1,20 @@
|
||||
<%include file="metadata-edit.html" />
|
||||
<section class="problem-editor editor">
|
||||
<div class="row">
|
||||
%if markdown != '' or data == '<problem>\n</problem>\n':
|
||||
%if enable_markdown:
|
||||
<div class="editor-bar">
|
||||
<ul class="format-buttons">
|
||||
<li><a href="#" class="header-button" data-tooltip="Heading 1"><span
|
||||
class="problem-editor-icon heading1"></span></a></li>
|
||||
<li><a href="#" class="multiple-choice-button" data-tooltip="Multiple Choice"><span
|
||||
class="problem-editor-icon multiple-choice"></span></a></li>
|
||||
<li><a href="#" class="checks-button" data-tooltip="Check Multiple"><span
|
||||
<li><a href="#" class="checks-button" data-tooltip="Checkboxes"><span
|
||||
class="problem-editor-icon checks"></span></a></li>
|
||||
<li><a href="#" class="string-button" data-tooltip="String Response"><span
|
||||
<li><a href="#" class="string-button" data-tooltip="Text Input"><span
|
||||
class="problem-editor-icon string"></span></a></li>
|
||||
<li><a href="#" class="number-button" data-tooltip="Numerical Response"><span
|
||||
<li><a href="#" class="number-button" data-tooltip="Numerical Input"><span
|
||||
class="problem-editor-icon number"></span></a></li>
|
||||
<li><a href="#" class="dropdown-button" data-tooltip="Option Response"><span
|
||||
<li><a href="#" class="dropdown-button" data-tooltip="Dropdown"><span
|
||||
class="problem-editor-icon dropdown"></span></a></li>
|
||||
<li><a href="#" class="explanation-button" data-tooltip="Explanation"><span
|
||||
class="problem-editor-icon explanation"></span></a></li>
|
||||
@@ -56,7 +56,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<h6>Check Multiple</h6>
|
||||
<h6>Checkboxes</h6>
|
||||
<div class="col sample check-multiple">
|
||||
<img src="/static/img/multi-example.png" />
|
||||
</div>
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<h6>String Response</h6>
|
||||
<h6>Text Input</h6>
|
||||
<div class="col sample string-response">
|
||||
<img src="/static/img/string-example.png" />
|
||||
</div>
|
||||
@@ -76,7 +76,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<h6>Numerical Response</h6>
|
||||
<h6>Numerical Input</h6>
|
||||
<div class="col sample numerical-response">
|
||||
<img src="/static/img/number-example.png" />
|
||||
</div>
|
||||
@@ -85,7 +85,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<h6>Option Response</h6>
|
||||
<h6>Dropdown</h6>
|
||||
<div class="col sample option-reponse">
|
||||
<img src="/static/img/select-example.png" />
|
||||
</div>
|
||||
|
||||
14
cms/urls.py
@@ -6,7 +6,8 @@ from django.conf.urls import patterns, include, url
|
||||
# admin.autodiscover()
|
||||
|
||||
urlpatterns = ('',
|
||||
url(r'^$', 'contentstore.views.index', name='index'),
|
||||
url(r'^$', 'contentstore.views.howitworks', name='homepage'),
|
||||
url(r'^listing', 'contentstore.views.index', name='index'),
|
||||
url(r'^edit/(?P<location>.*?)$', 'contentstore.views.edit_unit', name='edit_unit'),
|
||||
url(r'^subsection/(?P<location>.*?)$', 'contentstore.views.edit_subsection', name='edit_subsection'),
|
||||
url(r'^preview_component/(?P<location>.*?)$', 'contentstore.views.preview_component', name='preview_component'),
|
||||
@@ -42,9 +43,10 @@ urlpatterns = ('',
|
||||
'contentstore.views.remove_user', name='remove_user'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/info/(?P<name>[^/]+)$', 'contentstore.views.course_info', name='course_info'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course_info/updates/(?P<provided_id>.*)$', 'contentstore.views.course_info_updates', name='course_info'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/settings/(?P<name>[^/]+)$', 'contentstore.views.get_course_settings', name='course_settings'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/settings/(?P<name>[^/]+)/section/(?P<section>[^/]+).*$', 'contentstore.views.course_settings_updates', name='course_settings'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/grades/(?P<name>[^/]+)/(?P<grader_index>.*)$', 'contentstore.views.course_grader_updates', name='course_settings'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/settings-details/(?P<name>[^/]+)$', 'contentstore.views.get_course_settings', name='course_settings'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/settings-grading/(?P<name>[^/]+)$', 'contentstore.views.course_config_graders_page', name='course_settings'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/settings-details/(?P<name>[^/]+)/section/(?P<section>[^/]+).*$', 'contentstore.views.course_settings_updates', name='course_settings'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/settings-grading/(?P<name>[^/]+)/(?P<grader_index>.*)$', 'contentstore.views.course_grader_updates', name='course_settings'),
|
||||
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/(?P<category>[^/]+)/(?P<name>[^/]+)/gradeas.*$', 'contentstore.views.assignment_type_update', name='assignment_type_update'),
|
||||
|
||||
@@ -76,13 +78,15 @@ urlpatterns = ('',
|
||||
|
||||
# User creation and updating views
|
||||
urlpatterns += (
|
||||
url(r'^howitworks$', 'contentstore.views.howitworks', name='howitworks'),
|
||||
url(r'^signup$', 'contentstore.views.signup', name='signup'),
|
||||
|
||||
url(r'^create_account$', 'student.views.create_account'),
|
||||
url(r'^activate/(?P<key>[^/]*)$', 'student.views.activate_account', name='activate'),
|
||||
|
||||
# form page
|
||||
url(r'^login$', 'contentstore.views.login_page', name='login'),
|
||||
url(r'^login$', 'contentstore.views.old_login_redirect', name='old_login'),
|
||||
url(r'^signin$', 'contentstore.views.login_page', name='login'),
|
||||
# ajax view that actually does the work
|
||||
url(r'^login_post$', 'student.views.login_user', name='login_post'),
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from django.template.loaders.app_directories import Loader as AppDirectoriesLoad
|
||||
from mitxmako.template import Template
|
||||
import mitxmako.middleware
|
||||
|
||||
import tempdir
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -30,7 +31,7 @@ class MakoLoader(object):
|
||||
|
||||
if module_directory is None:
|
||||
log.warning("For more caching of mako templates, set the MAKO_MODULE_DIR in settings!")
|
||||
module_directory = tempfile.mkdtemp()
|
||||
module_directory = tempdir.mkdtemp_clean()
|
||||
|
||||
self.module_directory = module_directory
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
from mako.lookup import TemplateLookup
|
||||
import tempfile
|
||||
import tempdir
|
||||
from django.template import RequestContext
|
||||
from django.conf import settings
|
||||
|
||||
@@ -29,7 +29,7 @@ class MakoMiddleware(object):
|
||||
module_directory = getattr(settings, 'MAKO_MODULE_DIR', None)
|
||||
|
||||
if module_directory is None:
|
||||
module_directory = tempfile.mkdtemp()
|
||||
module_directory = tempdir.mkdtemp_clean()
|
||||
|
||||
for location in template_locations:
|
||||
lookup[location] = TemplateLookup(directories=template_locations[location],
|
||||
|
||||
@@ -7,6 +7,7 @@ import logging
|
||||
import os
|
||||
from tempfile import mkdtemp
|
||||
import cStringIO
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from django.test import TestCase
|
||||
@@ -143,23 +144,18 @@ class PearsonTestCase(TestCase):
|
||||
'''
|
||||
Base class for tests running Pearson-related commands
|
||||
'''
|
||||
import_dir = mkdtemp(prefix="import")
|
||||
export_dir = mkdtemp(prefix="export")
|
||||
|
||||
def assertErrorContains(self, error_message, expected):
|
||||
self.assertTrue(error_message.find(expected) >= 0, 'error message "{}" did not contain "{}"'.format(error_message, expected))
|
||||
|
||||
def setUp(self):
|
||||
self.import_dir = mkdtemp(prefix="import")
|
||||
self.addCleanup(shutil.rmtree, self.import_dir)
|
||||
self.export_dir = mkdtemp(prefix="export")
|
||||
self.addCleanup(shutil.rmtree, self.export_dir)
|
||||
|
||||
def tearDown(self):
|
||||
def delete_temp_dir(dirname):
|
||||
if os.path.exists(dirname):
|
||||
for filename in os.listdir(dirname):
|
||||
os.remove(os.path.join(dirname, filename))
|
||||
os.rmdir(dirname)
|
||||
|
||||
# clean up after any test data was dumped to temp directory
|
||||
delete_temp_dir(self.import_dir)
|
||||
delete_temp_dir(self.export_dir)
|
||||
|
||||
pass
|
||||
# and clean up the database:
|
||||
# TestCenterUser.objects.all().delete()
|
||||
# TestCenterRegistration.objects.all().delete()
|
||||
|
||||
@@ -11,8 +11,9 @@ from django.core.management import call_command
|
||||
|
||||
@before.harvest
|
||||
def initial_setup(server):
|
||||
# Launch firefox
|
||||
# Launch the browser app (choose one of these below)
|
||||
world.browser = Browser('chrome')
|
||||
# world.browser = Browser('firefox')
|
||||
|
||||
|
||||
@before.each_scenario
|
||||
@@ -633,9 +633,11 @@ class MultipleChoiceResponse(LoncapaResponse):
|
||||
# define correct choices (after calling secondary setup)
|
||||
xml = self.xml
|
||||
cxml = xml.xpath('//*[@id=$id]//choice', id=xml.get('id'))
|
||||
|
||||
# contextualize correct attribute and then select ones for which
|
||||
# correct = "true"
|
||||
self.correct_choices = [contextualize_text(choice.get('name'), self.context)
|
||||
self.correct_choices = [
|
||||
contextualize_text(choice.get('name'), self.context)
|
||||
for choice in cxml
|
||||
if contextualize_text(choice.get('correct'), self.context) == "true"]
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
},
|
||||
smartIndent: false
|
||||
});
|
||||
$("#textbox_${id}").find('.CodeMirror-scroll').height(${int(13.5*eval(rows))});
|
||||
});
|
||||
</script>
|
||||
</section>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<section id="designprotein2dinput_${id}" class="designprotein2dinput">
|
||||
<div class="script_placeholder" data-src="/static/js/capa/protex/protex.nocache.js"/>
|
||||
<div class="script_placeholder" data-src="/static/js/capa/protex/protex.nocache.js?raw"/>
|
||||
<div class="script_placeholder" data-src="${applet_loader}"/>
|
||||
|
||||
% if status == 'unsubmitted':
|
||||
|
||||
17
common/lib/tempdir.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""Make temporary directories nicely."""
|
||||
|
||||
import atexit
|
||||
import os.path
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
def mkdtemp_clean(suffix="", prefix="tmp", dir=None):
|
||||
"""Just like mkdtemp, but the directory will be deleted when the process ends."""
|
||||
the_dir = tempfile.mkdtemp(suffix=suffix, prefix=prefix, dir=dir)
|
||||
atexit.register(cleanup_tempdir, the_dir)
|
||||
return the_dir
|
||||
|
||||
def cleanup_tempdir(the_dir):
|
||||
"""Called on process exit to remove a temp directory."""
|
||||
if os.path.exists(the_dir):
|
||||
shutil.rmtree(the_dir)
|
||||
@@ -37,6 +37,7 @@ setup(
|
||||
"timelimit = xmodule.timelimit_module:TimeLimitDescriptor",
|
||||
"vertical = xmodule.vertical_module:VerticalDescriptor",
|
||||
"video = xmodule.video_module:VideoDescriptor",
|
||||
"videoalpha = xmodule.videoalpha_module:VideoAlphaDescriptor",
|
||||
"videodev = xmodule.backcompat_module:TranslateCustomTagDescriptor",
|
||||
"videosequence = xmodule.seq_module:SequenceDescriptor",
|
||||
"discussion = xmodule.discussion_module:DiscussionDescriptor",
|
||||
@@ -44,7 +45,8 @@ setup(
|
||||
"static_tab = xmodule.html_module:StaticTabDescriptor",
|
||||
"custom_tag_template = xmodule.raw_module:RawDescriptor",
|
||||
"about = xmodule.html_module:AboutDescriptor",
|
||||
"graphical_slider_tool = xmodule.gst_module:GraphicalSliderToolDescriptor"
|
||||
]
|
||||
"graphical_slider_tool = xmodule.gst_module:GraphicalSliderToolDescriptor",
|
||||
"foldit = xmodule.foldit_module:FolditDescriptor",
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
@@ -703,15 +703,15 @@ class CapaDescriptor(RawDescriptor):
|
||||
|
||||
def get_context(self):
|
||||
_context = RawDescriptor.get_context(self)
|
||||
_context.update({'markdown': self.metadata.get('markdown', '')})
|
||||
_context.update({'markdown': self.metadata.get('markdown', ''),
|
||||
'enable_markdown' : 'markdown' in self.metadata})
|
||||
return _context
|
||||
|
||||
@property
|
||||
def editable_metadata_fields(self):
|
||||
"""Remove metadata from the editable fields since it has its own editor"""
|
||||
subset = super(CapaDescriptor, self).editable_metadata_fields
|
||||
if 'markdown' in subset:
|
||||
subset.remove('markdown')
|
||||
"""Remove any metadata from the editable fields which have their own editor or shouldn't be edited by user."""
|
||||
subset = [field for field in super(CapaDescriptor,self).editable_metadata_fields
|
||||
if field not in ['markdown', 'empty']]
|
||||
return subset
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +1,13 @@
|
||||
import copy
|
||||
from fs.errors import ResourceNotFoundError
|
||||
import itertools
|
||||
import json
|
||||
import logging
|
||||
from lxml import etree
|
||||
from lxml.html import rewrite_links
|
||||
from path import path
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pkg_resources import resource_string
|
||||
|
||||
from .capa_module import only_one, ComplexEncoder
|
||||
from .editing_module import EditingDescriptor
|
||||
from .html_checker import check_html
|
||||
from progress import Progress
|
||||
from .stringify import stringify_children
|
||||
from .x_module import XModule
|
||||
from .xml_module import XmlDescriptor
|
||||
from xmodule.modulestore import Location
|
||||
from combined_open_ended_modulev1 import CombinedOpenEndedV1Module, CombinedOpenEndedV1Descriptor
|
||||
from xmodule.open_ended_grading_classes.combined_open_ended_modulev1 import CombinedOpenEndedV1Module, CombinedOpenEndedV1Descriptor
|
||||
|
||||
log = logging.getLogger("mitx.courseware")
|
||||
|
||||
@@ -120,11 +108,13 @@ class CombinedOpenEndedModule(XModule):
|
||||
instance_state = {}
|
||||
|
||||
self.version = self.metadata.get('version', DEFAULT_VERSION)
|
||||
version_error_string = "Version of combined open ended module {0} is not correct. Going with version {1}"
|
||||
if not isinstance(self.version, basestring):
|
||||
try:
|
||||
self.version = str(self.version)
|
||||
except:
|
||||
log.error("Version {0} is not correct. Going with version {1}".format(self.version, DEFAULT_VERSION))
|
||||
#This is a dev_facing_error
|
||||
log.info(version_error_string.format(self.version, DEFAULT_VERSION))
|
||||
self.version = DEFAULT_VERSION
|
||||
|
||||
versions = [i[0] for i in VERSION_TUPLES]
|
||||
@@ -134,7 +124,8 @@ class CombinedOpenEndedModule(XModule):
|
||||
try:
|
||||
version_index = versions.index(self.version)
|
||||
except:
|
||||
log.error("Version {0} is not correct. Going with version {1}".format(self.version, DEFAULT_VERSION))
|
||||
#This is a dev_facing_error
|
||||
log.error(version_error_string.format(self.version, DEFAULT_VERSION))
|
||||
self.version = DEFAULT_VERSION
|
||||
version_index = versions.index(self.version)
|
||||
|
||||
@@ -217,4 +208,4 @@ class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
for child in ['task']:
|
||||
add_child(child)
|
||||
|
||||
return elt
|
||||
return elt
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
import logging
|
||||
from lxml import etree
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RubricParsingError(Exception):
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
|
||||
class CombinedOpenEndedRubric(object):
|
||||
|
||||
def __init__ (self, system, view_only = False):
|
||||
self.has_score = False
|
||||
self.view_only = view_only
|
||||
self.system = system
|
||||
|
||||
def render_rubric(self, rubric_xml):
|
||||
'''
|
||||
render_rubric: takes in an xml string and outputs the corresponding
|
||||
html for that xml, given the type of rubric we're generating
|
||||
Input:
|
||||
rubric_xml: an string that has not been parsed into xml that
|
||||
represents this particular rubric
|
||||
Output:
|
||||
html: the html that corresponds to the xml given
|
||||
'''
|
||||
success = False
|
||||
try:
|
||||
rubric_categories = self.extract_categories(rubric_xml)
|
||||
max_scores = map((lambda cat: cat['options'][-1]['points']), rubric_categories)
|
||||
max_score = max(max_scores)
|
||||
html = self.system.render_template('open_ended_rubric.html',
|
||||
{'categories': rubric_categories,
|
||||
'has_score': self.has_score,
|
||||
'view_only': self.view_only,
|
||||
'max_score': max_score})
|
||||
success = True
|
||||
except:
|
||||
error_message = "[render_rubric] Could not parse the rubric with xml: {0}".format(rubric_xml)
|
||||
log.error(error_message)
|
||||
raise RubricParsingError(error_message)
|
||||
return success, html
|
||||
|
||||
def check_if_rubric_is_parseable(self, rubric_string, location, max_score_allowed, max_score):
|
||||
success, rubric_feedback = self.render_rubric(rubric_string)
|
||||
if not success:
|
||||
error_message = "Could not parse rubric : {0} for location {1}".format(rubric_string, location.url())
|
||||
log.error(error_message)
|
||||
raise RubricParsingError(error_message)
|
||||
|
||||
rubric_categories = self.extract_categories(rubric_string)
|
||||
total = 0
|
||||
for category in rubric_categories:
|
||||
total = total + len(category['options']) - 1
|
||||
if len(category['options']) > (max_score_allowed + 1):
|
||||
error_message = "Number of score points in rubric {0} higher than the max allowed, which is {1}".format(
|
||||
len(category['options']), max_score_allowed)
|
||||
log.error(error_message)
|
||||
raise RubricParsingError(error_message)
|
||||
|
||||
if total != max_score:
|
||||
error_msg = "The max score {0} for problem {1} does not match the total number of points in the rubric {2}".format(
|
||||
max_score, location, total)
|
||||
log.error(error_msg)
|
||||
raise RubricParsingError(error_msg)
|
||||
|
||||
def extract_categories(self, element):
|
||||
'''
|
||||
Contstruct a list of categories such that the structure looks like:
|
||||
[ { category: "Category 1 Name",
|
||||
options: [{text: "Option 1 Name", points: 0}, {text:"Option 2 Name", points: 5}]
|
||||
},
|
||||
{ category: "Category 2 Name",
|
||||
options: [{text: "Option 1 Name", points: 0},
|
||||
{text: "Option 2 Name", points: 1},
|
||||
{text: "Option 3 Name", points: 2]}]
|
||||
|
||||
'''
|
||||
if isinstance(element, basestring):
|
||||
element = etree.fromstring(element)
|
||||
categories = []
|
||||
for category in element:
|
||||
if category.tag != 'category':
|
||||
raise RubricParsingError("[extract_categories] Expected a <category> tag: got {0} instead".format(category.tag))
|
||||
else:
|
||||
categories.append(self.extract_category(category))
|
||||
return categories
|
||||
|
||||
|
||||
def extract_category(self, category):
|
||||
'''
|
||||
construct an individual category
|
||||
{category: "Category 1 Name",
|
||||
options: [{text: "Option 1 text", points: 1},
|
||||
{text: "Option 2 text", points: 2}]}
|
||||
|
||||
all sorting and auto-point generation occurs in this function
|
||||
'''
|
||||
descriptionxml = category[0]
|
||||
optionsxml = category[1:]
|
||||
scorexml = category[1]
|
||||
score = None
|
||||
if scorexml.tag == 'score':
|
||||
score_text = scorexml.text
|
||||
optionsxml = category[2:]
|
||||
score = int(score_text)
|
||||
self.has_score = True
|
||||
# if we are missing the score tag and we are expecting one
|
||||
elif self.has_score:
|
||||
raise RubricParsingError("[extract_category] Category {0} is missing a score".format(descriptionxml.text))
|
||||
|
||||
|
||||
# parse description
|
||||
if descriptionxml.tag != 'description':
|
||||
raise RubricParsingError("[extract_category]: expected description tag, got {0} instead".format(descriptionxml.tag))
|
||||
|
||||
description = descriptionxml.text
|
||||
|
||||
cur_points = 0
|
||||
options = []
|
||||
autonumbering = True
|
||||
# parse options
|
||||
for option in optionsxml:
|
||||
if option.tag != 'option':
|
||||
raise RubricParsingError("[extract_category]: expected option tag, got {0} instead".format(option.tag))
|
||||
else:
|
||||
pointstr = option.get("points")
|
||||
if pointstr:
|
||||
autonumbering = False
|
||||
# try to parse this into an int
|
||||
try:
|
||||
points = int(pointstr)
|
||||
except ValueError:
|
||||
raise RubricParsingError("[extract_category]: expected points to have int, got {0} instead".format(pointstr))
|
||||
elif autonumbering:
|
||||
# use the generated one if we're in the right mode
|
||||
points = cur_points
|
||||
cur_points = cur_points + 1
|
||||
else:
|
||||
raise Exception("[extract_category]: missing points attribute. Cannot continue to auto-create points values after a points value is explicitly defined.")
|
||||
|
||||
selected = score == points
|
||||
optiontext = option.text
|
||||
options.append({'text': option.text, 'points': points, 'selected': selected})
|
||||
|
||||
# sort and check for duplicates
|
||||
options = sorted(options, key=lambda option: option['points'])
|
||||
CombinedOpenEndedRubric.validate_options(options)
|
||||
|
||||
return {'description': description, 'options': options}
|
||||
|
||||
|
||||
@staticmethod
|
||||
def validate_options(options):
|
||||
'''
|
||||
Validates a set of options. This can and should be extended to filter out other bad edge cases
|
||||
'''
|
||||
if len(options) == 0:
|
||||
raise RubricParsingError("[extract_category]: no options associated with this category")
|
||||
if len(options) == 1:
|
||||
return
|
||||
prev = options[0]['points']
|
||||
for option in options[1:]:
|
||||
if prev == option['points']:
|
||||
raise RubricParsingError("[extract_category]: found duplicate point values between two different options")
|
||||
else:
|
||||
prev = option['points']
|
||||
@@ -24,14 +24,11 @@ section.combined-open-ended {
|
||||
@include clearfix;
|
||||
.status-container
|
||||
{
|
||||
float:right;
|
||||
width:40%;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.item-container
|
||||
{
|
||||
float:left;
|
||||
width: 53%;
|
||||
padding-bottom: 50px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.result-container
|
||||
@@ -46,14 +43,26 @@ section.combined-open-ended {
|
||||
}
|
||||
}
|
||||
|
||||
section.legend-container {
|
||||
.legenditem {
|
||||
background-color : #d4d4d4;
|
||||
font-size: .9em;
|
||||
padding: 2px;
|
||||
display: inline;
|
||||
width: 20%;
|
||||
}
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
section.combined-open-ended-status {
|
||||
|
||||
.statusitem {
|
||||
background-color: #FAFAFA;
|
||||
color: #2C2C2C;
|
||||
font-family: monospace;
|
||||
font-size: 1em;
|
||||
padding: 10px;
|
||||
background-color : #d4d4d4;
|
||||
font-size: .9em;
|
||||
padding: 2px;
|
||||
display: inline;
|
||||
width: 20%;
|
||||
.show-results {
|
||||
margin-top: .3em;
|
||||
text-align:right;
|
||||
@@ -61,12 +70,12 @@ section.combined-open-ended-status {
|
||||
.show-results-button {
|
||||
font: 1em monospace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statusitem-current {
|
||||
background-color: #d4d4d4;
|
||||
background-color: #B2B2B2;
|
||||
color: #222;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
&.unanswered {
|
||||
@@ -98,8 +107,29 @@ section.combined-open-ended-status {
|
||||
}
|
||||
}
|
||||
|
||||
div.result-container {
|
||||
div.combined-rubric-container {
|
||||
ul.rubric-list{
|
||||
list-style-type: none;
|
||||
padding:0;
|
||||
margin:0;
|
||||
li {
|
||||
&.rubric-list-item{
|
||||
margin-bottom: 2px;
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span.rubric-category {
|
||||
font-size: .9em;
|
||||
}
|
||||
padding-bottom: 5px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
div.result-container {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 5px;
|
||||
.evaluation {
|
||||
|
||||
p {
|
||||
@@ -113,9 +143,8 @@ div.result-container {
|
||||
}
|
||||
|
||||
.evaluation-response {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 2px;
|
||||
header {
|
||||
text-align: right;
|
||||
a {
|
||||
font-size: .85em;
|
||||
}
|
||||
@@ -198,20 +227,6 @@ div.result-container {
|
||||
}
|
||||
}
|
||||
|
||||
.result-correct {
|
||||
background: url('../images/correct-icon.png') left 20px no-repeat;
|
||||
.result-actual-output {
|
||||
color: #090;
|
||||
}
|
||||
}
|
||||
|
||||
.result-incorrect {
|
||||
background: url('../images/incorrect-icon.png') left 20px no-repeat;
|
||||
.result-actual-output {
|
||||
color: #B00;
|
||||
}
|
||||
}
|
||||
|
||||
.markup-text{
|
||||
margin: 5px;
|
||||
padding: 20px 0px 15px 50px;
|
||||
@@ -229,6 +244,16 @@ div.result-container {
|
||||
}
|
||||
}
|
||||
}
|
||||
.rubric-result-container {
|
||||
.rubric-result {
|
||||
font-size: .9em;
|
||||
padding: 2px;
|
||||
display: inline-table;
|
||||
}
|
||||
padding: 2px;
|
||||
margin: 0px;
|
||||
display : inline;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -404,7 +429,7 @@ section.open-ended-child {
|
||||
div.short-form-response {
|
||||
background: #F6F6F6;
|
||||
border: 1px solid #ddd;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 0px;
|
||||
overflow-y: auto;
|
||||
height: 200px;
|
||||
@include clearfix;
|
||||
@@ -478,6 +503,18 @@ section.open-ended-child {
|
||||
margin-left: .75rem;
|
||||
}
|
||||
|
||||
ul.rubric-list{
|
||||
list-style-type: none;
|
||||
padding:0;
|
||||
margin:0;
|
||||
li {
|
||||
&.rubric-list-item{
|
||||
margin-bottom: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: decimal outside none;
|
||||
margin-bottom: lh();
|
||||
@@ -503,9 +540,8 @@ section.open-ended-child {
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.4em;
|
||||
margin-bottom: lh(.5);
|
||||
|
||||
margin-bottom: 0px;
|
||||
padding: 0px;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -49,10 +49,18 @@ p {
|
||||
|
||||
em, i {
|
||||
font-style: italic;
|
||||
|
||||
span {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
strong, b {
|
||||
font-weight: bold;
|
||||
|
||||
span {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
p + p, ul + p, ol + p {
|
||||
|
||||
559
common/lib/xmodule/xmodule/css/videoalpha/display.scss
Normal file
@@ -0,0 +1,559 @@
|
||||
& {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
div.video {
|
||||
@include clearfix();
|
||||
background: #f3f3f3;
|
||||
display: block;
|
||||
margin: 0 -12px;
|
||||
padding: 12px;
|
||||
border-radius: 5px;
|
||||
|
||||
article.video-wrapper {
|
||||
float: left;
|
||||
margin-right: flex-gutter(9);
|
||||
width: flex-grid(6, 9);
|
||||
|
||||
section.video-player {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
padding-bottom: 56.25%;
|
||||
position: relative;
|
||||
|
||||
object, iframe {
|
||||
border: none;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
section.video-controls {
|
||||
@include clearfix();
|
||||
background: #333;
|
||||
border: 1px solid #000;
|
||||
border-top: 0;
|
||||
color: #ccc;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
ul, div {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
div.slider {
|
||||
@include clearfix();
|
||||
background: #c2c2c2;
|
||||
border: 1px solid #000;
|
||||
@include border-radius(0);
|
||||
border-top: 1px solid #000;
|
||||
@include box-shadow(inset 0 1px 0 #eee, 0 1px 0 #555);
|
||||
height: 7px;
|
||||
margin-left: -1px;
|
||||
margin-right: -1px;
|
||||
@include transition(height 2.0s ease-in-out);
|
||||
|
||||
div.ui-widget-header {
|
||||
background: #777;
|
||||
@include box-shadow(inset 0 1px 0 #999);
|
||||
}
|
||||
|
||||
a.ui-slider-handle {
|
||||
background: $pink url(../images/slider-handle.png) center center no-repeat;
|
||||
@include background-size(50%);
|
||||
border: 1px solid darken($pink, 20%);
|
||||
@include border-radius(15px);
|
||||
@include box-shadow(inset 0 1px 0 lighten($pink, 10%));
|
||||
cursor: pointer;
|
||||
height: 15px;
|
||||
margin-left: -7px;
|
||||
top: -4px;
|
||||
@include transition(height 2.0s ease-in-out, width 2.0s ease-in-out);
|
||||
width: 15px;
|
||||
|
||||
&:focus, &:hover {
|
||||
background-color: lighten($pink, 10%);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul.vcr {
|
||||
@extend .dullify;
|
||||
float: left;
|
||||
list-style: none;
|
||||
margin: 0 lh() 0 0;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
float: left;
|
||||
margin-bottom: 0;
|
||||
|
||||
a {
|
||||
border-bottom: none;
|
||||
border-right: 1px solid #000;
|
||||
@include box-shadow(1px 0 0 #555);
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
line-height: 46px;
|
||||
padding: 0 lh(.75);
|
||||
text-indent: -9999px;
|
||||
@include transition(background-color, opacity);
|
||||
width: 14px;
|
||||
background: url('../images/vcr.png') 15px 15px no-repeat;
|
||||
outline: 0;
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&:empty {
|
||||
height: 46px;
|
||||
background: url('../images/vcr.png') 15px 15px no-repeat;
|
||||
}
|
||||
|
||||
&.play {
|
||||
background-position: 17px -114px;
|
||||
|
||||
&:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
}
|
||||
|
||||
&.pause {
|
||||
background-position: 16px -50px;
|
||||
|
||||
&:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.vidtime {
|
||||
padding-left: lh(.75);
|
||||
font-weight: bold;
|
||||
line-height: 46px; //height of play pause buttons
|
||||
padding-left: lh(.75);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.secondary-controls {
|
||||
@extend .dullify;
|
||||
float: right;
|
||||
|
||||
div.speeds {
|
||||
float: left;
|
||||
position: relative;
|
||||
|
||||
&.open {
|
||||
&>a {
|
||||
background: url('../images/open-arrow.png') 10px center no-repeat;
|
||||
}
|
||||
|
||||
ol.video_speeds {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
|
||||
&>a {
|
||||
background: url('../images/closed-arrow.png') 10px center no-repeat;
|
||||
border-left: 1px solid #000;
|
||||
border-right: 1px solid #000;
|
||||
@include box-shadow(1px 0 0 #555, inset 1px 0 0 #555);
|
||||
@include clearfix();
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
line-height: 46px; //height of play pause buttons
|
||||
margin-right: 0;
|
||||
padding-left: 15px;
|
||||
position: relative;
|
||||
@include transition();
|
||||
-webkit-font-smoothing: antialiased;
|
||||
width: 116px;
|
||||
outline: 0;
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #999;
|
||||
float: left;
|
||||
font-size: em(14);
|
||||
font-weight: normal;
|
||||
letter-spacing: 1px;
|
||||
padding: 0 lh(.25) 0 lh(.5);
|
||||
line-height: 46px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
p.active {
|
||||
float: left;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0;
|
||||
padding: 0 lh(.5) 0 0;
|
||||
line-height: 46px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:hover, &:active, &:focus {
|
||||
opacity: 1;
|
||||
background-color: #444;
|
||||
}
|
||||
}
|
||||
|
||||
// fix for now
|
||||
ol.video_speeds {
|
||||
@include box-shadow(inset 1px 0 0 #555, 0 3px 0 #444);
|
||||
@include transition();
|
||||
background-color: #444;
|
||||
border: 1px solid #000;
|
||||
bottom: 46px;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
width: 133px;
|
||||
z-index: 10;
|
||||
|
||||
li {
|
||||
@include box-shadow( 0 1px 0 #555);
|
||||
border-bottom: 1px solid #000;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
|
||||
a {
|
||||
border: 0;
|
||||
color: #fff;
|
||||
display: block;
|
||||
padding: lh(.5);
|
||||
|
||||
&:hover {
|
||||
background-color: #666;
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@include box-shadow(none);
|
||||
border-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.volume {
|
||||
float: left;
|
||||
position: relative;
|
||||
|
||||
&.open {
|
||||
.volume-slider-container {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.muted {
|
||||
&>a {
|
||||
background: url('../images/mute.png') 10px center no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
> a {
|
||||
background: url('../images/volume.png') 10px center no-repeat;
|
||||
border-right: 1px solid #000;
|
||||
@include box-shadow(1px 0 0 #555, inset 1px 0 0 #555);
|
||||
@include clearfix();
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
height: 46px;
|
||||
margin-right: 0;
|
||||
padding-left: 15px;
|
||||
position: relative;
|
||||
@include transition();
|
||||
-webkit-font-smoothing: antialiased;
|
||||
width: 30px;
|
||||
|
||||
&:hover, &:active, &:focus {
|
||||
background-color: #444;
|
||||
}
|
||||
}
|
||||
|
||||
.volume-slider-container {
|
||||
@include box-shadow(inset 1px 0 0 #555, 0 3px 0 #444);
|
||||
@include transition();
|
||||
background-color: #444;
|
||||
border: 1px solid #000;
|
||||
bottom: 46px;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
width: 45px;
|
||||
height: 125px;
|
||||
margin-left: -1px;
|
||||
z-index: 10;
|
||||
|
||||
.volume-slider {
|
||||
height: 100px;
|
||||
border: 0;
|
||||
width: 5px;
|
||||
margin: 14px auto;
|
||||
background: #666;
|
||||
border: 1px solid #000;
|
||||
@include box-shadow(0 1px 0 #333);
|
||||
|
||||
a.ui-slider-handle {
|
||||
background: $pink url(../images/slider-handle.png) center center no-repeat;
|
||||
@include background-size(50%);
|
||||
border: 1px solid darken($pink, 20%);
|
||||
@include border-radius(15px);
|
||||
@include box-shadow(inset 0 1px 0 lighten($pink, 10%));
|
||||
cursor: pointer;
|
||||
height: 15px;
|
||||
left: -6px;
|
||||
@include transition(height 2.0s ease-in-out, width 2.0s ease-in-out);
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.ui-slider-range {
|
||||
background: #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a.add-fullscreen {
|
||||
background: url(../images/fullscreen.png) center no-repeat;
|
||||
border-right: 1px solid #000;
|
||||
@include box-shadow(1px 0 0 #555, inset 1px 0 0 #555);
|
||||
color: #797979;
|
||||
display: block;
|
||||
float: left;
|
||||
line-height: 46px; //height of play pause buttons
|
||||
margin-left: 0;
|
||||
padding: 0 lh(.5);
|
||||
text-indent: -9999px;
|
||||
@include transition();
|
||||
width: 30px;
|
||||
|
||||
&:hover {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
a.quality_control {
|
||||
background: url(../images/hd.png) center no-repeat;
|
||||
border-right: 1px solid #000;
|
||||
@include box-shadow(1px 0 0 #555, inset 1px 0 0 #555);
|
||||
color: #797979;
|
||||
display: block;
|
||||
float: left;
|
||||
line-height: 46px; //height of play pause buttons
|
||||
margin-left: 0;
|
||||
padding: 0 lh(.5);
|
||||
text-indent: -9999px;
|
||||
@include transition();
|
||||
width: 30px;
|
||||
|
||||
&:hover {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #F44;
|
||||
color: #0ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
a.hide-subtitles {
|
||||
background: url('../images/cc.png') center no-repeat;
|
||||
color: #797979;
|
||||
display: block;
|
||||
float: left;
|
||||
font-weight: 800;
|
||||
line-height: 46px; //height of play pause buttons
|
||||
margin-left: 0;
|
||||
opacity: 1;
|
||||
padding: 0 lh(.5);
|
||||
position: relative;
|
||||
text-indent: -9999px;
|
||||
@include transition();
|
||||
-webkit-font-smoothing: antialiased;
|
||||
width: 30px;
|
||||
|
||||
&:hover {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.off {
|
||||
opacity: .7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover section.video-controls {
|
||||
ul, div {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div.slider {
|
||||
height: 14px;
|
||||
margin-top: -7px;
|
||||
|
||||
a.ui-slider-handle {
|
||||
@include border-radius(20px);
|
||||
height: 20px;
|
||||
margin-left: -10px;
|
||||
top: -4px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ol.subtitles {
|
||||
padding-left: 0;
|
||||
float: left;
|
||||
max-height: 460px;
|
||||
overflow: auto;
|
||||
width: flex-grid(3, 9);
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
border: 0;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
margin-bottom: 8px;
|
||||
padding: 0;
|
||||
line-height: lh();
|
||||
|
||||
&.current {
|
||||
color: #333;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
&:empty {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.closed {
|
||||
@extend .trans;
|
||||
|
||||
article.video-wrapper {
|
||||
width: flex-grid(9,9);
|
||||
}
|
||||
|
||||
ol.subtitles {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.fullscreen {
|
||||
background: rgba(#000, .95);
|
||||
border: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
vertical-align: middle;
|
||||
|
||||
&.closed {
|
||||
ol.subtitles {
|
||||
right: -(flex-grid(4));
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
div.tc-wrapper {
|
||||
@include clearfix;
|
||||
display: table;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
article.video-wrapper {
|
||||
width: 100%;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
float: none;
|
||||
}
|
||||
|
||||
object, iframe {
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
section.video-controls {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
}
|
||||
}
|
||||
|
||||
ol.subtitles {
|
||||
background: rgba(#000, .8);
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
max-width: flex-grid(3);
|
||||
padding: lh();
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
@include transition();
|
||||
|
||||
li {
|
||||
color: #aaa;
|
||||
|
||||
&.current {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
124
common/lib/xmodule/xmodule/foldit_module.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import logging
|
||||
from lxml import etree
|
||||
from dateutil import parser
|
||||
|
||||
from pkg_resources import resource_string
|
||||
|
||||
from xmodule.editing_module import EditingDescriptor
|
||||
from xmodule.x_module import XModule
|
||||
from xmodule.xml_module import XmlDescriptor
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class FolditModule(XModule):
|
||||
def __init__(self, system, location, definition, descriptor,
|
||||
instance_state=None, shared_state=None, **kwargs):
|
||||
XModule.__init__(self, system, location, definition, descriptor,
|
||||
instance_state, shared_state, **kwargs)
|
||||
# ooh look--I'm lazy, so hardcoding the 7.00x required level.
|
||||
# If we need it generalized, can pull from the xml later
|
||||
self.required_level = 4
|
||||
self.required_sublevel = 5
|
||||
|
||||
def parse_due_date():
|
||||
"""
|
||||
Pull out the date, or None
|
||||
"""
|
||||
s = self.metadata.get("due")
|
||||
if s:
|
||||
return parser.parse(s)
|
||||
else:
|
||||
return None
|
||||
|
||||
self.due_str = self.metadata.get("due", "None")
|
||||
self.due = parse_due_date()
|
||||
|
||||
def is_complete(self):
|
||||
"""
|
||||
Did the user get to the required level before the due date?
|
||||
"""
|
||||
# We normally don't want django dependencies in xmodule. foldit is
|
||||
# special. Import this late to avoid errors with things not yet being
|
||||
# initialized.
|
||||
from foldit.models import PuzzleComplete
|
||||
|
||||
complete = PuzzleComplete.is_level_complete(
|
||||
self.system.anonymous_student_id,
|
||||
self.required_level,
|
||||
self.required_sublevel,
|
||||
self.due)
|
||||
return complete
|
||||
|
||||
def completed_puzzles(self):
|
||||
"""
|
||||
Return a list of puzzles that this user has completed, as an array of
|
||||
dicts:
|
||||
|
||||
[ {'set': int,
|
||||
'subset': int,
|
||||
'created': datetime} ]
|
||||
|
||||
The list is sorted by set, then subset
|
||||
"""
|
||||
from foldit.models import PuzzleComplete
|
||||
|
||||
return sorted(
|
||||
PuzzleComplete.completed_puzzles(self.system.anonymous_student_id),
|
||||
key=lambda d: (d['set'], d['subset']))
|
||||
|
||||
|
||||
def get_html(self):
|
||||
"""
|
||||
Render the html for the module.
|
||||
"""
|
||||
goal_level = '{0}-{1}'.format(
|
||||
self.required_level,
|
||||
self.required_sublevel)
|
||||
|
||||
context = {
|
||||
'due': self.due_str,
|
||||
'success': self.is_complete(),
|
||||
'goal_level': goal_level,
|
||||
'completed': self.completed_puzzles(),
|
||||
}
|
||||
|
||||
return self.system.render_template('foldit.html', context)
|
||||
|
||||
|
||||
def get_score(self):
|
||||
"""
|
||||
0 / 1 based on whether student has gotten far enough.
|
||||
"""
|
||||
score = 1 if self.is_complete() else 0
|
||||
return {'score': score,
|
||||
'total': self.max_score()}
|
||||
|
||||
def max_score(self):
|
||||
return 1
|
||||
|
||||
|
||||
class FolditDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
"""
|
||||
Module for adding open ended response questions to courses
|
||||
"""
|
||||
mako_template = "widgets/html-edit.html"
|
||||
module_class = FolditModule
|
||||
filename_extension = "xml"
|
||||
|
||||
stores_state = True
|
||||
has_score = True
|
||||
template_dir_name = "foldit"
|
||||
|
||||
js = {'coffee': [resource_string(__name__, 'js/src/html/edit.coffee')]}
|
||||
js_module_name = "HTMLEditingDescriptor"
|
||||
|
||||
# The grade changes without any student interaction with the edx website,
|
||||
# so always need to actually check.
|
||||
always_recalculate_grades = True
|
||||
|
||||
@classmethod
|
||||
def definition_from_xml(cls, xml_object, system):
|
||||
"""
|
||||
For now, don't need anything from the xml
|
||||
"""
|
||||
return {}
|
||||