Commit ebe67d2d by Calen Pennington

Merge remote-tracking branch 'origin/release/1.0'

Conflicts:
	common/djangoapps/student/views.py
parents 40dc770a b7a389df
......@@ -22,3 +22,5 @@ reports/
\#*\#
*.egg-info
Gemfile.lock
.env/
see doc/ for documentation.
See doc/ for documentation.
......@@ -6,11 +6,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
<%static:css group='base-style'/>
% else:
<link rel="stylesheet" href="${static.url('css/base-style.css')}">
% endif
<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')}" />
<title><%block name="title"></%block></title>
......@@ -27,12 +23,7 @@
<script type="text/javascript" src="${static.url('js/vendor/backbone-min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/markitup/jquery.markitup.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/markitup/sets/wiki/set.js')}"></script>
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
<%static:js group='main'/>
% else:
<script src="${ STATIC_URL }/js/main.js"></script>
% endif
<%static:js group='module-js'/>
<script src="${static.url('js/vendor/jquery.inlineedit.js')}"></script>
<script src="${static.url('js/vendor/jquery.cookie.js')}"></script>
......
......@@ -10,10 +10,10 @@
<hr>
</header>
<div id="enroll">
<div id="register">
<form id="enroll_form" method="post">
<div id="enroll_error" name="enroll_error"></div>
<form id="register_form" method="post">
<div id="register_error" name="register_error"></div>
<label>E-mail</label>
<input name="email" type="email" placeholder="E-mail">
<label>Password</label>
......@@ -64,17 +64,17 @@
});
}
$('form#enroll_form').submit(function(e) {
$('form#register_form').submit(function(e) {
e.preventDefault();
var submit_data = $('#enroll_form').serialize();
var submit_data = $('#register_form').serialize();
postJSON('/create_account',
submit_data,
function(json) {
if(json.success) {
$('#enroll').html(json.value);
$('#register').html(json.value);
} else {
$('#enroll_error').html(json.value).stop().css("background-color", "#933").animate({ backgroundColor: "#333"}, 2000);
$('#register_error').html(json.value).stop().css("background-color", "#933").animate({ backgroundColor: "#333"}, 2000);
}
}
);
......
......@@ -5,6 +5,24 @@ from static_replace import replace_urls
%>
<%def name='url(file)'>${staticfiles_storage.url(file)}</%def>
<%def name='css(group)'>${compressed_css(group)}</%def>
<%def name='js(group)'>${compressed_js(group)}</%def>
<%def name='css(group)'>
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
${compressed_css(group)}
% else:
% for filename in settings.PIPELINE_CSS[group]['source_filenames']:
<link rel="stylesheet" href="${staticfiles_storage.url(filename.replace('.scss', '.css'))}" type="text/css" media="all" / >
% endfor
%endif
</%def>
<%def name='js(group)'>
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
${compressed_js(group)}
% else:
% for filename in settings.PIPELINE_JS[group]['source_filenames']:
<script type="text/javascript" src="${staticfiles_storage.url(filename.replace('.coffee', '.js'))}"></script>
% endfor
%endif
</%def>
<%def name='replace_urls(text)'>${replace_urls(text)}</%def>
##
## One-off script to export 6.002x users into the edX framework
##
## Could be modified to be general by:
## * Changing user_keys and up_keys to handle dates more cleanly
## * Providing a generic set of tables, rather than just users and user profiles
## * Handling certificates and grades
## * Handling merge/forks of UserProfile.meta
import datetime
import json
import os.path
from lxml import etree
from django.core.management.base import BaseCommand
from django.conf import settings
from django.contrib.auth.models import User
from student.models import UserProfile
import mitxmako.middleware as middleware
middleware.MakoMiddleware()
class Command(BaseCommand):
help = \
'''Exports all users and user profiles.
Caveat: Should be looked over before any run
for schema changes.
Current version grabs user_keys from
django.contrib.auth.models.User and up_keys
from student.userprofile. '''
def handle(self, *args, **options):
users = list(User.objects.all())
user_profiles = list(UserProfile.objects.all())
user_profile_dict = dict([(up.user_id, up) for up in user_profiles])
user_tuples = [(user_profile_dict[u.id], u) for u in users if u.id in user_profile_dict]
user_keys = ['id', 'username', 'email', 'password', 'is_staff',
'is_active', 'is_superuser', 'last_login', 'date_joined',
'password']
up_keys = ['language', 'location','meta','name', 'id','user_id']
def extract_dict(keys, object):
d = {}
for key in keys:
item = object.__getattribute__(key)
if type(item) == datetime.datetime:
item = item.isoformat()
d[key] = item
return d
extracted = [{'up':extract_dict(up_keys, t[0]), 'u':extract_dict(user_keys, t[1])} for t in user_tuples]
fp = open('transfer_users.txt', 'w')
json.dump(extracted, fp)
fp.close()
##
## One-off script to import 6.002x users into the edX framework
## See export for more info
import datetime
import json
import dateutil.parser
import os.path
from lxml import etree
from django.core.management.base import BaseCommand
from django.conf import settings
from django.contrib.auth.models import User
from student.models import UserProfile
import mitxmako.middleware as middleware
middleware.MakoMiddleware()
def import_user(u):
user_info = u['u']
up_info = u['up']
# HACK to handle dates
user_info['last_login'] = dateutil.parser.parse(user_info['last_login'])
user_info['date_joined'] = dateutil.parser.parse(user_info['date_joined'])
user_keys = ['id', 'username', 'email', 'password', 'is_staff',
'is_active', 'is_superuser', 'last_login', 'date_joined',
'password']
up_keys = ['language', 'location','meta','name', 'id','user_id']
u = User()
for key in user_keys:
u.__setattr__(key, user_info[key])
u.save()
up = UserProfile()
up.user = u
for key in up_keys:
up.__setattr__(key, up_info[key])
up.save()
class Command(BaseCommand):
help = \
'''Exports all users and user profiles.
Caveat: Should be looked over before any run
for schema changes.
Current version grabs user_keys from
django.contrib.auth.models.User and up_keys
from student.userprofile. '''
def handle(self, *args, **options):
extracted = json.load(open('transfer_users.txt'))
n=0
for u in extracted:
import_user(u)
if n%100 == 0:
print n
n = n+1
......@@ -8,19 +8,22 @@ from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Removing unique constraint on 'CourseEnrollment', fields ['user']
db.delete_unique('student_courseenrollment', ['user_id'])
# Changing field 'CourseEnrollment.user'
db.alter_column('student_courseenrollment', 'user_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User']))
# This table is dropped in a subsequent migration. This migration was causing problems when using InnoDB,
# so we are just dropping it.
pass
# # Removing unique constraint on 'CourseEnrollment', fields ['user']
# db.delete_unique('student_courseenrollment', ['user_id'])
#
#
# # Changing field 'CourseEnrollment.user'
# db.alter_column('student_courseenrollment', 'user_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User']))
def backwards(self, orm):
# Changing field 'CourseEnrollment.user'
db.alter_column('student_courseenrollment', 'user_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True))
# Adding unique constraint on 'CourseEnrollment', fields ['user']
db.create_unique('student_courseenrollment', ['user_id'])
pass
# # Changing field 'CourseEnrollment.user'
# db.alter_column('student_courseenrollment', 'user_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True))
# # Adding unique constraint on 'CourseEnrollment', fields ['user']
# db.create_unique('student_courseenrollment', ['user_id'])
models = {
......
......@@ -5,14 +5,16 @@ If you make changes to this model, be sure to create an appropriate migration
file and check it in at the same time as your model changes. To do that,
1. Go to the mitx dir
2. ./manage.py schemamigration user --auto description_of_your_change
3. Add the migration file created in mitx/courseware/migrations/
2. django-admin.py schemamigration student --auto --settings=lms.envs.dev --pythonpath=. description_of_your_change
3. Add the migration file created in mitx/common/djangoapps/student/migrations/
"""
from datetime import datetime
import json
import uuid
from django.db import models
from django.contrib.auth.models import User
import json
from django_countries import CountryField
#from cache_toolbox import cache_model, cache_relation
......@@ -20,23 +22,43 @@ class UserProfile(models.Model):
class Meta:
db_table = "auth_userprofile"
GENDER_CHOICES = (('male', 'Male'), ('female', 'Female'), ('other', 'Other'))
## CRITICAL TODO/SECURITY
# Sanitize all fields.
# This is not visible to other users, but could introduce holes later
user = models.OneToOneField(User, unique=True, db_index=True, related_name='profile')
name = models.CharField(blank=True, max_length=255, db_index=True)
language = models.CharField(blank=True, max_length=255, db_index=True)
location = models.CharField(blank=True, max_length=255, db_index=True) # TODO: What are we doing with this?
meta = models.TextField(blank=True) # JSON dictionary for future expansion
courseware = models.CharField(blank=True, max_length=255, default='course.xml')
gender = models.CharField(blank=True, null=True, max_length=6, choices=GENDER_CHOICES)
date_of_birth = models.DateField(blank=True, null=True)
# Location is no longer used, but is held here for backwards compatibility
# for users imported from our first class.
language = models.CharField(blank=True, max_length=255, db_index=True)
location = models.CharField(blank=True, max_length=255, db_index=True)
# Optional demographic data we started capturing from Fall 2012
this_year = datetime.now().year
VALID_YEARS = range(this_year, this_year - 120, -1)
year_of_birth = models.IntegerField(blank=True, null=True, db_index=True)
GENDER_CHOICES = (('m', 'Male'), ('f', 'Female'), ('o', 'Other'))
gender = models.CharField(blank=True, null=True, max_length=6, db_index=True,
choices=GENDER_CHOICES)
LEVEL_OF_EDUCATION_CHOICES = (('p_se', 'Doctorate in science or engineering'),
('p_oth', 'Doctorate in another field'),
('m', "Master's or professional degree"),
('b', "Bachelor's degree"),
('hs', "Secondary/high school"),
('jhs', "Junior secondary/junior high/middle school"),
('el', "Elementary/primary school"),
('none', "None"),
('other', "Other"))
level_of_education = models.CharField(
blank=True, null=True, max_length=6, db_index=True,
choices=LEVEL_OF_EDUCATION_CHOICES
)
mailing_address = models.TextField(blank=True, null=True)
country = models.CharField(blank=True, null=True, max_length=255)
telephone_number = models.CharField(blank=True, null=True, max_length=25)
occupation = models.CharField(blank=True, null=True, max_length=255)
goals = models.TextField(blank=True, null=True)
def get_meta(self):
js_str = self.meta
......@@ -90,8 +112,13 @@ class PendingEmailChange(models.Model):
activation_key = models.CharField(('activation key'), max_length=32, unique=True, db_index=True)
class CourseEnrollment(models.Model):
user = models.ForeignKey(User, db_index=True)
course_id = models.CharField(max_length=255)
user = models.ForeignKey(User)
course_id = models.CharField(max_length=255, db_index=True)
created = models.DateTimeField(auto_now_add=True, null=True, db_index=True)
class Meta:
unique_together = (('user', 'course_id'), )
#cache_relation(User.profile)
......
......@@ -12,8 +12,32 @@ class TrackMiddleware:
if request.META['PATH_INFO'] in ['/event', '/login']:
return
event = { 'GET' : dict(request.GET),
'POST' : dict(request.POST)}
# Removes passwords from the tracking logs
# WARNING: This list needs to be changed whenever we change
# password handling functionality.
#
# As of the time of this comment, only 'password' is used
# The rest are there for future extension.
#
# Passwords should never be sent as GET requests, but
# this can happen due to older browser bugs. We censor
# this too.
#
# We should manually confirm no passwords make it into log
# files when we change this.
censored_strings = ['password', 'newpassword', 'new_password',
'oldpassword', 'old_password']
post_dict = dict(request.POST)
get_dict = dict(request.GET)
for string in censored_strings:
if string in post_dict:
post_dict[string] = '*'*8
if string in get_dict:
get_dict[string] = '*'*8
event = { 'GET' : dict(get_dict),
'POST' : dict(post_dict)}
# TODO: Confirm no large file uploads
event = json.dumps(event)
......
import time
import dateutil.parser
from fs.errors import ResourceNotFoundError
import logging
from path import path
from xmodule.modulestore import Location
from xmodule.seq_module import SequenceDescriptor, SequenceModule
......@@ -29,7 +27,7 @@ class CourseDescriptor(SequenceDescriptor):
def has_started(self):
return time.gmtime() > self.start
@classmethod
def id_to_location(cls, course_id):
org, course, name = course_id.split('/')
......@@ -40,82 +38,21 @@ class CourseDescriptor(SequenceDescriptor):
return "/".join([self.location.org, self.location.course, self.location.name])
@property
def start_date_text(self):
return time.strftime("%b %d, %Y", self.start)
@property
def title(self):
return self.metadata['display_name']
@property
def number(self):
return self.location.course
@property
def instructors(self):
return self.get_about_section("instructors").split("\n")
@property
def wiki_namespace(self):
return self.location.course
def get_about_section(self, section_key):
"""
This returns the snippet of html to be rendered on the course about page, given the key for the section.
Valid keys:
- overview
- title
- university
- number
- short_description
- description
- key_dates (includes start, end, exams, etc)
- video
- course_staff_short
- course_staff_extended
- requirements
- syllabus
- textbook
- faq
- more_info
"""
# Many of these are stored as html files instead of some semantic markup. This can change without effecting
# this interface when we find a good format for defining so many snippets of text/html.
# TODO: Remove number, instructors from this list
if section_key in ['short_description', 'description', 'key_dates', 'video', 'course_staff_short', 'course_staff_extended',
'requirements', 'syllabus', 'textbook', 'faq', 'more_info', 'number', 'instructors', 'overview']:
try:
with self.system.resources_fs.open(path("about") / section_key + ".html") as htmlFile:
return htmlFile.read().decode('utf-8')
except ResourceNotFoundError:
log.exception("Missing about section {key} in course {url}".format(key=section_key, url=self.location.url()))
return "! About section missing !"
elif section_key == "title":
return self.metadata.get('display_name', self.name)
elif section_key == "university":
return self.location.org
elif section_key == "number":
return self.number
raise KeyError("Invalid about key " + str(section_key))
def get_info_section(self, section_key):
"""
This returns the snippet of html to be rendered on the course info page, given the key for the section.
Valid keys:
- handouts
- guest_handouts
- updates
- guest_updates
"""
# Many of these are stored as html files instead of some semantic markup. This can change without effecting
# this interface when we find a good format for defining so many snippets of text/html.
if section_key in ['handouts', 'guest_handouts', 'updates', 'guest_updates']:
try:
with self.system.resources_fs.open(path("info") / section_key + ".html") as htmlFile:
return htmlFile.read().decode('utf-8')
except ResourceNotFoundError:
log.exception("Missing info section {key} in course {url}".format(key=section_key, url=self.location.url()))
return "! Info section missing !"
raise KeyError("Invalid about key " + str(section_key))
@property
def org(self):
return self.location.org
\ No newline at end of file
from fs.errors import ResourceNotFoundError
from functools import wraps
import logging
from path import path
from django.conf import settings
from django.http import Http404
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
log = logging.getLogger(__name__)
def check_course(course_id, course_must_be_open=True, course_required=True):
"""
......@@ -24,10 +30,88 @@ def check_course(course_id, course_must_be_open=True, course_required=True):
try:
course_loc = CourseDescriptor.id_to_location(course_id)
course = modulestore().get_item(course_loc)
except KeyError:
except (KeyError, ItemNotFoundError):
raise Http404("Course not found.")
if course_must_be_open and not course.has_started():
raise Http404("This course has not yet started.")
return course
### These methods look like they should be on the course_module object itself, but they rely
### on the lms. Maybe they should be added dynamically to the class?
def course_static_url(course):
return settings.STATIC_URL + "/" + course.metadata['data_dir'] + "/"
def course_image_url(course):
return course_static_url(course) + "images/course_image.png"
def get_course_about_section(course, section_key):
"""
This returns the snippet of html to be rendered on the course about page, given the key for the section.
Valid keys:
- overview
- title
- university
- number
- short_description
- description
- key_dates (includes start, end, exams, etc)
- video
- course_staff_short
- course_staff_extended
- requirements
- syllabus
- textbook
- faq
- more_info
"""
# Many of these are stored as html files instead of some semantic markup. This can change without effecting
# this interface when we find a good format for defining so many snippets of text/html.
# TODO: Remove number, instructors from this list
if section_key in ['short_description', 'description', 'key_dates', 'video', 'course_staff_short', 'course_staff_extended',
'requirements', 'syllabus', 'textbook', 'faq', 'more_info', 'number', 'instructors', 'overview',
'effort', 'end_date', 'prerequisites']:
try:
with course.system.resources_fs.open(path("about") / section_key + ".html") as htmlFile:
return htmlFile.read().decode('utf-8').format(COURSE_STATIC_URL = course_static_url(course) )
except ResourceNotFoundError:
log.warning("Missing about section {key} in course {url}".format(key=section_key, url=course.location.url()))
return None
elif section_key == "title":
return course.metadata.get('display_name', course.name)
elif section_key == "university":
return course.location.org
elif section_key == "number":
return course.number
raise KeyError("Invalid about key " + str(section_key))
def get_course_info_section(course, section_key):
"""
This returns the snippet of html to be rendered on the course info page, given the key for the section.
Valid keys:
- handouts
- guest_handouts
- updates
- guest_updates
"""
# Many of these are stored as html files instead of some semantic markup. This can change without effecting
# this interface when we find a good format for defining so many snippets of text/html.
if section_key in ['handouts', 'guest_handouts', 'updates', 'guest_updates']:
try:
with course.system.resources_fs.open(path("info") / section_key + ".html") as htmlFile:
return htmlFile.read().decode('utf-8')
except ResourceNotFoundError:
log.exception("Missing info section {key} in course {url}".format(key=section_key, url=course.location.url()))
return "! Info section missing !"
raise KeyError("Invalid about key " + str(section_key))
\ No newline at end of file
from collections import defaultdict
import json
import logging
import urllib
import itertools
from django.conf import settings
from django.core.context_processors import csrf
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.http import Http404
from django.http import Http404, HttpResponse
from django.shortcuts import redirect
from mitxmako.shortcuts import render_to_response, render_to_string
#from django.views.decorators.csrf import ensure_csrf_cookie
......@@ -18,7 +22,7 @@ from student.models import UserProfile
from multicourse import multicourse_settings
from util.cache import cache, cache_if_anonymous
from student.models import UserTestGroup
from student.models import UserTestGroup, CourseEnrollment
from courseware import grades
from courseware.courses import check_course
from xmodule.modulestore.django import modulestore
......@@ -53,8 +57,12 @@ def format_url_params(params):
@cache_if_anonymous
def courses(request):
# TODO: Clean up how 'error' is done.
context = {'courses': modulestore().get_courses()}
return render_to_response("courses.html", context)
courses = sorted(modulestore().get_courses(), key=lambda course: course.number)
universities = defaultdict(list)
for course in courses:
universities[course.org].append(course)
return render_to_response("courses.html", { 'universities': universities })
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def gradebook(request, course_id):
......@@ -249,3 +257,32 @@ def course_info(request, course_id):
course = check_course(course_id)
return render_to_response('info.html', {'course': course})
@ensure_csrf_cookie
@cache_if_anonymous
def course_about(request, course_id):
def registered_for_course(course, user):
if user.is_authenticated():
return CourseEnrollment.objects.filter(user = user, course_id=course.id).exists()
else:
return False
course = check_course(course_id, course_must_be_open=False)
registered = registered_for_course(course, request.user)
return render_to_response('portal/course_about.html', {'course': course, 'registered': registered})
@ensure_csrf_cookie
@cache_if_anonymous
def university_profile(request, org_id):
all_courses = sorted(modulestore().get_courses(), key=lambda course: course.number)
valid_org_ids = set(c.org for c in all_courses)
if org_id not in valid_org_ids:
raise Http404("University Profile not found for {0}".format(org_id))
# Only grab courses for this org...
courses=[c for c in all_courses if c.org == org_id]
context = dict(courses=courses, org_id=org_id)
template_file = "university_profile/{0}.html".format(org_id).lower()
return render_to_response(template_file, context)
......@@ -17,7 +17,7 @@ class ShouldHaveExactlyOneRootSlug(Exception):
pass
class Namespace(models.Model):
name = models.CharField(max_length=30, db_index=True, unique=True, verbose_name=_('namespace'))
name = models.CharField(max_length=30, unique=True, verbose_name=_('namespace'))
# TODO: We may want to add permissions, etc later
@classmethod
......
......@@ -16,10 +16,6 @@ if settings.STATIC_GRAB:
valid_templates = valid_templates+['server-down.html',
'server-error.html'
'server-overloaded.html',
'mitx_global.html',
'mitx-overview.html',
'6002x-faq.html',
'6002x-press-release.html'
]
def index(request, template):
......@@ -40,15 +36,11 @@ def render(request, template):
url(r'^jobs$', 'static_template_view.views.render', {'template': 'jobs.html'}, name="jobs")
"""
return render_to_response('static_templates/' + template, {})
valid_auth_templates=[]
def render_404(request):
return render_to_response('static_templates/404.html', {})
def render_500(request):
return render_to_response('static_templates/server-error.html', {})
def auth_index(request, template):
if not request.user.is_authenticated():
return redirect('/')
if template in valid_auth_templates:
return render_to_response(template,{})
else:
return redirect('/')
......@@ -104,6 +104,8 @@ LIB_URL = '/static/js/'
# Dev machines shouldn't need the book
# BOOK_URL = '/static/book/'
BOOK_URL = 'https://mitxstatic.s3.amazonaws.com/book_images/' # For AWS deploys
# RSS_URL = r'lms/templates/feed.rss'
RSS_TIMEOUT = 600
# Configuration option for when we want to grab server error pages
STATIC_GRAB = False
......@@ -260,7 +262,6 @@ TEMPLATE_LOADERS = (
MIDDLEWARE_CLASSES = (
'util.middleware.ExceptionLoggingMiddleware',
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
......@@ -292,15 +293,59 @@ PIPELINE_CSS = {
'source_filenames': ['sass/application.scss'],
'output_filename': 'css/application.css',
},
'course': {
'source_filenames': ['sass/application.scss', 'css/vendor/codemirror.css', 'css/vendor/jquery.treeview.css'],
'output_filename': 'css/course.css',
},
'ie-fixes': {
'source_filenames': ['sass/ie.scss'],
'output_filename': 'css/ie.css',
},
}
PIPELINE_ALWAYS_RECOMPILE = ['sass/application.scss']
PIPELINE_ALWAYS_RECOMPILE = ['sass/application.scss', 'sass/ie.scss']
courseware_only_js = [
PROJECT_ROOT / 'static/coffee/src/' + pth + '.coffee'
for pth
in ['courseware', 'histogram', 'navigation', 'time', ]
]
courseware_only_js += [
pth for pth
in glob2.glob(PROJECT_ROOT / 'static/coffee/src/modules/**/*.coffee')
]
main_vendor_js = [
'js/vendor/jquery.min.js',
'js/vendor/jquery-ui.min.js',
'js/vendor/swfobject/swfobject.js',
'js/vendor/jquery.cookie.js',
'js/vendor/jquery.qtip.min.js',
]
PIPELINE_JS = {
'application': {
'source_filenames': [pth.replace(PROJECT_ROOT / 'static/', '') for pth in glob2.glob(PROJECT_ROOT / 'static/coffee/src/**/*.coffee')],
# Application will contain all paths not in courseware_only_js
'source_filenames': [
pth.replace(PROJECT_ROOT / 'static/', '')
for pth in glob2.glob(PROJECT_ROOT / 'static/coffee/src/**/*.coffee')\
if pth not in courseware_only_js
] + [
'js/form.ext.js',
'js/my_courses_dropdown.js',
'js/toggle_login_modal.js',
'js/sticky_filter.js',
],
'output_filename': 'js/application.js'
},
'courseware': {
'source_filenames': [pth.replace(PROJECT_ROOT / 'static/', '') for pth in courseware_only_js],
'output_filename': 'js/courseware.js'
},
'main_vendor': {
'source_filenames': main_vendor_js,
'output_filename': 'js/main_vendor.js',
},
'spec': {
'source_filenames': [pth.replace(PROJECT_ROOT / 'static/', '') for pth in glob2.glob(PROJECT_ROOT / 'static/coffee/spec/**/*.coffee')],
'output_filename': 'js/spec.js'
......
......@@ -4,4 +4,5 @@
*.swp
*.orig
*.DS_Store
application.css
\ No newline at end of file
application.css
ie.css
\ No newline at end of file
(function($, undefined) {
var form_ext;
$.form_ext = form_ext = {
ajax: function(options) {
return $.ajax(options);
},
handleRemote: function(element) {
var method = element.attr('method');
var url = element.attr('action');
var data = element.serializeArray();
var options = {
type: method || 'GET',
data: data,
dataType: 'text json',
success: function(data, status, xhr) {
element.trigger("ajax:success", [data, status, xhr]);
},
complete: function(xhr, status) {
element.trigger("ajax:complete", [xhr, status]);
},
error: function(xhr, status, error) {
element.trigger("ajax:error", [xhr, status, error]);
}
}
if(url) { options.url = url; }
return form_ext.ajax(options)
},
CSRFProtection: function(xhr) {
var token = $.cookie('csrftoken');
if (token) xhr.setRequestHeader('X-CSRFToken', token);
},
}
$.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { form_ext.CSRFProtection(xhr); }});
$(document).delegate('form', 'submit', function(e) {
var form = $(this),
remote = form.data("remote") !== undefined;
if(remote) {
form_ext.handleRemote(form);
return false;
}
});
})(jQuery);
......@@ -7,9 +7,11 @@
closeButton: null,
position: 'fixed'
}
var overlay = $("<div id='lean_overlay'></div>");
$("body").append(overlay);
if ($("#lean_overlay").length == 0) {
var overlay = $("<div id='lean_overlay'></div>");
$("body").append(overlay);
}
options = $.extend(defaults, options);
......@@ -48,6 +50,16 @@
})
$(modal_id).fadeTo(200,1);
$(modal_id).find(".notice").hide().html("");
var notice = $(this).data('notice')
if(notice !== undefined) {
$notice = $(modal_id).find(".notice");
$notice.show().html(notice);
// This is for activating leanModal links that were in the notice. We should have a cleaner way of
// allowing all dynamically added leanmodal links to work.
$notice.find("a[rel*=leanModal]").leanModal({ top : 120, overlay: 1, closeButton: ".close-modal", position: 'absolute' });
}
window.scrollTo(0, 0);
e.preventDefault();
});
......@@ -65,8 +77,13 @@
$(this).leanModal({ top : 120, overlay: 1, closeButton: ".close-modal", position: 'absolute' });
embed = $($(this).attr('href')).find('iframe')
if(embed.length > 0) {
embed.data('src', embed.attr('src') + '?autoplay=1');
embed.attr('src', '');
if(embed.attr('src').indexOf("?") > 0) {
embed.data('src', embed.attr('src') + '&autoplay=1&rel=0');
embed.attr('src', '');
} else {
embed.data('src', embed.attr('src') + '?autoplay=1&rel=0');
embed.attr('src', '');
}
}
});
})(jQuery);
......@@ -8,13 +8,6 @@
@import 'base/extends';
@import 'base/animations';
// Courseware styles
@import 'sass_old/base/variables';
@import 'sass_old/base/extends';
@import 'sass_old/base/functions';
// Multicourse styles
@import 'shared/forms';
@import 'shared/footer';
......@@ -24,20 +17,12 @@
@import 'shared/modal';
@import 'shared/activation_messages';
@import 'home';
@import 'dashboard';
@import 'courseware_subnav';
@import 'courses';
@import 'course_about';
@import 'jobs';
@import 'about_pages';
@import 'press_release';
// Courseware styles
@import 'sass_old/courseware/courseware';
@import 'sass_old/courseware/sequence-nav';
@import 'sass_old/courseware/sidebar';
@import 'sass_old/courseware/video';
@import 'sass_old/courseware/amplifier';
@import 'sass_old/courseware/problems';
@import 'multicourse/home';
@import 'multicourse/dashboard';
@import 'multicourse/courses';
@import 'multicourse/course_about';
@import 'multicourse/jobs';
@import 'multicourse/about_pages';
@import 'multicourse/press_release';
@import 'multicourse/password_reset';
@import 'multicourse/error-pages';
......@@ -3,7 +3,7 @@ html, body {
font-family: $sans-serif;
font-size: 1em;
line-height: 1em;
-webkit-font-smoothing: antialiased;
//-webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6 {
......@@ -83,7 +83,12 @@ a:link, a:visited {
@include clearfix;
margin: 0 auto 0;
padding: 0px 10px;
width: grid-width(12);
max-width: grid-width(12);
}
span.edx {
text-transform: none;
font: inherit;
}
.static-container {
......
/* Generated by Font Squirrel (http://www.fontsquirrel.com) on January 25, 2012 05:06:34 PM America/New_York */
@font-face {
font-family: 'Open Sans';
src: url('../fonts/OpenSans-Light-webfont.eot');
......@@ -32,7 +31,7 @@
url('../fonts/OpenSans-Regular-webfont.woff') format('woff'),
url('../fonts/OpenSans-Regular-webfont.ttf') format('truetype'),
url('../fonts/OpenSans-Regular-webfont.svg#OpenSansRegular') format('svg');
font-weight: 600;
font-weight: 400;
font-style: normal;
}
......@@ -49,31 +48,6 @@
}
// Not used in UI
// @font-face {
// font-family: 'Open Sans';
// src: url('../fonts/OpenSans-Semibold-webfont.eot');
// src: url('../fonts/OpenSans-Semibold-webfont.eot?#iefix') format('embedded-opentype'),
// url('../fonts/OpenSans-Semibold-webfont.woff') format('woff'),
// url('../fonts/OpenSans-Semibold-webfont.ttf') format('truetype'),
// url('../fonts/OpenSans-Semibold-webfont.svg#OpenSansSemibold') format('svg');
// font-weight: 600;
// font-style: normal;
// }
// @font-face {
// font-family: 'Open Sans';
// src: url('../fonts/OpenSans-SemiboldItalic-webfont.eot');
// src: url('../fonts/OpenSans-SemiboldItalic-webfont.eot?#iefix') format('embedded-opentype'),
// url('../fonts/OpenSans-SemiboldItalic-webfont.woff') format('woff'),
// url('../fonts/OpenSans-SemiboldItalic-webfont.ttf') format('truetype'),
// url('../fonts/OpenSans-SemiboldItalic-webfont.svg#OpenSansSemiboldItalic') format('svg');
// font-weight: 600;
// font-style: italic;
// }
@font-face {
font-family: 'Open Sans';
src: url('../fonts/OpenSans-Bold-webfont.eot');
......
@import 'bourbon/bourbon';
@import 'base/reset';
@import 'base/font_face';
@import 'base/variables';
@import 'base/base';
@import 'base/mixins';
@import 'base/extends';
@import 'base/animations';
// Course styles
@import 'course/courseware_subnav';
// Courseware styles
@import 'course/old/base/variables';
@import 'course/old/base/extends';
@import 'course/old/base/functions';
@import 'course/old/courseware/courseware';
@import 'course/old/courseware/sequence-nav';
@import 'course/old/courseware/sidebar';
@import 'course/old/courseware/video';
@import 'course/old/courseware/amplifier';
@import 'course/old/courseware/problems';
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment