diff --git a/apt-packages.txt b/apt-packages.txt index b783ccb67e..0560dfcbc2 100644 --- a/apt-packages.txt +++ b/apt-packages.txt @@ -9,6 +9,7 @@ gfortran liblapack-dev libfreetype6-dev libpng12-dev +libjpeg-dev libxml2-dev libxslt-dev yui-compressor diff --git a/cms/djangoapps/contentstore/module_info_model.py b/cms/djangoapps/contentstore/module_info_model.py index 0b64426137..2282333eb0 100644 --- a/cms/djangoapps/contentstore/module_info_model.py +++ b/cms/djangoapps/contentstore/module_info_model.py @@ -1,35 +1,33 @@ -import logging from static_replace import replace_static_urls from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore import Location -from xmodule.modulestore.django import modulestore -from lxml import etree -import re -from django.http import HttpResponseBadRequest, Http404 +from django.http import Http404 def get_module_info(store, location, parent_location=None, rewrite_static_links=False): - try: - if location.revision is None: - module = store.get_item(location) - else: - module = store.get_item(location) - except ItemNotFoundError: - raise Http404 + try: + if location.revision is None: + module = store.get_item(location) + else: + module = store.get_item(location) + except ItemNotFoundError: + # create a new one + template_location = Location(['i4x', 'edx', 'templates', location.category, 'Empty']) + module = store.clone_item(template_location, location) - data = module.data - if rewrite_static_links: - data = replace_static_urls( - module.data, - None, - course_namespace=Location([ - module.location.tag, - module.location.org, - module.location.course, + data = module.data + if rewrite_static_links: + data = replace_static_urls( + module.data, None, - None - ]) - ) + course_namespace=Location([ + module.location.tag, + module.location.org, + module.location.course, + None, + None + ]) + ) return { 'id': module.location.url(), @@ -41,7 +39,6 @@ def get_module_info(store, location, parent_location=None, rewrite_static_links= def set_module_info(store, location, post_data): module = None - isNew = False try: if location.revision is None: module = store.get_item(location) @@ -55,7 +52,6 @@ def set_module_info(store, location, post_data): # presume that we have an 'Empty' template template_location = Location(['i4x', 'edx', 'templates', location.category, 'Empty']) module = store.clone_item(template_location, location) - isNew = True if post_data.get('data') is not None: data = post_data['data'] diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index 72ae3821cc..dcd1f408cd 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -1,7 +1,7 @@ import json import shutil from django.test.client import Client -from override_settings import override_settings +from django.test.utils import override_settings from django.conf import settings from django.core.urlresolvers import reverse from path import path @@ -10,6 +10,7 @@ import json from fs.osfs import OSFS import copy from mock import Mock +from json import dumps, loads from student.models import Registration from django.contrib.auth.models import User @@ -207,6 +208,15 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase): # check for custom_tags self.verify_content_existence(ms, root_dir, location, 'custom_tags', 'custom_tag_template') + # check for graiding_policy.json + fs = OSFS(root_dir / 'test_export/policies/6.002_Spring_2012') + self.assertTrue(fs.exists('grading_policy.json')) + + # 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) + self.assertEqual(on_disk, course.definition['data']['grading_policy']) # remove old course delete_course(ms, cs, location) diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index 7d3843db3f..6a73248b81 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -249,8 +249,7 @@ class CourseGradingTest(CourseTestCase): altered_grader = CourseGradingModel.update_from_json(test_grader.__dict__) self.assertDictEqual(test_grader.__dict__, altered_grader.__dict__, "cutoff add D") - test_grader.grace_period = {'hours': '4'} - + test_grader.grace_period = {'hours': 4, 'minutes': 5, 'seconds': 0} altered_grader = CourseGradingModel.update_from_json(test_grader.__dict__) print test_grader.__dict__ print altered_grader.__dict__ diff --git a/cms/djangoapps/contentstore/tests/tests.py b/cms/djangoapps/contentstore/tests/tests.py index 9af5b09276..d2f18f2e49 100644 --- a/cms/djangoapps/contentstore/tests/tests.py +++ b/cms/djangoapps/contentstore/tests/tests.py @@ -1,7 +1,6 @@ import json import shutil from django.test.client import Client -from override_settings import override_settings from django.conf import settings from django.core.urlresolvers import reverse from path import path diff --git a/cms/djangoapps/contentstore/tests/utils.py b/cms/djangoapps/contentstore/tests/utils.py index 4e3510463f..be028b2836 100644 --- a/cms/djangoapps/contentstore/tests/utils.py +++ b/cms/djangoapps/contentstore/tests/utils.py @@ -2,7 +2,6 @@ import json import copy from time import time from django.test import TestCase -from override_settings import override_settings from django.conf import settings from student.models import Registration diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 93e6841b8d..0d905ffa32 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -126,7 +126,8 @@ def index(request): course.location.course, course.location.name])) for course in courses], - 'user': request.user + 'user': request.user, + 'disable_course_creation': settings.MITX_FEATURES.get('DISABLE_COURSE_CREATION', False) and not request.user.is_staff }) @@ -1254,6 +1255,10 @@ def edge(request): @login_required @expect_json def create_new_course(request): + + if settings.MITX_FEATURES.get('DISABLE_COURSE_CREATION', False) and not request.user.is_staff: + raise PermissionDenied() + # This logic is repeated in xmodule/modulestore/tests/factories.py # so if you change anything here, you need to also change it there. # TODO: write a test that creates two courses, one with the factory and diff --git a/cms/djangoapps/models/settings/course_grading.py b/cms/djangoapps/models/settings/course_grading.py index 85c76d6a5d..bf01fb4549 100644 --- a/cms/djangoapps/models/settings/course_grading.py +++ b/cms/djangoapps/models/settings/course_grading.py @@ -156,13 +156,8 @@ class CourseGradingModel(object): if 'grace_period' in graceperiodjson: graceperiodjson = graceperiodjson['grace_period'] - timedelta_kwargs = dict( - (key, float(val)) - for key, val - in graceperiodjson.items() - if key in ('days', 'seconds', 'minutes', 'hours') - ) - grace_rep = timedelta(**timedelta_kwargs) + # lms requires these to be in a fixed order + grace_rep = "{0[hours]:d} hours {0[minutes]:d} minutes {0[seconds]:d} seconds".format(graceperiodjson) descriptor = get_modulestore(course_location).get_item(course_location) descriptor.lms.graceperiod = grace_rep @@ -241,6 +236,7 @@ class CourseGradingModel(object): @staticmethod def convert_set_grace_period(descriptor): +<<<<<<< HEAD # 5 hours 59 minutes 59 seconds => converted to iso format rawgrace = descriptor.lms.graceperiod if rawgrace: @@ -265,6 +261,14 @@ class CourseGradingModel(object): return graceperiod else: return None +======= + # 5 hours 59 minutes 59 seconds => { hours: 5, minutes : 59, seconds : 59} + rawgrace = descriptor.metadata.get('graceperiod', None) + if rawgrace: + parsedgrace = {str(key): int(val) for (val, key) in re.findall('\s*(\d+)\s*(\w+)', rawgrace)} + return parsedgrace + else: return None +>>>>>>> origin/master @staticmethod def parse_grader(json_grader): diff --git a/cms/envs/common.py b/cms/envs/common.py index ef7a4f43fa..30aac6ea01 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -165,13 +165,6 @@ STATICFILES_DIRS = [ # This is how you would use the textbook images locally # ("book", ENV_ROOT / "book_images") ] -if os.path.isdir(GITHUB_REPO_ROOT): - STATICFILES_DIRS += [ - # TODO (cpennington): When courses aren't loaded from github, remove this - (course_dir, GITHUB_REPO_ROOT / course_dir) - for course_dir in os.listdir(GITHUB_REPO_ROOT) - if os.path.isdir(GITHUB_REPO_ROOT / course_dir) - ] # Locale/Internationalization TIME_ZONE = 'America/New_York' # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name diff --git a/cms/static/js/views/overview.js b/cms/static/js/views/overview.js index 8cbae177a8..7d92ab69ad 100644 --- a/cms/static/js/views/overview.js +++ b/cms/static/js/views/overview.js @@ -58,6 +58,9 @@ $(document).ready(function() { drop: onSectionReordered, greedy: true }); + + // stop clicks on drag bars from doing their thing w/o stopping drag + $('.drag-handle').click(function(e) {e.preventDefault(); }); }); @@ -202,13 +205,17 @@ function _handleReorder(event, ui, parentIdField, childrenSelector) { children = _.without(children, ui.draggable.data('id')); } // add to this parent (figure out where) - for (var i = 0; i < _els.length; i++) { - if (!ui.draggable.is(_els[i]) && ui.offset.top < $(_els[i]).offset().top) { + for (var i = 0, bump = 0; i < _els.length; i++) { + if (ui.draggable.is(_els[i])) { + bump = -1; // bump indicates that the draggable was passed in the dom but not children's list b/c + // it's not in that list + } + else if (ui.offset.top < $(_els[i]).offset().top) { // insert at i in children and _els ui.draggable.insertBefore($(_els[i])); // TODO figure out correct way to have it remove the style: top:n; setting (and similar line below) ui.draggable.attr("style", "position:relative;"); - children.splice(i, 0, ui.draggable.data('id')); + children.splice(i + bump, 0, ui.draggable.data('id')); break; } } diff --git a/cms/static/js/views/settings/main_settings_view.js b/cms/static/js/views/settings/main_settings_view.js index 826b385dff..f4c7df41a6 100644 --- a/cms/static/js/views/settings/main_settings_view.js +++ b/cms/static/js/views/settings/main_settings_view.js @@ -227,7 +227,7 @@ CMS.Views.Settings.Details = CMS.Views.ValidatingView.extend({ time = 0; } var newVal = new Date(date.getTime() + time * 1000); - if (cacheModel.get(fieldName).getTime() !== newVal.getTime()) { + if (!cacheModel.has(fieldName) || cacheModel.get(fieldName).getTime() !== newVal.getTime()) { cacheModel.save(fieldName, newVal, { error: CMS.ServerError}); } } diff --git a/cms/templates/edit_subsection.html b/cms/templates/edit_subsection.html index afb33ba7ac..8dc787e484 100644 --- a/cms/templates/edit_subsection.html +++ b/cms/templates/edit_subsection.html @@ -107,6 +107,8 @@ + + + +% if timer_expiration_duration: + +% endif + -<%include file="/courseware/course_navigation.html" args="active_page='courseware'" /> +% if timer_expiration_duration: +
+
+ % if timer_navigation_return_url: + Return to Exam + % endif +
Time Remaining:
 
