-
+
@@ -48,7 +48,7 @@
-
Subsection Settings
+Subsection Settings
diff --git a/cms/templates/unit.html b/cms/templates/unit.html
index ef94d51576..f3a779604e 100644
--- a/cms/templates/unit.html
+++ b/cms/templates/unit.html
@@ -98,7 +98,7 @@
-
Unit Settings
+Unit Settings
@@ -126,7 +126,7 @@
-
Unit Location
+Unit Location
-
diff --git a/cms/templates/widgets/problem-edit.html b/cms/templates/widgets/problem-edit.html
index 74d35cd646..4ff9d299ab 100644
--- a/cms/templates/widgets/problem-edit.html
+++ b/cms/templates/widgets/problem-edit.html
@@ -4,8 +4,8 @@
%if markdown != '' or data == '
- +
-
-
Header
-+Heading 1
+
diff --git a/common/lib/xmodule/xmodule/css/html/display.scss b/common/lib/xmodule/xmodule/css/html/display.scss new file mode 100644 index 0000000000..18794dd0b7 --- /dev/null +++ b/common/lib/xmodule/xmodule/css/html/display.scss @@ -0,0 +1,123 @@ +// HTML component display: +* { + line-height: 1.4em; +} + +h1 { + color: $baseFontColor; + font: normal 2em/1.4em $sans-serif; + letter-spacing: 1px; + margin: 0 0 1.416em 0; + } + +h2 { + color: #646464; + font: normal 1.2em/1.2em $sans-serif; + letter-spacing: 1px; + margin-bottom: 15px; + text-transform: uppercase; + -webkit-font-smoothing: antialiased; +} + +h3, h4, h5, h6 { + margin: 0 0 10px 0; + font-weight: 600; +} + +h3 { + font-size: 1.2em; +} + +h4 { + font-size: 1em; +} + +h5 { + font-size: .83em; +} + +h6 { + font-size: 0.75em; +} + +p { + margin-bottom: 1.416em; + font-size: 1em; + line-height: 1.6em !important; + color: $baseFontColor; +} + +em, i { + font-style: italic; +} + +strong, b { + font-style: bold; +} + +p + p, ul + p, ol + p { + margin-top: 20px; +} + +ol, ul { + margin: 1em 0; + padding: 0 0 0 1em; + color: $baseFontColor; + + li { + margin-bottom: 0.708em; + } +} + +ol { + list-style: decimal outside none; +} + +ul { + list-style: disc outside none; +} + +a { + &:link, &:visited, &:hover, &:active { + color: #1d9dd9; + } +} + +img { + max-width: 100%; +} + +pre { + margin: 1em 0; + color: $baseFontColor; + font-family: monospace, serif; + font-size: 1em; + white-space: pre-wrap; + word-wrap: break-word; +} + +code { + color: $baseFontColor; + font-family: monospace, serif; + background: none; + padding: 0; +} + +table { + width: 100%; + border-collapse: collapse; + font-size: 16px; +} + +th { + background: #eee; + font-weight: bold; +} + +table td, th { + margin: 20px 0; + padding: 10px; + border: 1px solid #ccc !important; + text-align: left; + font-size: 14px; +} \ No newline at end of file diff --git a/common/lib/xmodule/xmodule/css/html/edit.scss b/common/lib/xmodule/xmodule/css/html/edit.scss index 5897e3570b..bd9722df67 100644 --- a/common/lib/xmodule/xmodule/css/html/edit.scss +++ b/common/lib/xmodule/xmodule/css/html/edit.scss @@ -1,3 +1,4 @@ +// HTML component editor: .html-editor { @include clearfix(); diff --git a/common/lib/xmodule/xmodule/css/problem/edit.scss b/common/lib/xmodule/xmodule/css/problem/edit.scss index eec36d4109..be5455e901 100644 --- a/common/lib/xmodule/xmodule/css/problem/edit.scss +++ b/common/lib/xmodule/xmodule/css/problem/edit.scss @@ -107,7 +107,7 @@ background: url(../img/problem-editor-icons.png) no-repeat; } -.problem-editor-icon.header { +.problem-editor-icon.heading1 { width: 18px; background-position: -265px 0; } diff --git a/common/lib/xmodule/xmodule/html_module.py b/common/lib/xmodule/xmodule/html_module.py index 33711c8161..612e78ce35 100644 --- a/common/lib/xmodule/xmodule/html_module.py +++ b/common/lib/xmodule/xmodule/html_module.py @@ -25,7 +25,8 @@ class HtmlModule(XModule): ] } js_module_name = "HTMLModule" - + css = {'scss': [resource_string(__name__, 'css/html/display.scss')]} + def get_html(self): return self.html diff --git a/common/lib/xmodule/xmodule/js/src/html/edit.coffee b/common/lib/xmodule/xmodule/js/src/html/edit.coffee index dfb197e204..35c4464c89 100644 --- a/common/lib/xmodule/xmodule/js/src/html/edit.coffee +++ b/common/lib/xmodule/xmodule/js/src/html/edit.coffee @@ -22,15 +22,24 @@ class @HTMLEditingDescriptor schema: "html5", # TODO: we should share this CSS with studio (and LMS) content_css : "/static/css/tiny-mce.css", + # Disable h4, h5, and h6 styles as we don't have CSS for them. + formats : { + h4: {}, + h5: {}, + h6: {} + }, + # Disable visual aid on borderless table. + visual:false, # We may want to add "styleselect" when we collect all styles used throughout the LMS - theme_advanced_buttons1 : "formatselect,bold,italic,underline,bullist,numlist,outdent,indent,blockquote,link,unlink", + theme_advanced_buttons1 : "formatselect,bold,italic,underline,|,bullist,numlist,outdent,indent,|,blockquote,wrapAsCode,|,link,unlink", theme_advanced_toolbar_location : "top", theme_advanced_toolbar_align : "left", theme_advanced_statusbar_location : "none", theme_advanced_resizing : true, - theme_advanced_blockformats : "p,code,h2,h3,blockquote", + theme_advanced_blockformats : "p,pre,h1,h2,h3", width: '100%', height: '400px', + setup : HTMLEditingDescriptor.setupTinyMCE, # Cannot get access to tinyMCE Editor instance (for focusing) until after it is rendered. # The tinyMCE callback passes in the editor as a paramter. init_instance_callback: @focusVisualEditor @@ -39,13 +48,25 @@ class @HTMLEditingDescriptor @showingVisualEditor = true @element.on('click', '.editor-tabs .tab', @onSwitchEditor) + @setupTinyMCE: (ed) -> + ed.addButton('wrapAsCode', { + title : 'Code Block', + image : '/static/images/ico-tinymce-code.png', + onclick : () -> + ed.formatter.toggle('code') + }) + + ed.onNodeChange.add((editor, command, e) -> + command.setActive('wrapAsCode', e.nodeName == 'CODE') + ) + onSwitchEditor: (e)=> e.preventDefault(); if not $(e.currentTarget).hasClass('current') - $('.editor-tabs .current').removeClass('current') + $('.editor-tabs .current', @element).removeClass('current') $(e.currentTarget).addClass('current') - $('table.mceToolbar').toggleClass(HTMLEditingDescriptor.isInactiveClass) + $('table.mceToolbar', @element).toggleClass(HTMLEditingDescriptor.isInactiveClass) $(@advanced_editor.getWrapperElement()).toggleClass(HTMLEditingDescriptor.isInactiveClass) visualEditor = @getVisualEditor() diff --git a/common/lib/xmodule/xmodule/modulestore/draft.py b/common/lib/xmodule/xmodule/modulestore/draft.py index 410ece0e75..ef2a848cac 100644 --- a/common/lib/xmodule/xmodule/modulestore/draft.py +++ b/common/lib/xmodule/xmodule/modulestore/draft.py @@ -73,7 +73,7 @@ class DraftModuleStore(ModuleStoreBase): except ItemNotFoundError: return wrap_draft(super(DraftModuleStore, self).get_instance(course_id, location, depth=0)) - def get_items(self, location, depth=0): + def get_items(self, location, course_id=None, depth=0): """ Returns a list of XModuleDescriptor instances for the items that match location. Any element of location that is None is treated @@ -89,8 +89,8 @@ class DraftModuleStore(ModuleStoreBase): draft_loc = as_draft(location) # cdodge: we're forcing depth=0 here as the Draft store is not handling caching well - draft_items = super(DraftModuleStore, self).get_items(draft_loc, depth=0) - items = super(DraftModuleStore, self).get_items(location, depth=0) + draft_items = super(DraftModuleStore, self).get_items(draft_loc, course_id=course_id, depth=0) + items = super(DraftModuleStore, self).get_items(location, course_id=course_id, depth=0) draft_locs_found = set(item.location._replace(revision=None) for item in draft_items) non_draft_items = [ diff --git a/common/lib/xmodule/xmodule/modulestore/xml_importer.py b/common/lib/xmodule/xmodule/modulestore/xml_importer.py index ca5fd34a5e..fa45f92dfc 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml_importer.py +++ b/common/lib/xmodule/xmodule/modulestore/xml_importer.py @@ -137,9 +137,6 @@ def import_from_xml(store, data_dir, course_dirs=None, module = remap_namespace(module, target_location_namespace) - # HACK: for now we don't support progress tabs. There's a special metadata configuration setting for this. - module.metadata['hide_progress_tab'] = True - # cdodge: more hacks (what else). Seems like we have a problem when importing a course (like 6.002) which # does not have any tabs defined in the policy file. The import goes fine and then displays fine in LMS, # but if someone tries to add a new tab in the CMS, then the LMS barfs because it expects that - diff --git a/common/static/images/ico-tinymce-code.png b/common/static/images/ico-tinymce-code.png new file mode 100644 index 0000000000..cc0b8e61f2 Binary files /dev/null and b/common/static/images/ico-tinymce-code.png differ diff --git a/common/templates/courseware_vendor_js.html b/common/templates/courseware_vendor_js.html index ab1842dfb4..a7e344f051 100644 --- a/common/templates/courseware_vendor_js.html +++ b/common/templates/courseware_vendor_js.html @@ -1,5 +1,6 @@ <%namespace name='static' file='static_content.html'/> + diff --git a/jenkins/test.sh b/jenkins/test.sh index 7651fcd917..5b9a5ed9bd 100755 --- a/jenkins/test.sh +++ b/jenkins/test.sh @@ -43,7 +43,9 @@ rake test_cms[false] || TESTS_FAILED=1 rake test_lms[false] || TESTS_FAILED=1 rake test_common/lib/capa || TESTS_FAILED=1 rake test_common/lib/xmodule || TESTS_FAILED=1 -rake phantomjs_jasmine_lms || true +# Don't run the lms jasmine tests for now because +# they mostly all fail anyhow +# rake phantomjs_jasmine_lms || true rake phantomjs_jasmine_cms || TESTS_FAILED=1 rake phantomjs_jasmine_common/lib/xmodule || TESTS_FAILED=1 diff --git a/lms/djangoapps/certificates/management/commands/gen_cert_report.py b/lms/djangoapps/certificates/management/commands/gen_cert_report.py index 93ee20b19f..198ed048fd 100644 --- a/lms/djangoapps/certificates/management/commands/gen_cert_report.py +++ b/lms/djangoapps/certificates/management/commands/gen_cert_report.py @@ -16,7 +16,6 @@ class Command(BaseCommand): This command does not do anything other than report the current certificate status. - unavailable - A student is not eligible for a certificate. generating - A request has been made to generate a certificate, but it has not been generated yet. regenerating - A request has been made to regenerate a certificate, @@ -64,11 +63,7 @@ class Command(BaseCommand): enrolled_students = User.objects.filter( courseenrollment__course_id=course_id).prefetch_related( "groups").order_by('username') - unavailable_count = enrolled_students.count() - \ - GeneratedCertificate.objects.filter( - course_id__exact=course_id).count() cert_data[course_id] = {'enrolled': enrolled_students.count()} - cert_data[course_id].update({'unavailable': unavailable_count}) tallies = GeneratedCertificate.objects.filter( course_id__exact=course_id).values('status').annotate( diff --git a/lms/djangoapps/certificates/management/commands/ungenerated_certs.py b/lms/djangoapps/certificates/management/commands/ungenerated_certs.py index 080918c0cc..071b2c261b 100644 --- a/lms/djangoapps/certificates/management/commands/ungenerated_certs.py +++ b/lms/djangoapps/certificates/management/commands/ungenerated_certs.py @@ -23,26 +23,37 @@ class Command(BaseCommand): option_list = BaseCommand.option_list + ( make_option('-n', '--noop', - action='store_true', - dest='noop', - default=False, - help="Don't add certificate requests to the queue"), + action='store_true', + dest='noop', + default=False, + help="Don't add certificate requests to the queue"), make_option('-c', '--course', - metavar='COURSE_ID', - dest='course', - default=False, - help='Grade and generate certificates for a specific course'), + metavar='COURSE_ID', + dest='course', + default=False, + help='Grade and generate certificates ' + 'for a specific course'), + make_option('-f', '--force-gen', + metavar='STATUS', + dest='force', + default=False, + help='Will generate new certificates for only those users ' + 'whose entry in the certificate table matches STATUS. ' + 'STATUS can be generating, unavailable, deleted, error ' + 'or notpassing.'), - ) + ) def handle(self, *args, **options): # Will only generate a certificate if the current - # status is in this state + # status is in the unavailable state, can be set + # to something else with the force flag - VALID_STATUSES = [ - CertificateStatuses.unavailable - ] + if options['force']: + valid_statuses = getattr(CertificateStatuses, options['force']) + else: + valid_statuses = [CertificateStatuses.unavailable] # Print update after this many students @@ -54,8 +65,8 @@ class Command(BaseCommand): # Find all courses that have ended ended_courses = [] for course_id in [course # all courses in COURSE_LISTINGS - for sub in settings.COURSE_LISTINGS - for course in settings.COURSE_LISTINGS[sub]]: + for sub in settings.COURSE_LISTINGS + for course in settings.COURSE_LISTINGS[sub]]: course_loc = CourseDescriptor.id_to_location(course_id) course = modulestore().get_instance(course_id, course_loc) if course.has_ended(): @@ -64,8 +75,8 @@ class Command(BaseCommand): for course_id in ended_courses: print "Fetching enrolled students for {0}".format(course_id) enrolled_students = User.objects.filter( - courseenrollment__course_id=course_id).prefetch_related( - "groups").order_by('username') + courseenrollment__course_id=course_id).prefetch_related( + "groups").order_by('username') xq = XQueueCertInterface() total = enrolled_students.count() count = 0 @@ -81,11 +92,11 @@ class Command(BaseCommand): hours, remainder = divmod(timeleft.seconds, 3600) minutes, seconds = divmod(remainder, 60) print "{0}/{1} completed ~{2:02}:{3:02}m remaining".format( - count, total, hours, minutes) + count, total, hours, minutes) start = datetime.datetime.now() if certificate_status_for_student( - student, course_id)['status'] in VALID_STATUSES: + student, course_id)['status'] in valid_statuses: if not options['noop']: # Add the certificate request to the queue ret = xq.add_cert(student, course_id) diff --git a/lms/djangoapps/certificates/queue.py b/lms/djangoapps/certificates/queue.py index 52a8dcae36..b9316220fa 100644 --- a/lms/djangoapps/certificates/queue.py +++ b/lms/djangoapps/certificates/queue.py @@ -192,7 +192,7 @@ class XQueueCertInterface(object): Will change the certificate status to 'deleting'. Certificate must be in the 'unavailable', 'error', - or 'deleted' state. + 'deleted' or 'generating' state. If a student has a passing grade a request will made for a new cert @@ -204,7 +204,8 @@ class XQueueCertInterface(object): """ - VALID_STATUSES = [status.unavailable, status.deleted, status.error, + VALID_STATUSES = [ status.generating, + status.unavailable, status.deleted, status.error, status.notpassing] cert_status = certificate_status_for_student( diff --git a/lms/static/sass/base/_variables.scss b/lms/static/sass/base/_variables.scss index 8d1b78a0eb..4d27798649 100644 --- a/lms/static/sass/base/_variables.scss +++ b/lms/static/sass/base/_variables.scss @@ -15,6 +15,8 @@ $monospace: Monaco, 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; $body-font-size: em(14); $body-line-height: golden-ratio(.875em, 1); $base-font-color: rgb(60,60,60); +$baseFontColor: rgb(60,60,60); +$base-font-color: rgb(60,60,60); $lighter-base-font-color: rgb(100,100,100); $blue: rgb(29,157,217); diff --git a/lms/templates/staff_problem_info.html b/lms/templates/staff_problem_info.html index 21044c1f80..61cda0c52b 100644 --- a/lms/templates/staff_problem_info.html +++ b/lms/templates/staff_problem_info.html @@ -1,7 +1,7 @@ ${module_content} %if edit_link:- Edit / + Edit / // assumes courseware.html's loaded this method. - % if staff_access: - setup_debug('${element_id}', - %if edit_link: - '${edit_link}', - %else: - null, - %endif - { - 'location': '${location}', - 'xqa_key': '${xqa_key}', - 'category': '${category}', - 'user': '${user}' - }); - % endif + setup_debug('${element_id}', + %if edit_link: + '${edit_link}', + %else: + null, + %endif + { + 'location': '${location}', + 'xqa_key': '${xqa_key}', + 'category': '${category}', + 'user': '${user}' + });
-
-