diff --git a/.gitignore b/.gitignore index 11982d1ef..a8c2d3a3a 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ bin/ src npm-debug.log +Vagrantfile test/dev/handlers/s3/composer.lock test/dev/handlers/traditional/files diff --git a/client/js/uploader.basic.api.js b/client/js/uploader.basic.api.js index 6468d77a4..cf507059e 100644 --- a/client/js/uploader.basic.api.js +++ b/client/js/uploader.basic.api.js @@ -397,6 +397,35 @@ return this._uploadData.uuidChanged(id, newUuid); }, + /** + * Expose the internal status of a file id to the public api for manual state changes + * @public + * + * @param {Number} id, + * @param {String} newStatus + * + * @todo Implement the remaining methods + */ + setStatus: function(id, newStatus) { + var fileRecord = this.getUploads({id: id}); + if (!fileRecord) { + throw new qq.Error(id + " is not a valid file ID."); + } + + switch (newStatus) { + case qq.status.DELETED: + this._onDeleteComplete(id, null, false); + break; + case qq.status.DELETE_FAILED: + this._onDeleteComplete(id, null, true); + break; + default: + var errorMessage = "Method setStatus called on '" + name + "' not implemented yet for " + newStatus; + this.log(errorMessage); + throw new qq.Error(errorMessage); + } + }, + uploadStoredFiles: function() { if (this._storedIds.length === 0) { this._itemError("noFilesError"); @@ -1067,6 +1096,35 @@ }); }, + _handleDeleteSuccess: function(id) { + if (this.getUploads({id: id}).status !== qq.status.DELETED) { + var name = this.getName(id); + + this._netUploadedOrQueued--; + this._netUploaded--; + this._handler.expunge(id); + this._uploadData.setStatus(id, qq.status.DELETED); + this.log("Delete request for '" + name + "' has succeeded."); + } + }, + + _handleDeleteFailed: function(id, xhrOrXdr) { + var name = this.getName(id); + + this._uploadData.setStatus(id, qq.status.DELETE_FAILED); + this.log("Delete request for '" + name + "' has failed.", "error"); + + // Check first if xhrOrXdr is actually passed or valid + // For error reporting, we only have access to the response status if this is not + // an `XDomainRequest`. + if (!xhrOrXdr || xhrOrXdr.withCredentials === undefined) { + this._options.callbacks.onError(id, name, "Delete request failed", xhrOrXdr); + } + else { + this._options.callbacks.onError(id, name, "Delete request failed with response code " + xhrOrXdr.status, xhrOrXdr); + } + }, + // Creates an extra button element _initExtraButton: function(spec) { var button = this._createUploadButton({ @@ -1420,24 +1478,10 @@ var name = this.getName(id); if (isError) { - this._uploadData.setStatus(id, qq.status.DELETE_FAILED); - this.log("Delete request for '" + name + "' has failed.", "error"); - - // For error reporting, we only have access to the response status if this is not - // an `XDomainRequest`. - if (xhrOrXdr.withCredentials === undefined) { - this._options.callbacks.onError(id, name, "Delete request failed", xhrOrXdr); - } - else { - this._options.callbacks.onError(id, name, "Delete request failed with response code " + xhrOrXdr.status, xhrOrXdr); - } + this._handleDeleteFailed(id, xhrOrXdr); } else { - this._netUploadedOrQueued--; - this._netUploaded--; - this._handler.expunge(id); - this._uploadData.setStatus(id, qq.status.DELETED); - this.log("Delete request for '" + name + "' has succeeded."); + this._handleDeleteSuccess(id); } }, diff --git a/docs/api/methods.jmd b/docs/api/methods.jmd index cd1d6f822..dedd9cdfa 100644 --- a/docs/api/methods.jmd +++ b/docs/api/methods.jmd @@ -25,7 +25,7 @@ $(document).ready(function() { # Methods Traditional {: .page-header } {% endmarkdown %} -
+
{% markdown %} ## Core @@ -548,6 +548,38 @@ A `resizeInfo` object, which will be passed to your (optional) `customResizer` f } ], null) }} +{{ api_method("setStatus", "setStatus (id, newStatus])", +""" Modify the status of an file. + +The status values correspond to those found in the `qq.status` object. For reference: + +* `SUBMITTED` +* `QUEUED` +* `UPLOADING` +* `UPLOAD_RETRYING` +* `UPLOAD_FAILED` +* `UPLOAD_SUCCESSFUL` +* `CANCELED` +* `REJECTED` +* `DELETED` +* `DELETING` +* `DELETE_FAILED` +* `PAUSED` + +""", +[ + { + "name": "id", + "type": "Integer", + "description": "The file id." + }, + { + "name": "newStatus", + "type": "String", + "description": "An integer corresponding to a file." + } +], null) }} + {{ api_method("uploadStoredFiles", "uploadStoredFiles ()", "Begin uploading all queued items. Throws a `NoFilesError` of there are no items to upload.", null, null) }}
diff --git a/test/unit/set-status.js b/test/unit/set-status.js new file mode 100644 index 000000000..ff7fe7fba --- /dev/null +++ b/test/unit/set-status.js @@ -0,0 +1,113 @@ +/* globals describe, beforeEach, qq, qqtest, assert, helpme, it */ + +describe("set-status.js", function() { + "use strict"; + + var testUploadEndpoint = "/test/upload", + fileTestHelper = helpme.setupFileTests(); + + var initialFiles = [{ + name: "left.jpg", + uuid: "e109af57-848b-4c2a-bca8-051374d01db1" + }, { + name: "right.jpg", + uuid: "949d16c3-727a-4c3c-8c0f-23404dcd6f3b" + }]; + + it("testing status change of DELETED with initialFiles", function() { + var uploader = new qq.FineUploaderBasic(); + uploader.addInitialFiles(initialFiles); + + var uploaderFiles = uploader.getUploads(); + var file = uploaderFiles[0]; + + uploader.setStatus(file.id, qq.status.DELETED); + + uploaderFiles = uploader.getUploads(); + file = uploaderFiles[0]; + + assert.equal(1, uploader.getNetUploads()); + assert.equal(qq.status.DELETED, file.status); + + // ensure same file can't be "deleted" twice + uploader.setStatus(file.id, qq.status.DELETED); + assert.equal(1, uploader.getNetUploads()); + }); + + it("testing status change of DELETE_FAILED with initialFiles", function() { + var uploader = new qq.FineUploaderBasic(); + uploader.addInitialFiles(initialFiles); + + var uploaderFiles = uploader.getUploads(); + var file = uploaderFiles[1]; + + uploader.setStatus(file.id, qq.status.DELETE_FAILED); + + uploaderFiles = uploader.getUploads(); + file = uploaderFiles[1]; + + assert.equal(2, uploader.getNetUploads()); + assert.equal(qq.status.DELETE_FAILED, file.status); + }); + + it("testing status change of DELETED with mock uploader", function(done) { + var uploader = new qq.FineUploaderBasic({ + autoUpload: true, + request: { + endpoint: testUploadEndpoint + } + }); + + qqtest.downloadFileAsBlob("up.jpg", "image/jpeg").then(function(blob) { + fileTestHelper.mockXhr(); + + uploader.addFiles({name: "test", blob: blob}); + uploader.uploadStoredFiles(); + fileTestHelper.getRequests()[0].respond(201, null, JSON.stringify({success: true})); + + var uploaderFiles = uploader.getUploads(); + var file = uploaderFiles[0]; + + uploader.setStatus(file.id, qq.status.DELETED); + + uploaderFiles = uploader.getUploads(); + file = uploaderFiles[0]; + + assert.equal(0, uploader.getNetUploads()); + assert.equal(qq.status.DELETED, file.status); + done(); + }); + + }); + + it("testing status change of DELETED with mock uploader", function(done) { + var uploader = new qq.FineUploaderBasic({ + autoUpload: true, + request: { + endpoint: testUploadEndpoint + } + }); + + qqtest.downloadFileAsBlob("up.jpg", "image/jpeg").then(function(blob) { + fileTestHelper.mockXhr(); + + uploader.addFiles({name: "test", blob: blob}); + uploader.uploadStoredFiles(); + fileTestHelper.getRequests()[0].respond(201, null, JSON.stringify({success: true})); + + var uploaderFiles = uploader.getUploads(); + var file = uploaderFiles[0]; + + uploader.setStatus(file.id, qq.status.DELETE_FAILED); + + uploaderFiles = uploader.getUploads(); + file = uploaderFiles[0]; + + assert.equal(1, uploader.getNetUploads()); + assert.equal(qq.status.DELETE_FAILED, file.status); + done(); + }); + + }); + +});