+
+
+% endif + +% if accordion: + <%include file="/courseware/course_navigation.html" args="active_page='courseware'" /> +% endif
+ +% if accordion:
close @@ -76,6 +140,7 @@
+% endif
${content} diff --git a/lms/templates/courseware/instructor_dashboard.html b/lms/templates/courseware/instructor_dashboard.html index 7b177c6b6c..a31ee0025e 100644 --- a/lms/templates/courseware/instructor_dashboard.html +++ b/lms/templates/courseware/instructor_dashboard.html @@ -64,6 +64,7 @@ function goto( mode) Admin | Forum Admin | Enrollment | + DataDump | Manage Groups ] @@ -269,6 +270,20 @@ function goto( mode) ##----------------------------------------------------------------------------- +%if modeflag.get('Data'): +
+

+ +

+

Problem urlname: + + +

+
+%endif + +##----------------------------------------------------------------------------- + %if modeflag.get('Manage Groups'): %if instructor_access:
diff --git a/lms/templates/main.html b/lms/templates/main.html index 5d3fd29104..42d5a71228 100644 --- a/lms/templates/main.html +++ b/lms/templates/main.html @@ -29,13 +29,18 @@ +% if not suppress_toplevel_navigation: <%include file="navigation.html" /> +% endif +
${self.body()} <%block name="bodyextra"/>
+% if not suppress_toplevel_navigation: <%include file="footer.html" /> +% endif <%static:js group='application'/> <%static:js group='module-js'/> diff --git a/lms/templates/staff_problem_info.html b/lms/templates/staff_problem_info.html index 361a0aa182..12fb5738c0 100644 --- a/lms/templates/staff_problem_info.html +++ b/lms/templates/staff_problem_info.html @@ -1,5 +1,6 @@ ${module_content} -%if edit_link: +%if location.category in ['problem','video','html']: +% if edit_link:
Edit / QA
-% endif +% endif
Staff Debug Info
@@ -34,25 +34,45 @@
-
+

