diff --git a/app/fng-jq-upload.js b/app/fng-jq-upload.js index 5b1dddd..c921b11 100644 --- a/app/fng-jq-upload.js +++ b/app/fng-jq-upload.js @@ -310,6 +310,11 @@ $scope.$on('fileuploaddone', function (event, data) { const field = $scope.dataField(true); + // $scope.dataField(true) should always return something, but have seen a case in Sentry where it did not. + // surely a very rare case where they cancelled / redirected etc. at a very inopportune moment + if (!field) { + return; + } const fileDetails = data.result.files[0]; const _id = fileDetails.id; const filename = fileDetails.name; @@ -327,9 +332,11 @@ // urls for downloading, deleting and retrieving a thumbnail for this item as setUpAttachments() isn't // called again here const queue = $scope.$$childHead.queue; - addAttachmentUrls(queue[queue.length - 1], location, _id, filename, thumbnailId); - $scope.ngModel.$setDirty(); - assignQueueToFormScope(); + if (queue) { + addAttachmentUrls(queue[queue.length - 1], location, _id, filename, thumbnailId); + $scope.ngModel.$setDirty(); + assignQueueToFormScope(); + } }); }, ]) diff --git a/dist/fng-jq-upload.js b/dist/fng-jq-upload.js index dc0e9db..fb70528 100644 --- a/dist/fng-jq-upload.js +++ b/dist/fng-jq-upload.js @@ -310,6 +310,11 @@ $scope.$on('fileuploaddone', function (event, data) { const field = $scope.dataField(true); + // $scope.dataField(true) should always return something, but have seen a case in Sentry where it did not. + // surely a very rare case where they cancelled / redirected etc. at a very inopportune moment + if (!field) { + return; + } const fileDetails = data.result.files[0]; const _id = fileDetails.id; const filename = fileDetails.name; @@ -327,11 +332,13 @@ // urls for downloading, deleting and retrieving a thumbnail for this item as setUpAttachments() isn't // called again here const queue = $scope.$$childHead.queue; - addAttachmentUrls(queue[queue.length - 1], location, _id, filename, thumbnailId); - $scope.ngModel.$setDirty(); - assignQueueToFormScope(); + if (queue) { + addAttachmentUrls(queue[queue.length - 1], location, _id, filename, thumbnailId); + $scope.ngModel.$setDirty(); + assignQueueToFormScope(); + } }); - }, + } ]) .directive('fngJqUploadForm', [ 'PluginHelperService', diff --git a/dist/fng-jq-upload.min.js b/dist/fng-jq-upload.min.js index 7c71946..f3319b4 100644 --- a/dist/fng-jq-upload.min.js +++ b/dist/fng-jq-upload.min.js @@ -1 +1 @@ -!function(){"use strict";var e=angular.module("uploadModule",["blueimp.fileupload"]);e.controller("FngUploadAdditFieldsCtrl",["$scope","$timeout",function(i,e){function t(){i.schema&&i.schema.forEach(function(e){var a=i.uploadForm.dataField()[i.file];i.record[e.name]=a?a[e.name]:void 0})}i.record={},i.$watch("record",function(e,a){e!==a&&(i.uploadForm.formScope[i.uploadForm.formScope.topLevelFormName].$setDirty(),angular.extend(i.uploadForm.dataField()[i.file],e))},!0),e(function(){var a;i.formScope.newRecord?i.record={}:a=i.formScope.$watch("phase",function(e){"ready"===e&&(t(),i.$on("fngCancel",function(){t()}),a())})})}]).directive("fngUploadAdditFields",function(){return{link:function(e,a,i){e.uploadForm=e.$parent.$parent.$parent,e.formScope=e.uploadForm.formScope,e.schema=e.uploadForm.directiveOptions.additFields,e.file=parseInt(i.file)},scope:{},controller:"FngUploadAdditFieldsCtrl",template:''}}).controller("FngJqUploadCtrl",["$scope","$http",function(r,i){function c(e,a){return 1{if(201===e.status)return e.data.clientUrl;throw e="Unexpected response to getPresignedUrl (status"+e.status+")",new Error(e)}):Promise.resolve(a)}function s(a,e,i,t,n){var l=r.formScope.modelName+"/"+e;let o="/api/file/"+l+"/"+i+"?fn="+t;if(c(e,o).then(e=>{a.url=e}),a.deleteUrl=o,n&&(a.deleteUrl+="&thumbnailId="+n),a.deleteType="DELETE",a.thumbnailUrl="https://upload.wikimedia.org/wikipedia/commons/6/6c/Iconoir_journal-page.svg",t&&!r.options.defaultThumbnail){var s=t.toLowerCase();for(const d of[".gif",".png",".jpg",".jpeg"])if(s.endsWith(d)){if(n){const o="/api/file/"+l+"/"+n;c(e,o).then(e=>{a.thumbnailUrl=e})}else a.thumbnailUrl="/api/file/"+l+"/thumbnail/"+i;break}}}function d(){r.formScope.fngJqUploadFileQueue=r.$$childHead.queue}function e(){if(r.$$childHead){r.$$childHead.queue=[];var e=r.dataField();if(e){for(const n of e){var a=void 0===n.location?1:n.location,i=n.filename,t={name:i,size:n.size,location:a};s(t,a,n._id,i,n.thumbnailId),r.$$childHead.queue||(r.$$childHead.queue=[]),r.$$childHead.queue.push(t)}d()}}}var a;r.loadingFiles=!1,r.formScope=r.$parent,r.dataField=function(e){if(-1===r.info.name.indexOf(".")){var a=r.options.model||"record",a=r.formScope[a];if(!(o=a?a[r.info.name]:o)&&e){if(!a)throw new Error(`Cannot initialise record. ${r.info.name} - record is not defined`);o=a[r.info.name]=[]}}else if(r.options.subschema){for(var e=r.formScope.record,a=r.info.name,i=r.options.subschemaroot,a=a.slice(i.length+1),t=(o=e,i.split(".")),n=0,l=t.length;n{var e=r.$$childHead;e&&"function"==typeof e.active&&e.active()&&"function"==typeof e.cancel&&e.cancel()}),r.$on("fileuploadfail",function(e,a){r.$$childHead.queue.pop();let i;if(a.xhr){var t,n=a.xhr();try{t=JSON.parse(n.responseText),i=t.error||t.message||t}catch(e){i=n.responseText}}"abort"!==(i=i||a.errorThrown||"an unexpected error occurred")&&(r.uploadError=i),i.includes("ENOTFOUND")&&(i="Failed to connect to storage service to upload file. Have you gone offline?")}),r.$on("fileuploaddone",function(e,a){var i=r.dataField(!0),a=a.result.files[0],t=a.id,n=a.name,l=a.location,o=a.thumbnailId,i=(i.push({_id:t,filename:n,size:a.size,location:l,thumbnailId:o}),r.$$childHead.queue);s(i[i.length-1],l,t,n,o),r.ngModel.$setDirty(),d()})}]).directive("fngJqUploadForm",["PluginHelperService","$rootScope",function(n,l){return{link:function(e,a,i,t){angular.extend(e,n.extractFromAttr(i,"fngJqUploadForm",e.formScope,{setUpDynamicLabelFunc:!0})),e.label=e.formScope.label,e.passedParams=e.formScope[i.schema],angular.extend(e.options,e.passedParams.fngJqUploadForm),e.options.additFields&&(e.directiveOptions.additFields=JSON.parse(e.options.additFields)),e.directiveOptions.url="/api/file/upload/"+e.formScope.modelName+"/"+encodeURIComponent(e.info.name),e.directiveOptions.autoUpload=e.directiveOptions.autoupload,e.passedParams.readonly?e.isDisabled=!0:e.isDisabled="function"==typeof l.isSecurelyDisabled&&l.isSecurelyDisabled(e.info.id),e.name=e.passedParams.name,e.ngModel=t},restrict:"E",require:"?ngModel",templateUrl:"templates/fileform.html",scope:{},controller:"FngJqUploadCtrl"}}]).controller("FileDestroyController",["$scope","$http",function(l,a){function t(e){a=e.deleteUrl;for(var a,i=/\/file\/(.+?)\/([0-9a-f]{24})/.exec(a)[2],t=l.uploadScope.dataField(),n=t.length-1;0<=n;n--)t[n]._id===i&&t.splice(n,1);l.clear(e),l.ngModel.$setDirty()}var n,o=l.file;l.uploadScope=l.$parent.$parent.$parent;o.deleteUrl?(o.$state=function(){return n},o.$destroy=function(e){if(!e||!e.target.className.includes("ng-hide")){const i=l.$parent.$parent;return i.mouseIn=0,n="pending",a({url:o.deleteUrl,method:o.deleteType}).then(()=>{n="resolved",t(o)}).catch(e=>{n="rejected";var a=e.data||e,a="string"==typeof a?a:a.message||JSON.stringify(a);403===e.status?o.error=a:(t(o),i.$parent.uploadError=a)})}}):o.$cancel||o._index||(o.$cancel=function(){t(o)})}])}(),angular.module("uploadModule").run(["$templateCache",function(e){"use strict";e.put("templates/fileform.html",'
Upload {{label ? label() : (info.label || (info.name | titleCase))}}
{{ queue[0].error }}
Add files...
{{ uploadError }}
 

{{file.name}} {{file.name}} {{file.name}}

{{ file.error }}

{{file.size | formatFileSize}}

')}]); \ No newline at end of file +!function(){"use strict";var e=angular.module("uploadModule",["blueimp.fileupload"]);e.controller("FngUploadAdditFieldsCtrl",["$scope","$timeout",function(i,e){function t(){i.schema&&i.schema.forEach(function(e){var a=i.uploadForm.dataField()[i.file];i.record[e.name]=a?a[e.name]:void 0})}i.record={},i.$watch("record",function(e,a){e!==a&&(i.uploadForm.formScope[i.uploadForm.formScope.topLevelFormName].$setDirty(),angular.extend(i.uploadForm.dataField()[i.file],e))},!0),e(function(){var a;i.formScope.newRecord?i.record={}:a=i.formScope.$watch("phase",function(e){"ready"===e&&(t(),i.$on("fngCancel",function(){t()}),a())})})}]).directive("fngUploadAdditFields",function(){return{link:function(e,a,i){e.uploadForm=e.$parent.$parent.$parent,e.formScope=e.uploadForm.formScope,e.schema=e.uploadForm.directiveOptions.additFields,e.file=parseInt(i.file)},scope:{},controller:"FngUploadAdditFieldsCtrl",template:''}}).controller("FngJqUploadCtrl",["$scope","$http",function(r,i){function c(e,a){return 1{if(201===e.status)return e.data.clientUrl;throw e="Unexpected response to getPresignedUrl (status"+e.status+")",new Error(e)}):Promise.resolve(a)}function s(a,e,i,t,n){var l=r.formScope.modelName+"/"+e;let o="/api/file/"+l+"/"+i+"?fn="+t;if(c(e,o).then(e=>{a.url=e}),a.deleteUrl=o,n&&(a.deleteUrl+="&thumbnailId="+n),a.deleteType="DELETE",a.thumbnailUrl="https://upload.wikimedia.org/wikipedia/commons/6/6c/Iconoir_journal-page.svg",t&&!r.options.defaultThumbnail){var s=t.toLowerCase();for(const d of[".gif",".png",".jpg",".jpeg"])if(s.endsWith(d)){if(n){const o="/api/file/"+l+"/"+n;c(e,o).then(e=>{a.thumbnailUrl=e})}else a.thumbnailUrl="/api/file/"+l+"/thumbnail/"+i;break}}}function d(){r.formScope.fngJqUploadFileQueue=r.$$childHead.queue}function e(){if(r.$$childHead){r.$$childHead.queue=[];var e=r.dataField();if(e){for(const n of e){var a=void 0===n.location?1:n.location,i=n.filename,t={name:i,size:n.size,location:a};s(t,a,n._id,i,n.thumbnailId),r.$$childHead.queue||(r.$$childHead.queue=[]),r.$$childHead.queue.push(t)}d()}}}var a;r.loadingFiles=!1,r.formScope=r.$parent,r.dataField=function(e){if(-1===r.info.name.indexOf(".")){var a=r.options.model||"record",a=r.formScope[a];if(!(o=a?a[r.info.name]:o)&&e){if(!a)throw new Error(`Cannot initialise record. ${r.info.name} - record is not defined`);o=a[r.info.name]=[]}}else if(r.options.subschema){for(var e=r.formScope.record,a=r.info.name,i=r.options.subschemaroot,a=a.slice(i.length+1),t=(o=e,i.split(".")),n=0,l=t.length;n{var e=r.$$childHead;e&&"function"==typeof e.active&&e.active()&&"function"==typeof e.cancel&&e.cancel()}),r.$on("fileuploadfail",function(e,a){r.$$childHead.queue.pop();let i;if(a.xhr){var t,n=a.xhr();try{t=JSON.parse(n.responseText),i=t.error||t.message||t}catch(e){i=n.responseText}}"abort"!==(i=i||a.errorThrown||"an unexpected error occurred")&&(r.uploadError=i),i.includes("ENOTFOUND")&&(i="Failed to connect to storage service to upload file. Have you gone offline?")}),r.$on("fileuploaddone",function(e,a){var i,t,n,l,o=r.dataField(!0);o&&(i=(a=a.result.files[0]).id,t=a.name,n=a.location,l=a.thumbnailId,o.push({_id:i,filename:t,size:a.size,location:n,thumbnailId:l}),o=r.$$childHead.queue)&&(s(o[o.length-1],n,i,t,l),r.ngModel.$setDirty(),d())})}]).directive("fngJqUploadForm",["PluginHelperService","$rootScope",function(n,l){return{link:function(e,a,i,t){angular.extend(e,n.extractFromAttr(i,"fngJqUploadForm",e.formScope,{setUpDynamicLabelFunc:!0})),e.label=e.formScope.label,e.passedParams=e.formScope[i.schema],angular.extend(e.options,e.passedParams.fngJqUploadForm),e.options.additFields&&(e.directiveOptions.additFields=JSON.parse(e.options.additFields)),e.directiveOptions.url="/api/file/upload/"+e.formScope.modelName+"/"+encodeURIComponent(e.info.name),e.directiveOptions.autoUpload=e.directiveOptions.autoupload,e.passedParams.readonly?e.isDisabled=!0:e.isDisabled="function"==typeof l.isSecurelyDisabled&&l.isSecurelyDisabled(e.info.id),e.name=e.passedParams.name,e.ngModel=t},restrict:"E",require:"?ngModel",templateUrl:"templates/fileform.html",scope:{},controller:"FngJqUploadCtrl"}}]).controller("FileDestroyController",["$scope","$http",function(l,a){function t(e){a=e.deleteUrl;for(var a,i=/\/file\/(.+?)\/([0-9a-f]{24})/.exec(a)[2],t=l.uploadScope.dataField(),n=t.length-1;0<=n;n--)t[n]._id===i&&t.splice(n,1);l.clear(e),l.ngModel.$setDirty()}var n,o=l.file;l.uploadScope=l.$parent.$parent.$parent;o.deleteUrl?(o.$state=function(){return n},o.$destroy=function(e){if(!e||!e.target.className.includes("ng-hide")){const i=l.$parent.$parent;return i.mouseIn=0,n="pending",a({url:o.deleteUrl,method:o.deleteType}).then(()=>{n="resolved",t(o)}).catch(e=>{n="rejected";var a=e.data||e,a="string"==typeof a?a:a.message||JSON.stringify(a);403===e.status?o.error=a:(t(o),i.$parent.uploadError=a)})}}):o.$cancel||o._index||(o.$cancel=function(){t(o)})}])}(),angular.module("uploadModule").run(["$templateCache",function(e){"use strict";e.put("templates/fileform.html",'
Upload {{label ? label() : (info.label || (info.name | titleCase))}}
{{ queue[0].error }}
Add files...
{{ uploadError }}
 

{{file.name}} {{file.name}} {{file.name}}

{{ file.error }}

{{file.size | formatFileSize}}

')}]); \ No newline at end of file diff --git a/lib/fng-jq-upload.js b/lib/fng-jq-upload.js index 2ece17a..29e3fed 100644 --- a/lib/fng-jq-upload.js +++ b/lib/fng-jq-upload.js @@ -350,6 +350,10 @@ function controller(fng, processArgs, options) { ])); // the route used when deleting a file (when clicking on the trashcan icon that is provided alongside the thumbnail) fng.app.delete.apply(fng.app, processArgs(modifiedFngOpts, [ + // if you think you want to change this route (in particular, changing the name of the :id param), + // ensure that the host app does not (through the modifiedFngArgs) inject some additional handlers + // (perhaps related to permissions) which expect certain params to exist. + // Plait developer note: :id is assumed to exist in the async function check() of permissions.ts "file/:model/:location/:id", function (req, res) { return __awaiter(this, void 0, void 0, function () {