From fba35df9d8f0106b8cc186fcdfe5692d9f88a6ad Mon Sep 17 00:00:00 2001 From: Christopher Joe Date: Thu, 15 Dec 2016 09:09:55 +1300 Subject: [PATCH 01/13] PreviewImageField component added to have upload replacement capability. --- client/dist/js/bundle.js | 1255 +++++++++-------- client/dist/styles/bundle.css | 56 + client/src/boot/index.js | 5 +- .../components/AssetDropzone/AssetDropzone.js | 3 +- .../AssetDropzone/AssetDropzone.scss | 4 + .../AssetDropzone/tests/AssetDropzone-test.js | 2 +- .../PreviewImageField/PreviewImageField.js | 264 ++++ .../PreviewImageField/PreviewImageField.scss | 52 + .../src/containers/AssetAdmin/AssetAdmin.js | 2 +- .../src/containers/HistoryList/HistoryList.js | 24 +- .../previewField/PreviewFieldActionTypes.js | 6 + .../state/previewField/PreviewFieldActions.js | 29 + .../state/previewField/PreviewFieldReducer.js | 42 + client/src/styles/_variables.scss | 4 + client/src/styles/bundle.scss | 1 + code/Controller/AssetAdmin.php | 81 +- code/Forms/AssetFormFactory.php | 18 +- code/Forms/FileFormFactory.php | 36 +- code/Forms/PreviewImageField.php | 70 + tests/php/Forms/FileFormBuilderTest.php | 45 +- webpack.config.js | 1 + 21 files changed, 1314 insertions(+), 686 deletions(-) create mode 100644 client/src/components/PreviewImageField/PreviewImageField.js create mode 100644 client/src/components/PreviewImageField/PreviewImageField.scss create mode 100644 client/src/state/previewField/PreviewFieldActionTypes.js create mode 100644 client/src/state/previewField/PreviewFieldActions.js create mode 100644 client/src/state/previewField/PreviewFieldReducer.js create mode 100644 code/Forms/PreviewImageField.php diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 5fdb540e5..344e0856b 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -2,30 +2,30 @@ var o=n[r]={exports:{},id:r,loaded:!1} return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={} return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict" -n(1),n(277),n(297),n(299)},function(e,t,n){(function(t){e.exports=t.InsertMediaModal=n(2)}).call(t,function(){return this}())},function(e,t,n){"use strict" +n(1),n(277),n(301),n(303)},function(e,t,n){(function(t){e.exports=t.InsertMediaModal=n(2)}).call(t,function(){return this}())},function(e,t,n){"use strict" function r(e){if(e&&e.__esModule)return e var t={} if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]) return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function s(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called") -return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function l(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t) -e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function a(e,t){var n=e.config.sections[_],r=t.fileAttributes?t.fileAttributes.ID:null,o=e.config.sections[_],i=r&&o.form.fileInsertForm.schemaUrl+"/"+r +return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t) +e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function l(e,t){var n=e.config.sections[S],r=t.fileAttributes?t.fileAttributes.ID:null,o=e.config.sections[S],i=r&&o.form.fileInsertForm.schemaUrl+"/"+r -return{sectionConfig:n,schemaUrl:i}}function u(e){return{actions:{schema:(0,m.bindActionCreators)(S,e)}}}Object.defineProperty(t,"__esModule",{value:!0}),t.InsertMediaModal=void 0 +return{sectionConfig:n,schemaUrl:i}}function u(e){return{actions:{schema:(0,m.bindActionCreators)(_,e)}}}Object.defineProperty(t,"__esModule",{value:!0}),t.InsertMediaModal=void 0 var p=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0 -try{for(var s=e[Symbol.iterator](),l;!(r=(l=s.next()).done)&&(n.push(l.value),!t||n.length!==t);r=!0);}catch(a){o=!0,i=a}finally{try{!r&&s["return"]&&s["return"]()}finally{if(o)throw i}}return n}return function(t,n){ +try{for(var s=e[Symbol.iterator](),a;!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(l){o=!0,i=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(o)throw i}}return n}return function(t,n){ if(Array.isArray(t))return t if(Symbol.iterator in Object(t))return e(t,n) throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),d=Object.assign||function(e){for(var t=1;t-1}},{key:"itemIsHighlighted",value:function M(e){return this.props.fileId===e}},{key:"handleOpenFolder",value:function H(e,t){ e.preventDefault(),this.props.onOpenFolder(t.id)}},{key:"handleOpenFile",value:function G(e,t){e.preventDefault(),null!==t.created&&this.props.onOpenFile(t.id,t)}},{key:"handleSelect",value:function z(e,t){ this.props.selectedFiles.indexOf(t.id)===-1?this.props.actions.gallery.selectFiles([t.id]):this.props.actions.gallery.deselectFiles([t.id])}},{key:"handleBackClick",value:function q(e){e.preventDefault(), -this.props.onOpenFolder(this.props.folder.parentID)}},{key:"handleViewChange",value:function Q(e){var t=e.currentTarget.value -this.props.onViewChange(t)}},{key:"renderSort",value:function V(){var e=this +this.props.onOpenFolder(this.props.folder.parentID)}},{key:"handleViewChange",value:function V(e){var t=e.currentTarget.value +this.props.onViewChange(t)}},{key:"renderSort",value:function W(){var e=this return"tile"!==this.props.view?null:v["default"].createElement("div",{className:"gallery__sort fieldholder-small"},v["default"].createElement("select",{className:"dropdown no-change-track no-chzn",tabIndex:"0", style:{width:"160px"},defaultValue:this.props.sort},B.map(function(t,n){return v["default"].createElement("option",{key:n,onClick:e.handleSelectSort,"data-field":t.field,"data-direction":t.direction,value:t.field+","+t.direction -},t.label)})))}},{key:"renderToolbar",value:function W(){var e=this.props.folder.canEdit +},t.label)})))}},{key:"renderToolbar",value:function Q(){var e=this.props.folder.canEdit return v["default"].createElement("div",{className:"toolbar--content toolbar--space-save"},v["default"].createElement("div",{className:"fill-width"},v["default"].createElement("div",{className:"flexbox-area-grow" },this.renderBackButton(),v["default"].createElement("button",{id:"upload-button",className:"btn btn-secondary font-icon-upload btn--icon-xl",type:"button",disabled:!e},v["default"].createElement("span",{ className:"btn__text"},g["default"]._t("AssetAdmin.DROPZONE_UPLOAD"))),v["default"].createElement("button",{id:"add-folder-button",className:"btn btn-secondary font-icon-folder-add btn--icon-xl",type:"button", @@ -232,33 +231,33 @@ return t===e.props.view?null:(o.push("font-icon-"+r),v["default"].createElement( value:function $(){var e=["btn","btn-secondary","btn--no-text","font-icon-level-up","btn--icon-large","gallery__back"].join(" ") return null!==this.props.folder.parentID?v["default"].createElement("button",{className:e,onClick:this.handleBackClick,ref:"backButton"}):null}},{key:"renderBulkActions",value:function Z(){var e=this,t=function i(t){ var n=t.map(function(e){return e.id}) -e.props.actions.gallery.deleteItems(e.props.deleteApi,n)},n=function s(t){e.props.onOpenFile(t[0].id)},r=U["default"].BULK_ACTIONS.map(function(e){return"delete"!==e.value||e.callback?"edit"!==e.value||e.callback?e:d({},e,{ +e.props.actions.gallery.deleteItems(e.props.deleteApi,n)},n=function s(t){e.props.onOpenFile(t[0].id)},r=L["default"].BULK_ACTIONS.map(function(e){return"delete"!==e.value||e.callback?"edit"!==e.value||e.callback?e:d({},e,{ callback:n}):d({},e,{callback:t})}),o=this.props.selectedFiles.map(function(t){return e.props.files.find(function(e){return t===e.id})}) -return o.length>0&&"admin"===this.props.type?v["default"].createElement(w["default"],{transitionName:"bulk-actions",transitionEnterTimeout:U["default"].CSS_TRANSITION_TIME,transitionLeaveTimeout:U["default"].CSS_TRANSITION_TIME +return o.length>0&&"admin"===this.props.type?v["default"].createElement(w["default"],{transitionName:"bulk-actions",transitionEnterTimeout:L["default"].CSS_TRANSITION_TIME,transitionLeaveTimeout:L["default"].CSS_TRANSITION_TIME },v["default"].createElement(A["default"],{actions:r,items:o,key:o.length>0})):null}},{key:"renderNoItemsNotice",value:function Y(){return 0!==this.props.files.length||this.props.loading?null:v["default"].createElement("p",{ -className:"gallery__no-item-notice"},g["default"]._t("AssetAdmin.NOITEMSFOUND"))}},{key:"renderGalleryView",value:function X(){var e=this,t="table"===this.props.view?N["default"]:D["default"],n=this.props.files.map(function(t){ -return d({},t,{selected:e.itemIsSelected(t.id),highlighted:e.itemIsHighlighted(t.id)})}),r=this.props.queuedFiles.items.map(function(e){return d({},e,{uploading:!0})}),o=[].concat(i(r),i(n)),s=this.props,l=s.type,a=s.loading,u=s.page,p=s.count,c=s.limit,f=s.sort,h={ -selectableItems:"admin"===l,files:o,loading:a,page:u,count:p,limit:c,sort:f,onSort:this.handleSort,onSetPage:this.handleSetPage,onOpenFile:this.handleOpenFile,onOpenFolder:this.handleOpenFolder,onSelect:this.handleSelect, +className:"gallery__no-item-notice"},g["default"]._t("AssetAdmin.NOITEMSFOUND"))}},{key:"renderGalleryView",value:function X(){var e=this,t="table"===this.props.view?R["default"]:D["default"],n=this.props.files.map(function(t){ +return d({},t,{selected:e.itemIsSelected(t.id),highlighted:e.itemIsHighlighted(t.id)})}),r=this.props.queuedFiles.items.map(function(e){return d({},e,{uploading:!0})}),o=[].concat(i(r),i(n)),s=this.props,a=s.type,l=s.loading,u=s.page,p=s.count,c=s.limit,f=s.sort,h={ +selectableItems:"admin"===a,files:o,loading:l,page:u,count:p,limit:c,sort:f,onSort:this.handleSort,onSetPage:this.handleSetPage,onOpenFile:this.handleOpenFile,onOpenFolder:this.handleOpenFolder,onSelect:this.handleSelect, onCancelUpload:this.handleCancelUpload,onRemoveErroredUpload:this.handleRemoveErroredUpload,renderNoItemsNotice:this.renderNoItemsNotice} return v["default"].createElement(t,h)}},{key:"render",value:function J(){if(!this.props.folder)return this.props.errorMessage?v["default"].createElement("div",{className:"gallery__error"},v["default"].createElement("div",{ className:"gallery__error-message"},v["default"].createElement("h3",null,this.props.errorMessage&&g["default"]._t("AssetAdmin.DROPZONE_RESPONSE_ERROR","Server responded with an error.")),v["default"].createElement("p",null,this.props.errorMessage))):null -var e={height:U["default"].THUMBNAIL_HEIGHT,width:U["default"].THUMBNAIL_WIDTH},t={url:this.props.createFileApiUrl,method:this.props.createFileApiMethod,paramName:"Upload",clickable:"#upload-button"},n=this.props.securityId,r=this.props.folder.canEdit,o=["panel","panel--padded","panel--scrollable","gallery__main"] +var e={height:L["default"].THUMBNAIL_HEIGHT,width:L["default"].THUMBNAIL_WIDTH},t={url:this.props.createFileApiUrl,method:this.props.createFileApiMethod,paramName:"Upload",clickable:"#upload-button"},n=this.props.securityId,r=this.props.folder.canEdit,o=["panel","panel--padded","panel--scrollable","gallery__main"] -return"insert"===this.props.type&&o.push("insert-media-modal__main"),v["default"].createElement("div",{className:"flexbox-area-grow gallery__outer"},this.renderBulkActions(),v["default"].createElement(T["default"],{ +return"insert"===this.props.type&&o.push("insert-media-modal__main"),v["default"].createElement("div",{className:"flexbox-area-grow gallery__outer"},this.renderBulkActions(),v["default"].createElement(I["default"],{ canUpload:r,handleAddedFile:this.handleAddedFile,handleError:this.handleFailedUpload,handleSuccess:this.handleSuccessfulUpload,handleSending:this.handleSending,handleUploadProgress:this.handleUploadProgress, preview:e,folderId:this.props.folderId,options:t,securityID:n,uploadButton:!1},v["default"].createElement("div",{className:o.join(" ")},this.renderToolbar(),this.renderGalleryView())))}}]),t}(y.Component),z={ page:0,limit:15,sort:B[0].field+","+B[0].direction},q={loading:y.PropTypes.bool,sort:y.PropTypes.string,files:y.PropTypes.arrayOf(y.PropTypes.shape({id:y.PropTypes.number,parent:y.PropTypes.shape({id:y.PropTypes.number })})).isRequired,count:y.PropTypes.number,page:y.PropTypes.number,limit:y.PropTypes.number,onOpenFile:y.PropTypes.func.isRequired,onOpenFolder:y.PropTypes.func.isRequired,onSort:y.PropTypes.func.isRequired, -onSetPage:y.PropTypes.func.isRequired},Q=d({},z,{selectableItems:!1}),V=d({},q,{selectableItems:y.PropTypes.bool,onSelect:y.PropTypes.func,onCancelUpload:y.PropTypes.func,onRemoveErroredUpload:y.PropTypes.func, +onSetPage:y.PropTypes.func.isRequired},V=d({},z,{selectableItems:!1}),W=d({},q,{selectableItems:y.PropTypes.bool,onSelect:y.PropTypes.func,onCancelUpload:y.PropTypes.func,onRemoveErroredUpload:y.PropTypes.func, renderNoItemsNotice:y.PropTypes.func.isRequired}) G.defaultProps=d({},z,{type:"admin",view:"tile"}),G.propTypes=d({},q,{type:y.PropTypes.oneOf(["insert","select","admin"]),view:y.PropTypes.oneOf(["tile","table"]),dialog:y.PropTypes.bool,fileId:y.PropTypes.number, folderId:y.PropTypes.number.isRequired,folder:y.PropTypes.shape({id:y.PropTypes.number,parentID:y.PropTypes.number,canView:y.PropTypes.bool,canEdit:y.PropTypes.bool}),queuedFiles:y.PropTypes.shape({items:y.PropTypes.array.isRequired }),selectedFiles:y.PropTypes.arrayOf(y.PropTypes.number),errorMessage:y.PropTypes.string,actions:y.PropTypes.object.isRequired,securityId:y.PropTypes.string,onViewChange:y.PropTypes.func.isRequired,createFileApiUrl:y.PropTypes.string, -createFileApiMethod:y.PropTypes.string,createFolderApi:y.PropTypes.func,readFolderApi:y.PropTypes.func,deleteApi:y.PropTypes.func}),t.Gallery=G,t.sorters=B,t.galleryViewPropTypes=V,t.galleryViewDefaultProps=Q, -t["default"]=(0,x.connect)(u,p)(G)},function(e,t){e.exports=jQuery},function(e,t){e.exports=ReactDom},function(e,t){e.exports=ReactAddonsTestUtils},function(e,t){e.exports=ReactAddonsCssTransitionGroup +createFileApiMethod:y.PropTypes.string,createFolderApi:y.PropTypes.func,readFolderApi:y.PropTypes.func,deleteApi:y.PropTypes.func}),t.Gallery=G,t.sorters=B,t.galleryViewPropTypes=W,t.galleryViewDefaultProps=V, +t["default"]=(0,P.connect)(u,p)(G)},function(e,t){e.exports=jQuery},function(e,t){e.exports=ReactDom},function(e,t){e.exports=ReactAddonsTestUtils},function(e,t){e.exports=ReactAddonsCssTransitionGroup },function(e,t,n){"use strict" function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called") @@ -267,23 +266,23 @@ function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e inst return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t) e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{ value:!0}) -var l=Object.assign||function(e){for(var t=1;t'),this.element.appendChild(n)),r=n.getElementsByTagName("span")[0],r&&(null!=r.textContent?r.textContent=this.options.dictFallbackMessage:null!=r.innerText&&(r.innerText=this.options.dictFallbackMessage)), @@ -360,30 +359,30 @@ this.element.appendChild(this.getFallbackForm())},resize:function(e){var t,n,r return t={srcX:0,srcY:0,srcWidth:e.width,srcHeight:e.height},n=e.width/e.height,t.optWidth=this.options.thumbnailWidth,t.optHeight=this.options.thumbnailHeight,null==t.optWidth&&null==t.optHeight?(t.optWidth=t.srcWidth, t.optHeight=t.srcHeight):null==t.optWidth?t.optWidth=n*t.optHeight:null==t.optHeight&&(t.optHeight=1/n*t.optWidth),r=t.optWidth/t.optHeight,e.heightr?(t.srcHeight=e.height,t.srcWidth=t.srcHeight*r):(t.srcWidth=e.width,t.srcHeight=t.srcWidth/r),t.srcX=(e.width-t.srcWidth)/2,t.srcY=(e.height-t.srcHeight)/2,t},drop:function(e){ -return this.element.classList.remove("dz-drag-hover")},dragstart:a,dragend:function(e){return this.element.classList.remove("dz-drag-hover")},dragenter:function(e){return this.element.classList.add("dz-drag-hover") +return this.element.classList.remove("dz-drag-hover")},dragstart:l,dragend:function(e){return this.element.classList.remove("dz-drag-hover")},dragenter:function(e){return this.element.classList.add("dz-drag-hover") -},dragover:function(e){return this.element.classList.add("dz-drag-hover")},dragleave:function(e){return this.element.classList.remove("dz-drag-hover")},paste:a,reset:function(){return this.element.classList.remove("dz-started") +},dragover:function(e){return this.element.classList.add("dz-drag-hover")},dragleave:function(e){return this.element.classList.remove("dz-drag-hover")},paste:l,reset:function(){return this.element.classList.remove("dz-started") -},addedfile:function(e){var n,r,o,i,s,l,a,u,p,d,c,f,h +},addedfile:function(e){var n,r,o,i,s,a,l,u,p,d,c,f,h if(this.element===this.previewsContainer&&this.element.classList.add("dz-started"),this.previewsContainer){for(e.previewElement=t.createElement(this.options.previewTemplate.trim()),e.previewTemplate=e.previewElement, -this.previewsContainer.appendChild(e.previewElement),d=e.previewElement.querySelectorAll("[data-dz-name]"),i=0,a=d.length;i'+this.options.dictRemoveFile+""),e.previewElement.appendChild(e._removeLink)), r=function(n){return function(r){return r.preventDefault(),r.stopPropagation(),e.status===t.UPLOADING?t.confirm(n.options.dictCancelUploadConfirmation,function(){return n.removeFile(e)}):n.options.dictRemoveFileConfirmation?t.confirm(n.options.dictRemoveFileConfirmation,function(){ -return n.removeFile(e)}):n.removeFile(e)}}(this),f=e.previewElement.querySelectorAll("[data-dz-remove]"),h=[],l=0,p=f.length;l\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n' +},canceledmultiple:l,complete:function(e){if(e._removeLink&&(e._removeLink.textContent=this.options.dictRemoveFile),e.previewElement)return e.previewElement.classList.add("dz-complete")},completemultiple:l, +maxfilesexceeded:l,maxfilesreached:l,queuecomplete:l,addedfiles:l,previewTemplate:'
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
' },n=function(){var e,t,n,r,o,i,s for(r=arguments[0],n=2<=arguments.length?p.call(arguments,1):[],i=0,s=n.length;i'+this.options.dictDefaultMessage+"")), this.clickableElements.length&&(r=function(e){return function(){return e.hiddenFileInput&&e.hiddenFileInput.parentNode.removeChild(e.hiddenFileInput),e.hiddenFileInput=document.createElement("input"),e.hiddenFileInput.setAttribute("type","file"), (null==e.options.maxFiles||e.options.maxFiles>1)&&e.hiddenFileInput.setAttribute("multiple","multiple"),e.hiddenFileInput.className="dz-hidden-input",null!=e.options.acceptedFiles&&e.hiddenFileInput.setAttribute("accept",e.options.acceptedFiles), @@ -403,7 +402,7 @@ null!=e.options.capture&&e.hiddenFileInput.setAttribute("capture",e.options.capt e.hiddenFileInput.style.left="0",e.hiddenFileInput.style.height="0",e.hiddenFileInput.style.width="0",document.querySelector(e.options.hiddenInputContainer).appendChild(e.hiddenFileInput),e.hiddenFileInput.addEventListener("change",function(){ var t,n,o,i if(n=e.hiddenFileInput.files,n.length)for(o=0,i=n.length;o0){for(s=["TB","GB","MB","KB","b"],n=l=0,a=s.length;l=t){r=e/Math.pow(this.options.filesizeBase,4-n),o=i +if(r=0,o="b",e>0){for(s=["TB","GB","MB","KB","b"],n=a=0,l=s.length;a=t){r=e/Math.pow(this.options.filesizeBase,4-n),o=i break}r=Math.round(10*r)/10}return""+r+" "+o},t.prototype._updateMaxFilesReachedClass=function(){return null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(this.getAcceptedFiles().length===this.options.maxFiles&&this.emit("maxfilesreached",this.files), this.element.classList.add("dz-max-files-reached")):this.element.classList.remove("dz-max-files-reached")},t.prototype.drop=function(e){var t,n e.dataTransfer&&(this.emit("drop",e),t=e.dataTransfer.files,this.emit("addedfiles",t),t.length&&(n=e.dataTransfer.items,n&&n.length&&null!=n[0].webkitGetAsEntry?this._addFilesFromItems(n):this.handleFiles(t))) @@ -476,10 +475,10 @@ return null},t.prototype.createThumbnail=function(e,t){var n return n=new FileReader,n.onload=function(r){return function(){return"image/svg+xml"===e.type?(r.emit("thumbnail",e,n.result),void(null!=t&&t())):r.createThumbnailFromUrl(e,n.result,t)}}(this),n.readAsDataURL(e) },t.prototype.createThumbnailFromUrl=function(e,t,n,r){var o -return o=document.createElement("img"),r&&(o.crossOrigin=r),o.onload=function(t){return function(){var r,i,s,a,u,p,d,c +return o=document.createElement("img"),r&&(o.crossOrigin=r),o.onload=function(t){return function(){var r,i,s,l,u,p,d,c if(e.width=o.width,e.height=o.height,s=t.options.resize.call(t,e),null==s.trgWidth&&(s.trgWidth=s.optWidth),null==s.trgHeight&&(s.trgHeight=s.optHeight),r=document.createElement("canvas"),i=r.getContext("2d"), -r.width=s.trgWidth,r.height=s.trgHeight,l(i,o,null!=(u=s.srcX)?u:0,null!=(p=s.srcY)?p:0,s.srcWidth,s.srcHeight,null!=(d=s.trgX)?d:0,null!=(c=s.trgY)?c:0,s.trgWidth,s.trgHeight),a=r.toDataURL("image/png"), -t.emit("thumbnail",e,a),null!=n)return n()}}(this),null!=n&&(o.onerror=n),o.src=t},t.prototype.processQueue=function(){var e,t,n,r +r.width=s.trgWidth,r.height=s.trgHeight,a(i,o,null!=(u=s.srcX)?u:0,null!=(p=s.srcY)?p:0,s.srcWidth,s.srcHeight,null!=(d=s.trgX)?d:0,null!=(c=s.trgY)?c:0,s.trgWidth,s.trgHeight),l=r.toDataURL("image/png"), +t.emit("thumbnail",e,l),null!=n)return n()}}(this),null!=n&&(o.onerror=n),o.src=t},t.prototype.processQueue=function(){var e,t,n,r if(t=this.options.parallelUploads,n=this.getUploadingFiles().length,e=n,!(n>=t)&&(r=this.getQueuedFiles(),r.length>0)){if(this.options.uploadMultiple)return this.processFiles(r.slice(0,t-n)) for(;e=L;p=0<=L?++I:--I)i.append(this._getParamName(p),e[p],this._renameFilename(e[p].name)) -return this.submitRequest(S,i,e)},t.prototype.submitRequest=function(e,t,n){return e.send(t)},t.prototype._finished=function(e,n,r){var o,i,s +for(h in k)E=k[h],i.append(h,E)}for(w=0,I=e.length;w=U;p=0<=U?++O:--O)i.append(this._getParamName(p),e[p],this._renameFilename(e[p].name)) +return this.submitRequest(_,i,e)},t.prototype.submitRequest=function(e,t,n){return e.send(t)},t.prototype._finished=function(e,n,r){var o,i,s for(i=0,s=e.length;ip;)t=o[4*(a-1)+3],0===t?i=a:p=a, -a=i+p>>1 -return u=a/s,0===u?1:u},l=function(e,t,n,r,o,i,l,a,u,p){var d -return d=s(t),e.drawImage(t,n,r,o,i,l,a,u,p/d)},i=function(e,t){var n,r,o,i,s,l,a,u,p -if(o=!1,p=!0,r=e.document,u=r.documentElement,n=r.addEventListener?"addEventListener":"attachEvent",a=r.addEventListener?"removeEventListener":"detachEvent",l=r.addEventListener?"":"on",i=function(n){if("readystatechange"!==n.type||"complete"===r.readyState)return("load"===n.type?e:r)[a](l+n.type,i,!1), +n.QUEUED="queued",n.ACCEPTED=n.QUEUED,n.UPLOADING="uploading",n.PROCESSING=n.UPLOADING,n.CANCELED="canceled",n.ERROR="error",n.SUCCESS="success",s=function(e){var t,n,r,o,i,s,a,l,u,p +for(a=e.naturalWidth,s=e.naturalHeight,n=document.createElement("canvas"),n.width=1,n.height=s,r=n.getContext("2d"),r.drawImage(e,0,0),o=r.getImageData(0,0,1,s).data,p=0,i=s,l=s;l>p;)t=o[4*(l-1)+3],0===t?i=l:p=l, +l=i+p>>1 +return u=l/s,0===u?1:u},a=function(e,t,n,r,o,i,a,l,u,p){var d +return d=s(t),e.drawImage(t,n,r,o,i,a,l,u,p/d)},i=function(e,t){var n,r,o,i,s,a,l,u,p +if(o=!1,p=!0,r=e.document,u=r.documentElement,n=r.addEventListener?"addEventListener":"attachEvent",l=r.addEventListener?"removeEventListener":"detachEvent",a=r.addEventListener?"":"on",i=function(n){if("readystatechange"!==n.type||"complete"===r.readyState)return("load"===n.type?e:r)[l](a+n.type,i,!1), !o&&(o=!0)?t.call(e,n.type||n):void 0},s=function(){var e -try{u.doScroll("left")}catch(t){return e=t,void setTimeout(s,50)}return i("poll")},"complete"!==r.readyState){if(r.createEventObject&&u.doScroll){try{p=!e.frameElement}catch(d){}p&&s()}return r[n](l+"DOMContentLoaded",i,!1), -r[n](l+"readystatechange",i,!1),e[n](l+"load",i,!1)}},n._autoDiscoverFunction=function(){if(n.autoDiscover)return n.discover()},i(window,n._autoDiscoverFunction)}).call(this)}).call(t,n(18),n(24)(e))},function(e,t){ -e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children=[],e.webpackPolyfill=1),e}},function(e,t){e.exports=DataFormat},function(e,t,n){"use strict" +try{u.doScroll("left")}catch(t){return e=t,void setTimeout(s,50)}return i("poll")},"complete"!==r.readyState){if(r.createEventObject&&u.doScroll){try{p=!e.frameElement}catch(d){}p&&s()}return r[n](a+"DOMContentLoaded",i,!1), +r[n](a+"readystatechange",i,!1),e[n](a+"load",i,!1)}},n._autoDiscoverFunction=function(){if(n.autoDiscover)return n.discover()},i(window,n._autoDiscoverFunction)}).call(this)}).call(t,n(18),n(24)(e))},function(e,t){ +e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children=[],e.webpackPolyfill=1),e}},function(e,t,n){"use strict" function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called") return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t) -e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function l(e){return{gallery:e.assetAdmin.gallery +e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function a(e){return{gallery:e.assetAdmin.gallery }}Object.defineProperty(t,"__esModule",{value:!0}),t.BulkActions=void 0 -var a=function(){function e(e,t){for(var n=0;n=0 return y.getAt(t,n||"").toString().toLowerCase().indexOf(e[n].toLowerCase())>=0})},this.props.results),n={columnFilters:e} e?(n.filteredResults=t,n.maxPage=this.getMaxPage(n.filteredResults)):this.state.filter?n.filteredResults=this.props.useCustomFilterer?this.props.customFilterer(this.props.results,filter):this.defaultFilter(this.props.results,filter):n.filteredResults=null, this.setState(n)},filterByColumn:function H(e,t){var n=this.state.columnFilters -if(n.hasOwnProperty(t)&&!e)n=O(n,t) +if(n.hasOwnProperty(t)&&!e)n=T(n,t) else{var r={} -r[t]=e,n=I({},n,r)}this.filterByColumnFilters(n)},setFilter:function B(e){if(this.props.useExternal)return void this.props.externalSetFilter(e) +r[t]=e,n=O({},n,r)}this.filterByColumnFilters(n)},setFilter:function B(e){if(this.props.useExternal)return void this.props.externalSetFilter(e) var t=this,n={page:0,filter:e} -n.filteredResults=this.props.useCustomFilterer?this.props.customFilterer(this.props.results,e):this.defaultFilter(this.props.results,e),n.maxPage=t.getMaxPage(n.filteredResults),(T(e)||F(e)||P(e))&&(n.filter=e, +n.filteredResults=this.props.useCustomFilterer?this.props.customFilterer(this.props.results,e):this.defaultFilter(this.props.results,e),n.maxPage=t.getMaxPage(n.filteredResults),(I(e)||F(e)||x(e))&&(n.filter=e, n.filteredResults=null),t.setState(n),this._resetSelectedRows()},setPageSize:function G(e){return this.props.useExternal?(this.setState({resultsPerPage:e}),void this.props.externalSetPageSize(e)):(this.state.resultsPerPage=e, -void this.setMaxPage())},toggleColumnChooser:function z(){this.setState({showColumnChooser:!this.state.showColumnChooser})},isNullOrUndefined:function q(e){return void 0===e||null===e},shouldUseCustomRowComponent:function Q(){ -return this.isNullOrUndefined(this.state.useCustomRowComponent)?this.props.useCustomRowComponent:this.state.useCustomRowComponent},shouldUseCustomGridComponent:function V(){return this.isNullOrUndefined(this.state.useCustomGridComponent)?this.props.useCustomGridComponent:this.state.useCustomGridComponent +void this.setMaxPage())},toggleColumnChooser:function z(){this.setState({showColumnChooser:!this.state.showColumnChooser})},isNullOrUndefined:function q(e){return void 0===e||null===e},shouldUseCustomRowComponent:function V(){ +return this.isNullOrUndefined(this.state.useCustomRowComponent)?this.props.useCustomRowComponent:this.state.useCustomRowComponent},shouldUseCustomGridComponent:function W(){return this.isNullOrUndefined(this.state.useCustomGridComponent)?this.props.useCustomGridComponent:this.state.useCustomGridComponent -},toggleCustomComponent:function W(){"grid"===this.state.customComponentType?this.setState({useCustomGridComponent:!this.shouldUseCustomGridComponent()}):"row"===this.state.customComponentType&&this.setState({ +},toggleCustomComponent:function Q(){"grid"===this.state.customComponentType?this.setState({useCustomGridComponent:!this.shouldUseCustomGridComponent()}):"row"===this.state.customComponentType&&this.setState({ useCustomRowComponent:!this.shouldUseCustomRowComponent()})},getMaxPage:function K(e,t){if(this.props.useExternal)return this.props.externalMaxPage t||(t=(e||this.getCurrentResults()).length) var n=Math.ceil(t/this.state.resultsPerPage) return n},setMaxPage:function $(e){var t=this.getMaxPage(e) this.state.maxPage!==t&&this.setState({page:0,maxPage:t,filteredColumns:this.columnSettings.filteredColumns})},setPage:function Z(e){if(this.props.useExternal)return void this.props.externalSetPage(e) if(e*this.state.resultsPerPage<=this.state.resultsPerPage*this.state.maxPage){var t=this,n={page:e} -t.setState(n)}this.props.enableInfiniteScroll&&this.setState({isSelectAllChecked:!1})},setColumns:function Y(e){this.columnSettings.filteredColumns=x(e)?e:[e],this.setState({filteredColumns:this.columnSettings.filteredColumns +t.setState(n)}this.props.enableInfiniteScroll&&this.setState({isSelectAllChecked:!1})},setColumns:function Y(e){this.columnSettings.filteredColumns=P(e)?e:[e],this.setState({filteredColumns:this.columnSettings.filteredColumns })},nextPage:function X(){var e=this.getCurrentPage() e0&&this.setPage(e-1)},changeSort:function ee(e){if(this.props.enableSort!==!1){if(this.props.useExternal){var t=this.props.externalSortColumn!==e||!this.props.externalSortAscending @@ -716,25 +715,25 @@ null===this.props.externalSetPageSize&&console.error("useExternal is set to true null===this.props.externalCurrentPage&&console.error("useExternal is set to true but externalCurrentPage is not set. Griddle will not page correctly without that property when using external data."))}, verifyCustom:function se(){this.props.useCustomGridComponent===!0&&null===this.props.customGridComponent&&console.error("useCustomGridComponent is set to true but no custom component was specified."),this.props.useCustomRowComponent===!0&&null===this.props.customRowComponent&&console.error("useCustomRowComponent is set to true but no custom component was specified."), this.props.useCustomGridComponent===!0&&this.props.useCustomRowComponent===!0&&console.error("Cannot currently use both customGridComponent and customRowComponent."),this.props.useCustomFilterer===!0&&null===this.props.customFilterer&&console.error("useCustomFilterer is set to true but no custom filter function was specified."), -this.props.useCustomFilterComponent===!0&&null===this.props.customFilterComponent&&console.error("useCustomFilterComponent is set to true but no customFilterComponent was specified.")},getDataForRender:function le(e,t,n){ +this.props.useCustomFilterComponent===!0&&null===this.props.customFilterComponent&&console.error("useCustomFilterComponent is set to true but no customFilterComponent was specified.")},getDataForRender:function ae(e,t,n){ var r=this,o=this -if(""!==this.state.sortColumn){var i=this.state.sortColumn,s=D(this.props.columnMetadata,{columnName:i}),l,a={columns:[],orders:[]} -if(s.length>0&&(l=s[0].hasOwnProperty("customCompareFn")&&s[0].customCompareFn,s[0].multiSort&&(a=s[0].multiSort)),this.state.sortDirection)if("function"==typeof l)2===l.length?(e=e.sort(function(e,t){ -return l(R(e,i),R(t,i))}),"desc"===this.state.sortDirection&&e.reverse()):1===l.length&&(e=k(e,function(e){return l(R(e,i))},[this.state.sortDirection])) -else{var u=[N(i)],p=[this.state.sortDirection] -a.columns.forEach(function(e,t){u.push(N(e)),"asc"===a.orders[t]||"desc"===a.orders[t]?p.push(a.orders[t]):p.push(r.state.sortDirection)}),e=k(e,u,p)}}var d=this.getCurrentPage() +if(""!==this.state.sortColumn){var i=this.state.sortColumn,s=D(this.props.columnMetadata,{columnName:i}),a,l={columns:[],orders:[]} +if(s.length>0&&(a=s[0].hasOwnProperty("customCompareFn")&&s[0].customCompareFn,s[0].multiSort&&(l=s[0].multiSort)),this.state.sortDirection)if("function"==typeof a)2===a.length?(e=e.sort(function(e,t){ +return a(N(e,i),N(t,i))}),"desc"===this.state.sortDirection&&e.reverse()):1===a.length&&(e=k(e,function(e){return a(N(e,i))},[this.state.sortDirection])) +else{var u=[R(i)],p=[this.state.sortDirection] +l.columns.forEach(function(e,t){u.push(R(e)),"asc"===l.orders[t]||"desc"===l.orders[t]?p.push(l.orders[t]):p.push(r.state.sortDirection)}),e=k(e,u,p)}}var d=this.getCurrentPage() if(!this.props.useExternal&&n&&this.state.resultsPerPage*(d+1)<=this.state.resultsPerPage*this.state.maxPage&&d>=0)if(this.isInfiniteScrollEnabled())e=E(e,(d+1)*this.state.resultsPerPage) else{var c=v(e,d*this.state.resultsPerPage) -e=(b||_)(c,c.length-this.state.resultsPerPage)}for(var f=this.columnSettings.getMetadataColumns,h=[],m=0;m0&&(g.children=o.getDataForRender(g[o.props.childrenColumnName],t,!1),"children"!==o.props.childrenColumnName&&delete g[o.props.childrenColumnName]), -h.push(g)}return h},getCurrentResults:function ae(e){return this.state.filteredResults||e||this.props.results},getCurrentPage:function ue(){return this.props.externalCurrentPage||this.state.page},getCurrentSort:function pe(){ +h.push(g)}return h},getCurrentResults:function le(e){return this.state.filteredResults||e||this.props.results},getCurrentPage:function ue(){return this.props.externalCurrentPage||this.state.page},getCurrentSort:function pe(){ return this.props.useExternal?this.props.externalSortColumn:this.state.sortColumn},getCurrentSortAscending:function de(){return this.props.useExternal?this.props.externalSortAscending:"asc"===this.state.sortDirection },getCurrentMaxPage:function ce(){return this.props.useExternal?this.props.externalMaxPage:this.state.maxPage},getSortObject:function fe(){return{enableSort:this.props.enableSort,changeSort:this.changeSort, sortColumn:this.getCurrentSort(),sortAscending:this.getCurrentSortAscending(),sortDirection:this.state.sortDirection,sortAscendingClassName:this.props.sortAscendingClassName,sortDescendingClassName:this.props.sortDescendingClassName, sortAscendingComponent:this.props.sortAscendingComponent,sortDescendingComponent:this.props.sortDescendingComponent,sortDefaultComponent:this.props.sortDefaultComponent}},_toggleSelectAll:function he(){ var e=this.getDataForRender(this.getCurrentResults(),this.columnSettings.getColumns(),!0),t=!this.state.isSelectAllChecked,n=JSON.parse(JSON.stringify(this.state.selectedRowIds)),r=this -S(e,function(e){r._updateSelectedRowIds(e[r.props.uniqueIdentifier],n,t)},this),this.setState({isSelectAllChecked:t,selectedRowIds:n}),this.props.onSelectionChange&&this.props.onSelectionChange(n,t)},_toggleSelectRow:function me(e,t){ +_(e,function(e){r._updateSelectedRowIds(e[r.props.uniqueIdentifier],n,t)},this),this.setState({isSelectAllChecked:t,selectedRowIds:n}),this.props.onSelectionChange&&this.props.onSelectionChange(n,t)},_toggleSelectRow:function me(e,t){ var n=this.getDataForRender(this.getCurrentResults(),this.columnSettings.getColumns(),!0),r=JSON.parse(JSON.stringify(this.state.selectedRowIds)) this._updateSelectedRowIds(e[this.props.uniqueIdentifier],r,t) var o=this._getAreAllRowsChecked(r,A(n,this.props.uniqueIdentifier)) @@ -742,49 +741,49 @@ this.setState({isSelectAllChecked:o,selectedRowIds:r}),this.props.onSelectionCha n?(r=C(t,function(t){return e===t}),void 0===r&&t.push(e)):t.splice(t.indexOf(e),1)},_getIsSelectAllChecked:function ye(){return this.state.isSelectAllChecked},_getAreAllRowsChecked:function ve(e,t){return t.length===w(t,e).length },_getIsRowChecked:function be(e){return this.state.selectedRowIds.indexOf(e[this.props.uniqueIdentifier])>-1},getSelectedRowIds:function Ce(){return this.state.selectedRowIds},_resetSelectedRows:function Ee(){ -this.setState({isSelectAllChecked:!1,selectedRowIds:[]})},getMultipleSelectionObject:function Se(){return{isMultipleSelection:!C(this.props.results,function(e){return"children"in e})&&this.props.isMultipleSelection, +this.setState({isSelectAllChecked:!1,selectedRowIds:[]})},getMultipleSelectionObject:function _e(){return{isMultipleSelection:!C(this.props.results,function(e){return"children"in e})&&this.props.isMultipleSelection, toggleSelectAll:this._toggleSelectAll,getIsSelectAllChecked:this._getIsSelectAllChecked,toggleSelectRow:this._toggleSelectRow,getSelectedRowIds:this.getSelectedRowIds,getIsRowChecked:this._getIsRowChecked -}},isInfiniteScrollEnabled:function _e(){return!this.props.useCustomPagerComponent&&this.props.enableInfiniteScroll},getClearFixStyles:function we(){return{clear:"both",display:"table",width:"100%"}},getSettingsStyles:function xe(){ -return{"float":"left",width:"50%",textAlign:"right"}},getFilterStyles:function Pe(){return{"float":"left",width:"50%",textAlign:"left",color:"#222",minHeight:"1px"}},getFilter:function Fe(){return this.props.showFilter&&this.shouldUseCustomGridComponent()===!1?this.props.useCustomFilterComponent?o.createElement(h,{ +}},isInfiniteScrollEnabled:function Se(){return!this.props.useCustomPagerComponent&&this.props.enableInfiniteScroll},getClearFixStyles:function we(){return{clear:"both",display:"table",width:"100%"}},getSettingsStyles:function Pe(){ +return{"float":"left",width:"50%",textAlign:"right"}},getFilterStyles:function xe(){return{"float":"left",width:"50%",textAlign:"left",color:"#222",minHeight:"1px"}},getFilter:function Fe(){return this.props.showFilter&&this.shouldUseCustomGridComponent()===!1?this.props.useCustomFilterComponent?o.createElement(h,{ changeFilter:this.setFilter,placeholderText:this.props.filterPlaceholderText,customFilterComponent:this.props.customFilterComponent,results:this.props.results,currentResults:this.getCurrentResults()}):o.createElement(s,{ -changeFilter:this.setFilter,placeholderText:this.props.filterPlaceholderText}):""},getSettings:function Te(){return this.props.showSettings?o.createElement("button",{type:"button",className:this.props.settingsToggleClassName, -onClick:this.toggleColumnChooser,style:this.props.useGriddleStyles?{background:"none",border:"none",padding:0,margin:0,fontSize:14}:null},this.props.settingsText,this.props.settingsIconComponent):""},getTopSection:function Oe(e,t){ +changeFilter:this.setFilter,placeholderText:this.props.filterPlaceholderText}):""},getSettings:function Ie(){return this.props.showSettings?o.createElement("button",{type:"button",className:this.props.settingsToggleClassName, +onClick:this.toggleColumnChooser,style:this.props.useGriddleStyles?{background:"none",border:"none",padding:0,margin:0,fontSize:14}:null},this.props.settingsText,this.props.settingsIconComponent):""},getTopSection:function Te(e,t){ if(this.props.showFilter===!1&&this.props.showSettings===!1)return"" var n=null,r=null,i=null return this.props.useGriddleStyles&&(n=this.getFilterStyles(),r=this.getSettingsStyles(),i=this.getClearFixStyles()),o.createElement("div",{className:"top-section",style:i},o.createElement("div",{className:"griddle-filter", style:n},e),o.createElement("div",{className:"griddle-settings-toggle",style:r},t))},getPagingSection:function Ae(e,t){if((this.props.showPager&&!this.isInfiniteScrollEnabled()&&!this.shouldUseCustomGridComponent())!==!1)return o.createElement("div",{ className:"griddle-footer"},this.props.useCustomPagerComponent?o.createElement(f,{customPagerComponentOptions:this.props.customPagerComponentOptions,next:this.nextPage,previous:this.previousPage,currentPage:e, -maxPage:t,setPage:this.setPage,nextText:this.props.nextText,previousText:this.props.previousText,customPagerComponent:this.props.customPagerComponent}):o.createElement(l,{useGriddleStyles:this.props.useGriddleStyles, +maxPage:t,setPage:this.setPage,nextText:this.props.nextText,previousText:this.props.previousText,customPagerComponent:this.props.customPagerComponent}):o.createElement(a,{useGriddleStyles:this.props.useGriddleStyles, next:this.nextPage,previous:this.previousPage,nextClassName:this.props.nextClassName,nextIconComponent:this.props.nextIconComponent,previousClassName:this.props.previousClassName,previousIconComponent:this.props.previousIconComponent, -currentPage:e,maxPage:t,setPage:this.setPage,nextText:this.props.nextText,previousText:this.props.previousText}))},getColumnSelectorSection:function Ie(e,t){return this.state.showColumnChooser?o.createElement(a,{ +currentPage:e,maxPage:t,setPage:this.setPage,nextText:this.props.nextText,previousText:this.props.previousText}))},getColumnSelectorSection:function Oe(e,t){return this.state.showColumnChooser?o.createElement(l,{ columns:e,selectedColumns:t,setColumns:this.setColumns,settingsText:this.props.settingsText,settingsIconComponent:this.props.settingsIconComponent,maxRowsText:this.props.maxRowsText,setPageSize:this.setPageSize, showSetPageSize:!this.shouldUseCustomGridComponent(),resultsPerPage:this.state.resultsPerPage,enableToggleCustom:this.props.enableToggleCustom,toggleCustomComponent:this.toggleCustomComponent,useCustomComponent:this.shouldUseCustomRowComponent()||this.shouldUseCustomGridComponent(), useGriddleStyles:this.props.useGriddleStyles,enableCustomFormatText:this.props.enableCustomFormatText,columnMetadata:this.props.columnMetadata}):""},getCustomGridSection:function De(){return o.createElement(this.props.customGridComponent,r({ data:this.props.results,className:this.props.customGridComponentClassName},this.props.gridMetadata))},getCustomRowSection:function ke(e,t,n,r,i){return o.createElement("div",null,o.createElement(c,{data:e, columns:t,metadataColumns:n,globalData:i,className:this.props.customRowComponentClassName,customComponent:this.props.customRowComponent,style:this.props.useGriddleStyles?this.getClearFixStyles():null}),this.props.showPager&&r) -},getStandardGridSection:function Ne(e,t,n,r,s){var l=this.getSortObject(),a=this.getMultipleSelectionObject(),u=this.shouldShowNoDataSection(e),p=this.getNoDataSection() +},getStandardGridSection:function Re(e,t,n,r,s){var a=this.getSortObject(),l=this.getMultipleSelectionObject(),u=this.shouldShowNoDataSection(e),p=this.getNoDataSection() return o.createElement("div",{className:"griddle-body"},o.createElement(i,{useGriddleStyles:this.props.useGriddleStyles,noDataSection:p,showNoData:u,columnSettings:this.columnSettings,rowSettings:this.rowSettings, -sortSettings:l,multipleSelectionSettings:a,filterByColumn:this.filterByColumn,isSubGriddle:this.props.isSubGriddle,useGriddleIcons:this.props.useGriddleIcons,useFixedLayout:this.props.useFixedLayout,showPager:this.props.showPager, +sortSettings:a,multipleSelectionSettings:l,filterByColumn:this.filterByColumn,isSubGriddle:this.props.isSubGriddle,useGriddleIcons:this.props.useGriddleIcons,useFixedLayout:this.props.useFixedLayout,showPager:this.props.showPager, pagingContent:r,data:e,className:this.props.tableClassName,enableInfiniteScroll:this.isInfiniteScrollEnabled(),nextPage:this.nextPage,showTableHeading:this.props.showTableHeading,useFixedHeader:this.props.useFixedHeader, parentRowCollapsedClassName:this.props.parentRowCollapsedClassName,parentRowExpandedClassName:this.props.parentRowExpandedClassName,parentRowCollapsedComponent:this.props.parentRowCollapsedComponent,parentRowExpandedComponent:this.props.parentRowExpandedComponent, bodyHeight:this.props.bodyHeight,paddingHeight:this.props.paddingHeight,rowHeight:this.props.rowHeight,infiniteScrollLoadTreshold:this.props.infiniteScrollLoadTreshold,externalLoadingComponent:this.props.externalLoadingComponent, -externalIsLoading:this.props.externalIsLoading,hasMorePages:s,onRowClick:this.props.onRowClick}))},getContentSection:function Re(e,t,n,r,o,i){return this.shouldUseCustomGridComponent()&&null!==this.props.customGridComponent?this.getCustomGridSection():this.shouldUseCustomRowComponent()?this.getCustomRowSection(e,t,n,r,i):this.getStandardGridSection(e,t,n,r,o) +externalIsLoading:this.props.externalIsLoading,hasMorePages:s,onRowClick:this.props.onRowClick}))},getContentSection:function Ne(e,t,n,r,o,i){return this.shouldUseCustomGridComponent()&&null!==this.props.customGridComponent?this.getCustomGridSection():this.shouldUseCustomRowComponent()?this.getCustomRowSection(e,t,n,r,i):this.getStandardGridSection(e,t,n,r,o) -},getNoDataSection:function Ue(){return null!=this.props.customNoDataComponent?o.createElement("div",{className:this.props.noDataClassName},o.createElement(this.props.customNoDataComponent,this.props.customNoDataComponentProps)):o.createElement(u,{ -noDataMessage:this.props.noDataMessage})},shouldShowNoDataSection:function Le(e){return!this.props.allowEmptyGrid&&(this.props.useExternal===!1&&("undefined"==typeof e||0===e.length)||this.props.useExternal===!0&&this.props.externalIsLoading===!1&&0===e.length) +},getNoDataSection:function Le(){return null!=this.props.customNoDataComponent?o.createElement("div",{className:this.props.noDataClassName},o.createElement(this.props.customNoDataComponent,this.props.customNoDataComponentProps)):o.createElement(u,{ +noDataMessage:this.props.noDataMessage})},shouldShowNoDataSection:function Ue(e){return!this.props.allowEmptyGrid&&(this.props.useExternal===!1&&("undefined"==typeof e||0===e.length)||this.props.useExternal===!0&&this.props.externalIsLoading===!1&&0===e.length) -},render:function je(){var e=this,t=this.getCurrentResults(),n=this.props.tableClassName+" table-header",r=this.getFilter(),i=this.getSettings(),s=this.getTopSection(r,i),l=[],a=this.columnSettings.getColumns(),u=this.getDataForRender(t,a,!0),p=this.columnSettings.getMetadataColumns() +},render:function je(){var e=this,t=this.getCurrentResults(),n=this.props.tableClassName+" table-header",r=this.getFilter(),i=this.getSettings(),s=this.getTopSection(r,i),a=[],l=this.columnSettings.getColumns(),u=this.getDataForRender(t,l,!0),p=this.columnSettings.getMetadataColumns() -l=y.keys(O(t[0],p)),l=this.columnSettings.orderColumns(l) -var d=this.getCurrentPage(),c=this.getCurrentMaxPage(),f=d+10?"griddle "+this.props.gridClassName:"griddle" +a=y.keys(T(t[0],p)),a=this.columnSettings.orderColumns(a) +var d=this.getCurrentPage(),c=this.getCurrentMaxPage(),f=d+10?"griddle "+this.props.gridClassName:"griddle" return v+=this.shouldUseCustomRowComponent()?" griddle-custom":"",o.createElement("div",{className:v},s,g,o.createElement("div",{className:"griddle-container",style:this.props.useGriddleStyles&&!this.props.isSubGriddle?{ border:"1px solid #DDD"}:null},m))}}) -d.Griddle=e.exports=U},function(e,t,n){"use strict" -var r=n(3),o=n(32),i=n(189),s=n(33),l=n(196),a=r.createClass({displayName:"GridTable",getDefaultProps:function u(){return{data:[],columnSettings:null,rowSettings:null,sortSettings:null,multipleSelectionSettings:null, +d.Griddle=e.exports=L},function(e,t,n){"use strict" +var r=n(3),o=n(31),i=n(188),s=n(32),a=n(195),l=r.createClass({displayName:"GridTable",getDefaultProps:function u(){return{data:[],columnSettings:null,rowSettings:null,sortSettings:null,multipleSelectionSettings:null, className:"",enableInfiniteScroll:!1,nextPage:null,hasMorePages:!1,useFixedHeader:!1,useFixedLayout:!0,paddingHeight:null,rowHeight:null,filterByColumn:null,infiniteScrollLoadTreshold:null,bodyHeight:null, useGriddleStyles:!0,useGriddleIcons:!0,isSubGriddle:!1,parentRowCollapsedClassName:"parent-row",parentRowExpandedClassName:"parent-row expanded",parentRowCollapsedComponent:"â–¶",parentRowExpandedComponent:"â–¼", externalLoadingComponent:null,externalIsLoading:!1,onRowClick:null}},getInitialState:function p(){return{scrollTop:0,scrollHeight:this.props.bodyHeight,clientHeight:this.props.bodyHeight}},componentDidMount:function d(){ @@ -797,426 +796,426 @@ s<=this.props.infiniteScrollLoadTreshold&&this.props.nextPage()}},verifyProps:fu null===this.props.rowSettings&&console.error("gridTable: The rowSettings prop is null and it shouldn't be")},getAdjustedRowHeight:function m(){return this.props.rowHeight+2*this.props.paddingHeight},getNodeContent:function g(){ this.verifyProps() var e=this,t=!1 -if(!this.props.externalIsLoading||this.props.enableInfiniteScroll){var n=e.props.data,o=null,s=null,l=!1 -if(this.props.enableInfiniteScroll&&null!==this.props.rowHeight&&void 0!==this.refs.scrollable){var a=e.getAdjustedRowHeight(),u=Math.ceil(e.state.clientHeight/a),p=Math.max(0,Math.floor(e.state.scrollTop/a)-.25*u),d=Math.min(p+1.25*u,this.props.data.length-1) +if(!this.props.externalIsLoading||this.props.enableInfiniteScroll){var n=e.props.data,o=null,s=null,a=!1 +if(this.props.enableInfiniteScroll&&null!==this.props.rowHeight&&void 0!==this.refs.scrollable){var l=e.getAdjustedRowHeight(),u=Math.ceil(e.state.clientHeight/l),p=Math.max(0,Math.floor(e.state.scrollTop/l)-.25*u),d=Math.min(p+1.25*u,this.props.data.length-1) n=n.slice(p,d+1) -var c={height:p*a+"px"} +var c={height:p*l+"px"} o=r.createElement("tr",{key:"above-"+c.height,style:c}) -var f={height:(this.props.data.length-d)*a+"px"} -s=r.createElement("tr",{key:"below-"+f.height,style:f})}var h=n.map(function(n,o){var s="undefined"!=typeof n.children&&n.children.length>0,l=e.props.rowSettings.getRowKey(n,o) +var f={height:(this.props.data.length-d)*l+"px"} +s=r.createElement("tr",{key:"below-"+f.height,style:f})}var h=n.map(function(n,o){var s="undefined"!=typeof n.children&&n.children.length>0,a=e.props.rowSettings.getRowKey(n,o) return s&&(t=s),r.createElement(i,{useGriddleStyles:e.props.useGriddleStyles,isSubGriddle:e.props.isSubGriddle,parentRowExpandedClassName:e.props.parentRowExpandedClassName,parentRowCollapsedClassName:e.props.parentRowCollapsedClassName, -parentRowExpandedComponent:e.props.parentRowExpandedComponent,parentRowCollapsedComponent:e.props.parentRowCollapsedComponent,data:n,key:l+"-container",uniqueId:l,columnSettings:e.props.columnSettings, +parentRowExpandedComponent:e.props.parentRowExpandedComponent,parentRowCollapsedComponent:e.props.parentRowCollapsedComponent,data:n,key:a+"-container",uniqueId:a,columnSettings:e.props.columnSettings, rowSettings:e.props.rowSettings,paddingHeight:e.props.paddingHeight,multipleSelectionSettings:e.props.multipleSelectionSettings,rowHeight:e.props.rowHeight,hasChildren:s,tableClassName:e.props.className, onRowClick:e.props.onRowClick})}) if(this.props.showNoData){var m=this.props.columnSettings.getVisibleColumnCount() h.push(r.createElement("tr",{key:"no-data-section"},r.createElement("td",{colSpan:m},this.props.noDataSection)))}return o&&h.unshift(o),s&&h.push(s),{nodes:h,anyHasChildren:t}}return null},render:function y(){ var e=this,t=[],n=!1,i=this.getNodeContent() i&&(t=i.nodes,n=i.anyHasChildren) -var s=null,l=null,a={width:"100%"} -if(this.props.useFixedLayout&&(a.tableLayout="fixed"),this.props.enableInfiniteScroll&&(s={position:"relative",overflowY:"scroll",height:this.props.bodyHeight+"px",width:"100%"}),this.props.externalIsLoading){ +var s=null,a=null,l={width:"100%"} +if(this.props.useFixedLayout&&(l.tableLayout="fixed"),this.props.enableInfiniteScroll&&(s={position:"relative",overflowY:"scroll",height:this.props.bodyHeight+"px",width:"100%"}),this.props.externalIsLoading){ var u=null,p=null this.props.useGriddleStyles&&(u={textAlign:"center",paddingBottom:"40px"}),p=this.props.columnSettings.getVisibleColumnCount() var d=this.props.externalLoadingComponent?r.createElement(this.props.externalLoadingComponent,null):r.createElement("div",null,"Loading...") -l=r.createElement("tbody",null,r.createElement("tr",null,r.createElement("td",{style:u,colSpan:p},d)))}var c=this.props.showTableHeading?r.createElement(o,{useGriddleStyles:this.props.useGriddleStyles, +a=r.createElement("tbody",null,r.createElement("tr",null,r.createElement("td",{style:u,colSpan:p},d)))}var c=this.props.showTableHeading?r.createElement(o,{useGriddleStyles:this.props.useGriddleStyles, useGriddleIcons:this.props.useGriddleIcons,sortSettings:this.props.sortSettings,multipleSelectionSettings:this.props.multipleSelectionSettings,columnSettings:this.props.columnSettings,filterByColumn:this.props.filterByColumn, rowSettings:this.props.rowSettings}):void 0 n||(t=r.createElement("tbody",null,t)) var f=r.createElement("tbody",null) if(this.props.showPager){var h=this.props.useGriddleStyles?{padding:"0px",backgroundColor:"#EDEDED",border:"0px",color:"#222",height:this.props.showNoData?"20px":null}:null f=r.createElement("tbody",null,r.createElement("tr",null,r.createElement("td",{colSpan:this.props.multipleSelectionSettings.isMultipleSelection?this.props.columnSettings.getVisibleColumnCount()+1:this.props.columnSettings.getVisibleColumnCount(), -style:h,className:"footer-container"},this.props.showNoData?null:this.props.pagingContent)))}return this.props.useFixedHeader?(this.props.useGriddleStyles&&(a.tableLayout="fixed"),r.createElement("div",null,r.createElement("table",{ -className:this.props.className,style:this.props.useGriddleStyles&&a||null},c),r.createElement("div",{ref:"scrollable",onScroll:this.gridScroll,style:s},r.createElement("table",{className:this.props.className, -style:this.props.useGriddleStyles&&a||null},t,l,f)))):r.createElement("div",{ref:"scrollable",onScroll:this.gridScroll,style:s},r.createElement("table",{className:this.props.className,style:this.props.useGriddleStyles&&a||null -},c,t,l,f))}}) -e.exports=a},function(e,t,n){"use strict" +style:h,className:"footer-container"},this.props.showNoData?null:this.props.pagingContent)))}return this.props.useFixedHeader?(this.props.useGriddleStyles&&(l.tableLayout="fixed"),r.createElement("div",null,r.createElement("table",{ +className:this.props.className,style:this.props.useGriddleStyles&&l||null},c),r.createElement("div",{ref:"scrollable",onScroll:this.gridScroll,style:s},r.createElement("table",{className:this.props.className, +style:this.props.useGriddleStyles&&l||null},t,a,f)))):r.createElement("div",{ref:"scrollable",onScroll:this.gridScroll,style:s},r.createElement("table",{className:this.props.className,style:this.props.useGriddleStyles&&l||null +},c,t,a,f))}}) +e.exports=l},function(e,t,n){"use strict" var r=Object.assign||function(e){for(var t=1;t0}},{key:"getMetadataColumnProperty", +value:function p(e){return a(this.columnMetadata,{columnName:e})}},{key:"hasColumnMetadata",value:function d(){return null!==this.columnMetadata&&this.columnMetadata.length>0}},{key:"getMetadataColumnProperty", value:function c(e,t,n){var r=this.getColumnMetadataByName(e) -return"undefined"==typeof r||null===r?n:r.hasOwnProperty(t)?r[t]:n}},{key:"orderColumns",value:function f(e){var t=this,n=100,r=a(e,function(e){var r=l(t.columnMetadata,{columnName:e}) +return"undefined"==typeof r||null===r?n:r.hasOwnProperty(t)?r[t]:n}},{key:"orderColumns",value:function f(e){var t=this,n=100,r=l(e,function(e){var r=a(t.columnMetadata,{columnName:e}) return"undefined"==typeof r||null===r||isNaN(r.order)?n:r.order}) return r}},{key:"getColumns",value:function h(){var e=0===this.filteredColumns.length?this.allColumns:this.filteredColumns return e=u(e,this.metadataColumns),e=this.orderColumns(e)}}]),e}() -e.exports=p},function(e,t,n){function r(e,t){var n=l(e)?o:s -return n(e,i(t,3))}var o=n(35),i=n(36),s=n(143),l=n(102) +e.exports=p},function(e,t,n){function r(e,t){var n=a(e)?o:s +return n(e,i(t,3))}var o=n(34),i=n(35),s=n(142),a=n(101) e.exports=r},function(e,t){function n(e,t){for(var n=-1,r=null==e?0:e.length,o=Array(r);++n-1}var o=n(43) +return n<0?void 0:t[n][1]}var o=n(42) +e.exports=r},function(e,t,n){function r(e){return o(this.__data__,e)>-1}var o=n(42) e.exports=r},function(e,t,n){function r(e,t){var n=this.__data__,r=o(n,e) -return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}var o=n(43) -e.exports=r},function(e,t,n){function r(){this.__data__=new o,this.size=0}var o=n(40) +return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}var o=n(42) +e.exports=r},function(e,t,n){function r(){this.__data__=new o,this.size=0}var o=n(39) e.exports=r},function(e,t){function n(e){var t=this.__data__,n=t["delete"](e) return this.size=t.size,n}e.exports=n},function(e,t){function n(e){return this.__data__.get(e)}e.exports=n},function(e,t){function n(e){return this.__data__.has(e)}e.exports=n},function(e,t,n){function r(e,t){ var n=this.__data__ if(n instanceof o){var r=n.__data__ -if(!i||r.lengthc))return!1 var h=p.get(e) if(h&&p.get(t))return h==t -var m=-1,g=!0,y=n&a?new o:void 0 +var m=-1,g=!0,y=n&l?new o:void 0 for(p.set(e,t),p.set(t,e);++m-1&&e%1==0&&e-1&&e%1==0&&e<=r +O[_]=O[S]=O[w]=O[P]=O[x]=O[F]=O[I]=O[T]=O[A]=!0,O[a]=O[l]=O[C]=O[u]=O[E]=O[p]=O[d]=O[c]=O[f]=O[h]=O[m]=O[g]=O[y]=O[v]=O[b]=!1,e.exports=r},function(e,t){function n(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=r }var r=9007199254740991 -e.exports=n},function(e,t){function n(e){return function(t){return e(t)}}e.exports=n},function(e,t,n){(function(e){var r=n(60),o="object"==typeof t&&t&&!t.nodeType&&t,i=o&&"object"==typeof e&&e&&!e.nodeType&&e,s=i&&i.exports===o,l=s&&r.process,a=function(){ -try{return l&&l.binding&&l.binding("util")}catch(e){}}() -e.exports=a}).call(t,n(24)(e))},function(e,t,n){function r(e){if(!o(e))return i(e) +e.exports=n},function(e,t){function n(e){return function(t){return e(t)}}e.exports=n},function(e,t,n){(function(e){var r=n(59),o="object"==typeof t&&t&&!t.nodeType&&t,i=o&&"object"==typeof e&&e&&!e.nodeType&&e,s=i&&i.exports===o,a=s&&r.process,l=function(){ +try{return a&&a.binding&&a.binding("util")}catch(e){}}() +e.exports=l}).call(t,n(24)(e))},function(e,t,n){function r(e){if(!o(e))return i(e) var t=[] -for(var n in Object(e))l.call(e,n)&&"constructor"!=n&&t.push(n) -return t}var o=n(112),i=n(113),s=Object.prototype,l=s.hasOwnProperty +for(var n in Object(e))a.call(e,n)&&"constructor"!=n&&t.push(n) +return t}var o=n(111),i=n(112),s=Object.prototype,a=s.hasOwnProperty e.exports=r},function(e,t){function n(e){var t=e&&e.constructor,n="function"==typeof t&&t.prototype||r return e===n}var r=Object.prototype -e.exports=n},function(e,t,n){var r=n(114),o=r(Object.keys,Object) -e.exports=o},function(e,t){function n(e,t){return function(n){return e(t(n))}}e.exports=n},function(e,t,n){function r(e){return null!=e&&i(e.length)&&!o(e)}var o=n(56),i=n(108) -e.exports=r},function(e,t,n){var r=n(117),o=n(53),i=n(118),s=n(119),l=n(120),a=n(57),u=n(66),p="[object Map]",d="[object Object]",c="[object Promise]",f="[object Set]",h="[object WeakMap]",m="[object DataView]",g=u(r),y=u(o),v=u(i),b=u(s),C=u(l),E=a +e.exports=n},function(e,t,n){var r=n(113),o=r(Object.keys,Object) +e.exports=o},function(e,t){function n(e,t){return function(n){return e(t(n))}}e.exports=n},function(e,t,n){function r(e){return null!=e&&i(e.length)&&!o(e)}var o=n(55),i=n(107) +e.exports=r},function(e,t,n){var r=n(116),o=n(52),i=n(117),s=n(118),a=n(119),l=n(56),u=n(65),p="[object Map]",d="[object Object]",c="[object Promise]",f="[object Set]",h="[object WeakMap]",m="[object DataView]",g=u(r),y=u(o),v=u(i),b=u(s),C=u(a),E=l -;(r&&E(new r(new ArrayBuffer(1)))!=m||o&&E(new o)!=p||i&&E(i.resolve())!=c||s&&E(new s)!=f||l&&E(new l)!=h)&&(E=function(e){var t=a(e),n=t==d?e.constructor:void 0,r=n?u(n):"" +;(r&&E(new r(new ArrayBuffer(1)))!=m||o&&E(new o)!=p||i&&E(i.resolve())!=c||s&&E(new s)!=f||a&&E(new a)!=h)&&(E=function(e){var t=l(e),n=t==d?e.constructor:void 0,r=n?u(n):"" if(r)switch(r){case g:return m case y:return p case v:return c case b:return f -case C:return h}return t}),e.exports=E},function(e,t,n){var r=n(54),o=n(59),i=r(o,"DataView") -e.exports=i},function(e,t,n){var r=n(54),o=n(59),i=r(o,"Promise") -e.exports=i},function(e,t,n){var r=n(54),o=n(59),i=r(o,"Set") -e.exports=i},function(e,t,n){var r=n(54),o=n(59),i=r(o,"WeakMap") +case C:return h}return t}),e.exports=E},function(e,t,n){var r=n(53),o=n(58),i=r(o,"DataView") +e.exports=i},function(e,t,n){var r=n(53),o=n(58),i=r(o,"Promise") +e.exports=i},function(e,t,n){var r=n(53),o=n(58),i=r(o,"Set") +e.exports=i},function(e,t,n){var r=n(53),o=n(58),i=r(o,"WeakMap") e.exports=i},function(e,t,n){function r(e){for(var t=i(e),n=t.length;n--;){var r=t[n],s=e[r] -t[n]=[r,s,o(s)]}return t}var o=n(122),i=n(96) -e.exports=r},function(e,t,n){function r(e){return e===e&&!o(e)}var o=n(63) -e.exports=r},function(e,t){function n(e,t){return function(n){return null!=n&&(n[e]===t&&(void 0!==t||e in Object(n)))}}e.exports=n},function(e,t,n){function r(e,t){return l(e)&&a(t)?u(p(e),t):function(n){ +t[n]=[r,s,o(s)]}return t}var o=n(121),i=n(95) +e.exports=r},function(e,t,n){function r(e){return e===e&&!o(e)}var o=n(62) +e.exports=r},function(e,t){function n(e,t){return function(n){return null!=n&&(n[e]===t&&(void 0!==t||e in Object(n)))}}e.exports=n},function(e,t,n){function r(e,t){return a(e)&&l(t)?u(p(e),t):function(n){ var r=i(n,e) -return void 0===r&&r===t?s(n,e):o(t,r,d|c)}}var o=n(83),i=n(125),s=n(136),l=n(128),a=n(122),u=n(123),p=n(135),d=1,c=2 +return void 0===r&&r===t?s(n,e):o(t,r,d|c)}}var o=n(82),i=n(124),s=n(135),a=n(127),l=n(121),u=n(122),p=n(134),d=1,c=2 e.exports=r},function(e,t,n){function r(e,t,n){var r=null==e?void 0:o(e,t) -return void 0===r?n:r}var o=n(126) +return void 0===r?n:r}var o=n(125) e.exports=r},function(e,t,n){function r(e,t){t=o(t,e) for(var n=0,r=t.length;null!=e&&n-1?l[a?t[u]:u]:void 0}}var o=n(36),i=n(115),s=n(96) +return o(e,function(e,r,o){t(e,r,o)&&n.push(e)}),n}var o=n(143) +e.exports=r},function(e,t,n){var r=n(152),o=n(153),i=r(o) +e.exports=i},function(e,t,n){function r(e){return function(t,n,r){var a=Object(t) +if(!i(t)){var l=o(n,3) +t=s(t),n=function(e){return l(a[e],e,a)}}var u=e(t,n,r) +return u>-1?a[l?t[u]:u]:void 0}}var o=n(35),i=n(114),s=n(95) e.exports=r},function(e,t,n){function r(e,t,n){var r=null==e?0:e.length if(!r)return-1 -var a=null==n?0:s(n) -return a<0&&(a=l(r+a,0)),o(e,i(t,3),a)}var o=n(155),i=n(36),s=n(156),l=Math.max +var l=null==n?0:s(n) +return l<0&&(l=a(r+l,0)),o(e,i(t,3),l)}var o=n(154),i=n(35),s=n(155),a=Math.max e.exports=r},function(e,t){function n(e,t,n,r){for(var o=e.length,i=n+(r?1:-1);r?i--:++i1&&s(e,t[0],t[1])?t=[]:n>2&&s(t[0],t[1],t[2])&&(t=[t[0]]),o(e,r(t,1),[])}) -e.exports=l},function(e,t,n){function r(e,t,n,s,l){var a=-1,u=e.length -for(n||(n=i),l||(l=[]);++a0&&n(p)?t>1?r(p,t-1,n,s,l):o(l,p):s||(l[l.length]=p)}return l}var o=n(161),i=n(162) +e.exports=a},function(e,t,n){function r(e,t,n,s,a){var l=-1,u=e.length +for(n||(n=i),a||(a=[]);++l0&&n(p)?t>1?r(p,t-1,n,s,a):o(a,p):s||(a[a.length]=p)}return a}var o=n(160),i=n(161) e.exports=r},function(e,t){function n(e,t){for(var n=-1,r=t.length,o=e.length;++n=a)return u +return e}e.exports=n},function(e,t,n){function r(e,t,n){for(var r=-1,i=e.criteria,s=t.criteria,a=i.length,l=n.length;++r=l)return u var p=n[r] -return u*("desc"==p?-1:1)}}return e.index-t.index}var o=n(166) -e.exports=r},function(e,t,n){function r(e,t){if(e!==t){var n=void 0!==e,r=null===e,i=e===e,s=o(e),l=void 0!==t,a=null===t,u=t===t,p=o(t) -if(!a&&!p&&!s&&e>t||s&&l&&u&&!a&&!p||r&&l&&u||!n&&u||!i)return 1 -if(!r&&!s&&!p&&et||s&&a&&u&&!l&&!p||r&&a&&u||!n&&u||!i)return 1 +if(!r&&!s&&!p&&e0){if(++t>=r)return arguments[0]}else t=0 +return function(){var s=i(),a=o-(s-n) +if(n=s,a>0){if(++t>=r)return arguments[0]}else t=0 return e.apply(void 0,arguments)}}var r=800,o=16,i=Date.now -e.exports=n},function(e,t,n){function r(e,t,n){if(!l(n))return!1 +e.exports=n},function(e,t,n){function r(e,t,n){if(!a(n))return!1 var r=typeof t -return!!("number"==r?i(n)&&s(t,n.length):"string"==r&&t in n)&&o(n[t],e)}var o=n(44),i=n(115),s=n(105),l=n(63) -e.exports=r},function(e,t,n){var r=n(177),o=n(160),i=n(167),s=n(183),l=i(function(e,t){return s(e)?r(e,o(t,1,s,!0)):[]}) -e.exports=l},function(e,t,n){function r(e,t,n,r){var d=-1,c=i,f=!0,h=e.length,m=[],g=t.length +return!!("number"==r?i(n)&&s(t,n.length):"string"==r&&t in n)&&o(n[t],e)}var o=n(43),i=n(114),s=n(104),a=n(62) +e.exports=r},function(e,t,n){var r=n(176),o=n(159),i=n(166),s=n(182),a=i(function(e,t){return s(e)?r(e,o(t,1,s,!0)):[]}) +e.exports=a},function(e,t,n){function r(e,t,n,r){var d=-1,c=i,f=!0,h=e.length,m=[],g=t.length if(!h)return m -n&&(t=l(t,a(n))),r?(c=s,f=!1):t.length>=p&&(c=u,f=!1,t=new o(t)) +n&&(t=a(t,l(n))),r?(c=s,f=!1):t.length>=p&&(c=u,f=!1,t=new o(t)) e:for(;++d-1}var o=n(179) -e.exports=r},function(e,t,n){function r(e,t,n){return t===t?s(e,t,n):o(e,i,n)}var o=n(155),i=n(180),s=n(181) +return!!n&&o(e,t,0)>-1}var o=n(178) +e.exports=r},function(e,t,n){function r(e,t,n){return t===t?s(e,t,n):o(e,i,n)}var o=n(154),i=n(179),s=n(180) e.exports=r},function(e,t){function n(e){return e!==e}e.exports=n},function(e,t){function n(e,t,n){for(var r=n-1,o=e.length;++r1?n[o-1]:void 0,l=o>2?n[2]:void 0 -for(s=e.length>3&&"function"==typeof s?(o--,s):void 0,l&&i(n[0],n[1],l)&&(s=o<3?void 0:s,o=1),t=Object(t);++r1?n[o-1]:void 0,a=o>2?n[2]:void 0 +for(s=e.length>3&&"function"==typeof s?(o--,s):void 0,a&&i(n[0],n[1],a)&&(s=o<3?void 0:s,o=1),t=Object(t);++r0&&(e=r.createElement("button",{type:"button",onClick:this.props.previous,style:this.props.useGriddleStyles?{color:"#222",border:"none",background:"none",margin:"0 0 0 10px"}:null },this.props.previousIconComponent,this.props.previousText)),this.props.currentPage!==this.props.maxPage-1&&(t=r.createElement("button",{type:"button",onClick:this.props.next,style:this.props.useGriddleStyles?{ color:"#222",border:"none",background:"none",margin:"0 10px 0 0"}:null},this.props.nextText,this.props.nextIconComponent)) var n=null,i=null,s=null -if(this.props.useGriddleStyles===!0){var l={"float":"left",minHeight:"1px",marginTop:"5px"} -s=o({textAlign:"right",width:"34%"},l),i=o({textAlign:"center",width:"33%"},l),n=o({width:"33%"},l)}for(var a=[],u=1;u<=this.props.maxPage;u++)a.push(r.createElement("option",{value:u,key:u},u)) +if(this.props.useGriddleStyles===!0){var a={"float":"left",minHeight:"1px",marginTop:"5px"} +s=o({textAlign:"right",width:"34%"},a),i=o({textAlign:"center",width:"33%"},a),n=o({width:"33%"},a)}for(var l=[],u=1;u<=this.props.maxPage;u++)l.push(r.createElement("option",{value:u,key:u},u)) return r.createElement("div",{style:this.props.useGriddleStyles?{minHeight:"35px"}:null},r.createElement("div",{className:this.props.previousClassName,style:n},e),r.createElement("div",{className:"griddle-page", -style:i},r.createElement("select",{value:this.props.currentPage+1,onChange:this.pageChange},a)," / ",this.props.maxPage),r.createElement("div",{className:this.props.nextClassName,style:s},t))}}) +style:i},r.createElement("select",{value:this.props.currentPage+1,onChange:this.pageChange},l)," / ",this.props.maxPage),r.createElement("div",{className:this.props.nextClassName,style:s},t))}}) e.exports=i},function(e,t,n){"use strict" -var r=n(3),o=n(201),i=n(205),s=n(152),l=r.createClass({displayName:"GridSettings",getDefaultProps:function a(){return{columns:[],columnMetadata:[],selectedColumns:[],settingsText:"",maxRowsText:"",resultsPerPage:0, +var r=n(3),o=n(200),i=n(204),s=n(151),a=r.createClass({displayName:"GridSettings",getDefaultProps:function l(){return{columns:[],columnMetadata:[],selectedColumns:[],settingsText:"",maxRowsText:"",resultsPerPage:0, enableToggleCustom:!1,useCustomComponent:!1,useGriddleStyles:!0,toggleCustomComponent:function e(){}}},setPageSize:function u(e){var t=parseInt(e.target.value,10) this.props.setPageSize(t)},handleChange:function p(e){var t=e.target.dataset?e.target.dataset.name:e.target.getAttribute("data-name") e.target.checked===!0&&o(this.props.selectedColumns,t)===!1?(this.props.selectedColumns.push(t),this.props.setColumns(this.props.selectedColumns)):this.props.setColumns(i(this.props.selectedColumns,t)) },render:function d(){var e=this,t=[] -e.props.useCustomComponent===!1&&(t=this.props.columns.map(function(t,n){var i=o(e.props.selectedColumns,t),l=s(e.props.columnMetadata,{columnName:t}),a=t -return"undefined"!=typeof l&&"undefined"!=typeof l.displayName&&null!=l.displayName&&(a=l.displayName),"undefined"!=typeof l&&null!=l&&l.locked?r.createElement("div",{className:"column checkbox"},r.createElement("label",null,r.createElement("input",{ -type:"checkbox",disabled:!0,name:"check",checked:i,"data-name":t}),a)):"undefined"!=typeof l&&null!=l&&"undefined"!=typeof l.visible&&l.visible===!1?null:r.createElement("div",{className:"griddle-column-selection checkbox", +e.props.useCustomComponent===!1&&(t=this.props.columns.map(function(t,n){var i=o(e.props.selectedColumns,t),a=s(e.props.columnMetadata,{columnName:t}),l=t +return"undefined"!=typeof a&&"undefined"!=typeof a.displayName&&null!=a.displayName&&(l=a.displayName),"undefined"!=typeof a&&null!=a&&a.locked?r.createElement("div",{className:"column checkbox"},r.createElement("label",null,r.createElement("input",{ +type:"checkbox",disabled:!0,name:"check",checked:i,"data-name":t}),l)):"undefined"!=typeof a&&null!=a&&"undefined"!=typeof a.visible&&a.visible===!1?null:r.createElement("div",{className:"griddle-column-selection checkbox", key:t,style:e.props.useGriddleStyles?{"float":"left",width:"20%"}:null},r.createElement("label",null,r.createElement("input",{type:"checkbox",name:"check",onChange:e.handleChange,checked:i,"data-name":t -}),a))})) +}),l))})) var n=e.props.enableToggleCustom?r.createElement("div",{className:"form-group"},r.createElement("label",{htmlFor:"maxRows"},r.createElement("input",{type:"checkbox",checked:this.props.useCustomComponent, onChange:this.props.toggleCustomComponent})," ",this.props.enableCustomFormatText)):"",i=this.props.showSetPageSize?r.createElement("div",null,r.createElement("label",{htmlFor:"maxRows"},this.props.maxRowsText,":",r.createElement("select",{ onChange:this.setPageSize,value:this.props.resultsPerPage},r.createElement("option",{value:"5"},"5"),r.createElement("option",{value:"10"},"10"),r.createElement("option",{value:"25"},"25"),r.createElement("option",{ value:"50"},"50"),r.createElement("option",{value:"100"},"100")))):"" return r.createElement("div",{className:"griddle-settings",style:this.props.useGriddleStyles?{backgroundColor:"#FFF",border:"1px solid #DDD",color:"#222",padding:"10px",marginBottom:"10px"}:null},r.createElement("h6",null,this.props.settingsText),r.createElement("div",{ className:"griddle-columns",style:this.props.useGriddleStyles?{clear:"both",display:"table",width:"100%",borderBottom:"1px solid #EDEDED",marginBottom:"10px"}:null},t),i,n)}}) -e.exports=l},function(e,t,n){function r(e,t,n,r){e=i(e)?e:a(e),n=n&&!r?l(n):0 +e.exports=a},function(e,t,n){function r(e,t,n,r){e=i(e)?e:l(e),n=n&&!r?a(n):0 var p=e.length -return n<0&&(n=u(p+n,0)),s(e)?n<=p&&e.indexOf(t,n)>-1:!!p&&o(e,t,n)>-1}var o=n(179),i=n(115),s=n(202),l=n(156),a=n(203),u=Math.max -e.exports=r},function(e,t,n){function r(e){return"string"==typeof e||!i(e)&&s(e)&&o(e)==l}var o=n(57),i=n(102),s=n(101),l="[object String]" -e.exports=r},function(e,t,n){function r(e){return null==e?[]:o(e,i(e))}var o=n(204),i=n(96) -e.exports=r},function(e,t,n){function r(e,t){return o(t,function(t){return e[t]})}var o=n(35) -e.exports=r},function(e,t,n){var r=n(177),o=n(167),i=n(183),s=o(function(e,t){return i(e)?r(e,t):[]}) +return n<0&&(n=u(p+n,0)),s(e)?n<=p&&e.indexOf(t,n)>-1:!!p&&o(e,t,n)>-1}var o=n(178),i=n(114),s=n(201),a=n(155),l=n(202),u=Math.max +e.exports=r},function(e,t,n){function r(e){return"string"==typeof e||!i(e)&&s(e)&&o(e)==a}var o=n(56),i=n(101),s=n(100),a="[object String]" +e.exports=r},function(e,t,n){function r(e){return null==e?[]:o(e,i(e))}var o=n(203),i=n(95) +e.exports=r},function(e,t,n){function r(e,t){return o(t,function(t){return e[t]})}var o=n(34) +e.exports=r},function(e,t,n){var r=n(176),o=n(166),i=n(182),s=o(function(e,t){return i(e)?r(e,t):[]}) e.exports=s},function(e,t,n){"use strict" var r=n(3),o=r.createClass({displayName:"GridNoData",getDefaultProps:function i(){return{noDataMessage:"No Data"}},render:function s(){var e=this return r.createElement("div",null,this.props.noDataMessage)}}) e.exports=o},function(e,t,n){"use strict" -var r=n(3),o=n(33),i=n(208),s=n(56),l=n(212),a=n(184),u=n(214),p=n(220),d=n(205),c=r.createClass({displayName:"GridRow",getDefaultProps:function f(){return{isChildRow:!1,showChildren:!1,data:{},columnSettings:null, +var r=n(3),o=n(32),i=n(207),s=n(55),a=n(211),l=n(183),u=n(213),p=n(219),d=n(204),c=r.createClass({displayName:"GridRow",getDefaultProps:function f(){return{isChildRow:!1,showChildren:!1,data:{},columnSettings:null, rowSettings:null,hasChildren:!1,useGriddleStyles:!0,useGriddleIcons:!0,isSubGriddle:!1,paddingHeight:null,rowHeight:null,parentRowCollapsedClassName:"parent-row",parentRowExpandedClassName:"parent-row expanded", parentRowCollapsedComponent:"â–¶",parentRowExpandedComponent:"â–¼",onRowClick:null,multipleSelectionSettings:null}},handleClick:function h(e){null!==this.props.onRowClick&&s(this.props.onRowClick)?this.props.onRowClick(this,e):this.props.hasChildren&&this.props.toggleChildren() @@ -1321,17 +1320,17 @@ this.verifyProps() var t=this,n=null this.props.useGriddleStyles&&(n={margin:"0px",padding:t.props.paddingHeight+"px 5px "+t.props.paddingHeight+"px 5px",height:t.props.rowHeight?this.props.rowHeight-2*t.props.paddingHeight+"px":null,backgroundColor:"#FFF", borderTopColor:"#DDD",color:"#222"}) -var o=this.props.columnSettings.getColumns(),c=l(o,[]),f=a({},this.props.data) +var o=this.props.columnSettings.getColumns(),c=a(o,[]),f=l({},this.props.data) u(f,c) -var h=p(i.pick(f,d(o,"children"))),m=h.map(function(t,o){var i=null,s=e.props.columnSettings.getColumnMetadataByName(t[0]),l=0===o&&e.props.hasChildren&&e.props.showChildren===!1&&e.props.useGriddleIcons?r.createElement("span",{ +var h=p(i.pick(f,d(o,"children"))),m=h.map(function(t,o){var i=null,s=e.props.columnSettings.getColumnMetadataByName(t[0]),a=0===o&&e.props.hasChildren&&e.props.showChildren===!1&&e.props.useGriddleIcons?r.createElement("span",{ style:e.props.useGriddleStyles?{fontSize:"10px",marginRight:"5px"}:null},e.props.parentRowCollapsedComponent):0===o&&e.props.hasChildren&&e.props.showChildren&&e.props.useGriddleIcons?r.createElement("span",{ style:e.props.useGriddleStyles?{fontSize:"10px"}:null},e.props.parentRowExpandedComponent):"" -if(0===o&&e.props.isChildRow&&e.props.useGriddleStyles&&(n=a(n,{paddingLeft:10})),e.props.columnSettings.hasColumnMetadata()&&"undefined"!=typeof s&&null!==s)if("undefined"!=typeof s.customComponent&&null!==s.customComponent){ +if(0===o&&e.props.isChildRow&&e.props.useGriddleStyles&&(n=l(n,{paddingLeft:10})),e.props.columnSettings.hasColumnMetadata()&&"undefined"!=typeof s&&null!==s)if("undefined"!=typeof s.customComponent&&null!==s.customComponent){ var u=r.createElement(s.customComponent,{data:t[1],rowData:f,metadata:s}) -i=r.createElement("td",{onClick:e.handleClick,className:s.cssClassName,key:o,style:n},u)}else i=r.createElement("td",{onClick:e.handleClick,className:s.cssClassName,key:o,style:n},l,e.formatData(t[1])) +i=r.createElement("td",{onClick:e.handleClick,className:s.cssClassName,key:o,style:n},u)}else i=r.createElement("td",{onClick:e.handleClick,className:s.cssClassName,key:o,style:n},a,e.formatData(t[1])) -return i||r.createElement("td",{onClick:e.handleClick,key:o,style:n},l,t[1])}),g,y +return i||r.createElement("td",{onClick:e.handleClick,key:o,style:n},a,t[1])}),g,y if(null!==this.props.onRowClick&&s(this.props.onRowClick)?(g=null,y=this.handleSelectClick):this.props.multipleSelectionSettings&&this.props.multipleSelectionSettings.isMultipleSelection?(g=this.handleSelectClick, y=null):(g=null,y=null),m&&this.props.multipleSelectionSettings&&this.props.multipleSelectionSettings.isMultipleSelection){var v=this.props.multipleSelectionSettings.getSelectedRowIds() m.unshift(r.createElement("td",{key:"selection",style:n,className:"griddle-select griddle-select-cell",onClick:y},r.createElement("input",{type:"checkbox",checked:this.props.multipleSelectionSettings.getIsRowChecked(f), @@ -1344,29 +1343,29 @@ return n}function o(e,t){if("string"==typeof t){if(void 0!==e[t])return e[t] t=r(t)}for(var n=-1,o=t.length;++no?0:o+t),n=n>o?o:n,n<0&&(n+=o),o=t>n?0:n-t>>>0,t>>>=0 for(var i=Array(o);++r=120&&y.length>=120)?new o(f&&y):void 0}y=e[0] +e.exports=a},function(e,t,n){function r(e,t,n){for(var r=n?s:i,d=e[0].length,c=e.length,f=c,h=Array(c),m=1/0,g=[];f--;){var y=e[f] +f&&t&&(y=a(y,l(t))),m=p(y.length,m),h[f]=!n&&(t||d>=120&&y.length>=120)?new o(f&&y):void 0}y=e[0] var v=-1,b=h[0] e:for(;++v1),t}),l(e,u(e),n),a&&(n=o(n,p|d|c)) +var l=!1 +t=r(t,function(t){return t=s(t,e),l||(l=t.length>1),t}),a(e,u(e),n),l&&(n=o(n,p|d|c)) for(var f=t.length;f--;)i(n,t[f]) return n}) -e.exports=f},function(e,t,n){function r(e,t,n,F,T,O){var A,k=t&_,N=t&w,U=t&x -if(n&&(A=T?n(e,F,T,O):n(e)),void 0!==A)return A +e.exports=f},function(e,t,n){function r(e,t,n,F,I,T){var A,k=t&S,R=t&w,L=t&P +if(n&&(A=I?n(e,F,I,T):n(e)),void 0!==A)return A if(!E(e))return e -var L=b(e) -if(L){if(A=g(e),!k)return p(e,A)}else{var j=m(e),M=j==I||j==D +var U=b(e) +if(U){if(A=g(e),!k)return p(e,A)}else{var j=m(e),M=j==O||j==D if(C(e))return u(e,k) -if(j==R||j==P||M&&!T){if(A=N||M?{}:v(e),!k)return N?c(e,a(A,e)):d(e,l(A,e))}else{if(!X[j])return T?e:{} -A=y(e,j,r,k)}}O||(O=new o) -var H=O.get(e) +if(j==N||j==x||M&&!I){if(A=R||M?{}:v(e),!k)return R?c(e,l(A,e)):d(e,a(A,e))}else{if(!X[j])return I?e:{} +A=y(e,j,r,k)}}T||(T=new o) +var H=T.get(e) if(H)return H -O.set(e,A) -var B=U?N?h:f:N?keysIn:S,G=L?void 0:B(e) -return i(G||e,function(o,i){G&&(i=o,o=e[i]),s(A,i,r(o,t,n,i,e,O))}),A}var o=n(39),i=n(210),s=n(185),l=n(240),a=n(241),u=n(242),p=n(243),d=n(244),c=n(247),f=n(250),h=n(252),m=n(116),g=n(253),y=n(254),v=n(265),b=n(102),C=n(103),E=n(63),S=n(96),_=1,w=2,x=4,P="[object Arguments]",F="[object Array]",T="[object Boolean]",O="[object Date]",A="[object Error]",I="[object Function]",D="[object GeneratorFunction]",k="[object Map]",N="[object Number]",R="[object Object]",U="[object RegExp]",L="[object Set]",j="[object String]",M="[object Symbol]",H="[object WeakMap]",B="[object ArrayBuffer]",G="[object DataView]",z="[object Float32Array]",q="[object Float64Array]",Q="[object Int8Array]",V="[object Int16Array]",W="[object Int32Array]",K="[object Uint8Array]",$="[object Uint8ClampedArray]",Z="[object Uint16Array]",Y="[object Uint32Array]",X={} +T.set(e,A) +var B=L?R?h:f:R?keysIn:_,G=U?void 0:B(e) +return i(G||e,function(o,i){G&&(i=o,o=e[i]),s(A,i,r(o,t,n,i,e,T))}),A}var o=n(38),i=n(209),s=n(184),a=n(239),l=n(240),u=n(241),p=n(242),d=n(243),c=n(246),f=n(249),h=n(251),m=n(115),g=n(252),y=n(253),v=n(264),b=n(101),C=n(102),E=n(62),_=n(95),S=1,w=2,P=4,x="[object Arguments]",F="[object Array]",I="[object Boolean]",T="[object Date]",A="[object Error]",O="[object Function]",D="[object GeneratorFunction]",k="[object Map]",R="[object Number]",N="[object Object]",L="[object RegExp]",U="[object Set]",j="[object String]",M="[object Symbol]",H="[object WeakMap]",B="[object ArrayBuffer]",G="[object DataView]",z="[object Float32Array]",q="[object Float64Array]",V="[object Int8Array]",W="[object Int16Array]",Q="[object Int32Array]",K="[object Uint8Array]",$="[object Uint8ClampedArray]",Z="[object Uint16Array]",Y="[object Uint32Array]",X={} -X[P]=X[F]=X[B]=X[G]=X[T]=X[O]=X[z]=X[q]=X[Q]=X[V]=X[W]=X[k]=X[N]=X[R]=X[U]=X[L]=X[j]=X[M]=X[K]=X[$]=X[Z]=X[Y]=!0,X[A]=X[I]=X[H]=!1,e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(187),i=n(96) +X[x]=X[F]=X[B]=X[G]=X[I]=X[T]=X[z]=X[q]=X[V]=X[W]=X[Q]=X[k]=X[R]=X[N]=X[L]=X[U]=X[j]=X[M]=X[K]=X[$]=X[Z]=X[Y]=!0,X[A]=X[O]=X[H]=!1,e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(186),i=n(95) -e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(187),i=n(217) +e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(186),i=n(216) e.exports=r},function(e,t,n){(function(e){function r(e,t){if(t)return e.slice() var n=e.length,r=u?u(n):new e.constructor(n) -return e.copy(r),r}var o=n(59),i="object"==typeof t&&t&&!t.nodeType&&t,s=i&&"object"==typeof e&&e&&!e.nodeType&&e,l=s&&s.exports===i,a=l?o.Buffer:void 0,u=a?a.allocUnsafe:void 0 +return e.copy(r),r}var o=n(58),i="object"==typeof t&&t&&!t.nodeType&&t,s=i&&"object"==typeof e&&e&&!e.nodeType&&e,a=s&&s.exports===i,l=a?o.Buffer:void 0,u=l?l.allocUnsafe:void 0 e.exports=r}).call(t,n(24)(e))},function(e,t){function n(e,t){var n=-1,r=e.length for(t||(t=Array(r));++n0)return p["default"].createElement("div",{className:"gallery__progress-bar--complete"}) var t={className:"gallery__progress-bar-progress",style:{width:e.progress+"%"}} -return p["default"].createElement("div",{className:"gallery__progress-bar"},p["default"].createElement("div",t))}},{key:"renderTitle",value:function _(e){var t=this.renderProgressBar(e.rowData) +return p["default"].createElement("div",{className:"gallery__progress-bar"},p["default"].createElement("div",t))}},{key:"renderTitle",value:function S(e){var t=this.renderProgressBar(e.rowData) return p["default"].createElement("div",{className:"fill-width"},p["default"].createElement("div",{className:"flexbox-area-grow"},e.data),t)}},{key:"renderSelect",value:function w(e){return p["default"].createElement("input",{ -type:"checkbox",title:h["default"]._t("AssetAdmin.SELECT"),checked:e.data,tabIndex:"-1",onMouseDown:this.preventFocus})}},{key:"renderDate",value:function x(e){return"folder"===e.rowData.type?null:p["default"].createElement("span",null,e.data) +type:"checkbox",title:h["default"]._t("AssetAdmin.SELECT"),checked:e.data,tabIndex:"-1",onMouseDown:this.preventFocus})}},{key:"renderDate",value:function P(e){return"folder"===e.rowData.type?null:p["default"].createElement("span",null,e.data) -}},{key:"renderThumbnail",value:function P(e){var t=e.data||e.rowData.url +}},{key:"renderThumbnail",value:function x(e){var t=e.data||e.rowData.url return t?p["default"].createElement("img",{src:t,alt:e.rowData.title,className:"gallery__table-image"}):p["default"].createElement("div",{className:"gallery__table-image--error"})}},{key:"render",value:function F(){ return p["default"].createElement(c["default"],this.getTableProps())}}]),t}(u.Component) -y.defaultProps=m.galleryViewDefaultProps,y.propTypes=m.galleryViewPropTypes,t["default"]=y},function(e,t,n){"use strict" +y.defaultProps=m.galleryViewDefaultProps,y.propTypes=m.galleryViewPropTypes,t["default"]=y},function(e,t){e.exports=DataFormat},function(e,t,n){"use strict" function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){return function(t){return t({type:d["default"].ADD_QUEUED_FILE,payload:{file:e}})}}function i(e,t){return function(n){var r=t.message return"string"==typeof t&&(r={value:t,type:"error"}),n({type:d["default"].FAIL_UPLOAD,payload:{queuedId:e,message:r}})}}function s(){return function(e){return e({type:d["default"].PURGE_UPLOAD_QUEUE,payload:null -})}}function l(e){return function(t){return t({type:d["default"].REMOVE_QUEUED_FILE,payload:{queuedId:e}})}}function a(e){return function(t){return t({type:d["default"].SUCCEED_UPLOAD,payload:{queuedId:e +})}}function a(e){return function(t){return t({type:d["default"].REMOVE_QUEUED_FILE,payload:{queuedId:e}})}}function l(e){return function(t){return t({type:d["default"].SUCCEED_UPLOAD,payload:{queuedId:e }})}}function u(e,t){return function(n){return n({type:d["default"].UPDATE_QUEUED_FILE,payload:{queuedId:e,updates:t}})}}Object.defineProperty(t,"__esModule",{value:!0}),t.addQueuedFile=o,t.failUpload=i, -t.purgeUploadQueue=s,t.removeQueuedFile=l,t.succeedUpload=a,t.updateQueuedFile=u +t.purgeUploadQueue=s,t.removeQueuedFile=a,t.succeedUpload=l,t.updateQueuedFile=u var p=n(273),d=r(p)},function(e,t){"use strict" Object.defineProperty(t,"__esModule",{value:!0}),t["default"]={ADD_QUEUED_FILE:"ADD_QUEUED_FILE",FAIL_UPLOAD:"FAIL_UPLOAD",PURGE_UPLOAD_QUEUE:"PURGE_UPLOAD_QUEUE",REMOVE_QUEUED_FILE:"REMOVE_QUEUED_FILE", SUCCEED_UPLOAD:"SUCCEED_UPLOAD",UPDATE_QUEUED_FILE:"UPDATE_QUEUED_FILE"}},function(e,t){e.exports=Breadcrumb},function(e,t){e.exports=Toolbar},function(e,t){e.exports=SchemaActions},function(e,t,n){"use strict" -function r(e){return e&&e.__esModule?e:{"default":e}}var o=n(4),i=n(278),s=r(i),l=n(279),a=r(l),u=n(280),p=r(u),d=n(281),c=r(d),f=n(283),h=r(f),m=n(285),g=r(m),y=n(287),v=r(y),b=n(289),C=r(b),E=n(290),S=r(E),_=n(295),w=r(_) +function r(e){return e&&e.__esModule?e:{"default":e}}var o=n(4),i=n(278),s=r(i),a=n(279),l=r(a),u=n(280),p=r(u),d=n(281),c=r(d),f=n(283),h=r(f),m=n(285),g=r(m),y=n(287),v=r(y),b=n(289),C=r(b),E=n(291),_=r(E),S=n(292),w=r(S),P=n(297),x=r(P),F=n(299),I=r(F) -document.addEventListener("DOMContentLoaded",function(){C["default"].register("UploadField",S["default"]),C["default"].register("HistoryList",w["default"]) -var e=s["default"].getSection("SilverStripe\\AssetAdmin\\Controller\\AssetAdmin") -a["default"].add({path:e.url,component:g["default"],indexRoute:{onEnter:function t(n,r){var o=[e.url,"show",0].join("/") -r(o)}},childRoutes:[{path:"show/:folderId/edit/:fileId",component:g["default"]},{path:"show/:folderId",component:g["default"]}]}),p["default"].add("assetAdmin",(0,o.combineReducers)({gallery:c["default"], -queuedFiles:h["default"],uploadField:v["default"]}))})},function(e,t){e.exports=Config},function(e,t){e.exports=ReactRouteRegister},function(e,t){e.exports=ReducerRegister},function(e,t,n){"use strict" +document.addEventListener("DOMContentLoaded",function(){_["default"].register("UploadField",w["default"]),_["default"].register("PreviewImageField",x["default"]),_["default"].register("HistoryList",I["default"]) +var e=s["default"].getSection("SilverStripe\\AssetAdmin\\Controller\\AssetAdmin") +l["default"].add({path:e.url,component:g["default"],indexRoute:{onEnter:function t(n,r){var o=[e.url,"show",0].join("/") +r(o)}},childRoutes:[{path:"show/:folderId/edit/:fileId",component:g["default"]},{path:"show/:folderId",component:g["default"]}]}),p["default"].add("assetAdmin",(0,o.combineReducers)({gallery:c["default"], +queuedFiles:h["default"],uploadField:v["default"],previewField:C["default"]}))})},function(e,t){e.exports=Config},function(e,t){e.exports=ReactRouteRegister},function(e,t){e.exports=ReducerRegister},function(e,t,n){ +"use strict" function r(e){return e&&e.__esModule?e:{"default":e}}function o(){var e=arguments.length<=0||void 0===arguments[0]?d:arguments[0],t=arguments[1] switch(t.type){case p["default"].ADD_FILES:var n=function(){var n=[] return t.payload.files.forEach(function(t){var r=!1 -e.files.forEach(function(e){e.id===t.id&&(r=!0)}),r||n.push(t)}),{v:(0,a["default"])(s({},e,{count:"undefined"!=typeof t.payload.count?t.payload.count:e.count,files:n.concat(e.files)}))}}() +e.files.forEach(function(e){e.id===t.id&&(r=!0)}),r||n.push(t)}),{v:(0,l["default"])(s({},e,{count:"undefined"!=typeof t.payload.count?t.payload.count:e.count,files:n.concat(e.files)}))}}() if("object"===("undefined"==typeof n?"undefined":i(n)))return n.v case p["default"].LOAD_FILE_SUCCESS:var r=e.files.find(function(e){return e.id===t.payload.id}) if(r){var o=function(){var n=s({},r,t.payload.file) -return{v:(0,a["default"])(s({},e,{files:e.files.map(function(e){return e.id===n.id?n:e})}))}}() -if("object"===("undefined"==typeof o?"undefined":i(o)))return o.v}else if(e.folder.id===t.payload.id)return(0,a["default"])(s({},e,{folder:s({},e.folder,t.payload.file)})) +return{v:(0,l["default"])(s({},e,{files:e.files.map(function(e){return e.id===n.id?n:e})}))}}() +if("object"===("undefined"==typeof o?"undefined":i(o)))return o.v}else if(e.folder.id===t.payload.id)return(0,l["default"])(s({},e,{folder:s({},e.folder,t.payload.file)})) return e case p["default"].UNLOAD_FOLDER:return s({},e,{files:[]}) -case p["default"].SELECT_FILES:var l=null -return l=null===t.payload.ids?e.files.map(function(e){return e.id}):e.selectedFiles.concat(t.payload.ids.filter(function(t){return e.selectedFiles.indexOf(t)===-1})),(0,a["default"])(s({},e,{selectedFiles:l +case p["default"].SELECT_FILES:var a=null +return a=null===t.payload.ids?e.files.map(function(e){return e.id}):e.selectedFiles.concat(t.payload.ids.filter(function(t){return e.selectedFiles.indexOf(t)===-1})),(0,l["default"])(s({},e,{selectedFiles:a })) case p["default"].DESELECT_FILES:var u=null -return u=null===t.payload.ids?[]:e.selectedFiles.filter(function(e){return t.payload.ids.indexOf(e)===-1}),(0,a["default"])(s({},e,{selectedFiles:u})) -case p["default"].DELETE_ITEM_SUCCESS:return(0,a["default"])(s({},e,{selectedFiles:e.selectedFiles.filter(function(e){return t.payload.ids.indexOf(e)===-1}),files:e.files.filter(function(e){return t.payload.ids.indexOf(e.id)===-1 +return u=null===t.payload.ids?[]:e.selectedFiles.filter(function(e){return t.payload.ids.indexOf(e)===-1}),(0,l["default"])(s({},e,{selectedFiles:u})) +case p["default"].DELETE_ITEM_SUCCESS:return(0,l["default"])(s({},e,{selectedFiles:e.selectedFiles.filter(function(e){return t.payload.ids.indexOf(e)===-1}),files:e.files.filter(function(e){return t.payload.ids.indexOf(e.id)===-1 }),count:e.count-1})) -case p["default"].LOAD_FOLDER_REQUEST:return(0,a["default"])(s({},e,{errorMessage:null,selectedFiles:[],loading:!0})) -case p["default"].LOAD_FOLDER_SUCCESS:return(0,a["default"])(s({},e,{folder:t.payload.folder,files:t.payload.files,count:t.payload.count,loading:!1})) -case p["default"].LOAD_FOLDER_FAILURE:return(0,a["default"])(s({},e,{errorMessage:t.payload.message,loading:!1})) +case p["default"].LOAD_FOLDER_REQUEST:return(0,l["default"])(s({},e,{errorMessage:null,selectedFiles:[],loading:!0})) +case p["default"].LOAD_FOLDER_SUCCESS:return(0,l["default"])(s({},e,{folder:t.payload.folder,files:t.payload.files,count:t.payload.count,loading:!1})) +case p["default"].LOAD_FOLDER_FAILURE:return(0,l["default"])(s({},e,{errorMessage:t.payload.message,loading:!1})) case p["default"].ADD_FOLDER_REQUEST:return e case p["default"].ADD_FOLDER_FAILURE:return e case p["default"].ADD_FOLDER_SUCCESS:return e @@ -1588,20 +1588,20 @@ var i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){re for(var t=1;t { Injector.register('UploadField', UploadField); + Injector.register('PreviewImageField', PreviewImageField); Injector.register('HistoryList', HistoryList); const sectionConfig = Config.getSection('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin'); - reactRouteRegister.add({ path: sectionConfig.url, component: AssetAdminRouter, @@ -47,5 +49,6 @@ document.addEventListener('DOMContentLoaded', () => { gallery: galleryReducer, queuedFiles: queuedFilesReducer, uploadField: uploadFieldReducer, + previewField: previewFieldReducer, })); }); diff --git a/client/src/components/AssetDropzone/AssetDropzone.js b/client/src/components/AssetDropzone/AssetDropzone.js index b77e2e350..a545e4992 100644 --- a/client/src/components/AssetDropzone/AssetDropzone.js +++ b/client/src/components/AssetDropzone/AssetDropzone.js @@ -3,7 +3,6 @@ import ReactDOM from 'react-dom'; import SilverStripeComponent from 'lib/SilverStripeComponent'; import i18n from 'i18n'; import DropzoneLib from 'dropzone'; -import { fileSize } from 'lib/DataFormat'; import $ from 'jQuery'; let idCounter = 0; @@ -326,7 +325,7 @@ class AssetDropzone extends SilverStripeComponent { category: this.getFileCategory(file.type), filename: file.name, queuedId: file._queuedId, - size: fileSize(file.size), + size: file.size, title: this.getFileTitle(file.name), extension: this.getFileExtension(file.name), type: file.type, diff --git a/client/src/components/AssetDropzone/AssetDropzone.scss b/client/src/components/AssetDropzone/AssetDropzone.scss index 376718d2d..3738d6b5c 100644 --- a/client/src/components/AssetDropzone/AssetDropzone.scss +++ b/client/src/components/AssetDropzone/AssetDropzone.scss @@ -22,3 +22,7 @@ } } } + +.asset-dropzone--button { + position: static; +} diff --git a/client/src/components/AssetDropzone/tests/AssetDropzone-test.js b/client/src/components/AssetDropzone/tests/AssetDropzone-test.js index 1deed6098..0a30bcd4b 100644 --- a/client/src/components/AssetDropzone/tests/AssetDropzone-test.js +++ b/client/src/components/AssetDropzone/tests/AssetDropzone-test.js @@ -125,7 +125,7 @@ describe('AssetDropzone', () => { .then((details) => { expect(uploadProps.handleAddedFile).toBeCalled(); expect(item.dropzone.processFile).toBeCalled(); - expect(details.size).toBe('123 bytes'); + expect(details.size).toBe(123); expect(details.title).toBe('Test file'); expect(details.url).toBeUndefined(); }) diff --git a/client/src/components/PreviewImageField/PreviewImageField.js b/client/src/components/PreviewImageField/PreviewImageField.js new file mode 100644 index 000000000..03a98e4ef --- /dev/null +++ b/client/src/components/PreviewImageField/PreviewImageField.js @@ -0,0 +1,264 @@ +import i18n from 'i18n'; +import React, { PropTypes, Component } from 'react'; +import AssetDropzone from 'components/AssetDropzone/AssetDropzone'; +import CONSTANTS from 'constants'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import * as previewFieldActions from 'state/previewField/PreviewFieldActions'; + +class PreviewImageField extends Component { + constructor(props) { + super(props); + + this.handleAddedFile = this.handleAddedFile.bind(this); + this.handleFailedUpload = this.handleFailedUpload.bind(this); + this.handleSuccessfulUpload = this.handleSuccessfulUpload.bind(this); + this.handleSending = this.handleSending.bind(this); + this.handleUploadProgress = this.handleUploadProgress.bind(this); + this.handleCancelUpload = this.handleCancelUpload.bind(this); + this.handleRemoveErroredUpload = this.handleRemoveErroredUpload.bind(this); + } + + componentWillUnmount() { + this.props.actions.previewField.removeFile(this.props.id); + } + + getDropzoneProps() { + const endpoint = this.props.data.uploadFileEndpoint; + const options = { + url: endpoint && endpoint.url, + method: endpoint && endpoint.method, + paramName: 'Upload', + clickable: '#preview-replace-button', + }; + const preview = { + height: CONSTANTS.THUMBNAIL_HEIGHT, + width: CONSTANTS.THUMBNAIL_WIDTH, + }; + const securityID = this.props.securityID; + + const classNames = [ + 'asset-dropzone--button', + 'preview__container', + this.props.className, + this.props.extraClass, + ]; + + return { + className: classNames.join(' '), + canUpload: endpoint && this.canEdit(), + preview, + folderId: this.props.data.parentid, + options, + securityID, + uploadButton: false, + handleAddedFile: this.handleAddedFile, + handleError: this.handleFailedUpload, + handleSuccess: this.handleSuccessfulUpload, + handleSending: this.handleSending, + handleUploadProgress: this.handleUploadProgress, + }; + } + + /** + * Defines whether this field can make changes/edits/uploads, looks at readonly, disabled and if + * the file category is a "folder" + * + * @returns {boolean} + */ + canEdit() { + return !this.props.readOnly + && !this.props.disabled + && this.props.data.category !== 'folder'; + } + + preventDefault(e) { + e.preventDefault(); + } + + /** + * Handles removing an upload and cancelling the request made to upload + */ + handleCancelUpload() { + if (this.props.upload.xhr) { + this.props.upload.xhr.abort(); + } + this.handleRemoveErroredUpload(); + } + + /** + * Handles removing an upload that had errored during/after upload + */ + handleRemoveErroredUpload() { + this.props.actions.previewField.removeFile(this.props.id); + } + + handleAddedFile(data) { + this.props.actions.previewField.addFile(this.props.id, data); + } + + handleFailedUpload(file, response) { + this.props.actions.previewField.failUpload(this.props.id, response); + } + + /** + * Update tuple detail fields when upload is successful. + * + * @param fileXhr + */ + handleSuccessfulUpload(fileXhr) { + const json = JSON.parse(fileXhr.xhr.response); + + if (typeof this.props.onAutofill === 'function') { + this.props.onAutofill('FileFilename', json.Filename); + this.props.onAutofill('FileHash', json.Hash); + this.props.onAutofill('FileVariant', json.Variant); + } + } + + /** + * Started the sending process for a file + * + * @param {object} file + * @param {object} xhr + */ + handleSending(file, xhr) { + this.props.actions.previewField.updateFile(this.props.id, { xhr }); + } + + /** + * Upload progress has changed, set changes to reflect it + * + * @param {object} file + * @param {object} progress + */ + handleUploadProgress(file, progress) { + this.props.actions.previewField.updateFile(this.props.id, { progress }); + } + + /** + * Renders the image markup as normal by LiteralField + * + * @returns {XML} + */ + renderImage() { + const data = this.props.data; + + if (!data.exists && !this.props.upload.url) { + return ( +
+ {i18n._t('AssetAdmin.FILE_MISSING', 'File cannot be found')} +
+ ); + } + + const preview = this.props.upload.url || data.preview || data.url; + const image = ; + + if (data.url) { + return ( + + {image} + + ); + } + return image; + } + + renderToolbar() { + const canEdit = this.canEdit(); + if (!this.props.data.url && !canEdit) { + return null; + } + return ( +
+ { (this.props.data.url) + ? ( + Open + ) + : null } + { (canEdit) + ? ( + Replace + ) + : null } +
+ ); + } + + render() { + const dropzoneProps = this.getDropzoneProps(); + + return ( + + {this.renderImage()} + {this.renderToolbar()} + + ); + } +} + +PreviewImageField.propTypes = { + id: PropTypes.string.isRequired, + name: PropTypes.string, + className: PropTypes.string, + extraClass: PropTypes.string, + readOnly: PropTypes.bool, + disabled: PropTypes.bool, + onAutofill: PropTypes.func, + data: PropTypes.shape({ + parentid: PropTypes.number, + url: PropTypes.string, + exists: PropTypes.bool, + preview: PropTypes.string, + category: PropTypes.string, + uploadFileEndpoint: PropTypes.shape({ + url: PropTypes.string.isRequired, + method: PropTypes.string.isRequired, + payloadFormat: PropTypes.string, + }), + }).isRequired, + upload: PropTypes.shape({ + url: PropTypes.string, + progress: PropTypes.number, + xhr: PropTypes.object, + }), + actions: PropTypes.object, + securityID: PropTypes.string, +}; + +PreviewImageField.defaultProps = { + // React considers "undefined" as an uncontrolled component. + extraClass: '', + className: '', +}; + +function mapStateToProps(state, ownprops) { + const securityID = state.config.SecurityID; + const id = ownprops.id; + const upload = state.assetAdmin.previewField[id] || {}; + + return { + securityID, + upload, + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: { + previewField: bindActionCreators(previewFieldActions, dispatch), + }, + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(PreviewImageField); diff --git a/client/src/components/PreviewImageField/PreviewImageField.scss b/client/src/components/PreviewImageField/PreviewImageField.scss new file mode 100644 index 000000000..bcffe082d --- /dev/null +++ b/client/src/components/PreviewImageField/PreviewImageField.scss @@ -0,0 +1,52 @@ +.preview__container { + position: relative; +} + +.preview__toolbar { + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + height: auto; + max-height: 100%; + background: $black; + opacity: 0.55; + color: $white; + border-bottom-left-radius: $border-radius; + border-top-left-radius: $border-radius; +} + +.preview__toolbar-button--link:before { + content: "\3d"; +} + +.preview__toolbar-button--replace:before { + content: "\62"; +} + +.preview__toolbar-button { + padding: $toolbar-button-padding; + height: $toolbar-button-height + (2 * $toolbar-button-padding); + width: $toolbar-button-width + (2 * $toolbar-button-padding); + overflow: hidden; + color: $white; + + &:hover, + &:active, + &:focus { + color: darken($white, 30%); + text-decoration: none; + } + + &:before { + margin-right: $toolbar-button-padding; + font-family: "silverstripe"; + font-style: normal; + speak: none; + line-height: 1; + font-size: $toolbar-button-height; + height: $toolbar-button-height; + width: $toolbar-button-width; + vertical-align: middle; + } +} diff --git a/client/src/containers/AssetAdmin/AssetAdmin.js b/client/src/containers/AssetAdmin/AssetAdmin.js index 55b3dba6b..784e08295 100644 --- a/client/src/containers/AssetAdmin/AssetAdmin.js +++ b/client/src/containers/AssetAdmin/AssetAdmin.js @@ -346,7 +346,7 @@ class AssetAdmin extends SilverStripeComponent { break; } - if (!this.props.fileId) { + if (!this.props.fileId || this.props.files.length === 0) { return null; } diff --git a/client/src/containers/HistoryList/HistoryList.js b/client/src/containers/HistoryList/HistoryList.js index 82f5593cc..be28ad450 100644 --- a/client/src/containers/HistoryList/HistoryList.js +++ b/client/src/containers/HistoryList/HistoryList.js @@ -27,8 +27,9 @@ export class HistoryList extends Component { this.refreshHistoryIfNeeded(); } - componentDidUpdate(prevProps) { - this.refreshHistoryIfNeeded(prevProps); + componentWillReceiveProps(nextProps) { + // TODO race conditions happening, this should have history state shifted to redux + this.refreshHistoryIfNeeded(nextProps); } /** @@ -43,18 +44,17 @@ export class HistoryList extends Component { /** * Determine if the history list requires a refresh * - * @param {object} prevProps + * @param {object} nextProps */ - refreshHistoryIfNeeded(prevProps) { - if (!prevProps - || (prevProps.data.fileId !== this.props.data.fileId) - || !this.state.history - || (prevProps.data.latestVersionId !== this.props.data.latestVersionId) + refreshHistoryIfNeeded(nextProps) { + if (!nextProps + || (nextProps.data.fileId !== this.props.data.fileId) + || (nextProps.data.latestVersionId !== this.props.data.latestVersionId) ) { - this.api({ fileId: this.props.data.fileId }).then((json) => { - this.setState({ - history: json, - }); + this.api({ + fileId: (nextProps) ? nextProps.data.fileId : this.props.data.fileId, + }).then((history) => { + this.setState({ history }); }); } } diff --git a/client/src/state/previewField/PreviewFieldActionTypes.js b/client/src/state/previewField/PreviewFieldActionTypes.js new file mode 100644 index 000000000..6488073ac --- /dev/null +++ b/client/src/state/previewField/PreviewFieldActionTypes.js @@ -0,0 +1,6 @@ +export default { + PREVIEWFIELD_ADD_FILE: 'PREVIEWFIELD_ADD_FILE', + PREVIEWFIELD_REMOVE_FILE: 'PREVIEWFIELD_REMOVE_FILE', + PREVIEWFIELD_UPDATE_FILE: 'PREVIEWFIELD_UPDATE_FILE', + PREVIEWFIELD_FAIL_UPLOAD: 'PREVIEWFIELD_FAIL_UPLOAD', +}; diff --git a/client/src/state/previewField/PreviewFieldActions.js b/client/src/state/previewField/PreviewFieldActions.js new file mode 100644 index 000000000..801d186fe --- /dev/null +++ b/client/src/state/previewField/PreviewFieldActions.js @@ -0,0 +1,29 @@ +import ACTION_TYPES from './PreviewFieldActionTypes'; + +export function removeFile(id) { + return { + type: ACTION_TYPES.PREVIEWFIELD_REMOVE_FILE, + payload: { id }, + }; +} + +export function addFile(id, file) { + return { + type: ACTION_TYPES.PREVIEWFIELD_ADD_FILE, + payload: { id, file }, + }; +} + +export function failUpload(id, message) { + return { + type: ACTION_TYPES.PREVIEWFIELD_FAIL_UPLOAD, + payload: { id, message }, + }; +} + +export function updateFile(id, data) { + return { + type: ACTION_TYPES.PREVIEWFIELD_UPDATE_FILE, + payload: { id, data }, + }; +} diff --git a/client/src/state/previewField/PreviewFieldReducer.js b/client/src/state/previewField/PreviewFieldReducer.js new file mode 100644 index 000000000..45655758c --- /dev/null +++ b/client/src/state/previewField/PreviewFieldReducer.js @@ -0,0 +1,42 @@ +import deepFreeze from 'deep-freeze-strict'; +import ACTION_TYPES from './PreviewFieldActionTypes'; + +const initialState = {}; + +function previewFieldReducer(state = initialState, action) { + switch (action.type) { + case ACTION_TYPES.PREVIEWFIELD_ADD_FILE: { + return deepFreeze(Object.assign({}, state, { + [action.payload.id]: action.payload.file, + })); + } + + case ACTION_TYPES.PREVIEWFIELD_FAIL_UPLOAD: { + return deepFreeze(Object.assign({}, state, { + [action.payload.id]: Object.assign({}, state[action.payload.id], { + message: { + type: 'error', + value: action.payload.message, + }, + }), + })); + } + + case ACTION_TYPES.PREVIEWFIELD_REMOVE_FILE: { + return deepFreeze(Object.assign({}, state, { + [action.payload.id]: undefined, + })); + } + + case ACTION_TYPES.PREVIEWFIELD_UPDATE_FILE: { + return deepFreeze(Object.assign({}, state, { + [action.payload.id]: Object.assign({}, state[action.payload.id], action.payload.data), + })); + } + + default: + return state; + } +} + +export default previewFieldReducer; diff --git a/client/src/styles/_variables.scss b/client/src/styles/_variables.scss index 62b7de4c6..93c0fa02d 100644 --- a/client/src/styles/_variables.scss +++ b/client/src/styles/_variables.scss @@ -2,3 +2,7 @@ // All generic variables for reuse across the CMS should be added to Framework variables $framework-path: "../../../../framework"; + +$toolbar-button-height: 25px; +$toolbar-button-width: $toolbar-button-height; +$toolbar-button-padding: 11px; diff --git a/client/src/styles/bundle.scss b/client/src/styles/bundle.scss index ab0be3c2a..2f4731b29 100644 --- a/client/src/styles/bundle.scss +++ b/client/src/styles/bundle.scss @@ -14,6 +14,7 @@ @import "../components/AssetDropzone/AssetDropzone"; @import "../components/UploadField/UploadField"; @import "../components/UploadField/UploadFieldItem"; +@import "../components/PreviewImageField/PreviewImageField"; // Layout and sections @import "../containers/Gallery/Gallery"; diff --git a/code/Controller/AssetAdmin.php b/code/Controller/AssetAdmin.php index e686df8d8..86bef32ff 100644 --- a/code/Controller/AssetAdmin.php +++ b/code/Controller/AssetAdmin.php @@ -40,6 +40,7 @@ use SilverStripe\Security\SecurityToken; use SilverStripe\View\Requirements; use SilverStripe\ORM\Versioning\Versioned; +use Exception; /** * AssetAdmin is the 'file store' section of the CMS. @@ -63,6 +64,7 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider // API access points with structured data 'POST api/createFolder' => 'apiCreateFolder', 'POST api/createFile' => 'apiCreateFile', + 'POST api/uploadFile' => 'apiUploadFile', 'GET api/readFolder' => 'apiReadFolder', 'PUT api/updateFolder' => 'apiUpdateFolder', 'DELETE api/delete' => 'apiDelete', @@ -99,6 +101,7 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider 'legacyRedirectForEditView', 'apiCreateFolder', 'apiCreateFile', + 'apiUploadFile', 'apiReadFolder', 'apiUpdateFolder', 'apiHistory', @@ -167,10 +170,15 @@ public function getClientConfig() 'method' => 'delete', 'payloadFormat' => 'urlencoded', ], + 'uploadFileEndpoint' => [ + 'url' => Controller::join_links($baseLink, 'api/uploadFile'), + 'method' => 'post', + 'payloadFormat' => 'urlencoded', + ], 'historyEndpoint' => [ 'url' => Controller::join_links($baseLink, 'api/history'), 'method' => 'get', - 'responseFormat' => 'json' + 'responseFormat' => 'json', ], 'limit' => $this->config()->page_length, 'form' => [ @@ -447,7 +455,72 @@ public function apiCreateFile(HTTPRequest $request) return (new HTTPResponse(json_encode($result))) ->addHeader('Content-Type', 'application/json'); } - + + /** + * Creates a single file based on a form-urlencoded upload. + * + * @param HTTPRequest $request + * @return HTTPRequest|HTTPResponse + */ + public function apiUploadFile(HTTPRequest $request) { + $data = $request->postVars(); + $upload = $this->getUpload(); + + // CSRF check + $token = SecurityToken::inst(); + if (empty($data[$token->getName()]) || !$token->check($data[$token->getName()])) { + return new HTTPResponse(null, 400); + } + + // Check parent record + /** @var Folder $parentRecord */ + $parentRecord = null; + if (!empty($data['ParentID']) && is_numeric($data['ParentID'])) { + $parentRecord = Folder::get()->byID($data['ParentID']); + } + + $tmpFile = $data['Upload']; + if(!$upload->validate($tmpFile)) { + $result = ['message' => null]; + $errors = $upload->getErrors(); + if ($message = array_shift($errors)) { + $result['message'] = [ + 'type' => 'error', + 'value' => $message, + ]; + } + return (new HTTPResponse(json_encode($result), 400)) + ->addHeader('Content-Type', 'application/json'); + } + + $folder = $parentRecord ? $parentRecord->getFilename() : '/'; + + try { + $tuple = $upload->load($tmpFile, $folder); + } catch (Exception $e) { + $result = [ + 'message' => [ + 'type' => 'error', + 'value' => $e->getMessage(), + ] + ]; + return (new HTTPResponse(json_encode($result), 400)) + ->addHeader('Content-Type', 'application/json'); + } + + if ($upload->isError()) { + $result['message'] = [ + 'type' => 'error', + 'value' => implode(' ' . PHP_EOL, $upload->getErrors()), + ]; + return (new HTTPResponse(json_encode($result), 400)) + ->addHeader('Content-Type', 'application/json'); + } + + return (new HTTPResponse(json_encode($tuple))) + ->addHeader('Content-Type', 'application/json'); + } + /** * Returns a JSON array for history of a given file ID. Returns a list of all the history. * @@ -1019,7 +1092,7 @@ protected function saveOrPublish($data, $form, $doPublish = false) $id = (int) $data['ID']; /** @var File $record */ - $record = $this->getList()->filter('ID', $id)->first(); + $record = DataObject::get_by_id(File::class, $id); if (!$record) { return (new HTTPResponse(json_encode(['status' => 'error']), 404)) @@ -1052,7 +1125,7 @@ public function unpublish($data, $form) $id = (int) $data['ID']; /** @var File $record */ - $record = $this->getList()->filter('ID', $id)->first(); + $record = DataObject::get_by_id(File::class, $id); if (!$record) { return (new HTTPResponse(json_encode(['status' => 'error']), 404)) diff --git a/code/Forms/AssetFormFactory.php b/code/Forms/AssetFormFactory.php index cf1f0ee9c..c7e0d4085 100644 --- a/code/Forms/AssetFormFactory.php +++ b/code/Forms/AssetFormFactory.php @@ -81,21 +81,6 @@ protected function getFormType($context) return empty($context['Type']) ? static::TYPE_ADMIN : $context['Type']; } - /** - * Get raw HTML for image markup - * - * @param File $file - * @return string - */ - protected function getIconMarkup($file) - { - if (!$file) { - return null; - } - $previewLink = Convert::raw2att($file->PreviewLink()); - return ""; - } - /** * Gets the main tabs for the file edit form * @@ -161,7 +146,8 @@ protected function getFormFields(Controller $controller, $name, $context = []) $fields = new FieldList( HeaderField::create('TitleHeader', $record ? $record->Title : null, 1) ->addExtraClass('editor__heading'), - LiteralField::create("IconFull", $this->getIconMarkup($record)) + PreviewImageField::create('PreviewImage') + ->setRecordID($record->ID) ->addExtraClass('editor__file-preview'), $this->getFormFieldTabs($record, $context) ); diff --git a/code/Forms/FileFormFactory.php b/code/Forms/FileFormFactory.php index aed7530e1..91e24f84d 100644 --- a/code/Forms/FileFormFactory.php +++ b/code/Forms/FileFormFactory.php @@ -2,11 +2,14 @@ namespace SilverStripe\AssetAdmin\Forms; +use SilverStripe\AssetAdmin\Controller\AssetAdmin; use SilverStripe\Assets\File; +use SilverStripe\Assets\Folder; use SilverStripe\Control\Controller; use SilverStripe\Forms\DatetimeField; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FormAction; +use SilverStripe\Forms\FormField; use SilverStripe\Forms\HiddenField; use SilverStripe\Forms\LiteralField; use SilverStripe\Forms\PopoverField; @@ -122,10 +125,14 @@ protected function getFormFields(Controller $controller, $name, $context = []) // Add status flag before extensions are triggered $this->beforeExtending('updateFormFields', function (FieldList $fields) use ($record) { + // @todo move specs to a component/class, so it can render similar to PreviewImageField $fields->insertAfter( 'TitleHeader', LiteralField::create('FileSpecs', $this->getSpecsMarkup($record)) ); + $fields->push(HiddenField::create('FileFilename')); + $fields->push(HiddenField::create('FileHash')); + $fields->push(HiddenField::create('FileVariant')); }); return parent::getFormFields($controller, $name, $context); @@ -172,35 +179,6 @@ protected function getFormActions(Controller $controller, $name, $context = []) return $actions; } - /** - * Get raw HTML for image markup - * - * @param File $file - * @return string - */ - protected function getIconMarkup($file) - { - $markup = parent::getIconMarkup($file); - if (!$markup) { - return null; - } - if (!$file->exists()) { - return sprintf( - '
%s
', - 'editor__file-preview-message--file-missing', - _t('AssetAdmin.FILE_MISSING', 'File cannot be found') - ); - } - $link = $file->Link(); - $linkedImage = sprintf( - '%s', - 'editor__file-preview-link', - $link, - $markup - ); - return $linkedImage; - } - /** * get HTML for status icon * diff --git a/code/Forms/PreviewImageField.php b/code/Forms/PreviewImageField.php new file mode 100644 index 000000000..d533bcff3 --- /dev/null +++ b/code/Forms/PreviewImageField.php @@ -0,0 +1,70 @@ + AssetAdmin::singleton()->Link('api/uploadFile'), + 'method' => 'post', + 'payloadFormat' => 'urlencoded', + ]; + return $defaults; + } + + public function getSchemaStateDefaults() + { + $defaults = parent::getSchemaStateDefaults(); + /** @var File $record */ + if ($record = $this->getRecord()) { + $parent = $record->Parent(); + $defaults['data'] = array_merge_recursive($defaults['data'], [ + 'parentid' => ($parent) ? $parent->ID : 0, + 'url' => $record->Link(), + 'exists' => $record->exists(), + 'preview' => $record->PreviewLink(), + 'category' => $record instanceof Folder ? 'folder' : $record->appCategory(), + ]); + } + return $defaults; + } + + /** + * @return DataObject + */ + public function getRecord() + { + return DataObject::get_by_id(File::class, $this->recordID); + } + + /** + * @param Integer $recordID + * @return $this + */ + public function setRecordID($recordID) + { + $this->recordID = $recordID; + return $this; + } +} diff --git a/tests/php/Forms/FileFormBuilderTest.php b/tests/php/Forms/FileFormBuilderTest.php index 89c1e1bc1..3bbfe0d57 100644 --- a/tests/php/Forms/FileFormBuilderTest.php +++ b/tests/php/Forms/FileFormBuilderTest.php @@ -64,14 +64,13 @@ public function testEditFileForm() $this->assertEquals('files/', $filePath); /** @var LiteralField $iconFullField */ - $iconFullField = $form->Fields()->fieldByName('IconFull'); - $fileThumbnail = $iconFullField->getContent(); - $this->assertEquals( - ''. - ''. - '', - $fileThumbnail - ); + $iconFullField = $form->Fields()->fieldByName('PreviewImage'); + $state = $iconFullField->getSchemaStateDefaults(); + $this->assertEquals($file->Parent()->ID, $state['data']['parentid']); + $this->assertContains('testfile.txt', $state['data']['url']); + $this->assertTrue($state['data']['exists']); + $this->assertContains('document_92.png', $state['data']['preview']); + $this->assertEquals('document', $state['data']['category']); // Usage tab $uploaded = $form->Fields()->fieldByName('Editor.Usage.Created'); @@ -118,6 +117,8 @@ public function testEditImageForm() $this->logInWithPermission('ADMIN'); $image = $this->objFromFixture(Image::class, 'image1'); + // write so that PreviewImageField could load this later on + $image->write(); $controller = new AssetAdmin(); $builder = new ImageFormFactory(); $form = $builder->getForm($controller, 'EditForm', ['Record' => $image]); @@ -125,12 +126,13 @@ public function testEditImageForm() // Check thumbnail // Note: force_resample is turned off for testing /** @var LiteralField $iconFullField */ - $iconFullField = $form->Fields()->fieldByName('IconFull'); - $fileThumbnail = $iconFullField->getContent(); - $this->assertContains( - '/FileFormBuilderTest/files/906835357d/testimage.png', - $fileThumbnail - ); + $iconFullField = $form->Fields()->fieldByName('PreviewImage'); + $state = $iconFullField->getSchemaStateDefaults(); + $this->assertEquals($image->Parent()->ID, $state['data']['parentid']); + $this->assertContains('testimage.png', $state['data']['url']); + $this->assertTrue($state['data']['exists']); + $this->assertContains('testimage.png', $state['data']['preview']); + $this->assertEquals('image', $state['data']['category']); } public function testInsertImageForm() @@ -162,14 +164,13 @@ public function testFolderForm() $this->assertNull($form->Fields()->fieldByName('Editor.Details.ClickableURL')); $this->assertNull($form->Fields()->fieldByName('Editor.Usage')); - /** @var LiteralField $iconField */ - $iconField = $form->Fields()->fieldByName('IconFull'); - $fileThumbnail = $iconField->getContent(); - $this->assertEquals( - '', - $fileThumbnail - ); - + /** @var LiteralField $iconFullField */ + $iconFullField = $form->Fields()->fieldByName('PreviewImage'); + $state = $iconFullField->getSchemaStateDefaults(); + $this->assertEquals($folder->Parent()->ID, $state['data']['parentid']); + $this->assertTrue($state['data']['exists']); + $this->assertContains('folder_icon_large.png', $state['data']['preview']); + $this->assertEquals('folder', $state['data']['category']); // Check actions $this->assertNotNull($form->Actions()->fieldByName('action_save')); diff --git a/webpack.config.js b/webpack.config.js index 6c3cf1bf5..460ff35cd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -39,6 +39,7 @@ module.exports = { 'components/Breadcrumb/Breadcrumb': 'Breadcrumb', 'state/schema/SchemaActions': 'SchemaActions', 'components/FieldHolder/FieldHolder': 'FieldHolder', + 'components/LiteralField/LiteralField': 'LiteralField', 'lib/DataFormat': 'DataFormat', 'lib/schemaFieldValues': 'schemaFieldValues', 'components/FormBuilderModal/FormBuilderModal': 'FormBuilderModal', From ede825b2abb6056ee3a05e68599aab1efd04230b Mon Sep 17 00:00:00 2001 From: Christopher Joe Date: Thu, 15 Dec 2016 12:08:13 +1300 Subject: [PATCH 02/13] Enhancement Added maxFiles handling for AssetDropzone --- client/dist/js/bundle.js | 42 ++++++++++--------- .../components/AssetDropzone/AssetDropzone.js | 11 +++++ .../PreviewImageField/PreviewImageField.js | 6 +++ .../state/previewField/PreviewFieldReducer.js | 5 +-- code/Forms/FileFormFactory.php | 2 +- 5 files changed, 41 insertions(+), 25 deletions(-) diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 344e0856b..a6ebd1002 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -185,7 +185,7 @@ var d=Object.assign||function(e){for(var t=1;t-1}},{key:"itemIsHighlighted",value:function M(e){return this.props.fileId===e}},{key:"handleOpenFolder",value:function H(e,t){ -e.preventDefault(),this.props.onOpenFolder(t.id)}},{key:"handleOpenFile",value:function G(e,t){e.preventDefault(),null!==t.created&&this.props.onOpenFile(t.id,t)}},{key:"handleSelect",value:function z(e,t){ +e.preventDefault(),this.props.onOpenFolder(t.id)}},{key:"handleOpenFile",value:function z(e,t){e.preventDefault(),null!==t.created&&this.props.onOpenFile(t.id,t)}},{key:"handleSelect",value:function G(e,t){ this.props.selectedFiles.indexOf(t.id)===-1?this.props.actions.gallery.selectFiles([t.id]):this.props.actions.gallery.deselectFiles([t.id])}},{key:"handleBackClick",value:function q(e){e.preventDefault(), this.props.onOpenFolder(this.props.folder.parentID)}},{key:"handleViewChange",value:function V(e){var t=e.currentTarget.value this.props.onViewChange(t)}},{key:"renderSort",value:function W(){var e=this @@ -248,16 +248,16 @@ var e={height:L["default"].THUMBNAIL_HEIGHT,width:L["default"].THUMBNAIL_WIDTH}, return"insert"===this.props.type&&o.push("insert-media-modal__main"),v["default"].createElement("div",{className:"flexbox-area-grow gallery__outer"},this.renderBulkActions(),v["default"].createElement(I["default"],{ canUpload:r,handleAddedFile:this.handleAddedFile,handleError:this.handleFailedUpload,handleSuccess:this.handleSuccessfulUpload,handleSending:this.handleSending,handleUploadProgress:this.handleUploadProgress, -preview:e,folderId:this.props.folderId,options:t,securityID:n,uploadButton:!1},v["default"].createElement("div",{className:o.join(" ")},this.renderToolbar(),this.renderGalleryView())))}}]),t}(y.Component),z={ +preview:e,folderId:this.props.folderId,options:t,securityID:n,uploadButton:!1},v["default"].createElement("div",{className:o.join(" ")},this.renderToolbar(),this.renderGalleryView())))}}]),t}(y.Component),G={ page:0,limit:15,sort:B[0].field+","+B[0].direction},q={loading:y.PropTypes.bool,sort:y.PropTypes.string,files:y.PropTypes.arrayOf(y.PropTypes.shape({id:y.PropTypes.number,parent:y.PropTypes.shape({id:y.PropTypes.number })})).isRequired,count:y.PropTypes.number,page:y.PropTypes.number,limit:y.PropTypes.number,onOpenFile:y.PropTypes.func.isRequired,onOpenFolder:y.PropTypes.func.isRequired,onSort:y.PropTypes.func.isRequired, -onSetPage:y.PropTypes.func.isRequired},V=d({},z,{selectableItems:!1}),W=d({},q,{selectableItems:y.PropTypes.bool,onSelect:y.PropTypes.func,onCancelUpload:y.PropTypes.func,onRemoveErroredUpload:y.PropTypes.func, +onSetPage:y.PropTypes.func.isRequired},V=d({},G,{selectableItems:!1}),W=d({},q,{selectableItems:y.PropTypes.bool,onSelect:y.PropTypes.func,onCancelUpload:y.PropTypes.func,onRemoveErroredUpload:y.PropTypes.func, renderNoItemsNotice:y.PropTypes.func.isRequired}) -G.defaultProps=d({},z,{type:"admin",view:"tile"}),G.propTypes=d({},q,{type:y.PropTypes.oneOf(["insert","select","admin"]),view:y.PropTypes.oneOf(["tile","table"]),dialog:y.PropTypes.bool,fileId:y.PropTypes.number, +z.defaultProps=d({},G,{type:"admin",view:"tile"}),z.propTypes=d({},q,{type:y.PropTypes.oneOf(["insert","select","admin"]),view:y.PropTypes.oneOf(["tile","table"]),dialog:y.PropTypes.bool,fileId:y.PropTypes.number, folderId:y.PropTypes.number.isRequired,folder:y.PropTypes.shape({id:y.PropTypes.number,parentID:y.PropTypes.number,canView:y.PropTypes.bool,canEdit:y.PropTypes.bool}),queuedFiles:y.PropTypes.shape({items:y.PropTypes.array.isRequired }),selectedFiles:y.PropTypes.arrayOf(y.PropTypes.number),errorMessage:y.PropTypes.string,actions:y.PropTypes.object.isRequired,securityId:y.PropTypes.string,onViewChange:y.PropTypes.func.isRequired,createFileApiUrl:y.PropTypes.string, -createFileApiMethod:y.PropTypes.string,createFolderApi:y.PropTypes.func,readFolderApi:y.PropTypes.func,deleteApi:y.PropTypes.func}),t.Gallery=G,t.sorters=B,t.galleryViewPropTypes=W,t.galleryViewDefaultProps=V, -t["default"]=(0,P.connect)(u,p)(G)},function(e,t){e.exports=jQuery},function(e,t){e.exports=ReactDom},function(e,t){e.exports=ReactAddonsTestUtils},function(e,t){e.exports=ReactAddonsCssTransitionGroup +createFileApiMethod:y.PropTypes.string,createFolderApi:y.PropTypes.func,readFolderApi:y.PropTypes.func,deleteApi:y.PropTypes.func}),t.Gallery=z,t.sorters=B,t.galleryViewPropTypes=W,t.galleryViewDefaultProps=V, +t["default"]=(0,P.connect)(u,p)(z)},function(e,t){e.exports=jQuery},function(e,t){e.exports=ReactDom},function(e,t){e.exports=ReactAddonsTestUtils},function(e,t){e.exports=ReactAddonsCssTransitionGroup },function(e,t,n){"use strict" function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called") @@ -303,6 +303,9 @@ this.props.canUpload&&e.target===t&&(this.dragging=!1,this.forceUpdate(),"functi }},{key:"handleSending",value:function S(e,t,n){n.append("SecurityID",this.props.securityID),n.append("ParentID",this.props.folderId),"function"==typeof this.props.handleSending&&this.props.handleSending(e,t,n) }},{key:"generateQueuedId",value:function w(){return++_}},{key:"handleAddedFile",value:function P(e){var t=this +if(this.props.options.maxFiles&&this.dropzone.files.length>this.props.options.maxFiles)return this.dropzone.removeFile(this.dropzone.files[0]),void("function"==typeof this.props.handleMaxFilesExceeded&&this.props.handleMaxFilesExceeded(e)) + + if(!this.props.canUpload)return Promise.reject(new Error(y["default"]._t("AssetAdmin.DROPZONE_CANNOT_UPLOAD"))) e._queuedId=this.generateQueuedId() var n=new Promise(function(n){var r=new FileReader @@ -318,12 +321,11 @@ var l=t.toDataURL("image/png") r({width:e.naturalWidth,height:e.naturalHeight,thumbnailURL:l})},e.src=t})}},{key:"handleError",value:function T(e,t){"function"==typeof this.props.handleError&&this.props.handleError(e,t)}},{key:"handleSuccess", value:function A(e){this.props.handleSuccess(e)}},{key:"setPromptOnRemove",value:function O(e){this.dropzone.options.dictRemoveFileConfirmation=e}}]),t}(m["default"]) S.propTypes={folderId:d["default"].PropTypes.number.isRequired,handleAddedFile:d["default"].PropTypes.func.isRequired,handleDragEnter:d["default"].PropTypes.func,handleDragLeave:d["default"].PropTypes.func, -handleDrop:d["default"].PropTypes.func,handleError:d["default"].PropTypes.func.isRequired,handleSending:d["default"].PropTypes.func,handleSuccess:d["default"].PropTypes.func.isRequired,options:d["default"].PropTypes.shape({ -url:d["default"].PropTypes.string.isRequired}),promptOnRemove:d["default"].PropTypes.string,securityID:d["default"].PropTypes.string.isRequired,uploadButton:d["default"].PropTypes.bool,uploadSelector:d["default"].PropTypes.string, -canUpload:d["default"].PropTypes.bool.isRequired,preview:d["default"].PropTypes.shape({width:d["default"].PropTypes.number,height:d["default"].PropTypes.number}),className:d["default"].PropTypes.string -},S.defaultProps={uploadButton:!0},t["default"]=S},function(e,t,n){(function(e,t){(function(){var n,r,o,i,s,a,l,u,p=[].slice,d={}.hasOwnProperty,c=function(e,t){function n(){this.constructor=e}for(var r in t)d.call(t,r)&&(e[r]=t[r]) - - +handleDrop:d["default"].PropTypes.func,handleError:d["default"].PropTypes.func.isRequired,handleSending:d["default"].PropTypes.func,handleSuccess:d["default"].PropTypes.func.isRequired,handleMaxFilesExceeded:d["default"].PropTypes.func, +options:d["default"].PropTypes.shape({url:d["default"].PropTypes.string.isRequired}),promptOnRemove:d["default"].PropTypes.string,securityID:d["default"].PropTypes.string.isRequired,uploadButton:d["default"].PropTypes.bool, +uploadSelector:d["default"].PropTypes.string,canUpload:d["default"].PropTypes.bool.isRequired,preview:d["default"].PropTypes.shape({width:d["default"].PropTypes.number,height:d["default"].PropTypes.number +}),className:d["default"].PropTypes.string},S.defaultProps={uploadButton:!0},t["default"]=S},function(e,t,n){(function(e,t){(function(){var n,r,o,i,s,a,l,u,p=[].slice,d={}.hasOwnProperty,c=function(e,t){ +function n(){this.constructor=e}for(var r in t)d.call(t,r)&&(e[r]=t[r]) return n.prototype=t.prototype,e.prototype=new n,e.__super__=t.prototype,e} l=function(){},r=function(){function e(){}return e.prototype.addEventListener=e.prototype.on,e.prototype.on=function(e,t){return this._callbacks=this._callbacks||{},this._callbacks[e]||(this._callbacks[e]=[]), this._callbacks[e].push(t),this},e.prototype.emit=function(){var e,t,n,r,o,i @@ -678,8 +680,8 @@ else{var r={} r[t]=e,n=O({},n,r)}this.filterByColumnFilters(n)},setFilter:function B(e){if(this.props.useExternal)return void this.props.externalSetFilter(e) var t=this,n={page:0,filter:e} n.filteredResults=this.props.useCustomFilterer?this.props.customFilterer(this.props.results,e):this.defaultFilter(this.props.results,e),n.maxPage=t.getMaxPage(n.filteredResults),(I(e)||F(e)||x(e))&&(n.filter=e, -n.filteredResults=null),t.setState(n),this._resetSelectedRows()},setPageSize:function G(e){return this.props.useExternal?(this.setState({resultsPerPage:e}),void this.props.externalSetPageSize(e)):(this.state.resultsPerPage=e, -void this.setMaxPage())},toggleColumnChooser:function z(){this.setState({showColumnChooser:!this.state.showColumnChooser})},isNullOrUndefined:function q(e){return void 0===e||null===e},shouldUseCustomRowComponent:function V(){ +n.filteredResults=null),t.setState(n),this._resetSelectedRows()},setPageSize:function z(e){return this.props.useExternal?(this.setState({resultsPerPage:e}),void this.props.externalSetPageSize(e)):(this.state.resultsPerPage=e, +void this.setMaxPage())},toggleColumnChooser:function G(){this.setState({showColumnChooser:!this.state.showColumnChooser})},isNullOrUndefined:function q(e){return void 0===e||null===e},shouldUseCustomRowComponent:function V(){ return this.isNullOrUndefined(this.state.useCustomRowComponent)?this.props.useCustomRowComponent:this.state.useCustomRowComponent},shouldUseCustomGridComponent:function W(){return this.isNullOrUndefined(this.state.useCustomGridComponent)?this.props.useCustomGridComponent:this.state.useCustomGridComponent },toggleCustomComponent:function Q(){"grid"===this.state.customComponentType?this.setState({useCustomGridComponent:!this.shouldUseCustomGridComponent()}):"row"===this.state.customComponentType&&this.setState({ @@ -1430,11 +1432,11 @@ A=y(e,j,r,k)}}T||(T=new o) var H=T.get(e) if(H)return H T.set(e,A) -var B=L?R?h:f:R?keysIn:_,G=U?void 0:B(e) -return i(G||e,function(o,i){G&&(i=o,o=e[i]),s(A,i,r(o,t,n,i,e,T))}),A}var o=n(38),i=n(209),s=n(184),a=n(239),l=n(240),u=n(241),p=n(242),d=n(243),c=n(246),f=n(249),h=n(251),m=n(115),g=n(252),y=n(253),v=n(264),b=n(101),C=n(102),E=n(62),_=n(95),S=1,w=2,P=4,x="[object Arguments]",F="[object Array]",I="[object Boolean]",T="[object Date]",A="[object Error]",O="[object Function]",D="[object GeneratorFunction]",k="[object Map]",R="[object Number]",N="[object Object]",L="[object RegExp]",U="[object Set]",j="[object String]",M="[object Symbol]",H="[object WeakMap]",B="[object ArrayBuffer]",G="[object DataView]",z="[object Float32Array]",q="[object Float64Array]",V="[object Int8Array]",W="[object Int16Array]",Q="[object Int32Array]",K="[object Uint8Array]",$="[object Uint8ClampedArray]",Z="[object Uint16Array]",Y="[object Uint32Array]",X={} +var B=L?R?h:f:R?keysIn:_,z=U?void 0:B(e) +return i(z||e,function(o,i){z&&(i=o,o=e[i]),s(A,i,r(o,t,n,i,e,T))}),A}var o=n(38),i=n(209),s=n(184),a=n(239),l=n(240),u=n(241),p=n(242),d=n(243),c=n(246),f=n(249),h=n(251),m=n(115),g=n(252),y=n(253),v=n(264),b=n(101),C=n(102),E=n(62),_=n(95),S=1,w=2,P=4,x="[object Arguments]",F="[object Array]",I="[object Boolean]",T="[object Date]",A="[object Error]",O="[object Function]",D="[object GeneratorFunction]",k="[object Map]",R="[object Number]",N="[object Object]",L="[object RegExp]",U="[object Set]",j="[object String]",M="[object Symbol]",H="[object WeakMap]",B="[object ArrayBuffer]",z="[object DataView]",G="[object Float32Array]",q="[object Float64Array]",V="[object Int8Array]",W="[object Int16Array]",Q="[object Int32Array]",K="[object Uint8Array]",$="[object Uint8ClampedArray]",Z="[object Uint16Array]",Y="[object Uint32Array]",X={} -X[x]=X[F]=X[B]=X[G]=X[I]=X[T]=X[z]=X[q]=X[V]=X[W]=X[Q]=X[k]=X[R]=X[N]=X[L]=X[U]=X[j]=X[M]=X[K]=X[$]=X[Z]=X[Y]=!0,X[A]=X[O]=X[H]=!1,e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(186),i=n(95) +X[x]=X[F]=X[B]=X[z]=X[I]=X[T]=X[G]=X[q]=X[V]=X[W]=X[Q]=X[k]=X[R]=X[N]=X[L]=X[U]=X[j]=X[M]=X[K]=X[$]=X[Z]=X[Y]=!0,X[A]=X[O]=X[H]=!1,e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(186),i=n(95) e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(186),i=n(216) @@ -1655,7 +1657,7 @@ function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t,n){return t switch(t.type){case p["default"].PREVIEWFIELD_ADD_FILE:return(0,l["default"])(s({},e,o({},t.payload.id,t.payload.file))) -case p["default"].PREVIEWFIELD_FAIL_UPLOAD:return(0,l["default"])(s({},e,o({},t.payload.id,s({},e[t.payload.id],{message:{type:"error",value:t.payload.message}})))) +case p["default"].PREVIEWFIELD_FAIL_UPLOAD:return(0,l["default"])(s({},e,o({},t.payload.id,s({},e[t.payload.id],{message:t.payload.message})))) case p["default"].PREVIEWFIELD_REMOVE_FILE:return(0,l["default"])(s({},e,o({},t.payload.id,void 0))) case p["default"].PREVIEWFIELD_UPDATE_FILE:return(0,l["default"])(s({},e,o({},t.payload.id,s({},e[t.payload.id],t.payload.data)))) default:return e}}Object.defineProperty(t,"__esModule",{value:!0}) @@ -1785,7 +1787,7 @@ var n=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e)) return n.handleAddedFile=n.handleAddedFile.bind(n),n.handleFailedUpload=n.handleFailedUpload.bind(n),n.handleSuccessfulUpload=n.handleSuccessfulUpload.bind(n),n.handleSending=n.handleSending.bind(n),n.handleUploadProgress=n.handleUploadProgress.bind(n), n.handleCancelUpload=n.handleCancelUpload.bind(n),n.handleRemoveErroredUpload=n.handleRemoveErroredUpload.bind(n),n}return a(t,e),p(t,[{key:"componentWillUnmount",value:function n(){this.props.actions.previewField.removeFile(this.props.id) -}},{key:"getDropzoneProps",value:function r(){var e=this.props.data.uploadFileEndpoint,t={url:e&&e.url,method:e&&e.method,paramName:"Upload",clickable:"#preview-replace-button"},n={height:v["default"].THUMBNAIL_HEIGHT, +}},{key:"getDropzoneProps",value:function r(){var e=this.props.data.uploadFileEndpoint,t={url:e&&e.url,method:e&&e.method,paramName:"Upload",clickable:"#preview-replace-button",maxFiles:1},n={height:v["default"].THUMBNAIL_HEIGHT, width:v["default"].THUMBNAIL_WIDTH},r=this.props.securityID,o=["asset-dropzone--button","preview__container",this.props.className,this.props.extraClass] return{className:o.join(" "),canUpload:e&&this.canEdit(),preview:n,folderId:this.props.data.parentid,options:t,securityID:r,uploadButton:!1,handleAddedFile:this.handleAddedFile,handleError:this.handleFailedUpload, handleSuccess:this.handleSuccessfulUpload,handleSending:this.handleSending,handleUploadProgress:this.handleUploadProgress}}},{key:"canEdit",value:function o(){return!this.props.readOnly&&!this.props.disabled&&"folder"!==this.props.data.category diff --git a/client/src/components/AssetDropzone/AssetDropzone.js b/client/src/components/AssetDropzone/AssetDropzone.js index a545e4992..f2e65916b 100644 --- a/client/src/components/AssetDropzone/AssetDropzone.js +++ b/client/src/components/AssetDropzone/AssetDropzone.js @@ -285,6 +285,16 @@ class AssetDropzone extends SilverStripeComponent { * @param file (object) - File interface. See https://developer.mozilla.org/en-US/docs/Web/API/File */ handleAddedFile(file) { + if (this.props.options.maxFiles && this.dropzone.files.length > this.props.options.maxFiles) { + this.dropzone.removeFile(this.dropzone.files[0]); + if (typeof this.props.handleMaxFilesExceeded === 'function') { + // can add a warning message here + this.props.handleMaxFilesExceeded(file); + } + // shouldn't return error, as there isn't a way to catch it... + return; + } + if (!this.props.canUpload) { return Promise.reject(new Error(i18n._t('AssetAdmin.DROPZONE_CANNOT_UPLOAD'))); } @@ -446,6 +456,7 @@ AssetDropzone.propTypes = { handleError: React.PropTypes.func.isRequired, handleSending: React.PropTypes.func, handleSuccess: React.PropTypes.func.isRequired, + handleMaxFilesExceeded: React.PropTypes.func, options: React.PropTypes.shape({ url: React.PropTypes.string.isRequired, }), diff --git a/client/src/components/PreviewImageField/PreviewImageField.js b/client/src/components/PreviewImageField/PreviewImageField.js index 03a98e4ef..6ecd40a3f 100644 --- a/client/src/components/PreviewImageField/PreviewImageField.js +++ b/client/src/components/PreviewImageField/PreviewImageField.js @@ -30,6 +30,7 @@ class PreviewImageField extends Component { method: endpoint && endpoint.method, paramName: 'Upload', clickable: '#preview-replace-button', + maxFiles: 1, }; const preview = { height: CONSTANTS.THUMBNAIL_HEIGHT, @@ -93,6 +94,11 @@ class PreviewImageField extends Component { this.props.actions.previewField.removeFile(this.props.id); } + /** + * Handles when a file is added to this field. + * + * @param {object} data + */ handleAddedFile(data) { this.props.actions.previewField.addFile(this.props.id, data); } diff --git a/client/src/state/previewField/PreviewFieldReducer.js b/client/src/state/previewField/PreviewFieldReducer.js index 45655758c..c2bf44350 100644 --- a/client/src/state/previewField/PreviewFieldReducer.js +++ b/client/src/state/previewField/PreviewFieldReducer.js @@ -14,10 +14,7 @@ function previewFieldReducer(state = initialState, action) { case ACTION_TYPES.PREVIEWFIELD_FAIL_UPLOAD: { return deepFreeze(Object.assign({}, state, { [action.payload.id]: Object.assign({}, state[action.payload.id], { - message: { - type: 'error', - value: action.payload.message, - }, + message: action.payload.message, }), })); } diff --git a/code/Forms/FileFormFactory.php b/code/Forms/FileFormFactory.php index 91e24f84d..8c747092c 100644 --- a/code/Forms/FileFormFactory.php +++ b/code/Forms/FileFormFactory.php @@ -125,7 +125,7 @@ protected function getFormFields(Controller $controller, $name, $context = []) // Add status flag before extensions are triggered $this->beforeExtending('updateFormFields', function (FieldList $fields) use ($record) { - // @todo move specs to a component/class, so it can render similar to PreviewImageField + // @todo move specs to a component/class, so it can update specs when a File is replaced $fields->insertAfter( 'TitleHeader', LiteralField::create('FileSpecs', $this->getSpecsMarkup($record)) From c118d0d6df541b382affda1b55de1a4ce9e5cc76 Mon Sep 17 00:00:00 2001 From: Christopher Joe Date: Thu, 15 Dec 2016 14:26:49 +1300 Subject: [PATCH 03/13] Enhancement Tweaked AssetDropzone internally for better support of "maxFiles" FIX GalleryItem callbacks Enhancement Added revert values on cancel or remove of replacement file upload Enhancement Added progress bar and error messages for uploading --- client/dist/js/bundle.js | 180 +++++++++--------- client/dist/styles/bundle.css | 48 ++++- .../components/AssetDropzone/AssetDropzone.js | 28 ++- .../src/components/GalleryItem/GalleryItem.js | 10 +- .../PreviewImageField/PreviewImageField.js | 90 +++++++-- .../PreviewImageField/PreviewImageField.scss | 49 ++++- .../state/previewField/PreviewFieldReducer.js | 4 +- code/Forms/PreviewImageField.php | 5 + 8 files changed, 286 insertions(+), 128 deletions(-) diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index a6ebd1002..b17f60378 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -58,7 +58,7 @@ return{loading:n,files:o,folder:r,securityId:e.config.SecurityID}}function u(e){ value:!0}),t.AssetAdmin=void 0 var p=Object.assign||function(e){for(var t=1;t-1}},{key:"itemIsHighlighted",value:function M(e){return this.props.fileId===e}},{key:"handleOpenFolder",value:function H(e,t){ -e.preventDefault(),this.props.onOpenFolder(t.id)}},{key:"handleOpenFile",value:function z(e,t){e.preventDefault(),null!==t.created&&this.props.onOpenFile(t.id,t)}},{key:"handleSelect",value:function G(e,t){ +e.preventDefault(),this.props.onOpenFolder(t.id)}},{key:"handleOpenFile",value:function B(e,t){e.preventDefault(),null!==t.created&&this.props.onOpenFile(t.id,t)}},{key:"handleSelect",value:function G(e,t){ this.props.selectedFiles.indexOf(t.id)===-1?this.props.actions.gallery.selectFiles([t.id]):this.props.actions.gallery.deselectFiles([t.id])}},{key:"handleBackClick",value:function q(e){e.preventDefault(), this.props.onOpenFolder(this.props.folder.parentID)}},{key:"handleViewChange",value:function V(e){var t=e.currentTarget.value this.props.onViewChange(t)}},{key:"renderSort",value:function W(){var e=this return"tile"!==this.props.view?null:v["default"].createElement("div",{className:"gallery__sort fieldholder-small"},v["default"].createElement("select",{className:"dropdown no-change-track no-chzn",tabIndex:"0", -style:{width:"160px"},defaultValue:this.props.sort},B.map(function(t,n){return v["default"].createElement("option",{key:n,onClick:e.handleSelectSort,"data-field":t.field,"data-direction":t.direction,value:t.field+","+t.direction +style:{width:"160px"},defaultValue:this.props.sort},z.map(function(t,n){return v["default"].createElement("option",{key:n,onClick:e.handleSelectSort,"data-field":t.field,"data-direction":t.direction,value:t.field+","+t.direction },t.label)})))}},{key:"renderToolbar",value:function Q(){var e=this.props.folder.canEdit return v["default"].createElement("div",{className:"toolbar--content toolbar--space-save"},v["default"].createElement("div",{className:"fill-width"},v["default"].createElement("div",{className:"flexbox-area-grow" },this.renderBackButton(),v["default"].createElement("button",{id:"upload-button",className:"btn btn-secondary font-icon-upload btn--icon-xl",type:"button",disabled:!e},v["default"].createElement("span",{ @@ -235,7 +235,7 @@ e.props.actions.gallery.deleteItems(e.props.deleteApi,n)},n=function s(t){e.prop callback:n}):d({},e,{callback:t})}),o=this.props.selectedFiles.map(function(t){return e.props.files.find(function(e){return t===e.id})}) return o.length>0&&"admin"===this.props.type?v["default"].createElement(w["default"],{transitionName:"bulk-actions",transitionEnterTimeout:L["default"].CSS_TRANSITION_TIME,transitionLeaveTimeout:L["default"].CSS_TRANSITION_TIME },v["default"].createElement(A["default"],{actions:r,items:o,key:o.length>0})):null}},{key:"renderNoItemsNotice",value:function Y(){return 0!==this.props.files.length||this.props.loading?null:v["default"].createElement("p",{ -className:"gallery__no-item-notice"},g["default"]._t("AssetAdmin.NOITEMSFOUND"))}},{key:"renderGalleryView",value:function X(){var e=this,t="table"===this.props.view?R["default"]:D["default"],n=this.props.files.map(function(t){ +className:"gallery__no-item-notice"},g["default"]._t("AssetAdmin.NOITEMSFOUND"))}},{key:"renderGalleryView",value:function X(){var e=this,t="table"===this.props.view?N["default"]:D["default"],n=this.props.files.map(function(t){ return d({},t,{selected:e.itemIsSelected(t.id),highlighted:e.itemIsHighlighted(t.id)})}),r=this.props.queuedFiles.items.map(function(e){return d({},e,{uploading:!0})}),o=[].concat(i(r),i(n)),s=this.props,a=s.type,l=s.loading,u=s.page,p=s.count,c=s.limit,f=s.sort,h={ selectableItems:"admin"===a,files:o,loading:l,page:u,count:p,limit:c,sort:f,onSort:this.handleSort,onSetPage:this.handleSetPage,onOpenFile:this.handleOpenFile,onOpenFolder:this.handleOpenFolder,onSelect:this.handleSelect, onCancelUpload:this.handleCancelUpload,onRemoveErroredUpload:this.handleRemoveErroredUpload,renderNoItemsNotice:this.renderNoItemsNotice} @@ -246,18 +246,18 @@ className:"gallery__error-message"},v["default"].createElement("h3",null,this.pr var e={height:L["default"].THUMBNAIL_HEIGHT,width:L["default"].THUMBNAIL_WIDTH},t={url:this.props.createFileApiUrl,method:this.props.createFileApiMethod,paramName:"Upload",clickable:"#upload-button"},n=this.props.securityId,r=this.props.folder.canEdit,o=["panel","panel--padded","panel--scrollable","gallery__main"] -return"insert"===this.props.type&&o.push("insert-media-modal__main"),v["default"].createElement("div",{className:"flexbox-area-grow gallery__outer"},this.renderBulkActions(),v["default"].createElement(I["default"],{ +return"insert"===this.props.type&&o.push("insert-media-modal__main"),v["default"].createElement("div",{className:"flexbox-area-grow gallery__outer"},this.renderBulkActions(),v["default"].createElement(T["default"],{ canUpload:r,handleAddedFile:this.handleAddedFile,handleError:this.handleFailedUpload,handleSuccess:this.handleSuccessfulUpload,handleSending:this.handleSending,handleUploadProgress:this.handleUploadProgress, preview:e,folderId:this.props.folderId,options:t,securityID:n,uploadButton:!1},v["default"].createElement("div",{className:o.join(" ")},this.renderToolbar(),this.renderGalleryView())))}}]),t}(y.Component),G={ -page:0,limit:15,sort:B[0].field+","+B[0].direction},q={loading:y.PropTypes.bool,sort:y.PropTypes.string,files:y.PropTypes.arrayOf(y.PropTypes.shape({id:y.PropTypes.number,parent:y.PropTypes.shape({id:y.PropTypes.number +page:0,limit:15,sort:z[0].field+","+z[0].direction},q={loading:y.PropTypes.bool,sort:y.PropTypes.string,files:y.PropTypes.arrayOf(y.PropTypes.shape({id:y.PropTypes.number,parent:y.PropTypes.shape({id:y.PropTypes.number })})).isRequired,count:y.PropTypes.number,page:y.PropTypes.number,limit:y.PropTypes.number,onOpenFile:y.PropTypes.func.isRequired,onOpenFolder:y.PropTypes.func.isRequired,onSort:y.PropTypes.func.isRequired, onSetPage:y.PropTypes.func.isRequired},V=d({},G,{selectableItems:!1}),W=d({},q,{selectableItems:y.PropTypes.bool,onSelect:y.PropTypes.func,onCancelUpload:y.PropTypes.func,onRemoveErroredUpload:y.PropTypes.func, renderNoItemsNotice:y.PropTypes.func.isRequired}) -z.defaultProps=d({},G,{type:"admin",view:"tile"}),z.propTypes=d({},q,{type:y.PropTypes.oneOf(["insert","select","admin"]),view:y.PropTypes.oneOf(["tile","table"]),dialog:y.PropTypes.bool,fileId:y.PropTypes.number, +B.defaultProps=d({},G,{type:"admin",view:"tile"}),B.propTypes=d({},q,{type:y.PropTypes.oneOf(["insert","select","admin"]),view:y.PropTypes.oneOf(["tile","table"]),dialog:y.PropTypes.bool,fileId:y.PropTypes.number, folderId:y.PropTypes.number.isRequired,folder:y.PropTypes.shape({id:y.PropTypes.number,parentID:y.PropTypes.number,canView:y.PropTypes.bool,canEdit:y.PropTypes.bool}),queuedFiles:y.PropTypes.shape({items:y.PropTypes.array.isRequired }),selectedFiles:y.PropTypes.arrayOf(y.PropTypes.number),errorMessage:y.PropTypes.string,actions:y.PropTypes.object.isRequired,securityId:y.PropTypes.string,onViewChange:y.PropTypes.func.isRequired,createFileApiUrl:y.PropTypes.string, -createFileApiMethod:y.PropTypes.string,createFolderApi:y.PropTypes.func,readFolderApi:y.PropTypes.func,deleteApi:y.PropTypes.func}),t.Gallery=z,t.sorters=B,t.galleryViewPropTypes=W,t.galleryViewDefaultProps=V, -t["default"]=(0,P.connect)(u,p)(z)},function(e,t){e.exports=jQuery},function(e,t){e.exports=ReactDom},function(e,t){e.exports=ReactAddonsTestUtils},function(e,t){e.exports=ReactAddonsCssTransitionGroup +createFileApiMethod:y.PropTypes.string,createFolderApi:y.PropTypes.func,readFolderApi:y.PropTypes.func,deleteApi:y.PropTypes.func}),t.Gallery=B,t.sorters=z,t.galleryViewPropTypes=W,t.galleryViewDefaultProps=V, +t["default"]=(0,P.connect)(u,p)(B)},function(e,t){e.exports=jQuery},function(e,t){e.exports=ReactDom},function(e,t){e.exports=ReactAddonsTestUtils},function(e,t){e.exports=ReactAddonsCssTransitionGroup },function(e,t,n){"use strict" function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called") @@ -300,12 +300,12 @@ var t=f["default"].findDOMNode(this) this.props.canUpload&&e.target===t&&(this.dragging=!1,this.forceUpdate(),"function"==typeof this.props.handleDragLeave&&this.props.handleDragLeave(e,t))}},{key:"handleUploadProgress",value:function v(e,t,n){ "function"==typeof this.props.handleUploadProgress&&this.props.handleUploadProgress(e,t,n)}},{key:"handleDrop",value:function C(e){this.dragging=!1,this.forceUpdate(),"function"==typeof this.props.handleDrop&&this.props.handleDrop(e) -}},{key:"handleSending",value:function S(e,t,n){n.append("SecurityID",this.props.securityID),n.append("ParentID",this.props.folderId),"function"==typeof this.props.handleSending&&this.props.handleSending(e,t,n) - -}},{key:"generateQueuedId",value:function w(){return++_}},{key:"handleAddedFile",value:function P(e){var t=this -if(this.props.options.maxFiles&&this.dropzone.files.length>this.props.options.maxFiles)return this.dropzone.removeFile(this.dropzone.files[0]),void("function"==typeof this.props.handleMaxFilesExceeded&&this.props.handleMaxFilesExceeded(e)) - - +}},{key:"handleSending",value:function S(e,t,n){var r=this +n.append("SecurityID",this.props.securityID),n.append("ParentID",this.props.folderId) +var o=a({},t,{abort:function i(){r.dropzone.cancelUpload(e),t.abort()}}) +"function"==typeof this.props.handleSending&&this.props.handleSending(e,o,n)}},{key:"generateQueuedId",value:function w(){return++_}},{key:"handleAddedFile",value:function P(e){var t=this +if(this.props.options.maxFiles&&this.dropzone.files.length>this.props.options.maxFiles)return this.dropzone.removeFile(this.dropzone.files[0]),"function"==typeof this.props.handleMaxFilesExceeded&&this.props.handleMaxFilesExceeded(e), +Promise.resolve() if(!this.props.canUpload)return Promise.reject(new Error(y["default"]._t("AssetAdmin.DROPZONE_CANNOT_UPLOAD"))) e._queuedId=this.generateQueuedId() var n=new Promise(function(n){var r=new FileReader @@ -314,12 +314,14 @@ n(t.loadImage(o,r.target.result))}else n({})},r.readAsDataURL(e)}) return n.then(function(n){var r={dimensions:{height:n.height,width:n.width},category:t.getFileCategory(e.type),filename:e.name,queuedId:e._queuedId,size:e.size,title:t.getFileTitle(e.name),extension:t.getFileExtension(e.name), type:e.type,url:n.thumbnailURL} return t.props.handleAddedFile(r),t.dropzone.processFile(e),r})}},{key:"getFileTitle",value:function x(e){return e.replace(/[.][^.]+$/,"").replace(/-_/," ")}},{key:"getFileExtension",value:function F(e){ -return/[.]/.exec(e)?e.replace(/^.+[.]/,""):""}},{key:"loadImage",value:function I(e,t){var n=this +return/[.]/.exec(e)?e.replace(/^.+[.]/,""):""}},{key:"loadImage",value:function T(e,t){var n=this return new Promise(function(r){e.onload=function(){var t=document.createElement("canvas"),o=t.getContext("2d"),i=2*n.props.preview.width,s=2*n.props.preview.height,a=e.naturalWidth/e.naturalHeight e.naturalWidth=U;p=0<=U?++O:--O)i.append(this._getParamName(p),e[p],this._renameFilename(e[p].name)) return this.submitRequest(_,i,e)},t.prototype.submitRequest=function(e,t,n){return e.send(t)},t.prototype._finished=function(e,n,r){var o,i,s @@ -623,9 +625,9 @@ n}return s(t,e),a(t,[{key:"handleActivate",value:function n(e){e.stopPropagation e.stopPropagation(),e.preventDefault(),"function"==typeof this.props.onSelect&&this.props.onSelect(e,this.props.item)}},{key:"getThumbnailStyles",value:function l(){if(this.isImage()&&(this.exists()||this.uploading())){ var e=this.props.item.thumbnail||this.props.item.url return{backgroundImage:"url("+e+")"}}return{}}},{key:"hasError",value:function p(){var p=!1 -return this.props.message&&(p="error"===this.props.message.type),p}},{key:"getErrorMessage",value:function c(){var e=null -return this.hasError()?e=this.props.message.value:this.exists()||this.uploading()||(e=u["default"]._t("AssetAdmin.FILE_MISSING","File cannot be found")),null!==e?d["default"].createElement("span",{className:"gallery-item__error-message" -},e):null}},{key:"getThumbnailClassNames",value:function h(){var e=["gallery-item__thumbnail"] +return this.props.item.message&&(p="error"===this.props.item.message.type),p}},{key:"getErrorMessage",value:function c(){var e=null +return this.hasError()?e=this.props.item.message.value:this.exists()||this.uploading()||(e=u["default"]._t("AssetAdmin.FILE_MISSING","File cannot be found")),null!==e?d["default"].createElement("span",{ +className:"gallery-item__error-message"},e):null}},{key:"getThumbnailClassNames",value:function h(){var e=["gallery-item__thumbnail"] return this.isImageSmallerThanThumbnail()&&e.push("gallery-item__thumbnail--small"),e.join(" ")}},{key:"getItemClassNames",value:function m(){var e=this.props.item.category||"false",t=["gallery-item gallery-item--"+e] @@ -639,7 +641,7 @@ value:function v(){return this.props.item.exists}},{key:"uploading",value:functi var e=this.props.item.dimensions return e&&e.height=0 return y.getAt(t,n||"").toString().toLowerCase().indexOf(e[n].toLowerCase())>=0})},this.props.results),n={columnFilters:e} e?(n.filteredResults=t,n.maxPage=this.getMaxPage(n.filteredResults)):this.state.filter?n.filteredResults=this.props.useCustomFilterer?this.props.customFilterer(this.props.results,filter):this.defaultFilter(this.props.results,filter):n.filteredResults=null, this.setState(n)},filterByColumn:function H(e,t){var n=this.state.columnFilters -if(n.hasOwnProperty(t)&&!e)n=T(n,t) +if(n.hasOwnProperty(t)&&!e)n=I(n,t) else{var r={} -r[t]=e,n=O({},n,r)}this.filterByColumnFilters(n)},setFilter:function B(e){if(this.props.useExternal)return void this.props.externalSetFilter(e) +r[t]=e,n=O({},n,r)}this.filterByColumnFilters(n)},setFilter:function z(e){if(this.props.useExternal)return void this.props.externalSetFilter(e) var t=this,n={page:0,filter:e} -n.filteredResults=this.props.useCustomFilterer?this.props.customFilterer(this.props.results,e):this.defaultFilter(this.props.results,e),n.maxPage=t.getMaxPage(n.filteredResults),(I(e)||F(e)||x(e))&&(n.filter=e, -n.filteredResults=null),t.setState(n),this._resetSelectedRows()},setPageSize:function z(e){return this.props.useExternal?(this.setState({resultsPerPage:e}),void this.props.externalSetPageSize(e)):(this.state.resultsPerPage=e, +n.filteredResults=this.props.useCustomFilterer?this.props.customFilterer(this.props.results,e):this.defaultFilter(this.props.results,e),n.maxPage=t.getMaxPage(n.filteredResults),(T(e)||F(e)||x(e))&&(n.filter=e, +n.filteredResults=null),t.setState(n),this._resetSelectedRows()},setPageSize:function B(e){return this.props.useExternal?(this.setState({resultsPerPage:e}),void this.props.externalSetPageSize(e)):(this.state.resultsPerPage=e, void this.setMaxPage())},toggleColumnChooser:function G(){this.setState({showColumnChooser:!this.state.showColumnChooser})},isNullOrUndefined:function q(e){return void 0===e||null===e},shouldUseCustomRowComponent:function V(){ return this.isNullOrUndefined(this.state.useCustomRowComponent)?this.props.useCustomRowComponent:this.state.useCustomRowComponent},shouldUseCustomGridComponent:function W(){return this.isNullOrUndefined(this.state.useCustomGridComponent)?this.props.useCustomGridComponent:this.state.useCustomGridComponent @@ -721,9 +723,9 @@ this.props.useCustomFilterComponent===!0&&null===this.props.customFilterComponen var r=this,o=this if(""!==this.state.sortColumn){var i=this.state.sortColumn,s=D(this.props.columnMetadata,{columnName:i}),a,l={columns:[],orders:[]} if(s.length>0&&(a=s[0].hasOwnProperty("customCompareFn")&&s[0].customCompareFn,s[0].multiSort&&(l=s[0].multiSort)),this.state.sortDirection)if("function"==typeof a)2===a.length?(e=e.sort(function(e,t){ -return a(N(e,i),N(t,i))}),"desc"===this.state.sortDirection&&e.reverse()):1===a.length&&(e=k(e,function(e){return a(N(e,i))},[this.state.sortDirection])) -else{var u=[R(i)],p=[this.state.sortDirection] -l.columns.forEach(function(e,t){u.push(R(e)),"asc"===l.orders[t]||"desc"===l.orders[t]?p.push(l.orders[t]):p.push(r.state.sortDirection)}),e=k(e,u,p)}}var d=this.getCurrentPage() +return a(R(e,i),R(t,i))}),"desc"===this.state.sortDirection&&e.reverse()):1===a.length&&(e=k(e,function(e){return a(R(e,i))},[this.state.sortDirection])) +else{var u=[N(i)],p=[this.state.sortDirection] +l.columns.forEach(function(e,t){u.push(N(e)),"asc"===l.orders[t]||"desc"===l.orders[t]?p.push(l.orders[t]):p.push(r.state.sortDirection)}),e=k(e,u,p)}}var d=this.getCurrentPage() if(!this.props.useExternal&&n&&this.state.resultsPerPage*(d+1)<=this.state.resultsPerPage*this.state.maxPage&&d>=0)if(this.isInfiniteScrollEnabled())e=E(e,(d+1)*this.state.resultsPerPage) else{var c=v(e,d*this.state.resultsPerPage) e=(b||S)(c,c.length-this.state.resultsPerPage)}for(var f=this.columnSettings.getMetadataColumns,h=[],m=0;m0?"griddle "+this.props.gridClassName:"griddle" @@ -969,8 +971,8 @@ var S=E==m,w=_==m,P=E==_ if(P&&p(e)){if(!p(t))return!1 b=!0,S=!1}if(P&&!S)return v||(v=new o),b||d(e)?i(e,t,n,r,g,v):s(e,t,E,n,r,g,v) if(!(n&c)){var x=S&&y.call(e,"__wrapped__"),F=w&&y.call(t,"__wrapped__") -if(x||F){var I=x?e.value():e,T=F?t.value():t -return v||(v=new o),g(I,T,n,r,v)}}return!!P&&(v||(v=new o),a(e,t,n,r,g,v))}var o=n(38),i=n(84),s=n(90),a=n(94),l=n(115),u=n(101),p=n(102),d=n(105),c=1,f="[object Arguments]",h="[object Array]",m="[object Object]",g=Object.prototype,y=g.hasOwnProperty +if(x||F){var T=x?e.value():e,I=F?t.value():t +return v||(v=new o),g(T,I,n,r,v)}}return!!P&&(v||(v=new o),a(e,t,n,r,g,v))}var o=n(38),i=n(84),s=n(90),a=n(94),l=n(115),u=n(101),p=n(102),d=n(105),c=1,f="[object Arguments]",h="[object Array]",m="[object Object]",g=Object.prototype,y=g.hasOwnProperty e.exports=r},function(e,t,n){function r(e,t,n,r,u,p){var d=n&a,c=e.length,f=t.length @@ -1000,11 +1002,11 @@ case y:case b:return e==t+"" case m:var x=l case v:var F=r&p if(x||(x=u),e.size!=t.size&&!F)return!1 -var I=P.get(e) -if(I)return I==t +var T=P.get(e) +if(T)return T==t r|=d,P.set(e,t) -var T=a(x(e),x(t),r,o,S,P) -return P["delete"](e),T +var I=a(x(e),x(t),r,o,S,P) +return P["delete"](e),I case C:if(w)return w.call(e)==w.call(t)}return!1}var o=n(57),i=n(91),s=n(43),a=n(84),l=n(92),u=n(93),p=1,d=2,c="[object Boolean]",f="[object Date]",h="[object Error]",m="[object Map]",g="[object Number]",y="[object RegExp]",v="[object Set]",b="[object String]",C="[object Symbol]",E="[object ArrayBuffer]",_="[object DataView]",S=o?o.prototype:void 0,w=S?S.valueOf:void 0 @@ -1043,10 +1045,10 @@ e.exports=p}).call(t,n(24)(e))},function(e,t){function n(){return!1}e.exports=n} e.exports=n},function(e,t,n){var r=n(106),o=n(108),i=n(109),s=i&&i.isTypedArray,a=s?o(s):r -e.exports=a},function(e,t,n){function r(e){return s(e)&&i(e.length)&&!!O[o(e)]}var o=n(56),i=n(107),s=n(100),a="[object Arguments]",l="[object Array]",u="[object Boolean]",p="[object Date]",d="[object Error]",c="[object Function]",f="[object Map]",h="[object Number]",m="[object Object]",g="[object RegExp]",y="[object Set]",v="[object String]",b="[object WeakMap]",C="[object ArrayBuffer]",E="[object DataView]",_="[object Float32Array]",S="[object Float64Array]",w="[object Int8Array]",P="[object Int16Array]",x="[object Int32Array]",F="[object Uint8Array]",I="[object Uint8ClampedArray]",T="[object Uint16Array]",A="[object Uint32Array]",O={} +e.exports=a},function(e,t,n){function r(e){return s(e)&&i(e.length)&&!!O[o(e)]}var o=n(56),i=n(107),s=n(100),a="[object Arguments]",l="[object Array]",u="[object Boolean]",p="[object Date]",d="[object Error]",c="[object Function]",f="[object Map]",h="[object Number]",m="[object Object]",g="[object RegExp]",y="[object Set]",v="[object String]",b="[object WeakMap]",C="[object ArrayBuffer]",E="[object DataView]",_="[object Float32Array]",S="[object Float64Array]",w="[object Int8Array]",P="[object Int16Array]",x="[object Int32Array]",F="[object Uint8Array]",T="[object Uint8ClampedArray]",I="[object Uint16Array]",A="[object Uint32Array]",O={} -O[_]=O[S]=O[w]=O[P]=O[x]=O[F]=O[I]=O[T]=O[A]=!0,O[a]=O[l]=O[C]=O[u]=O[E]=O[p]=O[d]=O[c]=O[f]=O[h]=O[m]=O[g]=O[y]=O[v]=O[b]=!1,e.exports=r},function(e,t){function n(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=r +O[_]=O[S]=O[w]=O[P]=O[x]=O[F]=O[T]=O[I]=O[A]=!0,O[a]=O[l]=O[C]=O[u]=O[E]=O[p]=O[d]=O[c]=O[f]=O[h]=O[m]=O[g]=O[y]=O[v]=O[b]=!1,e.exports=r},function(e,t){function n(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=r }var r=9007199254740991 e.exports=n},function(e,t){function n(e){return function(t){return e(t)}}e.exports=n},function(e,t,n){(function(e){var r=n(59),o="object"==typeof t&&t&&!t.nodeType&&t,i=o&&"object"==typeof e&&e&&!e.nodeType&&e,s=i&&i.exports===o,a=s&&r.process,l=function(){ @@ -1421,22 +1423,22 @@ var l=!1 t=r(t,function(t){return t=s(t,e),l||(l=t.length>1),t}),a(e,u(e),n),l&&(n=o(n,p|d|c)) for(var f=t.length;f--;)i(n,t[f]) return n}) -e.exports=f},function(e,t,n){function r(e,t,n,F,I,T){var A,k=t&S,R=t&w,L=t&P -if(n&&(A=I?n(e,F,I,T):n(e)),void 0!==A)return A +e.exports=f},function(e,t,n){function r(e,t,n,F,T,I){var A,k=t&S,N=t&w,L=t&P +if(n&&(A=T?n(e,F,T,I):n(e)),void 0!==A)return A if(!E(e))return e var U=b(e) if(U){if(A=g(e),!k)return p(e,A)}else{var j=m(e),M=j==O||j==D if(C(e))return u(e,k) -if(j==N||j==x||M&&!I){if(A=R||M?{}:v(e),!k)return R?c(e,l(A,e)):d(e,a(A,e))}else{if(!X[j])return I?e:{} -A=y(e,j,r,k)}}T||(T=new o) -var H=T.get(e) +if(j==R||j==x||M&&!T){if(A=N||M?{}:v(e),!k)return N?c(e,l(A,e)):d(e,a(A,e))}else{if(!X[j])return T?e:{} +A=y(e,j,r,k)}}I||(I=new o) +var H=I.get(e) if(H)return H -T.set(e,A) -var B=L?R?h:f:R?keysIn:_,z=U?void 0:B(e) -return i(z||e,function(o,i){z&&(i=o,o=e[i]),s(A,i,r(o,t,n,i,e,T))}),A}var o=n(38),i=n(209),s=n(184),a=n(239),l=n(240),u=n(241),p=n(242),d=n(243),c=n(246),f=n(249),h=n(251),m=n(115),g=n(252),y=n(253),v=n(264),b=n(101),C=n(102),E=n(62),_=n(95),S=1,w=2,P=4,x="[object Arguments]",F="[object Array]",I="[object Boolean]",T="[object Date]",A="[object Error]",O="[object Function]",D="[object GeneratorFunction]",k="[object Map]",R="[object Number]",N="[object Object]",L="[object RegExp]",U="[object Set]",j="[object String]",M="[object Symbol]",H="[object WeakMap]",B="[object ArrayBuffer]",z="[object DataView]",G="[object Float32Array]",q="[object Float64Array]",V="[object Int8Array]",W="[object Int16Array]",Q="[object Int32Array]",K="[object Uint8Array]",$="[object Uint8ClampedArray]",Z="[object Uint16Array]",Y="[object Uint32Array]",X={} +I.set(e,A) +var z=L?N?h:f:N?keysIn:_,B=U?void 0:z(e) +return i(B||e,function(o,i){B&&(i=o,o=e[i]),s(A,i,r(o,t,n,i,e,I))}),A}var o=n(38),i=n(209),s=n(184),a=n(239),l=n(240),u=n(241),p=n(242),d=n(243),c=n(246),f=n(249),h=n(251),m=n(115),g=n(252),y=n(253),v=n(264),b=n(101),C=n(102),E=n(62),_=n(95),S=1,w=2,P=4,x="[object Arguments]",F="[object Array]",T="[object Boolean]",I="[object Date]",A="[object Error]",O="[object Function]",D="[object GeneratorFunction]",k="[object Map]",N="[object Number]",R="[object Object]",L="[object RegExp]",U="[object Set]",j="[object String]",M="[object Symbol]",H="[object WeakMap]",z="[object ArrayBuffer]",B="[object DataView]",G="[object Float32Array]",q="[object Float64Array]",V="[object Int8Array]",W="[object Int16Array]",Q="[object Int32Array]",K="[object Uint8Array]",$="[object Uint8ClampedArray]",Z="[object Uint16Array]",Y="[object Uint32Array]",X={} -X[x]=X[F]=X[B]=X[z]=X[I]=X[T]=X[G]=X[q]=X[V]=X[W]=X[Q]=X[k]=X[R]=X[N]=X[L]=X[U]=X[j]=X[M]=X[K]=X[$]=X[Z]=X[Y]=!0,X[A]=X[O]=X[H]=!1,e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(186),i=n(95) +X[x]=X[F]=X[z]=X[B]=X[T]=X[I]=X[G]=X[q]=X[V]=X[W]=X[Q]=X[k]=X[N]=X[R]=X[L]=X[U]=X[j]=X[M]=X[K]=X[$]=X[Z]=X[Y]=!0,X[A]=X[O]=X[H]=!1,e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(186),i=n(95) e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(186),i=n(216) @@ -1461,12 +1463,12 @@ e.exports=n},function(e,t,n){function r(e,t,n,r){var A=e.constructor switch(t){case b:return o(e) case d:case c:return new A((+e)) case C:return i(e,r) -case E:case _:case S:case w:case P:case x:case F:case I:case T:return p(e,r) +case E:case _:case S:case w:case P:case x:case F:case T:case I:return p(e,r) case f:return s(e,r,n) case h:case y:return new A(e) case m:return a(e) case g:return l(e,r,n) -case v:return u(e)}}var o=n(254),i=n(255),s=n(256),a=n(259),l=n(260),u=n(262),p=n(263),d="[object Boolean]",c="[object Date]",f="[object Map]",h="[object Number]",m="[object RegExp]",g="[object Set]",y="[object String]",v="[object Symbol]",b="[object ArrayBuffer]",C="[object DataView]",E="[object Float32Array]",_="[object Float64Array]",S="[object Int8Array]",w="[object Int16Array]",P="[object Int32Array]",x="[object Uint8Array]",F="[object Uint8ClampedArray]",I="[object Uint16Array]",T="[object Uint32Array]" +case v:return u(e)}}var o=n(254),i=n(255),s=n(256),a=n(259),l=n(260),u=n(262),p=n(263),d="[object Boolean]",c="[object Date]",f="[object Map]",h="[object Number]",m="[object RegExp]",g="[object Set]",y="[object String]",v="[object Symbol]",b="[object ArrayBuffer]",C="[object DataView]",E="[object Float32Array]",_="[object Float64Array]",S="[object Int8Array]",w="[object Int16Array]",P="[object Int32Array]",x="[object Uint8Array]",F="[object Uint8ClampedArray]",T="[object Uint16Array]",I="[object Uint32Array]" e.exports=r},function(e,t,n){function r(e){var t=new e.constructor(e.byteLength) @@ -1549,10 +1551,10 @@ Object.defineProperty(t,"__esModule",{value:!0}),t["default"]={ADD_QUEUED_FILE:" SUCCEED_UPLOAD:"SUCCEED_UPLOAD",UPDATE_QUEUED_FILE:"UPDATE_QUEUED_FILE"}},function(e,t){e.exports=Breadcrumb},function(e,t){e.exports=Toolbar},function(e,t){e.exports=SchemaActions},function(e,t,n){"use strict" -function r(e){return e&&e.__esModule?e:{"default":e}}var o=n(4),i=n(278),s=r(i),a=n(279),l=r(a),u=n(280),p=r(u),d=n(281),c=r(d),f=n(283),h=r(f),m=n(285),g=r(m),y=n(287),v=r(y),b=n(289),C=r(b),E=n(291),_=r(E),S=n(292),w=r(S),P=n(297),x=r(P),F=n(299),I=r(F) +function r(e){return e&&e.__esModule?e:{"default":e}}var o=n(4),i=n(278),s=r(i),a=n(279),l=r(a),u=n(280),p=r(u),d=n(281),c=r(d),f=n(283),h=r(f),m=n(285),g=r(m),y=n(287),v=r(y),b=n(289),C=r(b),E=n(291),_=r(E),S=n(292),w=r(S),P=n(297),x=r(P),F=n(299),T=r(F) -document.addEventListener("DOMContentLoaded",function(){_["default"].register("UploadField",w["default"]),_["default"].register("PreviewImageField",x["default"]),_["default"].register("HistoryList",I["default"]) +document.addEventListener("DOMContentLoaded",function(){_["default"].register("UploadField",w["default"]),_["default"].register("PreviewImageField",x["default"]),_["default"].register("HistoryList",T["default"]) var e=s["default"].getSection("SilverStripe\\AssetAdmin\\Controller\\AssetAdmin") @@ -1657,7 +1659,7 @@ function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t,n){return t switch(t.type){case p["default"].PREVIEWFIELD_ADD_FILE:return(0,l["default"])(s({},e,o({},t.payload.id,t.payload.file))) -case p["default"].PREVIEWFIELD_FAIL_UPLOAD:return(0,l["default"])(s({},e,o({},t.payload.id,s({},e[t.payload.id],{message:t.payload.message})))) +case p["default"].PREVIEWFIELD_FAIL_UPLOAD:return(0,l["default"])(s({},e,o({},t.payload.id,s({},e[t.payload.id],t.payload.message)))) case p["default"].PREVIEWFIELD_REMOVE_FILE:return(0,l["default"])(s({},e,o({},t.payload.id,void 0))) case p["default"].PREVIEWFIELD_UPDATE_FILE:return(0,l["default"])(s({},e,o({},t.payload.id,s({},e[t.payload.id],t.payload.data)))) default:return e}}Object.defineProperty(t,"__esModule",{value:!0}) @@ -1681,7 +1683,7 @@ var o=e.config.SecurityID return{files:r,securityId:o}}function u(e){return{actions:{uploadField:(0,y.bindActionCreators)(k,e)}}}Object.defineProperty(t,"__esModule",{value:!0}),t.ConnectedUploadField=t.UploadField=void 0 var p=Object.assign||function(e){for(var t=1;t0&&o<100?h["default"].createElement("div",{className:"preview__progress"},h["default"].createElement("div",{className:"preview__progress-bar",style:{ +width:o+"%"}})):null,s=this.props.upload.message,a=s?h["default"].createElement("div",{className:"preview__message preview__message--"+s.type},s.value):null +return h["default"].createElement("div",{className:"editor__thumbnail-container"},r||n,i,a)}},{key:"renderToolbar",value:function _(){var e=this.canEdit() return this.props.data.url||e?h["default"].createElement("div",{className:"preview__toolbar fill-height"},this.props.data.url?h["default"].createElement("a",{href:this.props.data.url,target:"_blank",className:"preview__toolbar-button--link preview__toolbar-button" -},"Open"):null,e?h["default"].createElement("a",{href:"#",id:"preview-replace-button",onClick:this.preventDefault,className:"preview__toolbar-button--replace preview__toolbar-button"},"Replace"):null):null - -}},{key:"render",value:function S(){var e=this.getDropzoneProps() -return h["default"].createElement(g["default"],e,this.renderImage(),this.renderToolbar())}}]),t}(f.Component) +},"Open"):null,e?h["default"].createElement("button",{id:"preview-replace-button",onClick:this.preventDefault,className:"preview__toolbar-button--replace preview__toolbar-button"},"Replace"):null,this.props.upload.progress||this.props.upload.message?h["default"].createElement("button",{ +onClick:this.handleCancelUpload,className:"preview__toolbar-button--remove preview__toolbar-button"},"Remove"):null):null}},{key:"render",value:function S(){var e=this.getDropzoneProps() +if(this.canEdit())return h["default"].createElement(g["default"],e,this.renderImage(),this.renderToolbar()) +var t=["preview__container",this.props.className,this.props.extraClass] +return h["default"].createElement("div",{className:t.join(" ")},this.renderImage(),this.renderToolbar())}}]),t}(f.Component) S.propTypes={id:f.PropTypes.string.isRequired,name:f.PropTypes.string,className:f.PropTypes.string,extraClass:f.PropTypes.string,readOnly:f.PropTypes.bool,disabled:f.PropTypes.bool,onAutofill:f.PropTypes.func, data:f.PropTypes.shape({parentid:f.PropTypes.number,url:f.PropTypes.string,exists:f.PropTypes.bool,preview:f.PropTypes.string,category:f.PropTypes.string,uploadFileEndpoint:f.PropTypes.shape({url:f.PropTypes.string.isRequired, -method:f.PropTypes.string.isRequired,payloadFormat:f.PropTypes.string})}).isRequired,upload:f.PropTypes.shape({url:f.PropTypes.string,progress:f.PropTypes.number,xhr:f.PropTypes.object}),actions:f.PropTypes.object, -securityID:f.PropTypes.string},S.defaultProps={extraClass:"",className:""},t["default"]=(0,b.connect)(l,u)(S)},function(e,t,n){"use strict" +method:f.PropTypes.string.isRequired,payloadFormat:f.PropTypes.string}),initialValues:f.PropTypes.object}).isRequired,upload:f.PropTypes.shape({url:f.PropTypes.string,progress:f.PropTypes.number,xhr:f.PropTypes.object, +message:f.PropTypes.shape({type:f.PropTypes.string.isRequired,value:f.PropTypes.string.isRequired})}),actions:f.PropTypes.object,securityID:f.PropTypes.string},S.defaultProps={extraClass:"",className:"" +},t["default"]=(0,b.connect)(l,u)(S)},function(e,t,n){"use strict" function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){return{type:u["default"].PREVIEWFIELD_REMOVE_FILE,payload:{id:e}}}function i(e,t){return{type:u["default"].PREVIEWFIELD_ADD_FILE,payload:{ id:e,file:t}}}function s(e,t){return{type:u["default"].PREVIEWFIELD_FAIL_UPLOAD,payload:{id:e,message:t}}}function a(e,t){return{type:u["default"].PREVIEWFIELD_UPDATE_FILE,payload:{id:e,data:t}}}Object.defineProperty(t,"__esModule",{ value:!0}),t.removeFile=o,t.addFile=i,t.failUpload=s,t.updateFile=a diff --git a/client/dist/styles/bundle.css b/client/dist/styles/bundle.css index 849dfa619..40ed20837 100644 --- a/client/dist/styles/bundle.css +++ b/client/dist/styles/bundle.css @@ -701,11 +701,9 @@ transform:translateY(-50%); height:auto; max-height:100%; - background:#000; - opacity:.55; - color:#fff; border-bottom-left-radius:.25rem; border-top-left-radius:.25rem; + overflow:hidden; } .preview__toolbar-button--link:before{ @@ -716,12 +714,19 @@ content:"b"; } +.preview__toolbar-button--remove:before{ + content:"D"; +} + .preview__toolbar-button{ padding:11px; height:47px; width:47px; overflow:hidden; + background:#000; + opacity:.55; color:#fff; + border:0; } .preview__toolbar-button:active,.preview__toolbar-button:focus,.preview__toolbar-button:hover{ @@ -741,6 +746,43 @@ vertical-align:middle; } +.preview__progress{ + height:18px; + width:75%; + position:absolute; + top:50%; + left:50%; + -webkit-transform:translateX(-50%) translateY(-50%); + transform:translateX(-50%) translateY(-50%); + border-radius:10px; + overflow:hidden; + background:#e6e6e6; +} + +.preview__progress-bar{ + height:100%; + background:#29abe2; + width:0; +} + +.preview__message{ + position:absolute; + bottom:0; + left:0; + right:0; + opacity:.8; +} + +.preview__message--error{ + color:#fff; + background:#d40404; +} + +.preview__message--success{ + color:#fff; + background:#3fa142; +} + .gallery,.gallery__main,.gallery__outer{ position:relative; } diff --git a/client/src/components/AssetDropzone/AssetDropzone.js b/client/src/components/AssetDropzone/AssetDropzone.js index f2e65916b..869ba89ec 100644 --- a/client/src/components/AssetDropzone/AssetDropzone.js +++ b/client/src/components/AssetDropzone/AssetDropzone.js @@ -265,8 +265,14 @@ class AssetDropzone extends SilverStripeComponent { formData.append('SecurityID', this.props.securityID); formData.append('ParentID', this.props.folderId); + const newXhr = Object.assign({}, xhr, { + abort: () => { + this.dropzone.cancelUpload(file); + xhr.abort(); + }, + }); if (typeof this.props.handleSending === 'function') { - this.props.handleSending(file, xhr, formData); + this.props.handleSending(file, newXhr, formData); } } @@ -292,7 +298,7 @@ class AssetDropzone extends SilverStripeComponent { this.props.handleMaxFilesExceeded(file); } // shouldn't return error, as there isn't a way to catch it... - return; + return Promise.resolve(); } if (!this.props.canUpload) { @@ -418,28 +424,34 @@ class AssetDropzone extends SilverStripeComponent { /** * Event handler for failed uploads. * - * @param file (object) - File interface. See https://developer.mozilla.org/en-US/docs/Web/API/File - * @param errorMessage (string) + * @param {object} file - File interface. See https://developer.mozilla.org/en-US/docs/Web/API/File + * @param {string} message */ - handleError(file, messages) { + handleError(file, message) { + // remove files list, as they are no longer needed + this.dropzone.removeAllFiles(); + if (typeof this.props.handleError === 'function') { - this.props.handleError(file, messages); + this.props.handleError(file, message); } } /** * Event handler for successfully upload files. * - * @param object file - File interface. See https://developer.mozilla.org/en-US/docs/Web/API/File + * @param {object} file - File interface. See https://developer.mozilla.org/en-US/docs/Web/API/File */ handleSuccess(file) { + // remove files list, as they are no longer needed + this.dropzone.removeAllFiles(); + this.props.handleSuccess(file); } /** * Set the text displayed when a user tries to remove a file. * - * @param string userPrompt - The message to display. + * @param {string} userPrompt - The message to display. */ setPromptOnRemove(userPrompt) { this.dropzone.options.dictRemoveFileConfirmation = userPrompt; diff --git a/client/src/components/GalleryItem/GalleryItem.js b/client/src/components/GalleryItem/GalleryItem.js index 297e42e71..6180e366d 100644 --- a/client/src/components/GalleryItem/GalleryItem.js +++ b/client/src/components/GalleryItem/GalleryItem.js @@ -64,8 +64,8 @@ class GalleryItem extends SilverStripeComponent { hasError() { let hasError = false; - if (this.props.message) { - hasError = this.props.message.type === 'error'; + if (this.props.item.message) { + hasError = this.props.item.message.type === 'error'; } return hasError; @@ -80,7 +80,7 @@ class GalleryItem extends SilverStripeComponent { let message = null; if (this.hasError()) { - message = this.props.message.value; + message = this.props.item.message.value; } else if (!this.exists() && !this.uploading()) { message = i18n._t('AssetAdmin.FILE_MISSING', 'File cannot be found'); } @@ -252,9 +252,9 @@ class GalleryItem extends SilverStripeComponent { event.stopPropagation(); if (this.hasError()) { - this.props.handleRemoveErroredUpload(this.props.item); + this.props.onRemoveErroredUpload(this.props.item); } else { - this.props.handleCancelUpload(this.props.item); + this.props.onCancelUpload(this.props.item); } } diff --git a/client/src/components/PreviewImageField/PreviewImageField.js b/client/src/components/PreviewImageField/PreviewImageField.js index 6ecd40a3f..9662a444a 100644 --- a/client/src/components/PreviewImageField/PreviewImageField.js +++ b/client/src/components/PreviewImageField/PreviewImageField.js @@ -91,6 +91,15 @@ class PreviewImageField extends Component { * Handles removing an upload that had errored during/after upload */ handleRemoveErroredUpload() { + // revert to initial values so errored or replaced replacement doesn't get used + if (typeof this.props.onAutofill === 'function') { + const initial = this.props.data.initialValues; + + this.props.onAutofill('FileFilename', initial.FileFilename); + this.props.onAutofill('FileHash', initial.FileHash); + this.props.onAutofill('FileVariant', initial.FileVariant); + } + this.props.actions.previewField.removeFile(this.props.id); } @@ -160,15 +169,31 @@ class PreviewImageField extends Component { const preview = this.props.upload.url || data.preview || data.url; const image = ; + const linkedImage = (data.url) ? ( + + {image} + + ) : null; + const progress = this.props.upload.progress; + const progressBar = (progress > 0 && progress < 100) ? ( +
+
+
+ ) : null; + const message = this.props.upload.message; + const messageBox = (message) ? ( +
+ {message.value} +
+ ) : null; - if (data.url) { - return ( - - {image} - - ); - } - return image; + return ( +
+ {linkedImage || image} + {progressBar} + {messageBox} +
+ ); } renderToolbar() { @@ -178,8 +203,7 @@ class PreviewImageField extends Component { } return (
- { (this.props.data.url) - ? ( + { (this.props.data.url) ? ( Open ) : null } - { (canEdit) - ? ( - Replace + >Replace ) : null } + { (this.props.upload.progress || this.props.upload.message) ? ( + + ) : null }
); } @@ -204,12 +232,27 @@ class PreviewImageField extends Component { render() { const dropzoneProps = this.getDropzoneProps(); - return ( - - {this.renderImage()} - {this.renderToolbar()} - - ); + if (this.canEdit()) { + return ( + + {this.renderImage()} + {this.renderToolbar()} + + ); + } else { + const classNames = [ + 'preview__container', + this.props.className, + this.props.extraClass, + ]; + + return ( +
+ {this.renderImage()} + {this.renderToolbar()} +
+ ); + } } } @@ -232,11 +275,16 @@ PreviewImageField.propTypes = { method: PropTypes.string.isRequired, payloadFormat: PropTypes.string, }), + initialValues: PropTypes.object, }).isRequired, upload: PropTypes.shape({ url: PropTypes.string, progress: PropTypes.number, xhr: PropTypes.object, + message: PropTypes.shape({ + type: PropTypes.string.isRequired, + value: PropTypes.string.isRequired, + }), }), actions: PropTypes.object, securityID: PropTypes.string, diff --git a/client/src/components/PreviewImageField/PreviewImageField.scss b/client/src/components/PreviewImageField/PreviewImageField.scss index bcffe082d..ca049fa74 100644 --- a/client/src/components/PreviewImageField/PreviewImageField.scss +++ b/client/src/components/PreviewImageField/PreviewImageField.scss @@ -9,11 +9,9 @@ transform: translateY(-50%); height: auto; max-height: 100%; - background: $black; - opacity: 0.55; - color: $white; border-bottom-left-radius: $border-radius; border-top-left-radius: $border-radius; + overflow: hidden; } .preview__toolbar-button--link:before { @@ -24,12 +22,20 @@ content: "\62"; } +.preview__toolbar-button--remove:before { + content: "\44"; +} + .preview__toolbar-button { padding: $toolbar-button-padding; height: $toolbar-button-height + (2 * $toolbar-button-padding); width: $toolbar-button-width + (2 * $toolbar-button-padding); overflow: hidden; color: $white; + background: $black; + opacity: 0.55; + color: $white; + border: 0; &:hover, &:active, @@ -50,3 +56,40 @@ vertical-align: middle; } } + + +.preview__progress { + height: 18px; + width: 75%; + position: absolute; + top: 50%; + left: 50%; + transform: translateX(-50%) translateY(-50%); + border-radius: 10px; + overflow: hidden; + background: darken($white, 10%); +} + +.preview__progress-bar { + height: 100%; + background: $brand-primary; + width: 0; +} + +.preview__message { + position: absolute; + bottom: 0; + left: 0; + right: 0; + opacity: .8; +} + +.preview__message--error { + color: $white; + background: $brand-danger; +} + +.preview__message--success { + color: $white; + background: $brand-success; +} diff --git a/client/src/state/previewField/PreviewFieldReducer.js b/client/src/state/previewField/PreviewFieldReducer.js index c2bf44350..f14a75f2f 100644 --- a/client/src/state/previewField/PreviewFieldReducer.js +++ b/client/src/state/previewField/PreviewFieldReducer.js @@ -13,9 +13,7 @@ function previewFieldReducer(state = initialState, action) { case ACTION_TYPES.PREVIEWFIELD_FAIL_UPLOAD: { return deepFreeze(Object.assign({}, state, { - [action.payload.id]: Object.assign({}, state[action.payload.id], { - message: action.payload.message, - }), + [action.payload.id]: Object.assign({}, state[action.payload.id], action.payload.message), })); } diff --git a/code/Forms/PreviewImageField.php b/code/Forms/PreviewImageField.php index d533bcff3..cddcee3c0 100644 --- a/code/Forms/PreviewImageField.php +++ b/code/Forms/PreviewImageField.php @@ -45,6 +45,11 @@ public function getSchemaStateDefaults() 'exists' => $record->exists(), 'preview' => $record->PreviewLink(), 'category' => $record instanceof Folder ? 'folder' : $record->appCategory(), + 'initialValues' => [ + 'FileFilename' => $record->FileFilename, + 'FileHash' => $record->FileHash, + 'FileVariant' => $record->FileVariant, + ], ]); } return $defaults; From 5839a1b7bc3b76ef66485a6c3f9db23ca80abccb Mon Sep 17 00:00:00 2001 From: Christopher Joe Date: Fri, 16 Dec 2016 09:55:05 +1300 Subject: [PATCH 04/13] Enhancement unit test hasError in GalleryItem Enhancement unit tests for AssetDropzone --- client/dist/js/bundle.js | 6 +- .../components/AssetDropzone/AssetDropzone.js | 4 +- .../AssetDropzone/tests/AssetDropzone-test.js | 92 ++++++++++++++++++- .../GalleryItem/tests/GalleryItem-test.js | 24 +++++ code/Forms/FileFormFactory.php | 3 - code/Forms/PreviewImageField.php | 4 +- 6 files changed, 121 insertions(+), 12 deletions(-) diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index b17f60378..6bc452ecb 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -318,10 +318,10 @@ return/[.]/.exec(e)?e.replace(/^.+[.]/,""):""}},{key:"loadImage",value:function return new Promise(function(r){e.onload=function(){var t=document.createElement("canvas"),o=t.getContext("2d"),i=2*n.props.preview.width,s=2*n.props.preview.height,a=e.naturalWidth/e.naturalHeight e.naturalWidth { options: { url: 'upload', }, - handleAddedFile: () => null, - handleError: () => null, - handleSuccess: () => null, + handleAddedFile: jest.genMockFunction(), + handleError: jest.genMockFunction(), + handleSuccess: jest.genMockFunction(), folderId: 1, securityID: '123', canUpload: true, @@ -43,6 +43,92 @@ describe('AssetDropzone', () => { }); }); + describe('handleError()', () => { + it('should remove the file in dropzone', () => { + const item = ReactTestUtils.renderIntoDocument( + + ); + item.dropzone = { + removeFile: jest.genMockFunction(), + }; + const file = {}; + + item.handleError(file, ''); + expect(item.dropzone.removeFile).toBeCalledWith(file); + expect(props.handleError).toBeCalledWith(file, ''); + }); + }); + + describe('handleSuccess()', () => { + it('should remove the file in dropzone', () => { + const item = ReactTestUtils.renderIntoDocument( + + ); + item.dropzone = { + removeFile: jest.genMockFunction(), + }; + const file = {}; + + item.handleSuccess(file); + expect(item.dropzone.removeFile).toBeCalledWith(file); + expect(props.handleSuccess).toBeCalledWith(file); + }); + }); + + describe('props.maxFiles', () => { + let item = null; + + beforeEach(() => { + props.handleMaxFilesExceeded = jest.genMockFunction(); + props.options.maxFiles = 2; + item = ReactTestUtils.renderIntoDocument( + + ); + }); + + it('should not remove anything if not exceeded', () => { + item.dropzone = { + files: ['a'], + removeFile: jest.genMockFunction(), + }; + + item.handleAddedFile({}); + + expect(item.dropzone.removeFile).not.toBeCalled(); + expect(props.handleMaxFilesExceeded).not.toBeCalled(); + }); + + it('should remove the first file if exceeded and trigger callback', () => { + item.dropzone = { + files: ['a', 'b', 'c'], + removeFile: jest.genMockFunction(), + }; + + item.handleAddedFile({}); + + expect(item.dropzone.removeFile).toBeCalled(); + expect(props.handleMaxFilesExceeded).toBeCalled(); + }); + }); + + describe('xhr.abort()', () => { + it('should call dropzone.cancelUpload() when abort is called', () => { + props.handleSending = (file, xhr) => { + xhr.abort(); + }; + const item = ReactTestUtils.renderIntoDocument( + + ); + item.dropzone = { + cancelUpload: jest.genMockFunction(), + }; + + item.handleSending({}, { abort: () => null }, new FormData()); + + expect(item.dropzone.cancelUpload).toBeCalled(); + }); + }); + describe('componentDidMount()', () => { let item = null; diff --git a/client/src/components/GalleryItem/tests/GalleryItem-test.js b/client/src/components/GalleryItem/tests/GalleryItem-test.js index 0cd82e2cb..c993b1b67 100644 --- a/client/src/components/GalleryItem/tests/GalleryItem-test.js +++ b/client/src/components/GalleryItem/tests/GalleryItem-test.js @@ -30,6 +30,30 @@ describe('GalleryItem', () => { }; }); + describe('hasError()', () => { + let item = null; + + beforeEach(() => { + item = ReactTestUtils.renderIntoDocument( + + ); + }); + + it('should give an error if message type is "error"', () => { + props.item.message = { type: 'error', value: '' }; + const error = item.hasError(); + + expect(error).toBe(true); + }); + + it('should give no error if message type is "success"', () => { + props.item.message = { type: 'success', value: '' }; + const error = item.hasError(); + + expect(error).toBe(false); + }); + }); + describe('handleActivate()', () => { let item = null; let event = null; diff --git a/code/Forms/FileFormFactory.php b/code/Forms/FileFormFactory.php index 8c747092c..a1f6f4e95 100644 --- a/code/Forms/FileFormFactory.php +++ b/code/Forms/FileFormFactory.php @@ -2,14 +2,11 @@ namespace SilverStripe\AssetAdmin\Forms; -use SilverStripe\AssetAdmin\Controller\AssetAdmin; use SilverStripe\Assets\File; -use SilverStripe\Assets\Folder; use SilverStripe\Control\Controller; use SilverStripe\Forms\DatetimeField; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FormAction; -use SilverStripe\Forms\FormField; use SilverStripe\Forms\HiddenField; use SilverStripe\Forms\LiteralField; use SilverStripe\Forms\PopoverField; diff --git a/code/Forms/PreviewImageField.php b/code/Forms/PreviewImageField.php index cddcee3c0..56e249c4c 100644 --- a/code/Forms/PreviewImageField.php +++ b/code/Forms/PreviewImageField.php @@ -9,7 +9,7 @@ use SilverStripe\ORM\DataObject; /** - * + * For providing schema data to the client side to build a preview field with upload replacement feature */ class PreviewImageField extends FormField { @@ -36,9 +36,11 @@ public function getSchemaDataDefaults() public function getSchemaStateDefaults() { $defaults = parent::getSchemaStateDefaults(); + /** @var File $record */ if ($record = $this->getRecord()) { $parent = $record->Parent(); + $defaults['data'] = array_merge_recursive($defaults['data'], [ 'parentid' => ($parent) ? $parent->ID : 0, 'url' => $record->Link(), From 9b3acc4d03111dbb98f5b1576936289d7c1a3e83 Mon Sep 17 00:00:00 2001 From: Christopher Joe Date: Fri, 16 Dec 2016 12:25:33 +1300 Subject: [PATCH 05/13] FIX remove count when unloading folder --- client/dist/js/bundle.js | 2 +- client/src/state/gallery/GalleryReducer.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 6bc452ecb..001b0cdae 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -1572,7 +1572,7 @@ if(r){var o=function(){var n=s({},r,t.payload.file) return{v:(0,l["default"])(s({},e,{files:e.files.map(function(e){return e.id===n.id?n:e})}))}}() if("object"===("undefined"==typeof o?"undefined":i(o)))return o.v}else if(e.folder.id===t.payload.id)return(0,l["default"])(s({},e,{folder:s({},e.folder,t.payload.file)})) return e -case p["default"].UNLOAD_FOLDER:return s({},e,{files:[]}) +case p["default"].UNLOAD_FOLDER:return s({},e,{files:[],count:0}) case p["default"].SELECT_FILES:var a=null return a=null===t.payload.ids?e.files.map(function(e){return e.id}):e.selectedFiles.concat(t.payload.ids.filter(function(t){return e.selectedFiles.indexOf(t)===-1})),(0,l["default"])(s({},e,{selectedFiles:a })) diff --git a/client/src/state/gallery/GalleryReducer.js b/client/src/state/gallery/GalleryReducer.js index ae13c11b8..4a2ef3516 100644 --- a/client/src/state/gallery/GalleryReducer.js +++ b/client/src/state/gallery/GalleryReducer.js @@ -71,6 +71,7 @@ export default function galleryReducer(state = initialState, action) { case GALLERY.UNLOAD_FOLDER: { return Object.assign({}, state, { files: [], + count: 0, }); } From b49af82cd7e0b2399d17e0f6d5b1931ad9e4f715 Mon Sep 17 00:00:00 2001 From: Christopher Joe Date: Fri, 16 Dec 2016 16:41:03 +1300 Subject: [PATCH 06/13] Enhancement Added extension check to File replacement and warn if it is different API Added callback for validation checking a file that was added to AssetDropzone Enhancement Added unit tests for GalleryItem, PreviewImageField and PreviewFieldReducer --- client/dist/js/bundle.js | 483 +++++++++--------- .../components/AssetDropzone/AssetDropzone.js | 17 +- .../GalleryItem/tests/GalleryItem-test.js | 28 + .../PreviewImageField/PreviewImageField.js | 60 ++- .../components/PreviewImageField/README.md | 37 ++ .../tests/PreviewImageField-test.js | 159 ++++++ client/src/constants/index.js | 1 + .../tests/PreviewFieldReducer-test.js | 80 +++ 8 files changed, 606 insertions(+), 259 deletions(-) create mode 100644 client/src/components/PreviewImageField/README.md create mode 100644 client/src/components/PreviewImageField/tests/PreviewImageField-test.js create mode 100644 client/src/state/previewField/tests/PreviewFieldReducer-test.js diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 001b0cdae..055132a67 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -165,9 +165,8 @@ className:"font-icon-trash",destructive:!0,callback:null,confirm:function(e){fun return e=confirm(t)?Promise.resolve():Promise.reject()})},{value:"edit",label:i["default"]._t("AssetAdmin.BULK_ACTIONS_EDIT","Edit"),className:"font-icon-edit",destructive:!1,canApply:function s(e){return 1===e.length -},callback:null}],BULK_ACTIONS_PLACEHOLDER:i["default"]._t("AssetAdmin.BULK_ACTIONS_PLACEHOLDER"),SPACE_KEY_CODE:32,RETURN_KEY_CODE:13}},function(e,t){e.exports=FormBuilderLoader},function(e,t){e.exports=FormBuilderModal - -},function(e,t,n){"use strict" +},callback:null}],BULK_ACTIONS_PLACEHOLDER:i["default"]._t("AssetAdmin.BULK_ACTIONS_PLACEHOLDER"),SPACE_KEY_CODE:32,RETURN_KEY_CODE:13,DEFAULT_PREVIEW:"framework/client/dist/images/app_icons/generic_92.png" +}},function(e,t){e.exports=FormBuilderLoader},function(e,t){e.exports=FormBuilderModal},function(e,t,n){"use strict" function r(e){if(e&&e.__esModule)return e var t={} if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]) @@ -183,7 +182,7 @@ return{loading:n,count:r,files:o,selectedFiles:i,errorMessage:s,queuedFiles:e.as queuedFiles:(0,x.bindActionCreators)(H,e)}}}Object.defineProperty(t,"__esModule",{value:!0}),t.galleryViewDefaultProps=t.galleryViewPropTypes=t.sorters=t.Gallery=void 0 var d=Object.assign||function(e){for(var t=1;t-1}},{key:"itemIsHighlighted",value:function M(e){return this.props.fileId===e}},{key:"handleOpenFolder",value:function H(e,t){ e.preventDefault(),this.props.onOpenFolder(t.id)}},{key:"handleOpenFile",value:function B(e,t){e.preventDefault(),null!==t.created&&this.props.onOpenFile(t.id,t)}},{key:"handleSelect",value:function G(e,t){ @@ -231,9 +230,9 @@ return t===e.props.view?null:(o.push("font-icon-"+r),v["default"].createElement( value:function $(){var e=["btn","btn-secondary","btn--no-text","font-icon-level-up","btn--icon-large","gallery__back"].join(" ") return null!==this.props.folder.parentID?v["default"].createElement("button",{className:e,onClick:this.handleBackClick,ref:"backButton"}):null}},{key:"renderBulkActions",value:function Z(){var e=this,t=function i(t){ var n=t.map(function(e){return e.id}) -e.props.actions.gallery.deleteItems(e.props.deleteApi,n)},n=function s(t){e.props.onOpenFile(t[0].id)},r=L["default"].BULK_ACTIONS.map(function(e){return"delete"!==e.value||e.callback?"edit"!==e.value||e.callback?e:d({},e,{ +e.props.actions.gallery.deleteItems(e.props.deleteApi,n)},n=function s(t){e.props.onOpenFile(t[0].id)},r=U["default"].BULK_ACTIONS.map(function(e){return"delete"!==e.value||e.callback?"edit"!==e.value||e.callback?e:d({},e,{ callback:n}):d({},e,{callback:t})}),o=this.props.selectedFiles.map(function(t){return e.props.files.find(function(e){return t===e.id})}) -return o.length>0&&"admin"===this.props.type?v["default"].createElement(w["default"],{transitionName:"bulk-actions",transitionEnterTimeout:L["default"].CSS_TRANSITION_TIME,transitionLeaveTimeout:L["default"].CSS_TRANSITION_TIME +return o.length>0&&"admin"===this.props.type?v["default"].createElement(w["default"],{transitionName:"bulk-actions",transitionEnterTimeout:U["default"].CSS_TRANSITION_TIME,transitionLeaveTimeout:U["default"].CSS_TRANSITION_TIME },v["default"].createElement(A["default"],{actions:r,items:o,key:o.length>0})):null}},{key:"renderNoItemsNotice",value:function Y(){return 0!==this.props.files.length||this.props.loading?null:v["default"].createElement("p",{ className:"gallery__no-item-notice"},g["default"]._t("AssetAdmin.NOITEMSFOUND"))}},{key:"renderGalleryView",value:function X(){var e=this,t="table"===this.props.view?N["default"]:D["default"],n=this.props.files.map(function(t){ return d({},t,{selected:e.itemIsSelected(t.id),highlighted:e.itemIsHighlighted(t.id)})}),r=this.props.queuedFiles.items.map(function(e){return d({},e,{uploading:!0})}),o=[].concat(i(r),i(n)),s=this.props,a=s.type,l=s.loading,u=s.page,p=s.count,c=s.limit,f=s.sort,h={ @@ -243,7 +242,7 @@ return v["default"].createElement(t,h)}},{key:"render",value:function J(){if(!th className:"gallery__error-message"},v["default"].createElement("h3",null,this.props.errorMessage&&g["default"]._t("AssetAdmin.DROPZONE_RESPONSE_ERROR","Server responded with an error.")),v["default"].createElement("p",null,this.props.errorMessage))):null -var e={height:L["default"].THUMBNAIL_HEIGHT,width:L["default"].THUMBNAIL_WIDTH},t={url:this.props.createFileApiUrl,method:this.props.createFileApiMethod,paramName:"Upload",clickable:"#upload-button"},n=this.props.securityId,r=this.props.folder.canEdit,o=["panel","panel--padded","panel--scrollable","gallery__main"] +var e={height:U["default"].THUMBNAIL_HEIGHT,width:U["default"].THUMBNAIL_WIDTH},t={url:this.props.createFileApiUrl,method:this.props.createFileApiMethod,paramName:"Upload",clickable:"#upload-button"},n=this.props.securityId,r=this.props.folder.canEdit,o=["panel","panel--padded","panel--scrollable","gallery__main"] return"insert"===this.props.type&&o.push("insert-media-modal__main"),v["default"].createElement("div",{className:"flexbox-area-grow gallery__outer"},this.renderBulkActions(),v["default"].createElement(T["default"],{ @@ -268,14 +267,14 @@ e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,wri value:!0}) var a=Object.assign||function(e){for(var t=1;tthis.props.options.maxFiles)return this.dropzone.removeFile(this.dropzone.files[0]),"function"==typeof this.props.handleMaxFilesExceeded&&this.props.handleMaxFilesExceeded(e), Promise.resolve() -if(!this.props.canUpload)return Promise.reject(new Error(y["default"]._t("AssetAdmin.DROPZONE_CANNOT_UPLOAD"))) +if("function"==typeof this.props.canFileUpload&&!this.props.canFileUpload(e))return this.dropzone.removeFile(e),Promise.resolve() +if(!this.props.canUpload)return this.dropzone.removeFile(e),Promise.reject(new Error(y["default"]._t("AssetAdmin.DROPZONE_CANNOT_UPLOAD"))) e._queuedId=this.generateQueuedId() var n=new Promise(function(n){var r=new FileReader r.onload=function(r){if("image"===t.getFileCategory(e.type)){var o=document.createElement("img") n(t.loadImage(o,r.target.result))}else n({})},r.readAsDataURL(e)}) -return n.then(function(n){var r={dimensions:{height:n.height,width:n.width},category:t.getFileCategory(e.type),filename:e.name,queuedId:e._queuedId,size:e.size,title:t.getFileTitle(e.name),extension:t.getFileExtension(e.name), -type:e.type,url:n.thumbnailURL} -return t.props.handleAddedFile(r),t.dropzone.processFile(e),r})}},{key:"getFileTitle",value:function x(e){return e.replace(/[.][^.]+$/,"").replace(/-_/," ")}},{key:"getFileExtension",value:function F(e){ -return/[.]/.exec(e)?e.replace(/^.+[.]/,""):""}},{key:"loadImage",value:function T(e,t){var n=this +return n.then(function(n){var r={dimensions:{height:n.height,width:n.width},category:t.getFileCategory(e.type),filename:e.name,queuedId:e._queuedId,size:e.size,title:t.getFileTitle(e.name),extension:(0, +_.getFileExtension)(e.name),type:e.type,url:n.thumbnailURL} +return t.props.handleAddedFile(r),t.dropzone.processFile(e),r})}},{key:"getFileTitle",value:function F(e){return e.replace(/[.][^.]+$/,"").replace(/-_/," ")}},{key:"loadImage",value:function T(e,t){var n=this + + return new Promise(function(r){e.onload=function(){var t=document.createElement("canvas"),o=t.getContext("2d"),i=2*n.props.preview.width,s=2*n.props.preview.height,a=e.naturalWidth/e.naturalHeight e.naturalWidth=U;p=0<=U?++O:--O)i.append(this._getParamName(p),e[p],this._renameFilename(e[p].name)) +else(!f||"checkbox"!==(U=f.toLowerCase())&&"radio"!==U||d.checked)&&i.append(c,d.value) +for(p=O=0,L=e.length-1;0<=L?O<=L:O>=L;p=0<=L?++O:--O)i.append(this._getParamName(p),e[p],this._renameFilename(e[p].name)) return this.submitRequest(_,i,e)},t.prototype.submitRequest=function(e,t,n){return e.send(t)},t.prototype._finished=function(e,n,r){var o,i,s for(i=0,s=e.length;i-1}var o=n(42) +return n<0?void 0:t[n][1]}var o=n(43) +e.exports=r},function(e,t,n){function r(e){return o(this.__data__,e)>-1}var o=n(43) e.exports=r},function(e,t,n){function r(e,t){var n=this.__data__,r=o(n,e) -return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}var o=n(42) -e.exports=r},function(e,t,n){function r(){this.__data__=new o,this.size=0}var o=n(39) +return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}var o=n(43) +e.exports=r},function(e,t,n){function r(){this.__data__=new o,this.size=0}var o=n(40) e.exports=r},function(e,t){function n(e){var t=this.__data__,n=t["delete"](e) return this.size=t.size,n}e.exports=n},function(e,t){function n(e){return this.__data__.get(e)}e.exports=n},function(e,t){function n(e){return this.__data__.has(e)}e.exports=n},function(e,t,n){function r(e,t){ var n=this.__data__ if(n instanceof o){var r=n.__data__ if(!i||r.length-1&&e%1==0&&e-1&&e%1==0&&e<=r }var r=9007199254740991 -e.exports=n},function(e,t){function n(e){return function(t){return e(t)}}e.exports=n},function(e,t,n){(function(e){var r=n(59),o="object"==typeof t&&t&&!t.nodeType&&t,i=o&&"object"==typeof e&&e&&!e.nodeType&&e,s=i&&i.exports===o,a=s&&r.process,l=function(){ +e.exports=n},function(e,t){function n(e){return function(t){return e(t)}}e.exports=n},function(e,t,n){(function(e){var r=n(60),o="object"==typeof t&&t&&!t.nodeType&&t,i=o&&"object"==typeof e&&e&&!e.nodeType&&e,s=i&&i.exports===o,a=s&&r.process,l=function(){ try{return a&&a.binding&&a.binding("util")}catch(e){}}() e.exports=l}).call(t,n(24)(e))},function(e,t,n){function r(e){if(!o(e))return i(e) var t=[] for(var n in Object(e))a.call(e,n)&&"constructor"!=n&&t.push(n) -return t}var o=n(111),i=n(112),s=Object.prototype,a=s.hasOwnProperty +return t}var o=n(112),i=n(113),s=Object.prototype,a=s.hasOwnProperty e.exports=r},function(e,t){function n(e){var t=e&&e.constructor,n="function"==typeof t&&t.prototype||r return e===n}var r=Object.prototype -e.exports=n},function(e,t,n){var r=n(113),o=r(Object.keys,Object) -e.exports=o},function(e,t){function n(e,t){return function(n){return e(t(n))}}e.exports=n},function(e,t,n){function r(e){return null!=e&&i(e.length)&&!o(e)}var o=n(55),i=n(107) -e.exports=r},function(e,t,n){var r=n(116),o=n(52),i=n(117),s=n(118),a=n(119),l=n(56),u=n(65),p="[object Map]",d="[object Object]",c="[object Promise]",f="[object Set]",h="[object WeakMap]",m="[object DataView]",g=u(r),y=u(o),v=u(i),b=u(s),C=u(a),E=l +e.exports=n},function(e,t,n){var r=n(114),o=r(Object.keys,Object) +e.exports=o},function(e,t){function n(e,t){return function(n){return e(t(n))}}e.exports=n},function(e,t,n){function r(e){return null!=e&&i(e.length)&&!o(e)}var o=n(56),i=n(108) +e.exports=r},function(e,t,n){var r=n(117),o=n(53),i=n(118),s=n(119),a=n(120),l=n(57),u=n(66),p="[object Map]",d="[object Object]",c="[object Promise]",f="[object Set]",h="[object WeakMap]",m="[object DataView]",g=u(r),y=u(o),v=u(i),b=u(s),C=u(a),E=l ;(r&&E(new r(new ArrayBuffer(1)))!=m||o&&E(new o)!=p||i&&E(i.resolve())!=c||s&&E(new s)!=f||a&&E(new a)!=h)&&(E=function(e){var t=l(e),n=t==d?e.constructor:void 0,r=n?u(n):"" if(r)switch(r){case g:return m case y:return p case v:return c case b:return f -case C:return h}return t}),e.exports=E},function(e,t,n){var r=n(53),o=n(58),i=r(o,"DataView") -e.exports=i},function(e,t,n){var r=n(53),o=n(58),i=r(o,"Promise") -e.exports=i},function(e,t,n){var r=n(53),o=n(58),i=r(o,"Set") -e.exports=i},function(e,t,n){var r=n(53),o=n(58),i=r(o,"WeakMap") +case C:return h}return t}),e.exports=E},function(e,t,n){var r=n(54),o=n(59),i=r(o,"DataView") +e.exports=i},function(e,t,n){var r=n(54),o=n(59),i=r(o,"Promise") +e.exports=i},function(e,t,n){var r=n(54),o=n(59),i=r(o,"Set") +e.exports=i},function(e,t,n){var r=n(54),o=n(59),i=r(o,"WeakMap") e.exports=i},function(e,t,n){function r(e){for(var t=i(e),n=t.length;n--;){var r=t[n],s=e[r] -t[n]=[r,s,o(s)]}return t}var o=n(121),i=n(95) -e.exports=r},function(e,t,n){function r(e){return e===e&&!o(e)}var o=n(62) +t[n]=[r,s,o(s)]}return t}var o=n(122),i=n(96) +e.exports=r},function(e,t,n){function r(e){return e===e&&!o(e)}var o=n(63) e.exports=r},function(e,t){function n(e,t){return function(n){return null!=n&&(n[e]===t&&(void 0!==t||e in Object(n)))}}e.exports=n},function(e,t,n){function r(e,t){return a(e)&&l(t)?u(p(e),t):function(n){ var r=i(n,e) -return void 0===r&&r===t?s(n,e):o(t,r,d|c)}}var o=n(82),i=n(124),s=n(135),a=n(127),l=n(121),u=n(122),p=n(134),d=1,c=2 +return void 0===r&&r===t?s(n,e):o(t,r,d|c)}}var o=n(83),i=n(125),s=n(136),a=n(128),l=n(122),u=n(123),p=n(135),d=1,c=2 e.exports=r},function(e,t,n){function r(e,t,n){var r=null==e?void 0:o(e,t) -return void 0===r?n:r}var o=n(125) +return void 0===r?n:r}var o=n(126) e.exports=r},function(e,t,n){function r(e,t){t=o(t,e) for(var n=0,r=t.length;null!=e&&n-1?a[l?t[u]:u]:void 0}}var o=n(35),i=n(114),s=n(95) +return u>-1?a[l?t[u]:u]:void 0}}var o=n(36),i=n(115),s=n(96) e.exports=r},function(e,t,n){function r(e,t,n){var r=null==e?0:e.length if(!r)return-1 var l=null==n?0:s(n) -return l<0&&(l=a(r+l,0)),o(e,i(t,3),l)}var o=n(154),i=n(35),s=n(155),a=Math.max +return l<0&&(l=a(r+l,0)),o(e,i(t,3),l)}var o=n(155),i=n(36),s=n(156),a=Math.max e.exports=r},function(e,t){function n(e,t,n,r){for(var o=e.length,i=n+(r?1:-1);r?i--:++i1&&s(e,t[0],t[1])?t=[]:n>2&&s(t[0],t[1],t[2])&&(t=[t[0]]),o(e,r(t,1),[])}) e.exports=a},function(e,t,n){function r(e,t,n,s,a){var l=-1,u=e.length for(n||(n=i),a||(a=[]);++l0&&n(p)?t>1?r(p,t-1,n,s,a):o(a,p):s||(a[a.length]=p)}return a}var o=n(160),i=n(161) +t>0&&n(p)?t>1?r(p,t-1,n,s,a):o(a,p):s||(a[a.length]=p)}return a}var o=n(161),i=n(162) e.exports=r},function(e,t){function n(e,t){for(var n=-1,r=t.length,o=e.length;++n=l)return u var p=n[r] -return u*("desc"==p?-1:1)}}return e.index-t.index}var o=n(165) +return u*("desc"==p?-1:1)}}return e.index-t.index}var o=n(166) e.exports=r},function(e,t,n){function r(e,t){if(e!==t){var n=void 0!==e,r=null===e,i=e===e,s=o(e),a=void 0!==t,l=null===t,u=t===t,p=o(t) if(!l&&!p&&!s&&e>t||s&&a&&u&&!l&&!p||r&&a&&u||!n&&u||!i)return 1 -if(!r&&!s&&!p&&e0){if(++t>=r)return arguments[0]}else t=0 return e.apply(void 0,arguments)}}var r=800,o=16,i=Date.now e.exports=n},function(e,t,n){function r(e,t,n){if(!a(n))return!1 var r=typeof t -return!!("number"==r?i(n)&&s(t,n.length):"string"==r&&t in n)&&o(n[t],e)}var o=n(43),i=n(114),s=n(104),a=n(62) -e.exports=r},function(e,t,n){var r=n(176),o=n(159),i=n(166),s=n(182),a=i(function(e,t){return s(e)?r(e,o(t,1,s,!0)):[]}) +return!!("number"==r?i(n)&&s(t,n.length):"string"==r&&t in n)&&o(n[t],e)}var o=n(44),i=n(115),s=n(105),a=n(63) +e.exports=r},function(e,t,n){var r=n(177),o=n(160),i=n(167),s=n(183),a=i(function(e,t){return s(e)?r(e,o(t,1,s,!0)):[]}) e.exports=a},function(e,t,n){function r(e,t,n,r){var d=-1,c=i,f=!0,h=e.length,m=[],g=t.length if(!h)return m n&&(t=a(t,l(n))),r?(c=s,f=!1):t.length>=p&&(c=u,f=!1,t=new o(t)) e:for(;++d-1}var o=n(178) -e.exports=r},function(e,t,n){function r(e,t,n){return t===t?s(e,t,n):o(e,i,n)}var o=n(154),i=n(179),s=n(180) +return!!n&&o(e,t,0)>-1}var o=n(179) +e.exports=r},function(e,t,n){function r(e,t,n){return t===t?s(e,t,n):o(e,i,n)}var o=n(155),i=n(180),s=n(181) e.exports=r},function(e,t){function n(e){return e!==e}e.exports=n},function(e,t){function n(e,t,n){for(var r=n-1,o=e.length;++r1?n[o-1]:void 0,a=o>2?n[2]:void 0 for(s=e.length>3&&"function"==typeof s?(o--,s):void 0,a&&i(n[0],n[1],a)&&(s=o<3?void 0:s,o=1),t=Object(t);++r0&&(e=r.createElement("button",{type:"button",onClick:this.props.previous,style:this.props.useGriddleStyles?{color:"#222",border:"none",background:"none",margin:"0 0 0 10px"}:null },this.props.previousIconComponent,this.props.previousText)),this.props.currentPage!==this.props.maxPage-1&&(t=r.createElement("button",{type:"button",onClick:this.props.next,style:this.props.useGriddleStyles?{ @@ -1283,7 +1284,7 @@ s=o({textAlign:"right",width:"34%"},a),i=o({textAlign:"center",width:"33%"},a),n return r.createElement("div",{style:this.props.useGriddleStyles?{minHeight:"35px"}:null},r.createElement("div",{className:this.props.previousClassName,style:n},e),r.createElement("div",{className:"griddle-page", style:i},r.createElement("select",{value:this.props.currentPage+1,onChange:this.pageChange},l)," / ",this.props.maxPage),r.createElement("div",{className:this.props.nextClassName,style:s},t))}}) e.exports=i},function(e,t,n){"use strict" -var r=n(3),o=n(200),i=n(204),s=n(151),a=r.createClass({displayName:"GridSettings",getDefaultProps:function l(){return{columns:[],columnMetadata:[],selectedColumns:[],settingsText:"",maxRowsText:"",resultsPerPage:0, +var r=n(3),o=n(201),i=n(205),s=n(152),a=r.createClass({displayName:"GridSettings",getDefaultProps:function l(){return{columns:[],columnMetadata:[],selectedColumns:[],settingsText:"",maxRowsText:"",resultsPerPage:0, enableToggleCustom:!1,useCustomComponent:!1,useGriddleStyles:!0,toggleCustomComponent:function e(){}}},setPageSize:function u(e){var t=parseInt(e.target.value,10) this.props.setPageSize(t)},handleChange:function p(e){var t=e.target.dataset?e.target.dataset.name:e.target.getAttribute("data-name") e.target.checked===!0&&o(this.props.selectedColumns,t)===!1?(this.props.selectedColumns.push(t),this.props.setColumns(this.props.selectedColumns)):this.props.setColumns(i(this.props.selectedColumns,t)) @@ -1302,16 +1303,16 @@ return r.createElement("div",{className:"griddle-settings",style:this.props.useG className:"griddle-columns",style:this.props.useGriddleStyles?{clear:"both",display:"table",width:"100%",borderBottom:"1px solid #EDEDED",marginBottom:"10px"}:null},t),i,n)}}) e.exports=a},function(e,t,n){function r(e,t,n,r){e=i(e)?e:l(e),n=n&&!r?a(n):0 var p=e.length -return n<0&&(n=u(p+n,0)),s(e)?n<=p&&e.indexOf(t,n)>-1:!!p&&o(e,t,n)>-1}var o=n(178),i=n(114),s=n(201),a=n(155),l=n(202),u=Math.max -e.exports=r},function(e,t,n){function r(e){return"string"==typeof e||!i(e)&&s(e)&&o(e)==a}var o=n(56),i=n(101),s=n(100),a="[object String]" -e.exports=r},function(e,t,n){function r(e){return null==e?[]:o(e,i(e))}var o=n(203),i=n(95) -e.exports=r},function(e,t,n){function r(e,t){return o(t,function(t){return e[t]})}var o=n(34) -e.exports=r},function(e,t,n){var r=n(176),o=n(166),i=n(182),s=o(function(e,t){return i(e)?r(e,t):[]}) +return n<0&&(n=u(p+n,0)),s(e)?n<=p&&e.indexOf(t,n)>-1:!!p&&o(e,t,n)>-1}var o=n(179),i=n(115),s=n(202),a=n(156),l=n(203),u=Math.max +e.exports=r},function(e,t,n){function r(e){return"string"==typeof e||!i(e)&&s(e)&&o(e)==a}var o=n(57),i=n(102),s=n(101),a="[object String]" +e.exports=r},function(e,t,n){function r(e){return null==e?[]:o(e,i(e))}var o=n(204),i=n(96) +e.exports=r},function(e,t,n){function r(e,t){return o(t,function(t){return e[t]})}var o=n(35) +e.exports=r},function(e,t,n){var r=n(177),o=n(167),i=n(183),s=o(function(e,t){return i(e)?r(e,t):[]}) e.exports=s},function(e,t,n){"use strict" var r=n(3),o=r.createClass({displayName:"GridNoData",getDefaultProps:function i(){return{noDataMessage:"No Data"}},render:function s(){var e=this return r.createElement("div",null,this.props.noDataMessage)}}) e.exports=o},function(e,t,n){"use strict" -var r=n(3),o=n(32),i=n(207),s=n(55),a=n(211),l=n(183),u=n(213),p=n(219),d=n(204),c=r.createClass({displayName:"GridRow",getDefaultProps:function f(){return{isChildRow:!1,showChildren:!1,data:{},columnSettings:null, +var r=n(3),o=n(33),i=n(208),s=n(56),a=n(212),l=n(184),u=n(214),p=n(220),d=n(205),c=r.createClass({displayName:"GridRow",getDefaultProps:function f(){return{isChildRow:!1,showChildren:!1,data:{},columnSettings:null, rowSettings:null,hasChildren:!1,useGriddleStyles:!0,useGriddleIcons:!0,isSubGriddle:!1,paddingHeight:null,rowHeight:null,parentRowCollapsedClassName:"parent-row",parentRowExpandedClassName:"parent-row expanded", parentRowCollapsedComponent:"â–¶",parentRowExpandedComponent:"â–¼",onRowClick:null,multipleSelectionSettings:null}},handleClick:function h(e){null!==this.props.onRowClick&&s(this.props.onRowClick)?this.props.onRowClick(this,e):this.props.hasChildren&&this.props.toggleChildren() @@ -1350,26 +1351,26 @@ i=function(e,t){return e in t},r=Object(r) for(var s=0,a=t.length;so?0:o+t),n=n>o?o:n,n<0&&(n+=o),o=t>n?0:n-t>>>0,t>>>=0 for(var i=Array(o);++r=120&&y.length>=120)?new o(f&&y):void 0}y=e[0] var v=-1,b=h[0] e:for(;++v1),t}),a(e,u(e),n),l&&(n=o(n,p|d|c)) for(var f=t.length;f--;)i(n,t[f]) return n}) -e.exports=f},function(e,t,n){function r(e,t,n,F,T,I){var A,k=t&S,N=t&w,L=t&P +e.exports=f},function(e,t,n){function r(e,t,n,F,T,I){var A,k=t&S,N=t&w,U=t&P if(n&&(A=T?n(e,F,T,I):n(e)),void 0!==A)return A if(!E(e))return e -var U=b(e) -if(U){if(A=g(e),!k)return p(e,A)}else{var j=m(e),M=j==O||j==D +var L=b(e) +if(L){if(A=g(e),!k)return p(e,A)}else{var j=m(e),M=j==O||j==D if(C(e))return u(e,k) if(j==R||j==x||M&&!T){if(A=N||M?{}:v(e),!k)return N?c(e,l(A,e)):d(e,a(A,e))}else{if(!X[j])return T?e:{} A=y(e,j,r,k)}}I||(I=new o) var H=I.get(e) if(H)return H I.set(e,A) -var z=L?N?h:f:N?keysIn:_,B=U?void 0:z(e) -return i(B||e,function(o,i){B&&(i=o,o=e[i]),s(A,i,r(o,t,n,i,e,I))}),A}var o=n(38),i=n(209),s=n(184),a=n(239),l=n(240),u=n(241),p=n(242),d=n(243),c=n(246),f=n(249),h=n(251),m=n(115),g=n(252),y=n(253),v=n(264),b=n(101),C=n(102),E=n(62),_=n(95),S=1,w=2,P=4,x="[object Arguments]",F="[object Array]",T="[object Boolean]",I="[object Date]",A="[object Error]",O="[object Function]",D="[object GeneratorFunction]",k="[object Map]",N="[object Number]",R="[object Object]",L="[object RegExp]",U="[object Set]",j="[object String]",M="[object Symbol]",H="[object WeakMap]",z="[object ArrayBuffer]",B="[object DataView]",G="[object Float32Array]",q="[object Float64Array]",V="[object Int8Array]",W="[object Int16Array]",Q="[object Int32Array]",K="[object Uint8Array]",$="[object Uint8ClampedArray]",Z="[object Uint16Array]",Y="[object Uint32Array]",X={} +var z=U?N?h:f:N?keysIn:_,B=L?void 0:z(e) +return i(B||e,function(o,i){B&&(i=o,o=e[i]),s(A,i,r(o,t,n,i,e,I))}),A}var o=n(39),i=n(210),s=n(185),a=n(240),l=n(241),u=n(242),p=n(243),d=n(244),c=n(247),f=n(250),h=n(252),m=n(116),g=n(253),y=n(254),v=n(265),b=n(102),C=n(103),E=n(63),_=n(96),S=1,w=2,P=4,x="[object Arguments]",F="[object Array]",T="[object Boolean]",I="[object Date]",A="[object Error]",O="[object Function]",D="[object GeneratorFunction]",k="[object Map]",N="[object Number]",R="[object Object]",U="[object RegExp]",L="[object Set]",j="[object String]",M="[object Symbol]",H="[object WeakMap]",z="[object ArrayBuffer]",B="[object DataView]",G="[object Float32Array]",q="[object Float64Array]",V="[object Int8Array]",W="[object Int16Array]",Q="[object Int32Array]",K="[object Uint8Array]",$="[object Uint8ClampedArray]",Z="[object Uint16Array]",Y="[object Uint32Array]",X={} -X[x]=X[F]=X[z]=X[B]=X[T]=X[I]=X[G]=X[q]=X[V]=X[W]=X[Q]=X[k]=X[N]=X[R]=X[L]=X[U]=X[j]=X[M]=X[K]=X[$]=X[Z]=X[Y]=!0,X[A]=X[O]=X[H]=!1,e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(186),i=n(95) +X[x]=X[F]=X[z]=X[B]=X[T]=X[I]=X[G]=X[q]=X[V]=X[W]=X[Q]=X[k]=X[N]=X[R]=X[U]=X[L]=X[j]=X[M]=X[K]=X[$]=X[Z]=X[Y]=!0,X[A]=X[O]=X[H]=!1,e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(187),i=n(96) -e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(186),i=n(216) +e.exports=r},function(e,t,n){function r(e,t){return e&&o(t,i(t),e)}var o=n(187),i=n(217) e.exports=r},function(e,t,n){(function(e){function r(e,t){if(t)return e.slice() var n=e.length,r=u?u(n):new e.constructor(n) -return e.copy(r),r}var o=n(58),i="object"==typeof t&&t&&!t.nodeType&&t,s=i&&"object"==typeof e&&e&&!e.nodeType&&e,a=s&&s.exports===i,l=a?o.Buffer:void 0,u=l?l.allocUnsafe:void 0 +return e.copy(r),r}var o=n(59),i="object"==typeof t&&t&&!t.nodeType&&t,s=i&&"object"==typeof e&&e&&!e.nodeType&&e,a=s&&s.exports===i,l=a?o.Buffer:void 0,u=l?l.allocUnsafe:void 0 e.exports=r}).call(t,n(24)(e))},function(e,t){function n(e,t){var n=-1,r=e.length for(t||(t=Array(r));++n0&&o<100?h["default"].createElement("div",{className:"preview__progress"},h["default"].createElement("div",{className:"preview__progress-bar",style:{ -width:o+"%"}})):null,s=this.props.upload.message,a=s?h["default"].createElement("div",{className:"preview__message preview__message--"+s.type},s.value):null -return h["default"].createElement("div",{className:"editor__thumbnail-container"},r||n,i,a)}},{key:"renderToolbar",value:function _(){var e=this.canEdit() +var t=this.props.upload.category,n=t&&"image"!==t?v["default"].DEFAULT_PREVIEW:this.props.upload.url||e.preview||e.url,r=h["default"].createElement("img",{src:n,className:"editor__thumbnail"}),o=e.url?h["default"].createElement("a",{ +className:"editor__file-preview-link",href:e.url,target:"_blank"},r):null,i=this.props.upload.progress,s=i>0&&i<100?h["default"].createElement("div",{className:"preview__progress"},h["default"].createElement("div",{ +className:"preview__progress-bar",style:{width:i+"%"}})):null,a=this.props.upload.message,l=a?h["default"].createElement("div",{className:"preview__message preview__message--"+a.type},a.value):null +return h["default"].createElement("div",{className:"editor__thumbnail-container"},o||r,s,l)}},{key:"renderToolbar",value:function w(){var e=this.canEdit() return this.props.data.url||e?h["default"].createElement("div",{className:"preview__toolbar fill-height"},this.props.data.url?h["default"].createElement("a",{href:this.props.data.url,target:"_blank",className:"preview__toolbar-button--link preview__toolbar-button" },"Open"):null,e?h["default"].createElement("button",{id:"preview-replace-button",onClick:this.preventDefault,className:"preview__toolbar-button--replace preview__toolbar-button"},"Replace"):null,this.props.upload.progress||this.props.upload.message?h["default"].createElement("button",{ -onClick:this.handleCancelUpload,className:"preview__toolbar-button--remove preview__toolbar-button"},"Remove"):null):null}},{key:"render",value:function S(){var e=this.getDropzoneProps() +onClick:this.handleCancelUpload,className:"preview__toolbar-button--remove preview__toolbar-button"},"Remove"):null):null}},{key:"render",value:function P(){var e=this.getDropzoneProps() if(this.canEdit())return h["default"].createElement(g["default"],e,this.renderImage(),this.renderToolbar()) var t=["preview__container",this.props.className,this.props.extraClass] return h["default"].createElement("div",{className:t.join(" ")},this.renderImage(),this.renderToolbar())}}]),t}(f.Component) -S.propTypes={id:f.PropTypes.string.isRequired,name:f.PropTypes.string,className:f.PropTypes.string,extraClass:f.PropTypes.string,readOnly:f.PropTypes.bool,disabled:f.PropTypes.bool,onAutofill:f.PropTypes.func, +w.propTypes={id:f.PropTypes.string.isRequired,name:f.PropTypes.string,className:f.PropTypes.string,extraClass:f.PropTypes.string,readOnly:f.PropTypes.bool,disabled:f.PropTypes.bool,onAutofill:f.PropTypes.func, data:f.PropTypes.shape({parentid:f.PropTypes.number,url:f.PropTypes.string,exists:f.PropTypes.bool,preview:f.PropTypes.string,category:f.PropTypes.string,uploadFileEndpoint:f.PropTypes.shape({url:f.PropTypes.string.isRequired, method:f.PropTypes.string.isRequired,payloadFormat:f.PropTypes.string}),initialValues:f.PropTypes.object}).isRequired,upload:f.PropTypes.shape({url:f.PropTypes.string,progress:f.PropTypes.number,xhr:f.PropTypes.object, -message:f.PropTypes.shape({type:f.PropTypes.string.isRequired,value:f.PropTypes.string.isRequired})}),actions:f.PropTypes.object,securityID:f.PropTypes.string},S.defaultProps={extraClass:"",className:"" -},t["default"]=(0,b.connect)(l,u)(S)},function(e,t,n){"use strict" +category:f.PropTypes.string,message:f.PropTypes.shape({type:f.PropTypes.string.isRequired,value:f.PropTypes.string.isRequired})}),actions:f.PropTypes.object,securityID:f.PropTypes.string,confirm:f.PropTypes.func +},w.defaultProps={extraClass:"",className:"",data:{},upload:{},confirm:window.confirm},t["default"]=(0,b.connect)(l,u)(w)},function(e,t,n){"use strict" function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){return{type:u["default"].PREVIEWFIELD_REMOVE_FILE,payload:{id:e}}}function i(e,t){return{type:u["default"].PREVIEWFIELD_ADD_FILE,payload:{ id:e,file:t}}}function s(e,t){return{type:u["default"].PREVIEWFIELD_FAIL_UPLOAD,payload:{id:e,message:t}}}function a(e,t){return{type:u["default"].PREVIEWFIELD_UPDATE_FILE,payload:{id:e,data:t}}}Object.defineProperty(t,"__esModule",{ value:!0}),t.removeFile=o,t.addFile=i,t.failUpload=s,t.updateFile=a diff --git a/client/src/components/AssetDropzone/AssetDropzone.js b/client/src/components/AssetDropzone/AssetDropzone.js index 355066655..31073f686 100644 --- a/client/src/components/AssetDropzone/AssetDropzone.js +++ b/client/src/components/AssetDropzone/AssetDropzone.js @@ -4,6 +4,7 @@ import SilverStripeComponent from 'lib/SilverStripeComponent'; import i18n from 'i18n'; import DropzoneLib from 'dropzone'; import $ from 'jQuery'; +import { getFileExtension } from 'lib/DataFormat'; let idCounter = 0; @@ -301,7 +302,14 @@ class AssetDropzone extends SilverStripeComponent { return Promise.resolve(); } + // check with parent if there are other forms of validation to be done + if (typeof this.props.canFileUpload === 'function' && !this.props.canFileUpload(file)) { + this.dropzone.removeFile(file); + return Promise.resolve(); + } + if (!this.props.canUpload) { + this.dropzone.removeFile(file); return Promise.reject(new Error(i18n._t('AssetAdmin.DROPZONE_CANNOT_UPLOAD'))); } @@ -343,7 +351,7 @@ class AssetDropzone extends SilverStripeComponent { queuedId: file._queuedId, size: file.size, title: this.getFileTitle(file.name), - extension: this.getFileExtension(file.name), + extension: getFileExtension(file.name), type: file.type, url: preview.thumbnailURL, }; @@ -367,12 +375,6 @@ class AssetDropzone extends SilverStripeComponent { .replace(/-_/, ' '); } - getFileExtension(filename) { - return /[.]/.exec(filename) - ? filename.replace(/^.+[.]/, '') - : ''; - } - /** * Returns a promise for loading an image to get the dataURL for previewing. * @@ -469,6 +471,7 @@ AssetDropzone.propTypes = { handleSending: React.PropTypes.func, handleSuccess: React.PropTypes.func.isRequired, handleMaxFilesExceeded: React.PropTypes.func, + canFileUpload: React.PropTypes.func, options: React.PropTypes.shape({ url: React.PropTypes.string.isRequired, }), diff --git a/client/src/components/GalleryItem/tests/GalleryItem-test.js b/client/src/components/GalleryItem/tests/GalleryItem-test.js index c993b1b67..2c8cf80b1 100644 --- a/client/src/components/GalleryItem/tests/GalleryItem-test.js +++ b/client/src/components/GalleryItem/tests/GalleryItem-test.js @@ -30,6 +30,34 @@ describe('GalleryItem', () => { }; }); + describe('handleCancelUpload()', () => { + let item = null; + const event = new Event('test'); + + beforeEach(() => { + props.onRemoveErroredUpload = jest.genMockFunction(); + props.onCancelUpload = jest.genMockFunction(); + + item = ReactTestUtils.renderIntoDocument( + + ); + }); + + it('should call onRemoveErroredUpload when there was an error', () => { + item.hasError = () => true; + item.handleCancelUpload(event); + + expect(props.onRemoveErroredUpload).toBeCalled(); + }); + + it('should call onCancelUpload when there were no errors found', () => { + item.hasError = () => false; + item.handleCancelUpload(event); + + expect(props.onCancelUpload).toBeCalled(); + }); + }); + describe('hasError()', () => { let item = null; diff --git a/client/src/components/PreviewImageField/PreviewImageField.js b/client/src/components/PreviewImageField/PreviewImageField.js index 9662a444a..7f8a811eb 100644 --- a/client/src/components/PreviewImageField/PreviewImageField.js +++ b/client/src/components/PreviewImageField/PreviewImageField.js @@ -5,6 +5,7 @@ import CONSTANTS from 'constants'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import * as previewFieldActions from 'state/previewField/PreviewFieldActions'; +import { getFileExtension } from 'lib/DataFormat'; class PreviewImageField extends Component { constructor(props) { @@ -17,6 +18,7 @@ class PreviewImageField extends Component { this.handleUploadProgress = this.handleUploadProgress.bind(this); this.handleCancelUpload = this.handleCancelUpload.bind(this); this.handleRemoveErroredUpload = this.handleRemoveErroredUpload.bind(this); + this.canFileUpload = this.canFileUpload.bind(this); } componentWillUnmount() { @@ -58,6 +60,7 @@ class PreviewImageField extends Component { handleSuccess: this.handleSuccessfulUpload, handleSending: this.handleSending, handleUploadProgress: this.handleUploadProgress, + canFileUpload: this.canFileUpload, }; } @@ -77,6 +80,28 @@ class PreviewImageField extends Component { e.preventDefault(); } + /** + * + * @param {File} file + * @returns {boolean} + */ + canFileUpload(file) { + const prevName = this.props.data.initialValues.FileFilename; + const prevExt = getFileExtension(prevName); + const nextExt = getFileExtension(file.name); + + if (!prevExt || prevExt === nextExt) { + return true; + } + + const message = i18n._t( + 'AssetAdmin.CONFIRM_CHANGE_EXTENSION', + 'Are you sure you want upload a file with a different extension?' + ); + + return this.props.confirm(message); + } + /** * Handles removing an upload and cancelling the request made to upload */ @@ -167,7 +192,10 @@ class PreviewImageField extends Component { ); } - const preview = this.props.upload.url || data.preview || data.url; + const category = this.props.upload.category; + const preview = (category && category !== 'image') + ? CONSTANTS.DEFAULT_PREVIEW + : this.props.upload.url || data.preview || data.url; const image = ; const linkedImage = (data.url) ? ( @@ -239,20 +267,19 @@ class PreviewImageField extends Component { {this.renderToolbar()} ); - } else { - const classNames = [ - 'preview__container', - this.props.className, - this.props.extraClass, - ]; - - return ( -
- {this.renderImage()} - {this.renderToolbar()} -
- ); } + const classNames = [ + 'preview__container', + this.props.className, + this.props.extraClass, + ]; + + return ( +
+ {this.renderImage()} + {this.renderToolbar()} +
+ ); } } @@ -281,6 +308,7 @@ PreviewImageField.propTypes = { url: PropTypes.string, progress: PropTypes.number, xhr: PropTypes.object, + category: PropTypes.string, message: PropTypes.shape({ type: PropTypes.string.isRequired, value: PropTypes.string.isRequired, @@ -288,12 +316,16 @@ PreviewImageField.propTypes = { }), actions: PropTypes.object, securityID: PropTypes.string, + confirm: PropTypes.func, }; PreviewImageField.defaultProps = { // React considers "undefined" as an uncontrolled component. extraClass: '', className: '', + data: {}, + upload: {}, + confirm: window.confirm, }; function mapStateToProps(state, ownprops) { diff --git a/client/src/components/PreviewImageField/README.md b/client/src/components/PreviewImageField/README.md new file mode 100644 index 000000000..a32653b61 --- /dev/null +++ b/client/src/components/PreviewImageField/README.md @@ -0,0 +1,37 @@ +# PreviewImageField Component + +A field for displaying a preview image to a given file, this opens the ability to upload a replacement file if desired and handles calling an API and replacing file data to be saved on Submit of the form. +This gives a warning if the replacement file changes file extension. + +Properties: + * `id` (string) (required): A unique identifier for the field + * `name` (string): Identifier for the form to use + * `className` (string): Any classes that should be applied to this component + * `extraClass` (string): Any extra classes provided by the schema + * `readOnly` (boolean): Defines whether this field is readonly + * `disabled` (boolean): Defines whether this field is disabled + * `onAutofill` (function): Callback to be used when an upload has finished and passing back values for other fields on the form to be filled out with + * `data` (object): Extra data that helps define this field uniquely + * `parentid` (number): The folder id which the replacement upload will go into + * `url` (string): The url for the current file + * `exists` (boolean): Defines whether the file in question exists on the filesystem + * `preview` (string): The url for a preview image of the file + * `category` (string): The category the current file belongs to + * `uploadFileEndpoint` (object): Data defining the endpoint to use for uploading a replacement file + * `url` (string) (required): The url for the endpoint to call + * `method` (string) (required): The method to use to call the endpoint with + * `payloadFormat` (string): The type of encoding to use when calling the endpoint + * `initialValues` (object): The initial values for the fields that autofill were going to autofill with, the fields are: + * `FileFilename` + * `FileHash` + * `FileVariant` + * `upload` (object): The upload data if a replacement file is being sent + * `url` (string): The url or embedded data uri string for displaying a preview for + * `progress` (number): The percentage of the upload is complete + * `xhr` (object): The request call object made, used to `abort()` if upload is cancelled + * `category` (string): Describes the category the uploading file belongs to + * `message` (object): A message object with regards to the upload + * `type` (string) (required): The type of message this can be `error`, `success`, etc... + * `value` (string) (required): The message to be shown to the user + * `actions` (object): A list of actions to be used for e.g. `redux` + * `securityID` (string): The security ID to be used for uploading diff --git a/client/src/components/PreviewImageField/tests/PreviewImageField-test.js b/client/src/components/PreviewImageField/tests/PreviewImageField-test.js new file mode 100644 index 000000000..e5567fe17 --- /dev/null +++ b/client/src/components/PreviewImageField/tests/PreviewImageField-test.js @@ -0,0 +1,159 @@ +/* global jest, jasmine, describe, it, expect, beforeEach */ + +jest.unmock('react'); +jest.unmock('../PreviewImageField'); +jest.mock('components/AssetDropzone/AssetDropzone', () => null); +jest.unmock('lib/DataFormat'); + +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; +import PreviewImageField from '../PreviewImageField'; + +describe('PreviewImageField', () => { + let props = null; + + beforeEach(() => { + props = { + id: 'Form_Test_Field', + data: { + category: 'file', + exists: true, + }, + onAutofill: jest.genMockFunction(), + actions: { + previewField: { + removeFile: jest.genMockFunction(), + }, + }, + }; + }); + + describe('handleSuccessfulUpload()', () => { + it('should auto the tuple fields with the given response data', () => { + const fileXhr = { + xhr: { + response: JSON.stringify({ + Filename: 'abc.jpg', + Hash: 'zxcvqwer', + Variant: '123', + }), + }, + }; + const item = ReactTestUtils.renderIntoDocument( + + ); + item.handleSuccessfulUpload(fileXhr); + + expect(props.onAutofill.mock.calls.length).toBe(3); + expect(props.onAutofill.mock.calls[0][0]).toBe('FileFilename'); + expect(props.onAutofill.mock.calls[0][1]).toBe('abc.jpg'); + expect(props.onAutofill.mock.calls[1][0]).toBe('FileHash'); + expect(props.onAutofill.mock.calls[1][1]).toBe('zxcvqwer'); + expect(props.onAutofill.mock.calls[2][0]).toBe('FileVariant'); + expect(props.onAutofill.mock.calls[2][1]).toBe('123'); + }); + }); + + describe('handleRemoveErroredUpload()', () => { + it('should autofill the tuple fields with initial values and call removeFile', () => { + props.data.initialValues = { + FileFilename: 'abc.jpg', + FileHash: 'zxcvqwer', + FileVariant: '123', + }; + const item = ReactTestUtils.renderIntoDocument( + + ); + item.handleRemoveErroredUpload(); + + expect(props.onAutofill.mock.calls.length).toBe(3); + expect(props.onAutofill.mock.calls[0][0]).toBe('FileFilename'); + expect(props.onAutofill.mock.calls[0][1]).toBe('abc.jpg'); + expect(props.onAutofill.mock.calls[1][0]).toBe('FileHash'); + expect(props.onAutofill.mock.calls[1][1]).toBe('zxcvqwer'); + expect(props.onAutofill.mock.calls[2][0]).toBe('FileVariant'); + expect(props.onAutofill.mock.calls[2][1]).toBe('123'); + expect(props.actions.previewField.removeFile).toBeCalled(); + }); + }); + + describe('canFileUpload()', () => { + let file = null; + beforeEach(() => { + props.data.initialValues = { FileFilename: 'test.jpg' }; + file = {}; + }); + + it('should return true if the extension is the same', () => { + file.name = 'test-replace.jpg'; + const item = ReactTestUtils.renderIntoDocument( + + ); + const canUpload = item.canFileUpload(file); + + expect(canUpload).toBe(true); + }); + + it('should return true if there is no initial filename defined', () => { + props.data.initialValues.FileFilename = ''; + const item = ReactTestUtils.renderIntoDocument( + + ); + const canUpload = item.canFileUpload(file); + + expect(canUpload).toBe(true); + }); + + it('should return what the confirm callback gives when the extension changes', () => { + props.confirm = () => 'abc'; + file.name = 'test.txt'; + const item = ReactTestUtils.renderIntoDocument( + + ); + const canUpload = item.canFileUpload(file); + + expect(canUpload).toBe('abc'); + }); + }); + + describe('canEdit()', () => { + it('should enable edit for file types', () => { + const item = ReactTestUtils.renderIntoDocument( + + ); + const edit = item.canEdit(); + + expect(edit).toBe(true); + }); + + it('should disable edit when readonly', () => { + props.readOnly = true; + const item = ReactTestUtils.renderIntoDocument( + + ); + const edit = item.canEdit(); + + expect(edit).toBe(false); + }); + + it('should disable edit when disabled', () => { + props.disabled = true; + const item = ReactTestUtils.renderIntoDocument( + + ); + const edit = item.canEdit(); + + expect(edit).toBe(false); + }); + + it('should disable edit when it is a folder', () => { + props.data.category = 'folder'; + const item = ReactTestUtils.renderIntoDocument( + + ); + const edit = item.canEdit(); + + expect(edit).toBe(false); + }); + }); +}); diff --git a/client/src/constants/index.js b/client/src/constants/index.js index 61313d6ac..6eea62815 100644 --- a/client/src/constants/index.js +++ b/client/src/constants/index.js @@ -43,4 +43,5 @@ export default { BULK_ACTIONS_PLACEHOLDER: i18n._t('AssetAdmin.BULK_ACTIONS_PLACEHOLDER'), SPACE_KEY_CODE: 32, RETURN_KEY_CODE: 13, + DEFAULT_PREVIEW: 'framework/client/dist/images/app_icons/generic_92.png', }; diff --git a/client/src/state/previewField/tests/PreviewFieldReducer-test.js b/client/src/state/previewField/tests/PreviewFieldReducer-test.js new file mode 100644 index 000000000..86b31bf39 --- /dev/null +++ b/client/src/state/previewField/tests/PreviewFieldReducer-test.js @@ -0,0 +1,80 @@ +/* global jest, describe, it, expect, beforeEach, jasmine */ + +jest.unmock('react'); +jest.unmock('deep-freeze-strict'); +jest.unmock('../PreviewFieldActionTypes'); +jest.unmock('../PreviewFieldReducer'); + +import previewFieldReducer from '../PreviewFieldReducer'; +import ACTION_TYPES from '../PreviewFieldActionTypes'; + +describe('previewFieldReducer', () => { + let id = null; + let initialState = null; + + beforeEach(() => { + id = 'MyPreview'; + initialState = {}; + }); + + describe('PREVIEWFIELD_ADD_FILE', () => { + it('should add the file to state', () => { + const file = { id: 543 }; + const nextState = previewFieldReducer(initialState, { + type: ACTION_TYPES.PREVIEWFIELD_ADD_FILE, + payload: { id, file }, + }); + + expect(nextState[id]).toBe(file); + }); + }); + + describe('PREVIEWFIELD_FAIL_UPLOAD', () => { + it('should add the message to the existing file state', () => { + const message = { + message: { + type: 'error', + value: 'failed to upload', + }, + }; + initialState[id] = { id: 543 }; + const nextState = previewFieldReducer(initialState, { + type: ACTION_TYPES.PREVIEWFIELD_FAIL_UPLOAD, + payload: { id, message }, + }); + + expect(nextState[id].id).toBe(543); + expect(nextState[id].message).toBe(message.message); + }); + }); + + describe('PREVIEWFIELD_REMOVE_FILE', () => { + it('should remove the file state', () => { + initialState[id] = { id: 321 }; + const nextState = previewFieldReducer(initialState, { + type: ACTION_TYPES.PREVIEWFIELD_REMOVE_FILE, + payload: { id }, + }); + + expect(typeof nextState[id]).toBe('undefined'); + }); + }); + + describe('PREVIEWFIELD_UPDATE_FILE', () => { + it('should add the given data to the existing file state', () => { + const data = { + xhr: {}, + progress: 32, + }; + initialState[id] = { id: 765 }; + const nextState = previewFieldReducer(initialState, { + type: ACTION_TYPES.PREVIEWFIELD_UPDATE_FILE, + payload: { id, data }, + }); + + expect(nextState[id].id).toBe(765); + expect(nextState[id].xhr).toBe(data.xhr); + expect(nextState[id].progress).toBe(32); + }); + }); +}); From 8c40c580806d78d895dfee1c02e9429cd609ba7b Mon Sep 17 00:00:00 2001 From: Christopher Joe Date: Mon, 19 Dec 2016 18:43:46 +1300 Subject: [PATCH 07/13] Enhancement Added name attribute to AssetDropzone, so multiple instances are distinguishable FIX readonly mode no longer switches to a ReadonlyField Enhancement added replace-file behat test FIX remove field upload data when getting new url FIX cast file to new class when appropriate --- client/dist/js/bundle.js | 69 ++++++++++--------- client/dist/styles/bundle.css | 7 +- client/lang/en.js | 1 + client/lang/src/en.js | 1 + .../components/AssetDropzone/AssetDropzone.js | 6 ++ .../PreviewImageField/PreviewImageField.js | 42 +++++++++-- .../PreviewImageField/PreviewImageField.scss | 5 +- .../src/components/UploadField/UploadField.js | 2 + client/src/containers/Editor/Editor.scss | 1 + client/src/containers/Gallery/Gallery.js | 1 + .../containers/HistoryList/HistoryList.scss | 1 + code/Controller/AssetAdmin.php | 15 +++- code/Forms/FileHistoryFormFactory.php | 4 +- code/Forms/PreviewImageField.php | 7 ++ tests/behat/features/manage-files.feature | 2 +- tests/behat/features/replace-file.feature | 36 ++++++++++ tests/behat/src/Context/FixtureContext.php | 24 +++---- 17 files changed, 165 insertions(+), 59 deletions(-) create mode 100644 tests/behat/features/replace-file.feature diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 055132a67..c2efe5190 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -246,7 +246,7 @@ var e={height:U["default"].THUMBNAIL_HEIGHT,width:U["default"].THUMBNAIL_WIDTH}, return"insert"===this.props.type&&o.push("insert-media-modal__main"),v["default"].createElement("div",{className:"flexbox-area-grow gallery__outer"},this.renderBulkActions(),v["default"].createElement(T["default"],{ -canUpload:r,handleAddedFile:this.handleAddedFile,handleError:this.handleFailedUpload,handleSuccess:this.handleSuccessfulUpload,handleSending:this.handleSending,handleUploadProgress:this.handleUploadProgress, +name:"gallery-container",canUpload:r,handleAddedFile:this.handleAddedFile,handleError:this.handleFailedUpload,handleSuccess:this.handleSuccessfulUpload,handleSending:this.handleSending,handleUploadProgress:this.handleUploadProgress, preview:e,folderId:this.props.folderId,options:t,securityID:n,uploadButton:!1},v["default"].createElement("div",{className:o.join(" ")},this.renderToolbar(),this.renderGalleryView())))}}]),t}(y.Component),G={ page:0,limit:15,sort:z[0].field+","+z[0].direction},q={loading:y.PropTypes.bool,sort:y.PropTypes.string,files:y.PropTypes.arrayOf(y.PropTypes.shape({id:y.PropTypes.number,parent:y.PropTypes.shape({id:y.PropTypes.number })})).isRequired,count:y.PropTypes.number,page:y.PropTypes.number,limit:y.PropTypes.number,onOpenFile:y.PropTypes.func.isRequired,onOpenFolder:y.PropTypes.func.isRequired,onSort:y.PropTypes.func.isRequired, @@ -281,10 +281,10 @@ n.handleUploadProgress=n.handleUploadProgress.bind(n),n.handleError=n.handleErro n}return s(t,e),l(t,[{key:"componentDidMount",value:function n(){u(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"componentDidMount",this).call(this) var e=this.getDefaultOptions(),n=this.props.uploadSelector if(!n&&this.props.uploadButton&&(n=".asset-dropzone__upload-button"),n){var r=(0,E["default"])(f["default"].findDOMNode(this)).find(n) -r&&r.length&&(e.clickable=r.toArray())}this.dropzone=new b["default"](f["default"].findDOMNode(this),a({},e,this.props.options)),"undefined"!=typeof this.props.promptOnRemove&&this.setPromptOnRemove(this.props.promptOnRemove) - -}},{key:"componentWillUnmount",value:function r(){u(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"componentWillUnmount",this).call(this),this.dropzone.disable()}},{key:"render",value:function p(){ -var e=["asset-dropzone"] +r&&r.length&&(e.clickable=r.toArray())}this.dropzone=new b["default"](f["default"].findDOMNode(this),a({},e,this.props.options)) +var o=this.props.name +o&&this.dropzone.hiddenFileInput.classList.add("dz-input-"+o),"undefined"!=typeof this.props.promptOnRemove&&this.setPromptOnRemove(this.props.promptOnRemove)}},{key:"componentWillUnmount",value:function r(){ +u(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"componentWillUnmount",this).call(this),this.dropzone.disable()}},{key:"render",value:function p(){var e=["asset-dropzone"] this.props.className&&e.push(this.props.className) var t={className:"asset-dropzone__upload-button ss-ui-button font-icon-upload",type:"button"} return this.props.canUpload||(t.disabled=!0),this.dragging===!0&&e.push("dragging"),d["default"].createElement("div",{className:e.join(" ")},this.props.uploadButton&&d["default"].createElement("button",t,y["default"]._t("AssetAdmin.DROPZONE_UPLOAD")),this.props.children) @@ -1703,14 +1703,14 @@ if("function"==typeof e.onChange){var t=e.files.filter(function(e){return e.id}) e.onChange(n)}}},{key:"handleSelect",value:function C(e){e.preventDefault()}},{key:"handleAddShow",value:function E(e){e.preventDefault(),this.setState({selecting:!0})}},{key:"handleAddHide",value:function _(){ this.setState({selecting:!1})}},{key:"handleAddInsert",value:function S(e,t){this.props.actions.uploadField.addFile(this.props.id,t),this.handleAddHide()}},{key:"render",value:function w(){return m["default"].createElement("div",{ className:"uploadfield"},this.renderDropzone(),this.props.files.map(this.renderChild),this.renderDialog())}},{key:"renderDropzone",value:function x(){if(!this.props.data.createFileEndpoint)return null -var e={height:b["default"].SMALL_THUMBNAIL_HEIGHT,width:b["default"].SMALL_THUMBNAIL_WIDTH},t={url:this.props.data.createFileEndpoint.url,method:this.props.data.createFileEndpoint.method,paramName:"Upload", -thumbnailWidth:b["default"].SMALL_THUMBNAIL_WIDTH,thumbnailHeight:b["default"].SMALL_THUMBNAIL_HEIGHT} -this.props.data.multi||(t.maxFiles=1) -var n=["uploadfield__dropzone"] -this.props.files.length&&!this.props.data.multi&&n.push("uploadfield__dropzone--hidden") -var r=this.props.securityId -return m["default"].createElement(F["default"],{canUpload:!0,uploadButton:!1,uploadSelector:".uploadfield__upload-button, .uploadfield__backdrop",folderId:this.props.data.parentid,handleAddedFile:this.handleAddedFile, -handleError:this.handleFailedUpload,handleSuccess:this.handleSuccessfulUpload,handleSending:this.handleSending,handleUploadProgress:this.handleUploadProgress,preview:e,options:t,securityID:r,className:n.join(" ") +var e={height:b["default"].SMALL_THUMBNAIL_HEIGHT,width:b["default"].SMALL_THUMBNAIL_WIDTH},t=this.props.name,n={url:this.props.data.createFileEndpoint.url,method:this.props.data.createFileEndpoint.method, +paramName:"Upload",thumbnailWidth:b["default"].SMALL_THUMBNAIL_WIDTH,thumbnailHeight:b["default"].SMALL_THUMBNAIL_HEIGHT} +this.props.data.multi||(n.maxFiles=1) +var r=["uploadfield__dropzone"] +this.props.files.length&&!this.props.data.multi&&r.push("uploadfield__dropzone--hidden") +var o=this.props.securityId +return m["default"].createElement(F["default"],{name:t,canUpload:!0,uploadButton:!1,uploadSelector:".uploadfield__upload-button, .uploadfield__backdrop",folderId:this.props.data.parentid,handleAddedFile:this.handleAddedFile, +handleError:this.handleFailedUpload,handleSuccess:this.handleSuccessfulUpload,handleSending:this.handleSending,handleUploadProgress:this.handleUploadProgress,preview:e,options:n,securityID:o,className:r.join(" ") },m["default"].createElement("div",{className:"uploadfield__backdrop"}),m["default"].createElement("span",{className:"uploadfield__droptext"},m["default"].createElement("button",{onClick:this.handleSelect, className:"uploadfield__upload-button"},f["default"]._t("AssetAdminUploadField.BROWSE","Browse"))," ",f["default"]._t("AssetAdminUploadField.OR","or")," ",m["default"].createElement("button",{onClick:this.handleAddShow, className:"uploadfield__add-button"},f["default"]._t("AssetAdminUploadField.ADD_FILES","Add from files"))))}},{key:"renderDialog",value:function T(){return m["default"].createElement(I["default"],{title:!1, @@ -1789,44 +1789,49 @@ function t(e){i(this,t) var n=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e)) return n.handleAddedFile=n.handleAddedFile.bind(n),n.handleFailedUpload=n.handleFailedUpload.bind(n),n.handleSuccessfulUpload=n.handleSuccessfulUpload.bind(n),n.handleSending=n.handleSending.bind(n),n.handleUploadProgress=n.handleUploadProgress.bind(n), n.handleCancelUpload=n.handleCancelUpload.bind(n),n.handleRemoveErroredUpload=n.handleRemoveErroredUpload.bind(n),n.canFileUpload=n.canFileUpload.bind(n),n}return a(t,e),p(t,[{key:"componentWillUnmount", -value:function n(){this.props.actions.previewField.removeFile(this.props.id)}},{key:"getDropzoneProps",value:function r(){var e=this.props.data.uploadFileEndpoint,t={url:e&&e.url,method:e&&e.method,paramName:"Upload", -clickable:"#preview-replace-button",maxFiles:1},n={height:v["default"].THUMBNAIL_HEIGHT,width:v["default"].THUMBNAIL_WIDTH},r=this.props.securityID,o=["asset-dropzone--button","preview__container",this.props.className,this.props.extraClass] - +value:function n(){this.props.actions.previewField.removeFile(this.props.id)}},{key:"componentWillReceiveProps",value:function r(e){this.props.data.url&&e.data.url!==this.props.data.url&&this.props.actions.previewField.removeFile(this.props.id) -return{className:o.join(" "),canUpload:e&&this.canEdit(),preview:n,folderId:this.props.data.parentid,options:t,securityID:r,uploadButton:!1,handleAddedFile:this.handleAddedFile,handleError:this.handleFailedUpload, -handleSuccess:this.handleSuccessfulUpload,handleSending:this.handleSending,handleUploadProgress:this.handleUploadProgress,canFileUpload:this.canFileUpload}}},{key:"canEdit",value:function o(){return!this.props.readOnly&&!this.props.disabled&&"folder"!==this.props.data.category +}},{key:"getDropzoneProps",value:function o(){var e=this.props.data.uploadFileEndpoint,t=this.props.name,n={url:e&&e.url,method:e&&e.method,paramName:"Upload",clickable:"#preview-replace-button",maxFiles:1 +},r={height:v["default"].THUMBNAIL_HEIGHT,width:v["default"].THUMBNAIL_WIDTH},o=this.props.securityID,i=["asset-dropzone--button","preview__container",this.props.className,this.props.extraClass] +return{name:t,className:i.join(" "),canUpload:e&&this.canEdit(),preview:r,folderId:this.props.data.parentid,options:n,securityID:o,uploadButton:!1,handleAddedFile:this.handleAddedFile,handleError:this.handleFailedUpload, +handleSuccess:this.handleSuccessfulUpload,handleSending:this.handleSending,handleUploadProgress:this.handleUploadProgress,canFileUpload:this.canFileUpload}}},{key:"canEdit",value:function l(){return!this.props.readOnly&&!this.props.disabled&&"folder"!==this.props.data.category -}},{key:"preventDefault",value:function l(e){e.preventDefault()}},{key:"canFileUpload",value:function u(e){var t=this.props.data.initialValues.FileFilename,n=(0,S.getFileExtension)(t),r=(0,S.getFileExtension)(e.name) +}},{key:"preventDefault",value:function u(e){e.preventDefault()}},{key:"canFileUpload",value:function d(e){var t=this.props.data.initialValues.FileFilename,n=(0,S.getFileExtension)(t),r=(0,S.getFileExtension)(e.name) if(!n||n===r)return!0 var o=c["default"]._t("AssetAdmin.CONFIRM_CHANGE_EXTENSION","Are you sure you want upload a file with a different extension?") -return this.props.confirm(o)}},{key:"handleCancelUpload",value:function d(){this.props.upload.xhr&&this.props.upload.xhr.abort(),this.handleRemoveErroredUpload()}},{key:"handleRemoveErroredUpload",value:function f(){ +return this.props.confirm(o)}},{key:"handleCancelUpload",value:function f(){this.props.upload.xhr&&this.props.upload.xhr.abort(),this.handleRemoveErroredUpload()}},{key:"handleRemoveErroredUpload",value:function m(){ if("function"==typeof this.props.onAutofill){var e=this.props.data.initialValues this.props.onAutofill("FileFilename",e.FileFilename),this.props.onAutofill("FileHash",e.FileHash),this.props.onAutofill("FileVariant",e.FileVariant)}this.props.actions.previewField.removeFile(this.props.id) -}},{key:"handleAddedFile",value:function m(e){this.props.actions.previewField.addFile(this.props.id,e)}},{key:"handleFailedUpload",value:function y(e,t){this.props.actions.previewField.failUpload(this.props.id,t) +}},{key:"handleAddedFile",value:function y(e){this.props.actions.previewField.addFile(this.props.id,e)}},{key:"handleFailedUpload",value:function b(e,t){this.props.actions.previewField.failUpload(this.props.id,t) -}},{key:"handleSuccessfulUpload",value:function b(e){var t=JSON.parse(e.xhr.response) -"function"==typeof this.props.onAutofill&&(this.props.onAutofill("FileFilename",t.Filename),this.props.onAutofill("FileHash",t.Hash),this.props.onAutofill("FileVariant",t.Variant))}},{key:"handleSending", -value:function C(e,t){this.props.actions.previewField.updateFile(this.props.id,{xhr:t})}},{key:"handleUploadProgress",value:function E(e,t){this.props.actions.previewField.updateFile(this.props.id,{progress:t -})}},{key:"renderImage",value:function _(){var e=this.props.data +}},{key:"handleSuccessfulUpload",value:function C(e){var t=JSON.parse(e.xhr.response) +if("function"==typeof this.props.onAutofill){this.props.onAutofill("FileFilename",t.Filename),this.props.onAutofill("FileHash",t.Hash),this.props.onAutofill("FileVariant",t.Variant) +var n=(0,S.getFileExtension)(this.props.data.url),r=(0,S.getFileExtension)(t.Filename) +n!==r&&this.props.onAutofill(this.props.data.nameField,t.Name)}}},{key:"handleSending",value:function E(e,t){this.props.actions.previewField.updateFile(this.props.id,{xhr:t})}},{key:"handleUploadProgress", +value:function _(e,t){this.props.actions.previewField.updateFile(this.props.id,{progress:t})}},{key:"renderImage",value:function w(){var e=this.props.data if(!e.exists&&!this.props.upload.url)return h["default"].createElement("div",{className:"editor__file-preview-message--file-missing"},c["default"]._t("AssetAdmin.FILE_MISSING","File cannot be found")) var t=this.props.upload.category,n=t&&"image"!==t?v["default"].DEFAULT_PREVIEW:this.props.upload.url||e.preview||e.url,r=h["default"].createElement("img",{src:n,className:"editor__thumbnail"}),o=e.url?h["default"].createElement("a",{ className:"editor__file-preview-link",href:e.url,target:"_blank"},r):null,i=this.props.upload.progress,s=i>0&&i<100?h["default"].createElement("div",{className:"preview__progress"},h["default"].createElement("div",{ -className:"preview__progress-bar",style:{width:i+"%"}})):null,a=this.props.upload.message,l=a?h["default"].createElement("div",{className:"preview__message preview__message--"+a.type},a.value):null -return h["default"].createElement("div",{className:"editor__thumbnail-container"},o||r,s,l)}},{key:"renderToolbar",value:function w(){var e=this.canEdit() +className:"preview__progress-bar",style:{width:i+"%"}})):null,a=this.props.upload.message,l=null +return a?l=h["default"].createElement("div",{className:"preview__message preview__message--"+a.type},a.value):100===i&&(l=h["default"].createElement("div",{className:"preview__message preview__message--success" +},c["default"]._t("AssetAdmin.REPlACE_FILE_SUCCESS","Upload successful, the file will be replaced when you Save."))),h["default"].createElement("div",{className:"editor__thumbnail-container"},o||r,s,l) + +}},{key:"renderToolbar",value:function P(){var e=this.canEdit() return this.props.data.url||e?h["default"].createElement("div",{className:"preview__toolbar fill-height"},this.props.data.url?h["default"].createElement("a",{href:this.props.data.url,target:"_blank",className:"preview__toolbar-button--link preview__toolbar-button" },"Open"):null,e?h["default"].createElement("button",{id:"preview-replace-button",onClick:this.preventDefault,className:"preview__toolbar-button--replace preview__toolbar-button"},"Replace"):null,this.props.upload.progress||this.props.upload.message?h["default"].createElement("button",{ -onClick:this.handleCancelUpload,className:"preview__toolbar-button--remove preview__toolbar-button"},"Remove"):null):null}},{key:"render",value:function P(){var e=this.getDropzoneProps() +onClick:this.handleCancelUpload,className:"preview__toolbar-button--remove preview__toolbar-button"},"Remove"):null):null}},{key:"render",value:function x(){var e=this.getDropzoneProps() if(this.canEdit())return h["default"].createElement(g["default"],e,this.renderImage(),this.renderToolbar()) var t=["preview__container",this.props.className,this.props.extraClass] return h["default"].createElement("div",{className:t.join(" ")},this.renderImage(),this.renderToolbar())}}]),t}(f.Component) w.propTypes={id:f.PropTypes.string.isRequired,name:f.PropTypes.string,className:f.PropTypes.string,extraClass:f.PropTypes.string,readOnly:f.PropTypes.bool,disabled:f.PropTypes.bool,onAutofill:f.PropTypes.func, -data:f.PropTypes.shape({parentid:f.PropTypes.number,url:f.PropTypes.string,exists:f.PropTypes.bool,preview:f.PropTypes.string,category:f.PropTypes.string,uploadFileEndpoint:f.PropTypes.shape({url:f.PropTypes.string.isRequired, -method:f.PropTypes.string.isRequired,payloadFormat:f.PropTypes.string}),initialValues:f.PropTypes.object}).isRequired,upload:f.PropTypes.shape({url:f.PropTypes.string,progress:f.PropTypes.number,xhr:f.PropTypes.object, -category:f.PropTypes.string,message:f.PropTypes.shape({type:f.PropTypes.string.isRequired,value:f.PropTypes.string.isRequired})}),actions:f.PropTypes.object,securityID:f.PropTypes.string,confirm:f.PropTypes.func -},w.defaultProps={extraClass:"",className:"",data:{},upload:{},confirm:window.confirm},t["default"]=(0,b.connect)(l,u)(w)},function(e,t,n){"use strict" +data:f.PropTypes.shape({parentid:f.PropTypes.number,url:f.PropTypes.string,exists:f.PropTypes.bool,preview:f.PropTypes.string,category:f.PropTypes.string,nameField:f.PropTypes.string,uploadFileEndpoint:f.PropTypes.shape({ +url:f.PropTypes.string.isRequired,method:f.PropTypes.string.isRequired,payloadFormat:f.PropTypes.string}),initialValues:f.PropTypes.object}).isRequired,upload:f.PropTypes.shape({url:f.PropTypes.string, +progress:f.PropTypes.number,xhr:f.PropTypes.object,category:f.PropTypes.string,message:f.PropTypes.shape({type:f.PropTypes.string.isRequired,value:f.PropTypes.string.isRequired})}),actions:f.PropTypes.object, +securityID:f.PropTypes.string,confirm:f.PropTypes.func},w.defaultProps={extraClass:"",className:"",data:{},upload:{},confirm:function P(e){return window.confirm(e)}},t["default"]=(0,b.connect)(l,u)(w)},function(e,t,n){ +"use strict" function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){return{type:u["default"].PREVIEWFIELD_REMOVE_FILE,payload:{id:e}}}function i(e,t){return{type:u["default"].PREVIEWFIELD_ADD_FILE,payload:{ id:e,file:t}}}function s(e,t){return{type:u["default"].PREVIEWFIELD_FAIL_UPLOAD,payload:{id:e,message:t}}}function a(e,t){return{type:u["default"].PREVIEWFIELD_UPDATE_FILE,payload:{id:e,data:t}}}Object.defineProperty(t,"__esModule",{ value:!0}),t.removeFile=o,t.addFile=i,t.failUpload=s,t.updateFile=a diff --git a/client/dist/styles/bundle.css b/client/dist/styles/bundle.css index 40ed20837..291c8c712 100644 --- a/client/dist/styles/bundle.css +++ b/client/dist/styles/bundle.css @@ -719,7 +719,7 @@ } .preview__toolbar-button{ - padding:11px; + padding:11px 0; height:47px; width:47px; overflow:hidden; @@ -735,7 +735,7 @@ } .preview__toolbar-button:before{ - margin-right:11px; + padding:0 11px; font-family:silverstripe; font-style:normal; speak:none; @@ -771,6 +771,7 @@ left:0; right:0; opacity:.8; + padding:1.5385rem; } .preview__message--error{ @@ -1140,6 +1141,7 @@ @media (min-width:992px){ .editor{ + box-sizing:content-box; width:300px; border-left:1px solid #d9dee2; position:relative; @@ -1441,5 +1443,6 @@ position:absolute; top:0; left:-.76925rem; + z-index:2; } diff --git a/client/lang/en.js b/client/lang/en.js index 8775dea99..13d548218 100644 --- a/client/lang/en.js +++ b/client/lang/en.js @@ -42,6 +42,7 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') { "AssetAdmin.LOADMORE": "Load more", "AssetAdmin.NOITEMSFOUND": "No items found", "AssetAdmin.PROMPTFOLDERNAME": "Please enter a folder name (or blank to cancel)", + "AssetAdmin.REPlACE_FILE_SUCCESS": "Upload successful, the file will be replaced when you Save.", "AssetAdmin.SAVE": "Save", "AssetAdmin.SELECT": "Select", "AssetAdmin.SIZE": "Size", diff --git a/client/lang/src/en.js b/client/lang/src/en.js index a249e0ee1..e356c57df 100644 --- a/client/lang/src/en.js +++ b/client/lang/src/en.js @@ -35,6 +35,7 @@ "AssetAdmin.LOADMORE": "Load more", "AssetAdmin.NOITEMSFOUND": "No items found", "AssetAdmin.PROMPTFOLDERNAME": "Please enter a folder name (or blank to cancel)", + "AssetAdmin.REPlACE_FILE_SUCCESS": "Upload successful, the file will be replaced when you Save.", "AssetAdmin.SAVE": "Save", "AssetAdmin.SELECT": "Select", "AssetAdmin.SIZE": "Size", diff --git a/client/src/components/AssetDropzone/AssetDropzone.js b/client/src/components/AssetDropzone/AssetDropzone.js index 31073f686..ece3e7b97 100644 --- a/client/src/components/AssetDropzone/AssetDropzone.js +++ b/client/src/components/AssetDropzone/AssetDropzone.js @@ -51,6 +51,12 @@ class AssetDropzone extends SilverStripeComponent { this.props.options )); + // attach the name as a class to the hidden input for easier identification + const name = this.props.name; + if (name) { + this.dropzone.hiddenFileInput.classList.add(`dz-input-${name}`); + } + // Set the user warning displayed when a user attempts to remove a file. // If the props hasn't been passed there will be no warning when removing files. if (typeof this.props.promptOnRemove !== 'undefined') { diff --git a/client/src/components/PreviewImageField/PreviewImageField.js b/client/src/components/PreviewImageField/PreviewImageField.js index 7f8a811eb..976dcf2ab 100644 --- a/client/src/components/PreviewImageField/PreviewImageField.js +++ b/client/src/components/PreviewImageField/PreviewImageField.js @@ -25,8 +25,15 @@ class PreviewImageField extends Component { this.props.actions.previewField.removeFile(this.props.id); } + componentWillReceiveProps(nextProps) { + if (this.props.data.url && nextProps.data.url !== this.props.data.url) { + this.props.actions.previewField.removeFile(this.props.id); + } + } + getDropzoneProps() { const endpoint = this.props.data.uploadFileEndpoint; + const name = this.props.name; const options = { url: endpoint && endpoint.url, method: endpoint && endpoint.method, @@ -48,6 +55,7 @@ class PreviewImageField extends Component { ]; return { + name, className: classNames.join(' '), canUpload: endpoint && this.canEdit(), preview, @@ -153,6 +161,13 @@ class PreviewImageField extends Component { this.props.onAutofill('FileFilename', json.Filename); this.props.onAutofill('FileHash', json.Hash); this.props.onAutofill('FileVariant', json.Variant); + + const oldExt = getFileExtension(this.props.data.url); + const newExt = getFileExtension(json.Filename); + if (oldExt !== newExt) { + // name field to autofill if extension has changed + this.props.onAutofill(this.props.data.nameField, json.Name); + } } } @@ -209,11 +224,24 @@ class PreviewImageField extends Component {
) : null; const message = this.props.upload.message; - const messageBox = (message) ? ( -
- {message.value} -
- ) : null; + let messageBox = null; + + if (message) { + messageBox = ( +
+ {message.value} +
+ ); + } else if (progress === 100) { + messageBox = ( +
+ {i18n._t( + "AssetAdmin.REPlACE_FILE_SUCCESS", + "Upload successful, the file will be replaced when you Save." + )} +
+ ) + } return (
@@ -297,6 +325,7 @@ PreviewImageField.propTypes = { exists: PropTypes.bool, preview: PropTypes.string, category: PropTypes.string, + nameField: PropTypes.string, uploadFileEndpoint: PropTypes.shape({ url: PropTypes.string.isRequired, method: PropTypes.string.isRequired, @@ -325,7 +354,8 @@ PreviewImageField.defaultProps = { className: '', data: {}, upload: {}, - confirm: window.confirm, + // eslint-disable-next-line no-alert + confirm: (msg) => window.confirm(msg), }; function mapStateToProps(state, ownprops) { diff --git a/client/src/components/PreviewImageField/PreviewImageField.scss b/client/src/components/PreviewImageField/PreviewImageField.scss index ca049fa74..88b024570 100644 --- a/client/src/components/PreviewImageField/PreviewImageField.scss +++ b/client/src/components/PreviewImageField/PreviewImageField.scss @@ -27,7 +27,7 @@ } .preview__toolbar-button { - padding: $toolbar-button-padding; + padding: $toolbar-button-padding 0; height: $toolbar-button-height + (2 * $toolbar-button-padding); width: $toolbar-button-width + (2 * $toolbar-button-padding); overflow: hidden; @@ -45,7 +45,7 @@ } &:before { - margin-right: $toolbar-button-padding; + padding: 0 $toolbar-button-padding; font-family: "silverstripe"; font-style: normal; speak: none; @@ -82,6 +82,7 @@ left: 0; right: 0; opacity: .8; + padding: $panel-padding-y $panel-padding-x; } .preview__message--error { diff --git a/client/src/components/UploadField/UploadField.js b/client/src/components/UploadField/UploadField.js index 05bfa49b5..cba65b2e4 100644 --- a/client/src/components/UploadField/UploadField.js +++ b/client/src/components/UploadField/UploadField.js @@ -193,6 +193,7 @@ class UploadField extends SilverStripeComponent { height: CONSTANTS.SMALL_THUMBNAIL_HEIGHT, width: CONSTANTS.SMALL_THUMBNAIL_WIDTH, }; + const name = this.props.name; const dropzoneOptions = { url: this.props.data.createFileEndpoint.url, method: this.props.data.createFileEndpoint.method, @@ -215,6 +216,7 @@ class UploadField extends SilverStripeComponent { // @todo add ` or add from files` once we implement file dialog return ( addHeader('Content-Type', 'application/json'); } + $tuple['Name'] = basename($tuple['Filename']); return (new HTTPResponse(json_encode($tuple))) ->addHeader('Content-Type', 'application/json'); } @@ -1103,7 +1104,19 @@ protected function saveOrPublish($data, $form, $doPublish = false) return (new HTTPResponse(json_encode(['status' => 'error']), 401)) ->addHeader('Content-Type', 'application/json'); } - + + // check File extension + $extension = File::get_file_extension($data['FileFilename']); + $newClass = File::get_class_for_file_extension($extension); + // if the class has changed, cast it to the proper class + if ($record->getClassName() !== $newClass) { + $record = $record->newClassInstance($newClass); + + // update the allowed category for the new file extension + $category = File::get_app_category($extension); + $record->File->setAllowedCategories($category); + } + $form->saveInto($record); $record->write(); diff --git a/code/Forms/FileHistoryFormFactory.php b/code/Forms/FileHistoryFormFactory.php index 0c43a575f..e7ef8e889 100644 --- a/code/Forms/FileHistoryFormFactory.php +++ b/code/Forms/FileHistoryFormFactory.php @@ -54,7 +54,9 @@ protected function getFormFields(Controller $controller, $name, $context = []) $record = $context['Record']; $fields = new FieldList( - LiteralField::create('Thumbnail', $this->getIconMarkup($record)), + PreviewImageField::create('PreviewImage') + ->setRecordID($record->ID) + ->addExtraClass('editor__file-preview'), LiteralField::create('FileSpecs', $this->getSpecsMarkup($record)), ReadonlyField::create("Title", File::singleton()->fieldLabel('Title')), ReadonlyField::create('Name', File::singleton()->fieldLabel('Filename')), diff --git a/code/Forms/PreviewImageField.php b/code/Forms/PreviewImageField.php index 56e249c4c..c058f5936 100644 --- a/code/Forms/PreviewImageField.php +++ b/code/Forms/PreviewImageField.php @@ -52,11 +52,18 @@ public function getSchemaStateDefaults() 'FileHash' => $record->FileHash, 'FileVariant' => $record->FileVariant, ], + 'nameField' => 'Name', ]); } return $defaults; } + public function performReadonlyTransformation() { + $this->setReadonly(true); + + return $this; + } + /** * @return DataObject */ diff --git a/tests/behat/features/manage-files.feature b/tests/behat/features/manage-files.feature index 6ad968293..3dc98ed89 100644 --- a/tests/behat/features/manage-files.feature +++ b/tests/behat/features/manage-files.feature @@ -26,7 +26,7 @@ Feature: Manage files Scenario: I can upload a file to a folder When I click on the file named "folder1" in the gallery - And I attach the file "testfile.jpg" to dropzone + And I attach the file "testfile.jpg" to dropzone "gallery-container" Then I should see the file named "testfile" in the gallery Scenario: I can edit a file diff --git a/tests/behat/features/replace-file.feature b/tests/behat/features/replace-file.feature new file mode 100644 index 000000000..629b217c1 --- /dev/null +++ b/tests/behat/features/replace-file.feature @@ -0,0 +1,36 @@ +@assets +Feature: Replace a file with a new file + As a cms author + I want to upload a new file that will replace an existing file + + Background: + Given a "image" "folder1/file1.jpg" + And a "image" "folder1/file2.jpg" + And I am logged in with "ADMIN" permissions + And I go to "/admin/assets" + And I select the file named "folder1" in the gallery + And I click on the file named "file1" in the gallery + And I wait for 2 second + Then I should see an "#Form_fileEditForm" element + + @javascript + Scenario: I upload a file into the file I have open + When I attach the file "testfile.jpg" to dropzone "PreviewImage" + And I wait for 1 second + Then I should see a ".preview__toolbar-button--remove" element + And I should see a ".preview__message--success" element + And I press the "Save" button + Then I should not see a ".preview__message--success" element + And I should not see a ".preview__toolbar-button--remove" element + + @javascript @modal + Scenario: I upload a pdf file to replace an image file + When I attach the file "document.pdf" to dropzone "PreviewImage" + And I confirm the dialog + And I wait for 1 second + Then I should see a ".preview__message--success" element + And the "Name" field should contain "document.pdf" + And I press the "Save" button + And I wait for 1 second + Then the "Name" field should contain "document.pdf" + And I should not see a ".preview__message--success" element diff --git a/tests/behat/src/Context/FixtureContext.php b/tests/behat/src/Context/FixtureContext.php index 356cdea0c..b34ba2517 100644 --- a/tests/behat/src/Context/FixtureContext.php +++ b/tests/behat/src/Context/FixtureContext.php @@ -96,10 +96,10 @@ public function iClickOnTheLatestHistoryItem() } /** - * @Given /^I attach the file "([^"]*)" to dropzone$/ + * @Given /^I attach the file "([^"]*)" to dropzone "([^"]*)"$/ * @see MinkContext::attachFileToField() */ - public function iAttachTheFileToDropzone($path) + public function iAttachTheFileToDropzone($path, $name) { // Get path $filesPath = $this->getMainContext()->getMinkParameter('files_path'); @@ -110,9 +110,14 @@ public function iAttachTheFileToDropzone($path) } } + assertFileExists($path, "$path does not exist"); // Find field - $input = $this->getSession()->getPage()->find('css', 'input[type="file"].dz-hidden-input'); - assertNotNull($input, "Could not find dz-hidden-input"); + $selector = "input[type=\"file\"].dz-hidden-input.dz-input-{$name}"; + + /** @var DocumentElement $page */ + $page = $this->getSession()->getPage(); + $input = $page->find('css', $selector); + assertNotNull($input, "Could not find {$selector}"); // Make visible temporarily while attaching $this->getSession()->executeScript( @@ -124,18 +129,9 @@ public function iAttachTheFileToDropzone($path) EOS ); + assert($input->isVisible()); // Attach via html5 $input->attachFile($path); - - // Restore hidden state - $this->getSession()->executeScript( - << Date: Tue, 20 Dec 2016 15:13:51 +1300 Subject: [PATCH 08/13] Fix PHP PSR2 linting issues --- code/Controller/AssetAdmin.php | 5 +++-- code/Forms/PreviewImageField.php | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/code/Controller/AssetAdmin.php b/code/Controller/AssetAdmin.php index ce11c479f..e67ae6b66 100644 --- a/code/Controller/AssetAdmin.php +++ b/code/Controller/AssetAdmin.php @@ -462,7 +462,8 @@ public function apiCreateFile(HTTPRequest $request) * @param HTTPRequest $request * @return HTTPRequest|HTTPResponse */ - public function apiUploadFile(HTTPRequest $request) { + public function apiUploadFile(HTTPRequest $request) + { $data = $request->postVars(); $upload = $this->getUpload(); @@ -480,7 +481,7 @@ public function apiUploadFile(HTTPRequest $request) { } $tmpFile = $data['Upload']; - if(!$upload->validate($tmpFile)) { + if (!$upload->validate($tmpFile)) { $result = ['message' => null]; $errors = $upload->getErrors(); if ($message = array_shift($errors)) { diff --git a/code/Forms/PreviewImageField.php b/code/Forms/PreviewImageField.php index c058f5936..b5ade3108 100644 --- a/code/Forms/PreviewImageField.php +++ b/code/Forms/PreviewImageField.php @@ -58,7 +58,8 @@ public function getSchemaStateDefaults() return $defaults; } - public function performReadonlyTransformation() { + public function performReadonlyTransformation() + { $this->setReadonly(true); return $this; From 4c01e42fc8b4d8d6830a00c0afcf311d0d41e703 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Tue, 20 Dec 2016 15:21:58 +1300 Subject: [PATCH 09/13] Fix NPM test for PreviewImageField-test.js --- .../PreviewImageField/tests/PreviewImageField-test.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/src/components/PreviewImageField/tests/PreviewImageField-test.js b/client/src/components/PreviewImageField/tests/PreviewImageField-test.js index e5567fe17..df6b4933b 100644 --- a/client/src/components/PreviewImageField/tests/PreviewImageField-test.js +++ b/client/src/components/PreviewImageField/tests/PreviewImageField-test.js @@ -18,6 +18,7 @@ describe('PreviewImageField', () => { data: { category: 'file', exists: true, + nameField: 'Name', }, onAutofill: jest.genMockFunction(), actions: { @@ -34,6 +35,7 @@ describe('PreviewImageField', () => { xhr: { response: JSON.stringify({ Filename: 'abc.jpg', + Name: 'abc.jpg', Hash: 'zxcvqwer', Variant: '123', }), @@ -44,13 +46,15 @@ describe('PreviewImageField', () => { ); item.handleSuccessfulUpload(fileXhr); - expect(props.onAutofill.mock.calls.length).toBe(3); + expect(props.onAutofill.mock.calls.length).toBe(4); expect(props.onAutofill.mock.calls[0][0]).toBe('FileFilename'); expect(props.onAutofill.mock.calls[0][1]).toBe('abc.jpg'); expect(props.onAutofill.mock.calls[1][0]).toBe('FileHash'); expect(props.onAutofill.mock.calls[1][1]).toBe('zxcvqwer'); expect(props.onAutofill.mock.calls[2][0]).toBe('FileVariant'); expect(props.onAutofill.mock.calls[2][1]).toBe('123'); + expect(props.onAutofill.mock.calls[3][0]).toBe('Name'); + expect(props.onAutofill.mock.calls[3][1]).toBe('abc.jpg'); }); }); From 1f3ab9337f2afc91bd563b25ddb6ac2700f07396 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Tue, 20 Dec 2016 15:27:04 +1300 Subject: [PATCH 10/13] Fix js linting issues --- client/dist/js/bundle.js | 10 +++++----- .../PreviewImageField/PreviewImageField.js | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index c2efe5190..e3f52bb66 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -1788,8 +1788,8 @@ r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Obj function t(e){i(this,t) var n=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e)) return n.handleAddedFile=n.handleAddedFile.bind(n),n.handleFailedUpload=n.handleFailedUpload.bind(n),n.handleSuccessfulUpload=n.handleSuccessfulUpload.bind(n),n.handleSending=n.handleSending.bind(n),n.handleUploadProgress=n.handleUploadProgress.bind(n), -n.handleCancelUpload=n.handleCancelUpload.bind(n),n.handleRemoveErroredUpload=n.handleRemoveErroredUpload.bind(n),n.canFileUpload=n.canFileUpload.bind(n),n}return a(t,e),p(t,[{key:"componentWillUnmount", -value:function n(){this.props.actions.previewField.removeFile(this.props.id)}},{key:"componentWillReceiveProps",value:function r(e){this.props.data.url&&e.data.url!==this.props.data.url&&this.props.actions.previewField.removeFile(this.props.id) +n.handleCancelUpload=n.handleCancelUpload.bind(n),n.handleRemoveErroredUpload=n.handleRemoveErroredUpload.bind(n),n.canFileUpload=n.canFileUpload.bind(n),n}return a(t,e),p(t,[{key:"componentWillReceiveProps", +value:function n(e){this.props.data.url&&e.data.url!==this.props.data.url&&this.props.actions.previewField.removeFile(this.props.id)}},{key:"componentWillUnmount",value:function r(){this.props.actions.previewField.removeFile(this.props.id) }},{key:"getDropzoneProps",value:function o(){var e=this.props.data.uploadFileEndpoint,t=this.props.name,n={url:e&&e.url,method:e&&e.method,paramName:"Upload",clickable:"#preview-replace-button",maxFiles:1 },r={height:v["default"].THUMBNAIL_HEIGHT,width:v["default"].THUMBNAIL_WIDTH},o=this.props.securityID,i=["asset-dropzone--button","preview__container",this.props.className,this.props.extraClass] @@ -1813,9 +1813,9 @@ var n=(0,S.getFileExtension)(this.props.data.url),r=(0,S.getFileExtension)(t.Fil n!==r&&this.props.onAutofill(this.props.data.nameField,t.Name)}}},{key:"handleSending",value:function E(e,t){this.props.actions.previewField.updateFile(this.props.id,{xhr:t})}},{key:"handleUploadProgress", value:function _(e,t){this.props.actions.previewField.updateFile(this.props.id,{progress:t})}},{key:"renderImage",value:function w(){var e=this.props.data if(!e.exists&&!this.props.upload.url)return h["default"].createElement("div",{className:"editor__file-preview-message--file-missing"},c["default"]._t("AssetAdmin.FILE_MISSING","File cannot be found")) -var t=this.props.upload.category,n=t&&"image"!==t?v["default"].DEFAULT_PREVIEW:this.props.upload.url||e.preview||e.url,r=h["default"].createElement("img",{src:n,className:"editor__thumbnail"}),o=e.url?h["default"].createElement("a",{ -className:"editor__file-preview-link",href:e.url,target:"_blank"},r):null,i=this.props.upload.progress,s=i>0&&i<100?h["default"].createElement("div",{className:"preview__progress"},h["default"].createElement("div",{ -className:"preview__progress-bar",style:{width:i+"%"}})):null,a=this.props.upload.message,l=null +var t=this.props.upload.category,n=t&&"image"!==t?v["default"].DEFAULT_PREVIEW:this.props.upload.url||e.preview||e.url,r=h["default"].createElement("img",{alt:"preview",src:n,className:"editor__thumbnail" +}),o=e.url?h["default"].createElement("a",{className:"editor__file-preview-link",href:e.url,target:"_blank"},r):null,i=this.props.upload.progress,s=i>0&&i<100?h["default"].createElement("div",{className:"preview__progress" +},h["default"].createElement("div",{className:"preview__progress-bar",style:{width:i+"%"}})):null,a=this.props.upload.message,l=null return a?l=h["default"].createElement("div",{className:"preview__message preview__message--"+a.type},a.value):100===i&&(l=h["default"].createElement("div",{className:"preview__message preview__message--success" },c["default"]._t("AssetAdmin.REPlACE_FILE_SUCCESS","Upload successful, the file will be replaced when you Save."))),h["default"].createElement("div",{className:"editor__thumbnail-container"},o||r,s,l) diff --git a/client/src/components/PreviewImageField/PreviewImageField.js b/client/src/components/PreviewImageField/PreviewImageField.js index 976dcf2ab..4639967ef 100644 --- a/client/src/components/PreviewImageField/PreviewImageField.js +++ b/client/src/components/PreviewImageField/PreviewImageField.js @@ -21,16 +21,16 @@ class PreviewImageField extends Component { this.canFileUpload = this.canFileUpload.bind(this); } - componentWillUnmount() { - this.props.actions.previewField.removeFile(this.props.id); - } - componentWillReceiveProps(nextProps) { if (this.props.data.url && nextProps.data.url !== this.props.data.url) { this.props.actions.previewField.removeFile(this.props.id); } } + componentWillUnmount() { + this.props.actions.previewField.removeFile(this.props.id); + } + getDropzoneProps() { const endpoint = this.props.data.uploadFileEndpoint; const name = this.props.name; @@ -211,7 +211,7 @@ class PreviewImageField extends Component { const preview = (category && category !== 'image') ? CONSTANTS.DEFAULT_PREVIEW : this.props.upload.url || data.preview || data.url; - const image = ; + const image = preview; const linkedImage = (data.url) ? ( {image} @@ -236,11 +236,11 @@ class PreviewImageField extends Component { messageBox = (
{i18n._t( - "AssetAdmin.REPlACE_FILE_SUCCESS", - "Upload successful, the file will be replaced when you Save." + 'AssetAdmin.REPlACE_FILE_SUCCESS', + 'Upload successful, the file will be replaced when you Save.' )}
- ) + ); } return ( From 379d29500dc89aa0480afeb1d2daa96efa2571c3 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Tue, 20 Dec 2016 15:45:36 +1300 Subject: [PATCH 11/13] Document apiUploadFile --- code/Controller/AssetAdmin.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/code/Controller/AssetAdmin.php b/code/Controller/AssetAdmin.php index e67ae6b66..b10cd521e 100644 --- a/code/Controller/AssetAdmin.php +++ b/code/Controller/AssetAdmin.php @@ -455,9 +455,9 @@ public function apiCreateFile(HTTPRequest $request) return (new HTTPResponse(json_encode($result))) ->addHeader('Content-Type', 'application/json'); } - + /** - * Creates a single file based on a form-urlencoded upload. + * Upload a new asset for a pre-existing record. Returns the asset tuple. * * @param HTTPRequest $request * @return HTTPRequest|HTTPResponse @@ -466,20 +466,20 @@ public function apiUploadFile(HTTPRequest $request) { $data = $request->postVars(); $upload = $this->getUpload(); - + // CSRF check $token = SecurityToken::inst(); if (empty($data[$token->getName()]) || !$token->check($data[$token->getName()])) { return new HTTPResponse(null, 400); } - + // Check parent record /** @var Folder $parentRecord */ $parentRecord = null; if (!empty($data['ParentID']) && is_numeric($data['ParentID'])) { $parentRecord = Folder::get()->byID($data['ParentID']); } - + $tmpFile = $data['Upload']; if (!$upload->validate($tmpFile)) { $result = ['message' => null]; @@ -493,9 +493,9 @@ public function apiUploadFile(HTTPRequest $request) return (new HTTPResponse(json_encode($result), 400)) ->addHeader('Content-Type', 'application/json'); } - + $folder = $parentRecord ? $parentRecord->getFilename() : '/'; - + try { $tuple = $upload->load($tmpFile, $folder); } catch (Exception $e) { @@ -508,7 +508,7 @@ public function apiUploadFile(HTTPRequest $request) return (new HTTPResponse(json_encode($result), 400)) ->addHeader('Content-Type', 'application/json'); } - + if ($upload->isError()) { $result['message'] = [ 'type' => 'error', @@ -517,12 +517,12 @@ public function apiUploadFile(HTTPRequest $request) return (new HTTPResponse(json_encode($result), 400)) ->addHeader('Content-Type', 'application/json'); } - + $tuple['Name'] = basename($tuple['Filename']); return (new HTTPResponse(json_encode($tuple))) ->addHeader('Content-Type', 'application/json'); } - + /** * Returns a JSON array for history of a given file ID. Returns a list of all the history. * @@ -1105,19 +1105,19 @@ protected function saveOrPublish($data, $form, $doPublish = false) return (new HTTPResponse(json_encode(['status' => 'error']), 401)) ->addHeader('Content-Type', 'application/json'); } - + // check File extension $extension = File::get_file_extension($data['FileFilename']); $newClass = File::get_class_for_file_extension($extension); // if the class has changed, cast it to the proper class if ($record->getClassName() !== $newClass) { $record = $record->newClassInstance($newClass); - + // update the allowed category for the new file extension $category = File::get_app_category($extension); $record->File->setAllowedCategories($category); } - + $form->saveInto($record); $record->write(); From 3f1d5e6a131e37416905d1713878444fba6d539d Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Tue, 20 Dec 2016 15:51:18 +1300 Subject: [PATCH 12/13] Disable preview on file replacement --- client/dist/js/bundle.js | 8 ++++---- .../src/components/PreviewImageField/PreviewImageField.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index e3f52bb66..0bbf7f37b 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -1814,10 +1814,10 @@ n!==r&&this.props.onAutofill(this.props.data.nameField,t.Name)}}},{key:"handleSe value:function _(e,t){this.props.actions.previewField.updateFile(this.props.id,{progress:t})}},{key:"renderImage",value:function w(){var e=this.props.data if(!e.exists&&!this.props.upload.url)return h["default"].createElement("div",{className:"editor__file-preview-message--file-missing"},c["default"]._t("AssetAdmin.FILE_MISSING","File cannot be found")) var t=this.props.upload.category,n=t&&"image"!==t?v["default"].DEFAULT_PREVIEW:this.props.upload.url||e.preview||e.url,r=h["default"].createElement("img",{alt:"preview",src:n,className:"editor__thumbnail" -}),o=e.url?h["default"].createElement("a",{className:"editor__file-preview-link",href:e.url,target:"_blank"},r):null,i=this.props.upload.progress,s=i>0&&i<100?h["default"].createElement("div",{className:"preview__progress" -},h["default"].createElement("div",{className:"preview__progress-bar",style:{width:i+"%"}})):null,a=this.props.upload.message,l=null -return a?l=h["default"].createElement("div",{className:"preview__message preview__message--"+a.type},a.value):100===i&&(l=h["default"].createElement("div",{className:"preview__message preview__message--success" -},c["default"]._t("AssetAdmin.REPlACE_FILE_SUCCESS","Upload successful, the file will be replaced when you Save."))),h["default"].createElement("div",{className:"editor__thumbnail-container"},o||r,s,l) +}),o=this.props.upload.progress,i=e.url&&!o?h["default"].createElement("a",{className:"editor__file-preview-link",href:e.url,target:"_blank"},r):null,s=o>0&&o<100?h["default"].createElement("div",{className:"preview__progress" +},h["default"].createElement("div",{className:"preview__progress-bar",style:{width:o+"%"}})):null,a=this.props.upload.message,l=null +return a?l=h["default"].createElement("div",{className:"preview__message preview__message--"+a.type},a.value):100===o&&(l=h["default"].createElement("div",{className:"preview__message preview__message--success" +},c["default"]._t("AssetAdmin.REPlACE_FILE_SUCCESS","Upload successful, the file will be replaced when you Save."))),h["default"].createElement("div",{className:"editor__thumbnail-container"},i||r,s,l) }},{key:"renderToolbar",value:function P(){var e=this.canEdit() return this.props.data.url||e?h["default"].createElement("div",{className:"preview__toolbar fill-height"},this.props.data.url?h["default"].createElement("a",{href:this.props.data.url,target:"_blank",className:"preview__toolbar-button--link preview__toolbar-button" diff --git a/client/src/components/PreviewImageField/PreviewImageField.js b/client/src/components/PreviewImageField/PreviewImageField.js index 4639967ef..0d81753c8 100644 --- a/client/src/components/PreviewImageField/PreviewImageField.js +++ b/client/src/components/PreviewImageField/PreviewImageField.js @@ -212,12 +212,12 @@ class PreviewImageField extends Component { ? CONSTANTS.DEFAULT_PREVIEW : this.props.upload.url || data.preview || data.url; const image = preview; - const linkedImage = (data.url) ? ( + const progress = this.props.upload.progress; + const linkedImage = (data.url && !progress) ? (
{image} ) : null; - const progress = this.props.upload.progress; const progressBar = (progress > 0 && progress < 100) ? (
From 2db9038e7b48664e8fd8e1922c8067a7f1fa768a Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Tue, 20 Dec 2016 16:10:10 +1300 Subject: [PATCH 13/13] FIX Regression in file size display in UploadFieldItem.js --- client/dist/js/bundle.js | 20 +++++++++---------- .../components/UploadField/UploadFieldItem.js | 7 ++++++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 0bbf7f37b..0abb586bc 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -1730,7 +1730,7 @@ return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("funct e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{ value:!0}) var a=function(){function e(e,t){for(var n=0;n0 -}},{key:"isImageSmallerThanThumbnail",value:function v(){if(!this.isImage()||!this.exists()&&!this.uploading())return!1 +}},{key:"isImageSmallerThanThumbnail",value:function b(){if(!this.isImage()||!this.exists()&&!this.uploading())return!1 var e=this.props.item.dimensions -return e&&e.height {this.props.item.title} - {this.props.item.extension}, {this.props.item.size} + {this.props.item.extension}{size}
);