EdX is looking to add new talent to our team!

Our mission is to give a world-class education to everyone, everywhere, regardless of gender, income or social status

Today, EdX.org, a not-for-profit provides hundreds of thousands of people from around the globe with access to free education.  We offer amazing quality classes by the best professors from the best schools. We enable our members to uncover a new passion that will transform their lives and their communities.

-

Around the world-from coast to coast, in over 192 countries, people are making the decision to take one or several of our courses. As we continue to grow our operations, we are looking for talented, passionate people with great ideas to join the edX team. We aim to create an environment that is supportive, diverse, and as fun as our brand. If you're results-oriented, dedicated, and ready to contribute to an unparalleled member experience for our community, we really want you to apply.

+

Around the world-from coast to coast, in over 192 countries, people are making the decision to take one or several of our courses. As we continue to grow our operations, we are looking for talented, passionate people with great ideas to join the edX team. We aim to create an environment that is supportive, diverse, and as fun as our brand. If you’re results-oriented, dedicated, and ready to contribute to an unparalleled member experience for our community, we really want you to apply.

As part of the edX team, you’ll receive:

  • Competitive compensation
  • Generous benefits package
  • Free lunch every day
  • -
  • A great working experience where everyone cares
  • +
  • A great working experience where everyone cares and wants to change the world (no, we’re not kidding)
-

While we appreciate every applicant's interest, only those under consideration will be contacted. We regret that phone calls will not be accepted.

+

While we appreciate every applicant’s interest, only those under consideration will be contacted. We regret that phone calls will not be accepted. Equal opportunity employer.

-

Positions

How to Apply

-

E-mail your resume, coverletter and any other materials to jobs@edx.org

+

E-mail your resume, cover letter and any other materials to jobs@edx.org

Our Location

11 Cambridge Center
- Cambridge, MA 02142

+ Cambridge, MA 02142

diff --git a/requirements.txt b/requirements.txt index 0faf2e3ba5..7bfaa11bc6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,7 +24,6 @@ django_nose nosexcover==1.0.7 rednose==0.3.3 GitPython==0.3.2.RC1 -django-override-settings==1.2 mock==0.8.0 PyYAML==3.10 South==0.7.6