mirror of
https://github.com/transloadit/uppy.git
synced 2026-07-21 01:15:35 +00:00
Extract metadata from form, closes #153
This PR adds support for extracting metadata from `<form>` elements. This was asked #153 and came up elsewhere too, I believe. You can use it like this: ```html <form class="MyForm" action="/"> <input type="file" /> <input type="hidden" name="bla" value="12333"> <input type="text" name="yo" value="1"> <button type="submit">Upload</button> </form> ``` ```js uppy.use(DragDrop, { target: '.MyForm', locale: { strings: {chooseFile: 'Выберите файл'} }, setMetaFromTargetForm: true }) ``` Then UI acquire type plugins, like Dashboard, FileInput and DragDrop, before mounting themselves or doing anything else, extract FormData from the `target` `<form>` element (it must be a form currently), and merge the object with the global `state.meta`. This is done via `setMeta` method on core. Then, when a file is added, `state.meta` is merged to that file’s meta right away. The ability to add more fields via UI plugin MetaData is also still there, though probably needs an update. In addition a new `meta` option is added in core: ```js const uppy = Uppy({ debug: true, autoProceed: true, meta: { username: 'Artur' } }) ``` This way some data can be set right away, it becomes the initial `state.meta` object.
This commit is contained in:
parent
362ed5ef9b
commit
6f80ea709c
9 changed files with 128 additions and 54 deletions
|
|
@ -7,12 +7,15 @@
|
|||
</head>
|
||||
<body>
|
||||
<h1>Uppy is here</h1>
|
||||
<button id="uppyModalOpener">Open Modal</button>
|
||||
<button id="uppyModalOpener">Choose Files</button>
|
||||
|
||||
<div class="Uppy">
|
||||
<form class="UppyForm" action="/">
|
||||
<form class="MyForm" action="/">
|
||||
<input type="file" />
|
||||
<input type="hidden" name="bla" value="12333">
|
||||
<input type="text" name="yo" value="1">
|
||||
<button type="submit">Upload</button>
|
||||
<input type="submit">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
const Uppy = require('../../src/core/Core.js')
|
||||
const Dashboard = require('../../src/plugins/Dashboard')
|
||||
const GoogleDrive = require('../../src/plugins/GoogleDrive')
|
||||
const Dropbox = require('../../src/plugins/Dropbox')
|
||||
const Webcam = require('../../src/plugins/Webcam')
|
||||
// const Dashboard = require('../../src/plugins/Dashboard')
|
||||
// const GoogleDrive = require('../../src/plugins/GoogleDrive')
|
||||
// const Dropbox = require('../../src/plugins/Dropbox')
|
||||
// const Webcam = require('../../src/plugins/Webcam')
|
||||
const Tus10 = require('../../src/plugins/Tus10')
|
||||
// const Multipart = require('../../src/plugins/Multipart')
|
||||
// const DragDrop = require('../../src/plugins/FileInput')
|
||||
const FileInput = require('../../src/plugins/FileInput')
|
||||
const MetaData = require('../../src/plugins/MetaData')
|
||||
// const Informer = require('../../src/plugins/Informer')
|
||||
// const StatusBar = require('../../src/plugins/StatusBar')
|
||||
|
|
@ -17,30 +19,44 @@ const TUS_ENDPOINT = PROTOCOL + '://master.tus.io/files/'
|
|||
// import MagicLog from '../../src/plugins/MagicLog'
|
||||
// import PersistentState from '../../src/plugins/PersistentState'
|
||||
|
||||
const uppy = Uppy({debug: true, autoProceed: false})
|
||||
.use(Dashboard, {
|
||||
trigger: '#uppyModalOpener',
|
||||
// maxWidth: 350,
|
||||
// maxHeight: 400,
|
||||
// inline: false,
|
||||
// disableStatusBar: true,
|
||||
// disableInformer: true,
|
||||
target: 'body',
|
||||
const uppy = Uppy({
|
||||
debug: true,
|
||||
autoProceed: true,
|
||||
meta: {
|
||||
username: 'Artur'
|
||||
}
|
||||
})
|
||||
// .use(Dashboard, {
|
||||
// trigger: '#uppyModalOpener',
|
||||
// // maxWidth: 350,
|
||||
// // maxHeight: 400,
|
||||
// inline: false,
|
||||
// // disableStatusBar: true,
|
||||
// // disableInformer: true,
|
||||
// setMetaFromTargetForm: true,
|
||||
// target: '.MyForm',
|
||||
// locale: {
|
||||
// strings: { browse: 'wow' }
|
||||
// }
|
||||
// })
|
||||
// .use(GoogleDrive, {target: Dashboard, host: 'http://localhost:3020'})
|
||||
// .use(Dropbox, {target: Dashboard, host: 'http://localhost:3020'})
|
||||
.use(FileInput, {
|
||||
target: '.MyForm',
|
||||
replaceTargetContent: false,
|
||||
setMetaFromTargetForm: true,
|
||||
locale: {
|
||||
strings: {browse: 'wow'}
|
||||
}
|
||||
})
|
||||
.use(GoogleDrive, {target: Dashboard, host: 'http://localhost:3020'})
|
||||
.use(Dropbox, {target: Dashboard, host: 'http://localhost:3020'})
|
||||
// .use(FileInput, {target: '.Uppy', locale: {
|
||||
// strings: {selectToUpload: 'Выберите файл для загрузки'}
|
||||
// }})
|
||||
// .use(DragDrop, {target: 'body', locale: {
|
||||
// strings: {chooseFile: 'Выберите файл'}
|
||||
// }})
|
||||
strings: { selectToUpload: 'Выберите файл для загрузки' }
|
||||
}})
|
||||
// .use(DragDrop, {
|
||||
// target: '.MyForm',
|
||||
// locale: {
|
||||
// strings: {chooseFile: 'Выберите файл'}
|
||||
// },
|
||||
// setMetaFromTargetForm: true
|
||||
// })
|
||||
// .use(ProgressBar, {target: 'body'})
|
||||
// .use(dummy)
|
||||
.use(Webcam, {target: Dashboard})
|
||||
// .use(Webcam, {target: Dashboard})
|
||||
// .use(Multipart, {endpoint: '//api2.transloadit.com'})
|
||||
.use(Tus10, {endpoint: TUS_ENDPOINT, resume: true})
|
||||
// .use(Informer, {target: Dashboard})
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ class Uppy {
|
|||
capabilities: {
|
||||
resumableUploads: false
|
||||
},
|
||||
totalProgress: 0
|
||||
totalProgress: 0,
|
||||
meta: Object.assign({}, this.opts.meta)
|
||||
}
|
||||
|
||||
// for debugging and testing
|
||||
|
|
@ -134,6 +135,11 @@ class Uppy {
|
|||
}
|
||||
}
|
||||
|
||||
setMeta (data) {
|
||||
const newMeta = Object.assign({}, this.getState().meta, data)
|
||||
this.setState({meta: newMeta})
|
||||
}
|
||||
|
||||
updateMeta (data, fileID) {
|
||||
const updatedFiles = Object.assign({}, this.getState().files)
|
||||
const newMeta = Object.assign({}, updatedFiles[fileID].meta, data)
|
||||
|
|
@ -159,9 +165,7 @@ class Uppy {
|
|||
id: fileID,
|
||||
name: fileName,
|
||||
extension: fileExtension || '',
|
||||
meta: {
|
||||
name: fileName
|
||||
},
|
||||
meta: Object.assign({}, this.getState().metaData),
|
||||
type: {
|
||||
general: fileTypeGeneral,
|
||||
specific: fileTypeSpecific
|
||||
|
|
@ -187,7 +191,7 @@ class Uppy {
|
|||
updatedFiles[fileID] = newFile
|
||||
this.setState({files: updatedFiles})
|
||||
|
||||
this.bus.emit('file-added', fileID)
|
||||
this.bus.emit('core:file-added', fileID)
|
||||
this.log(`Added file: ${fileName}, ${fileID}, mime type: ${fileType}`)
|
||||
|
||||
if (this.opts.autoProceed && !this.scheduledAutoProceed) {
|
||||
|
|
@ -526,16 +530,18 @@ class Uppy {
|
|||
if (!this.opts.debug) {
|
||||
return
|
||||
}
|
||||
|
||||
if (type === 'error') {
|
||||
console.error(`LOG: ${msg}`)
|
||||
return
|
||||
}
|
||||
|
||||
if (msg === `${msg}`) {
|
||||
console.log(`LOG: ${msg}`)
|
||||
} else {
|
||||
console.dir(msg)
|
||||
}
|
||||
|
||||
if (type === 'error') {
|
||||
console.error(`LOG: ${msg}`)
|
||||
}
|
||||
|
||||
global.uppyLog = global.uppyLog + '\n' + 'DEBUG LOG: ' + msg
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -401,6 +401,27 @@ function findDOMElement (element) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata object from a form element
|
||||
*
|
||||
* @param {Node|string} element
|
||||
* @return {Object|null}
|
||||
*/
|
||||
|
||||
function getMetaFromForm (element) {
|
||||
if (!element || element.tagName !== 'FORM') {
|
||||
console.error('Metadata can only be extracted from form elements')
|
||||
return null
|
||||
}
|
||||
|
||||
var formData = new FormData(element)
|
||||
var result = {}
|
||||
for (var entry of formData.entries()) {
|
||||
result[entry[0]] = entry[1]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function getSocketHost (url) {
|
||||
// get the host domain
|
||||
var regex = /^(?:https?:\/\/|\/\/)?(?:[^@\n]+@)?(?:www\.)?([^\n]+)/
|
||||
|
|
@ -451,6 +472,7 @@ module.exports = {
|
|||
copyToClipboard,
|
||||
prettyETA,
|
||||
findDOMElement,
|
||||
getMetaFromForm,
|
||||
getSocketHost,
|
||||
emitSocketProgress
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ module.exports = class DashboardUI extends Plugin {
|
|||
semiTransparent: false,
|
||||
defaultTabIcon: defaultTabIcon(),
|
||||
showProgressDetails: false,
|
||||
setMetaFromTargetForm: false,
|
||||
locale: defaultLocale
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +89,7 @@ module.exports = class DashboardUI extends Plugin {
|
|||
if (callerPluginType !== 'acquirer' &&
|
||||
callerPluginType !== 'progressindicator' &&
|
||||
callerPluginType !== 'presenter') {
|
||||
let msg = 'Error: Modal can only be used by plugins of types: acquirer, progressindicator, presenter'
|
||||
let msg = 'Dashboard: Modal can only be used by plugins of types: acquirer, progressindicator, presenter'
|
||||
this.core.log(msg)
|
||||
return
|
||||
}
|
||||
|
|
@ -162,9 +163,9 @@ module.exports = class DashboardUI extends Plugin {
|
|||
// focus on modal inner block
|
||||
this.target.querySelector('.UppyDashboard-inner').focus()
|
||||
|
||||
this.updateDashboardElWidth()
|
||||
// this.updateDashboardElWidth()
|
||||
// to be sure, sometimes when the function runs, container size is still 0
|
||||
setTimeout(this.updateDashboardElWidth, 300)
|
||||
setTimeout(this.updateDashboardElWidth, 500)
|
||||
}
|
||||
|
||||
// Close the Modal on esc key press
|
||||
|
|
@ -175,14 +176,14 @@ module.exports = class DashboardUI extends Plugin {
|
|||
}
|
||||
|
||||
initEvents () {
|
||||
// const dashboardEl = this.target.querySelector(`${this.opts.target} .UppyDashboard`)
|
||||
|
||||
// Modal open button
|
||||
const showModalTrigger = findDOMElement(this.opts.trigger)
|
||||
if (!this.opts.inline && showModalTrigger) {
|
||||
showModalTrigger.addEventListener('click', this.showModal)
|
||||
} else {
|
||||
this.core.log('Modal trigger wasn’t found')
|
||||
}
|
||||
|
||||
if (!this.opts.inline && !showModalTrigger) {
|
||||
console.error('Dashboard modal trigger not found, you won’t be able to select files. Make sure `trigger` is set correctly in Dashboard options')
|
||||
}
|
||||
|
||||
document.body.addEventListener('keyup', this.handleEscapeKeyPress)
|
||||
|
|
@ -223,7 +224,7 @@ module.exports = class DashboardUI extends Plugin {
|
|||
|
||||
updateDashboardElWidth () {
|
||||
const dashboardEl = this.target.querySelector('.UppyDashboard-inner')
|
||||
// console.log(dashboardEl.offsetWidth)
|
||||
this.core.log(`Dashboard width: ${dashboardEl.offsetWidth}`)
|
||||
|
||||
const modal = this.core.getState().modal
|
||||
this.core.setState({
|
||||
|
|
@ -303,9 +304,9 @@ module.exports = class DashboardUI extends Plugin {
|
|||
return target.type === 'progressindicator'
|
||||
})
|
||||
|
||||
const addFile = (file) => {
|
||||
this.core.emitter.emit('core:file-add', file)
|
||||
}
|
||||
// const addFile = (file) => {
|
||||
// this.core.emitter.emit('core:file-add', file)
|
||||
// }
|
||||
|
||||
const removeFile = (fileID) => {
|
||||
this.core.emitter.emit('core:file-remove', fileID)
|
||||
|
|
@ -364,7 +365,7 @@ module.exports = class DashboardUI extends Plugin {
|
|||
i18n: this.containerWidth,
|
||||
pauseAll: this.pauseAll,
|
||||
resumeAll: this.resumeAll,
|
||||
addFile: addFile,
|
||||
addFile: this.core.addFile,
|
||||
removeFile: removeFile,
|
||||
info: info,
|
||||
metaFields: state.metaFields,
|
||||
|
|
@ -392,6 +393,10 @@ module.exports = class DashboardUI extends Plugin {
|
|||
targets: []
|
||||
}})
|
||||
|
||||
if (this.opts.setMetaFromTargetForm) {
|
||||
this.setMetaFromTargetForm()
|
||||
}
|
||||
|
||||
const target = this.opts.target
|
||||
const plugin = this
|
||||
this.target = this.mount(target, plugin)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ module.exports = class DragDrop extends Plugin {
|
|||
// Default options
|
||||
const defaultOpts = {
|
||||
target: '.UppyDragDrop',
|
||||
setMetaFromTargetForm: false,
|
||||
locale: defaultLocale
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +144,10 @@ module.exports = class DragDrop extends Plugin {
|
|||
}
|
||||
|
||||
install () {
|
||||
if (this.opts.setMetaFromTargetForm) {
|
||||
this.setMetaFromTargetForm()
|
||||
}
|
||||
|
||||
const target = this.opts.target
|
||||
const plugin = this
|
||||
this.target = this.mount(target, plugin)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module.exports = class FileInput extends Plugin {
|
|||
constructor (core, opts) {
|
||||
super(core, opts)
|
||||
this.id = 'FileInput'
|
||||
this.title = 'FileInput'
|
||||
this.title = 'File Input'
|
||||
this.type = 'acquirer'
|
||||
|
||||
const defaultLocale = {
|
||||
|
|
@ -23,7 +23,8 @@ module.exports = class FileInput extends Plugin {
|
|||
multipleFiles: true,
|
||||
pretty: true,
|
||||
locale: defaultLocale,
|
||||
inputName: 'files[]'
|
||||
inputName: 'files[]',
|
||||
getMetaDataFromForm: false
|
||||
}
|
||||
|
||||
// Merge default options with the ones set by user
|
||||
|
|
@ -77,6 +78,10 @@ module.exports = class FileInput extends Plugin {
|
|||
}
|
||||
|
||||
install () {
|
||||
if (this.opts.setMetaFromTargetForm) {
|
||||
this.setMetaFromTargetForm()
|
||||
}
|
||||
|
||||
const target = this.opts.target
|
||||
const plugin = this
|
||||
this.target = this.mount(target, plugin)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ module.exports = class MetaData extends Plugin {
|
|||
metaFields: metaFields
|
||||
})
|
||||
|
||||
this.core.emitter.on('file-added', this.handleFileAdded)
|
||||
this.core.emitter.on('core:file-added', this.handleFileAdded)
|
||||
}
|
||||
|
||||
install () {
|
||||
|
|
@ -46,6 +46,6 @@ module.exports = class MetaData extends Plugin {
|
|||
}
|
||||
|
||||
uninstall () {
|
||||
this.core.emitter.off('file-added', this.handleFileAdded)
|
||||
this.core.emitter.off('core:file-added', this.handleFileAdded)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
const yo = require('yo-yo')
|
||||
const nanoraf = require('nanoraf')
|
||||
const { findDOMElement } = require('../core/Utils')
|
||||
const { findDOMElement, getMetaFromForm } = require('../core/Utils')
|
||||
|
||||
/**
|
||||
* Boilerplate that all Plugins share - and should not be used
|
||||
|
|
@ -89,6 +89,18 @@ module.exports = class Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
setMetaFromTargetForm () {
|
||||
const el = findDOMElement(this.opts.target)
|
||||
const formMetaData = getMetaFromForm(el)
|
||||
if (formMetaData) {
|
||||
this.core.log('Adding metadata from form:')
|
||||
this.core.log(formMetaData)
|
||||
this.core.setMeta(formMetaData)
|
||||
} else {
|
||||
this.core.log('Couldn’t extract metadata from form')
|
||||
}
|
||||
}
|
||||
|
||||
// focus () {
|
||||
// return
|
||||
// }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue