diff --git a/contentcuration/contentcuration/api.py b/contentcuration/contentcuration/api.py index a6660ed95c..8f20c77a63 100644 --- a/contentcuration/contentcuration/api.py +++ b/contentcuration/contentcuration/api.py @@ -35,7 +35,7 @@ def write_file_to_storage(fobj, check_valid = False, name=None): name = name or fobj._name or "" filename, ext = os.path.splitext(name) hashed_filename = checksum.hexdigest() - full_filename = "{}{}".format(hashed_filename, ext) + full_filename = "{}{}".format(hashed_filename, ext.lower()) fobj.seek(0) if check_valid and hashed_filename != filename: @@ -54,7 +54,7 @@ def write_raw_content_to_storage(contents, ext=None): checksum = hashlib.md5() checksum.update(contents) filename = checksum.hexdigest() - full_filename = "{}.{}".format(filename, ext) + full_filename = "{}.{}".format(filename, ext.lower()) # Get location of file file_path = models.generate_file_on_disk_name(filename, full_filename) diff --git a/contentcuration/contentcuration/management/commands/exportchannel.py b/contentcuration/contentcuration/management/commands/exportchannel.py index a660ec6c0f..0a2538b88e 100644 --- a/contentcuration/contentcuration/management/commands/exportchannel.py +++ b/contentcuration/contentcuration/management/commands/exportchannel.py @@ -74,9 +74,11 @@ def handle(self, *args, **options): logging.warning("Exited early due to {message}.".format(message=e.message)) self.stdout.write("You can find your database in {path}".format(path=e.db_path)) -def create_kolibri_license_object(license): +def create_kolibri_license_object(ccnode): + use_license_description = ccnode.license.license_name != licenses.SPECIAL_PERMISSIONS return kolibrimodels.License.objects.get_or_create( - license_name=license.license_name + license_name=ccnode.license.license_name, + license_description=ccnode.license.license_description if use_license_description else ccnode.license_description ) @@ -129,10 +131,10 @@ def queue_get_return_none_when_empty(): kolibrinode = create_bare_contentnode(node) if node.kind.kind == content_kinds.EXERCISE: + exercise_data = process_assessment_metadata(ccnode, kolibrinode) if node.changed or not node.files.filter(preset_id=format_presets.EXERCISE).exists(): - create_perseus_exercise(node, kolibrinode) - if node.kind.kind != content_kinds.TOPIC: - create_associated_file_objects(kolibrinode, node) + create_perseus_exercise(node, kolibrinode, exercise_data) + create_associated_file_objects(kolibrinode, node) map_tags_to_node(kolibrinode, node) def create_bare_contentnode(ccnode): @@ -141,7 +143,7 @@ def create_bare_contentnode(ccnode): kolibri_license = None if ccnode.license is not None: - kolibri_license = create_kolibri_license_object(ccnode.license)[0] + kolibri_license = create_kolibri_license_object(ccnode)[0] kolibrinode, is_new = kolibrimodels.ContentNode.objects.update_or_create( pk=ccnode.node_id, @@ -197,12 +199,11 @@ def create_associated_file_objects(kolibrinode, ccnode): thumbnail=preset.thumbnail, ) -def create_perseus_exercise(ccnode, kolibrinode): +def create_perseus_exercise(ccnode, kolibrinode, exercise_data): logging.debug("Creating Perseus Exercise for Node {}".format(ccnode.title)) filename="{0}.{ext}".format(ccnode.title, ext=file_formats.PERSEUS) with tempfile.NamedTemporaryFile(suffix="zip", delete=False) as tempf: - data = process_assessment_metadata(ccnode, kolibrinode) - create_perseus_zip(ccnode, data, tempf) + create_perseus_zip(ccnode, exercise_data, tempf) file_size = tempf.tell() tempf.flush() diff --git a/contentcuration/contentcuration/static/js/bundle_modules/base.js b/contentcuration/contentcuration/static/js/bundle_modules/base.js index a5b398080c..76d62373fa 100644 --- a/contentcuration/contentcuration/static/js/bundle_modules/base.js +++ b/contentcuration/contentcuration/static/js/bundle_modules/base.js @@ -1,62 +1,66 @@ -var attachfastclick = require("fastclick"); +var attachfastclick = require('fastclick'); -var $ = require("jquery"); -var get_cookie = require("utils/get_cookie"); +var $ = require('jquery'); +var get_cookie = require('utils/get_cookie'); global.$ = $; global.jQuery = $; -require("handlebars/helpers"); +require('handlebars/helpers'); -require("../../less/styles.less"); -require("bootstrap/dist/js/npm.js"); -require("../../js/utils/jquery-ui"); -// require("../../../../../node_modules/jquery-ui/ui/widgets/sortable"); +require('../../less/styles.less'); +require('bootstrap/dist/js/npm.js'); +require('../../js/utils/jquery-ui'); -var csrftoken = get_cookie("csrftoken") || ""; +// Promise polyfill +if(!global.Promise) { + global.Promise = require('promise-polyfill'); +} + +var csrftoken = get_cookie('csrftoken') || ''; function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection - return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } function sameOrigin(url) { // test that a given url is a same-origin URL // url could be relative or scheme relative or absolute - var host = document.location.host; // host + port - var protocol = document.location.protocol; - var sr_origin = '//' + host; - var origin = protocol + sr_origin; + var host = document.location.host; // host + port + var protocol = document.location.protocol; + var sr_origin = '//' + host; + var origin = protocol + sr_origin; // Allow absolute or scheme relative URLs to same origin - return (url === origin || url.slice(0, origin.length + 1) === origin + '/') || + return (url === origin || url.slice(0, origin.length + 1) === origin + '/') || (url === sr_origin || url.slice(0, sr_origin.length + 1) === sr_origin + '/') || // or any other URL that isn't scheme relative or absolute i.e relative. !(/^(\/\/|http:|https:).*/.test(url)); } $.ajaxSetup({ - cache: false, - beforeSend: function(xhr, settings) { - if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) { + cache: false, + beforeSend: function(xhr, settings) { + if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) { // Send the token to same-origin, relative URLs only. // Send the token only if the method warrants CSRF protection // Using the CSRFToken value acquired earlier - xhr.setRequestHeader("X-CSRFToken", csrftoken); - } - } + xhr.setRequestHeader('X-CSRFToken', csrftoken); + } + } }); -$.extend( $.expr[ ":" ], { - data: $.expr.createPseudo ? +$.extend( $.expr[ ':' ], { + data: $.expr.createPseudo ? $.expr.createPseudo(function( dataName ) { - return function( elem ) { - return !!$.data( elem, dataName ); - }; + return function( elem ) { + return !!$.data( elem, dataName ); + }; }) : // support: jQuery <1.8 function( elem, i, match ) { - return !!$.data( elem, match[ 3 ] ); + return !!$.data( elem, match[ 3 ] ); } }); $(function() { - attachfastclick(document.body); + attachfastclick(document.body); }); \ No newline at end of file diff --git a/contentcuration/contentcuration/view/internal_views.py b/contentcuration/contentcuration/view/internal_views.py index ec225105c9..d006382f8d 100644 --- a/contentcuration/contentcuration/view/internal_views.py +++ b/contentcuration/contentcuration/view/internal_views.py @@ -15,7 +15,7 @@ from django.views.decorators.csrf import csrf_exempt from django.template.loader import render_to_string from contentcuration.api import write_file_to_storage -from contentcuration.models import Exercise, AssessmentItem, Channel, License, FileFormat, File, FormatPreset, ContentKind, ContentNode, ContentTag, Invitation, Language, generate_file_on_disk_name +from contentcuration.models import Exercise, AssessmentItem, Channel, License, FileFormat, File, FormatPreset, ContentKind, ContentNode, ContentTag, Invitation, Language, generate_file_on_disk_name, generate_storage_url from contentcuration import ricecooker_versions as rc from le_utils.constants import content_kinds from django.db.models.functions import Concat @@ -305,7 +305,7 @@ def map_files_to_node(node, data): if file_data.get('language'): language = Language.objects.get(pk=file_data['language']) - file_path=generate_file_on_disk_name(file_hash[0], file_data['filename']) + file_path=generate_storage_url(file_data['filename']) if not os.path.isfile(file_path): raise IOError('{} not found'.format(file_path)) @@ -326,7 +326,7 @@ def map_files_to_assessment_item(question, data): """ Generate files that reference the content node's assessment items """ for file_data in data: file_hash = file_data['filename'].split(".") - file_path = generate_file_on_disk_name(file_hash[0], file_data['filename']) + file_path = generate_storage_url(file_data['filename']) if not os.path.isfile(file_path): raise IOError('{} not found'.format(file_path)) diff --git a/package.json b/package.json index 751bef261b..7e1a8459dc 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "mathquill": "^0.10.1-a", "minifyify": "^7.0.3", "node-lessify": "https://github.com/DXCanas/node-lessify.git", + "promise-polyfill": "^6.0.2", "summernote": "^0.8.2", "to-markdown": "^3.0.3", "underscore": "^1.8.3", diff --git a/requirements.txt b/requirements.txt index 428022560c..3966e9caf7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ django-registration==2.1.2 django-hashedfilenamestorage==1.0.0 djangorestframework-bulk==0.2.1 django-email-extras==0.3.3 -https://storage.googleapis.com/le-downloads/kolibri/kolibri-0.2.0b1.dev218-py2.py3-none-any.whl +https://storage.googleapis.com/le-downloads/kolibri/kolibri-0.4.0b1.dev20-py2.py3-none-any.whl validators le-utils==0.0.9rc23 gunicorn==19.6.0 @@ -26,4 +26,5 @@ metaphone==0.6 porter2stemmer==1.0 django-postmark==0.1.6 google-cloud-error-reporting==0.24.0 -ddtrace==0.8.0 \ No newline at end of file +ddtrace==0.8.0 +jsonfield==2.0.1 \ No newline at end of file