From e8b79dc69696bec7efa610c7166b20daa657b148 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Thu, 20 Jun 2013 11:34:20 -0400 Subject: [PATCH] PDF textbooks only accept PDF files All other file types will trigger a validation error --- .../coffee/spec/models/textbook_spec.coffee | 23 ++++++++++++ .../coffee/spec/views/textbook_spec.coffee | 17 +++++++-- cms/static/js/models/textbook.js | 7 +++- cms/static/js/views/textbook.js | 36 +++++++++++-------- cms/templates/js/upload-dialog.underscore | 1 + 5 files changed, 67 insertions(+), 17 deletions(-) diff --git a/cms/static/coffee/spec/models/textbook_spec.coffee b/cms/static/coffee/spec/models/textbook_spec.coffee index d893b229a7..7153122a8c 100644 --- a/cms/static/coffee/spec/models/textbook_spec.coffee +++ b/cms/static/coffee/spec/models/textbook_spec.coffee @@ -133,3 +133,26 @@ describe "CMS.Collections.ChapterSet", -> # try going back one @collection.remove(@collection.last()) expect(@collection.nextOrder()).toEqual(2) + + +describe "CMS.Models.FileUpload", -> + beforeEach -> + @model = new CMS.Models.FileUpload() + + it "is valid by default", -> + expect(@model.isValid()).toBeTruthy() + + it "is valid for PDF files", -> + file = {"type": "application/pdf"} + @model.set("selectedFile", file); + expect(@model.isValid()).toBeTruthy() + + it "is invalid for text files", -> + file = {"type": "text/plain"} + @model.set("selectedFile", file); + expect(@model.isValid()).toBeFalsy() + + it "is invalid for PNG files", -> + file = {"type": "image/png"} + @model.set("selectedFile", file); + expect(@model.isValid()).toBeFalsy() diff --git a/cms/static/coffee/spec/views/textbook_spec.coffee b/cms/static/coffee/spec/views/textbook_spec.coffee index bc54d8e5ca..343e24e109 100644 --- a/cms/static/coffee/spec/views/textbook_spec.coffee +++ b/cms/static/coffee/spec/views/textbook_spec.coffee @@ -230,12 +230,25 @@ describe "CMS.Views.UploadDialog", -> expect(@view.$el).toContain("input[type=file]") expect(@view.$(".action-upload")).toBeDisabled() - it "should render with a file selected", -> - @mockFiles.push({name: "fake.pdf"}) + it "should render with a PDF selected", -> + file = {name: "fake.pdf", "type": "application/pdf"} + @mockFiles.push(file) + @model.set("selectedFile", file) @view.render() expect(@view.$el).toContain("input[type=file]") + expect(@view.$el).not.toContain("p.error") expect(@view.$(".action-upload")).not.toBeDisabled() + it "should render an error with an invalid file type selected", -> + file = {name: "fake.png", "type": "image/png"} + @mockFiles.push(file) + @model.set("selectedFile", file) + @view.render() + expect(@view.$el).toContain("input[type=file]") + expect(@view.$el).toContain("p.error") + expect(@view.$(".action-upload")).toBeDisabled() + + it "adds body class on show()", -> @view.show() expect(@view.options.shown).toBeTruthy() diff --git a/cms/static/js/models/textbook.js b/cms/static/js/models/textbook.js index edbf4109ec..f84c4f0d86 100644 --- a/cms/static/js/models/textbook.js +++ b/cms/static/js/models/textbook.js @@ -94,9 +94,14 @@ CMS.Models.FileUpload = Backbone.Model.extend({ defaults: { "title": "", "message": "", - "selectFile": null, + "selectedFile": null, "uploading": false, "uploadedBytes": 0, "totalBytes": 0 + }, + validate: function(attrs, options) { + if(attrs.selectedFile && attrs.selectedFile.type !== "application/pdf") { + return gettext("Only PDF files can be uploaded"); + } } }); diff --git a/cms/static/js/views/textbook.js b/cms/static/js/views/textbook.js index 4795638fd4..8d5611c30d 100644 --- a/cms/static/js/views/textbook.js +++ b/cms/static/js/views/textbook.js @@ -233,18 +233,12 @@ CMS.Views.UploadDialog = Backbone.View.extend({ initialize: function() { this.template = _.template($("#upload-dialog-tpl").text()); this.listenTo(this.model, "change", this.render); + this.listenTo(this.model, "invalid", this.handleInvalid); }, render: function() { - // some browsers (like Chrome) allow you to assign to the .files attribute - // of an DOM element -- for those browsers, we can - // create a new DOM element and assign the old content to it. Other browsers - // (like Firefox) make this attribute read-only, and we have to save the - // old DOM element in order to save it's content. For compatibility purposes, - // we'll just save the old element every time. - var oldInput = this.$("input[type=file]").get(0), selectedFile; - if (oldInput && oldInput.files.length) { - selectedFile = oldInput.files[0]; - } + if(!this.model.isValid()) {return this;} + var selectedFile = this.model.get('selectedFile'); + var oldInput = this.$("input[type=file]").get(0); this.$el.html(this.template({ shown: this.options.shown, url: UPLOAD_ASSET_CALLBACK_URL, @@ -253,12 +247,17 @@ CMS.Views.UploadDialog = Backbone.View.extend({ selectedFile: selectedFile, uploading: this.model.get('uploading'), uploadedBytes: this.model.get('uploadedBytes'), - totalBytes: this.model.get('totalBytes') + totalBytes: this.model.get('totalBytes'), + error: this.model.get('error') })); - if (oldInput) { + // ideally, we'd like to tell the browser to pre-populate the + // with the selectedFile if we have one -- but + // browser security prohibits that. So instead, we'll swap out the + // new input (that has no file selected) with the old input (that + // already has the selectedFile selected). + if (selectedFile) { this.$('input[type=file]').replaceWith(oldInput); } - return this; }, events: { @@ -267,7 +266,10 @@ CMS.Views.UploadDialog = Backbone.View.extend({ "click .action-upload": "upload" }, selectFile: function(e) { - this.model.set('fileList', e.target.files); + this.model.set({ + selectedFile: e.target.files[0] || null, + error: null + }); }, show: function(e) { if(e && e.preventDefault) { e.preventDefault(); } @@ -285,6 +287,12 @@ CMS.Views.UploadDialog = Backbone.View.extend({ if(e && e.preventDefault) { e.preventDefault(); } return this.hide().remove(); }, + handleInvalid: function(model, error, options) { + model.set({ + selectedFile: null, + error: error + }); + }, upload: function(e) { this.model.set('uploading', true); this.$("form").ajaxSubmit({ diff --git a/cms/templates/js/upload-dialog.underscore b/cms/templates/js/upload-dialog.underscore index 4edd11ffb1..40e9586657 100644 --- a/cms/templates/js/upload-dialog.underscore +++ b/cms/templates/js/upload-dialog.underscore @@ -11,6 +11,7 @@

<%= title %>

<%= message %>

+ <% if(error) {%>

<%= error %>

<% } %> <% if(uploading) { %> <% if (uploadedBytes && totalBytes) { %>