Pārlūkot izejas kodu

Merge branch 'master' into bugfix/add-file-promises

Artur Paikin 7 gadi atpakaļ
vecāks
revīzija
f32ba35e83

+ 2 - 0
CHANGELOG.md

@@ -62,6 +62,7 @@ Sort of like jQuery UI: https://jqueryui.com/download/
 - [ ] provider: Add Facebook
 - [ ] provider: Add Facebook
 - [ ] provider: Add OneDrive
 - [ ] provider: Add OneDrive
 - [ ] provider: Add Box
 - [ ] provider: Add Box
+- [ ] provider: change ProviderViews signature to receive Provider instance in second param. ref https://github.com/transloadit/uppy/pull/743#discussion_r180106070
 
 
 ## 1.0 Goals
 ## 1.0 Goals
 
 
@@ -123,6 +124,7 @@ To be released: 2018-03-29.
 - [ ] test: add typescript with JSDoc (@arturi)
 - [ ] test: add typescript with JSDoc (@arturi)
 - [ ] dragdrop: allow customizing arrow icon https://github.com/transloadit/uppy/pull/374#issuecomment-334116208 (@arturi)
 - [ ] dragdrop: allow customizing arrow icon https://github.com/transloadit/uppy/pull/374#issuecomment-334116208 (@arturi)
 - [x] core: ⚠️ **breaking** `onBeforeFileAdded()`, `onBeforeUpload()` and `addFile()` are now synchronous. You can no longer return a Promise from the `onBefore*()` functions. (#294, @goto-bus-stop)
 - [x] core: ⚠️ **breaking** `onBeforeFileAdded()`, `onBeforeUpload()` and `addFile()` are now synchronous. You can no longer return a Promise from the `onBefore*()` functions. (#294, @goto-bus-stop)
+- [x] react: Allow overriding `<DashboardModal />` `target` prop (#740, @goto-bus-stop)
 
 
 ## 0.23.3
 ## 0.23.3
 
 

+ 1 - 1
examples/aws-presigned-url/index.html

@@ -3,7 +3,7 @@
   <head>
   <head>
     <meta charset="utf-8">
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>Uppy AWS Example</title>
+    <title>Uppy AWS Presigned URL Example</title>
     <link href="uppy.min.css" rel="stylesheet">
     <link href="uppy.min.css" rel="stylesheet">
   </head>
   </head>
   <body>
   <body>

+ 1 - 1
examples/aws-presigned-url/package.json

@@ -1,6 +1,6 @@
 {
 {
   "private": true,
   "private": true,
-  "name": "uppy-aws-php-example",
+  "name": "uppy-aws-presigned-url-example",
   "scripts": {
   "scripts": {
     "start": "node ./serve.js"
     "start": "node ./serve.js"
   },
   },

+ 1 - 1
examples/aws-uppy-server/index.html

@@ -3,7 +3,7 @@
   <head>
   <head>
     <meta charset="utf-8">
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>Uppy AWS Example</title>
+    <title>Uppy Server + AWS Example</title>
     <link href="uppy.min.css" rel="stylesheet">
     <link href="uppy.min.css" rel="stylesheet">
   </head>
   </head>
   <body>
   <body>

+ 4 - 0
examples/custom-provider/.gitignore

@@ -0,0 +1,4 @@
+uppy.min.css
+node_modules
+output/*
+!output/.empty

+ 108 - 0
examples/custom-provider/client/MyCustomProvider.js

@@ -0,0 +1,108 @@
+const Plugin = require('uppy/lib/core/Plugin')
+const { Provider } = require('uppy/lib/server')
+const { ProviderView } = require('uppy/lib/views')
+const { h } = require('preact')
+
+module.exports = class MyCustomProvider extends Plugin {
+  constructor (uppy, opts) {
+    super(uppy, opts)
+    this.type = 'acquirer'
+    this.id = this.opts.id || 'MyCustomProvider'
+    this.title = 'MyCustomProvider'
+    this.icon = () => (
+      <img src="https://uppy.io/images/logos/uppy-dog-head-arrow.svg" width="23" />
+    )
+
+    // writing out the key explicitly for readability the key used to store
+    // the provider instance must be equal to this.id.
+    this[this.id] = new Provider(uppy, {
+      host: this.opts.host,
+      provider: 'mycustomprovider'
+    })
+
+    this.files = []
+    this.onAuth = this.onAuth.bind(this)
+    this.render = this.render.bind(this)
+
+    // merge default options with the ones set by user
+    this.opts = Object.assign({}, opts)
+  }
+
+  install () {
+    this.view = new ProviderView(this)
+    // Set default state
+    this.setPluginState({
+      authenticated: false,
+      files: [],
+      folders: [],
+      directories: [],
+      activeRow: -1,
+      filterInput: '',
+      isSearchVisible: false
+    })
+
+    const target = this.opts.target
+    if (target) {
+      this.mount(target, this)
+    }
+  }
+
+  uninstall () {
+    this.view.tearDown()
+    this.unmount()
+  }
+
+  onAuth (authenticated) {
+    this.setPluginState({ authenticated })
+    if (authenticated) {
+      this.view.getFolder()
+    }
+  }
+
+  isFolder (item) {
+    return false
+  }
+
+  getItemData (item) {
+    return item
+  }
+
+  getItemIcon (item) {
+    return () => (
+      <img src="https://uppy.io/images/logos/uppy-dog-head-arrow.svg" />
+    )
+  }
+
+  getItemSubList (item) {
+    return item.entries
+  }
+
+  getItemName (item) {
+    return item.name
+  }
+
+  getMimeType (item) {
+    // mime types aren't supported.
+    return null
+  }
+
+  getItemId (item) {
+    return item.name
+  }
+
+  getItemRequestPath (item) {
+    return encodeURIComponent(item.name)
+  }
+
+  getItemModifiedDate (item) {
+    return Date.now()
+  }
+
+  getItemThumbnailUrl (item) {
+    return 'https://uppy.io/images/logos/uppy-dog-head-arrow.svg'
+  }
+
+  render (state) {
+    return this.view.render(state)
+  }
+}

+ 7 - 0
examples/custom-provider/client/aliasify.js

@@ -0,0 +1,7 @@
+const path = require('path')
+
+module.exports = {
+  replacements: {
+    '^uppy/lib/(.*?)$': path.join(__dirname, '../../../src/$1')
+  }
+}

+ 27 - 0
examples/custom-provider/client/main.js

@@ -0,0 +1,27 @@
+const Uppy = require('uppy/lib/core')
+const GoogleDrive = require('uppy/lib/plugins/GoogleDrive')
+const Tus = require('uppy/lib/plugins/Tus')
+const MyCustomProvider = require('./MyCustomProvider')
+const Dashboard = require('uppy/lib/plugins/Dashboard')
+
+const uppy = Uppy({
+  debug: true,
+  autoProceed: false
+})
+
+uppy.use(GoogleDrive, {
+  host: 'http://localhost:3020'
+})
+
+uppy.use(MyCustomProvider, {
+  host: 'http://localhost:3020'
+})
+
+uppy.use(Dashboard, {
+  inline: true,
+  target: 'body',
+  plugins: ['GoogleDrive', 'MyCustomProvider']
+})
+
+uppy.use(Tus, {endpoint: 'https://master.tus.io/files/'})
+uppy.run()

+ 12 - 0
examples/custom-provider/index.html

@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Uppy Custom provider Example</title>
+    <link href="uppy.min.css" rel="stylesheet">
+  </head>
+  <body>
+    <script src="bundle.js"></script>
+  </body>
+</html>

+ 0 - 0
examples/custom-provider/output/.empty


+ 21 - 0
examples/custom-provider/package.json

@@ -0,0 +1,21 @@
+{
+  "private": true,
+  "name": "custom-provider-example",
+  "scripts": {
+    "copy": "cp ../../dist/uppy.min.css .",
+    "start:client": "budo client/main.js:bundle.js -- -t babelify -g aliasify",
+    "start:server": "node server/index.js",
+    "start": "npm-run-all --serial copy --parallel start:*"
+  },
+  "aliasify": "client/aliasify.js",
+  "dependencies": {
+    "aliasify": "^2.1.0",
+    "babelify": "^7.3.0",
+    "body-parser": "^1.18.2",
+    "budo": "^10.0.4",
+    "express": "^4.16.2",
+    "express-session": "^1.15.6",
+    "npm-run-all": "^4.1.2",
+    "uppy-server": "^0.11.0"
+  }
+}

+ 13 - 0
examples/custom-provider/readme.md

@@ -0,0 +1,13 @@
+# Uppy + Server + Custom Provider  Example
+
+This example uses uppy-server with a dummy custom provider.
+This serves as an illustration on how integrating custom providers would work
+
+## Run it
+
+Move into this directory, then:
+
+```bash
+npm install
+npm start
+```

+ 42 - 0
examples/custom-provider/server/customprovider.js

@@ -0,0 +1,42 @@
+const fs = require('fs')
+const path = require('path')
+const DUMM_FILE = path.join(__dirname, 'fixtures/image.jpg')
+
+/**
+ * an example of a custom provider module. It implements uppy-server's Provider interface
+ */
+class MyCustomProvider {
+  constructor (options) {
+    this.authProvider = MyCustomProvider.authProvider
+  }
+
+  static get authProvider () {
+    return 'mycustomprovider'
+  }
+
+  list (options, done) {
+    const response = {
+      body: {
+        entries: [
+          { name: 'file1.jpg' },
+          { name: 'file2.jpg' },
+          {name: 'file3.jpg'}
+        ]
+      }
+    }
+    return done(null, response, response.body)
+  }
+
+  download ({ id, token }, onData) {
+    return fs.readFile(DUMM_FILE, (err, data) => {
+      if (err) console.error(err)
+      onData(data)
+    })
+  }
+
+  size ({ id, token }, done) {
+    return done(fs.statSync(DUMM_FILE).size)
+  }
+}
+
+module.exports = MyCustomProvider

BIN
examples/custom-provider/server/fixtures/image.jpg


+ 89 - 0
examples/custom-provider/server/index.js

@@ -0,0 +1,89 @@
+const express = require('express')
+const uppy = require('uppy-server')
+const bodyParser = require('body-parser')
+const session = require('express-session')
+
+const app = express()
+
+app.use(bodyParser.json())
+app.use(session({
+  secret: 'some-secret',
+  resave: true,
+  saveUninitialized: true
+}))
+
+app.use((req, res, next) => {
+  res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*')
+  res.setHeader(
+    'Access-Control-Allow-Methods',
+    'GET, POST, OPTIONS, PUT, PATCH, DELETE'
+  )
+  res.setHeader(
+    'Access-Control-Allow-Headers',
+    'Authorization, Origin, Content-Type, Accept'
+  )
+  res.setHeader('Access-Control-Allow-Credentials', 'true')
+  next()
+})
+
+// Routes
+app.get('/', (req, res) => {
+  res.setHeader('Content-Type', 'text/plain')
+  res.send('Welcome to my uppy server')
+})
+
+// initialize uppy
+const uppyOptions = {
+  providerOptions: {
+    google: {
+      key: 'your google key',
+      secret: 'your google secret'
+    }
+  },
+  customProviders: {
+    mycustomprovider: {
+      config: {
+        // your oauth handlers
+        authorize_url: 'http://localhost:3020/oauth/authorize',
+        access_url: 'http://localhost:3020/oauth/token',
+        oauth: 2,
+        key: '***',
+        secret: '**',
+        scope: ['read', 'write']
+      },
+      // you provider module
+      module: require('./customprovider')
+    }
+  },
+  server: {
+    host: 'localhost:3020',
+    protocol: 'http'
+  },
+  filePath: './output',
+  secret: 'some-secret',
+  debug: true
+}
+
+app.get('/oauth/authorize', (req, res) => {
+  // skips the default oauth process.
+  // ideally this endpoint should handle the actual oauth process
+  res.redirect(`http://localhost:3020/mycustomprovider/callback?state=${req.query.state}&access_token=randombytes`)
+})
+
+app.use(uppy.app(uppyOptions))
+
+// handle 404
+app.use((req, res, next) => {
+  return res.status(404).json({ message: 'Not Found' })
+})
+
+// handle server errors
+app.use((err, req, res, next) => {
+  console.error('\x1b[31m', err.stack, '\x1b[0m')
+  res.status(err.status || 500).json({ message: err.message, error: err })
+})
+
+uppy.socket(app.listen(3020), uppyOptions)
+
+console.log('Welcome to Uppy Server!')
+console.log(`Listening on http://0.0.0.0:${3020}`)

+ 1 - 1
examples/digitalocean-spaces/package.json

@@ -1,6 +1,6 @@
 {
 {
   "private": true,
   "private": true,
-  "name": "uppy-digitalocean-spaces",
+  "name": "uppy-digitalocean-spaces-example",
   "scripts": {
   "scripts": {
     "start": "node ./server.js"
     "start": "node ./server.js"
   },
   },

+ 1 - 0
examples/react-example/App.js

@@ -73,6 +73,7 @@ module.exports = class App extends React.Component {
           <DashboardModal
           <DashboardModal
             uppy={this.uppy2}
             uppy={this.uppy2}
             open={this.state.open}
             open={this.state.open}
+            target={document.body}
             onRequestClose={() => this.setState({ open: false })}
             onRequestClose={() => this.setState({ open: false })}
           />
           />
         </div>
         </div>

+ 1 - 0
examples/uppy-with-server/package.json

@@ -1,4 +1,5 @@
 {
 {
+  "private": true,
   "name": "uppy-with-server-example",
   "name": "uppy-with-server-example",
   "version": "1.0.0",
   "version": "1.0.0",
   "description": "",
   "description": "",

+ 1 - 1
examples/xhr-bundle/package.json

@@ -1,6 +1,6 @@
 {
 {
   "private": true,
   "private": true,
-  "name": "uppy-multiple-instances-example",
+  "name": "uppy-xhr-bundle-example",
   "scripts": {
   "scripts": {
     "css": "cp ../../dist/uppy.min.css .",
     "css": "cp ../../dist/uppy.min.css .",
     "start:client": "npm run css && budo main.js:bundle.js -- -t babelify -g aliasify",
     "start:client": "npm run css && budo main.js:bundle.js -- -t babelify -g aliasify",

+ 7 - 1
src/react/DashboardModal.js

@@ -17,10 +17,14 @@ class DashboardModal extends React.Component {
       {},
       {},
       this.props,
       this.props,
       {
       {
-        target: this.container,
         onRequestCloseModal: this.props.onRequestClose
         onRequestCloseModal: this.props.onRequestClose
       }
       }
     )
     )
+
+    if (!options.target) {
+      options.target = this.container
+    }
+
     delete options.uppy
     delete options.uppy
     uppy.use(DashboardPlugin, options)
     uppy.use(DashboardPlugin, options)
 
 
@@ -55,6 +59,8 @@ class DashboardModal extends React.Component {
 
 
 DashboardModal.propTypes = {
 DashboardModal.propTypes = {
   uppy: PropTypes.instanceOf(UppyCore).isRequired,
   uppy: PropTypes.instanceOf(UppyCore).isRequired,
+  // Only check this prop type in the browser.
+  target: typeof window !== 'undefined' ? PropTypes.instanceOf(window.HTMLElement) : PropTypes.any,
   open: PropTypes.bool,
   open: PropTypes.bool,
   onRequestClose: PropTypes.func,
   onRequestClose: PropTypes.func,
   plugins: PropTypes.arrayOf(PropTypes.string),
   plugins: PropTypes.arrayOf(PropTypes.string),

+ 1 - 0
src/scss/_dashboard.scss

@@ -1054,6 +1054,7 @@
   max-height: 90%;
   max-height: 90%;
   object-fit: cover;
   object-fit: cover;
   border-radius: 3px;
   border-radius: 3px;
+  position: absolute;
 }
 }
 
 
 .uppy-DashboardFileCard-info {
 .uppy-DashboardFileCard-info {

+ 4 - 1
src/views/ProviderView/Item.js

@@ -31,7 +31,10 @@ module.exports = (props) => {
           onkeyup={stop}
           onkeyup={stop}
           onkeydown={stop}
           onkeydown={stop}
           onkeypress={stop} />
           onkeypress={stop} />
-        <label for={props.id} />
+        <label
+          for={props.id}
+          onclick={props.handleCheckboxClick}
+         />
       </div>
       </div>
       <button type="button"
       <button type="button"
         class="uppy-ProviderBrowserItem-inner"
         class="uppy-ProviderBrowserItem-inner"

+ 0 - 1
src/views/ProviderView/index.js

@@ -184,7 +184,6 @@ module.exports = class ProviderView {
    */
    */
   logout () {
   logout () {
     this.Provider.logout(location.href)
     this.Provider.logout(location.href)
-      .then((res) => res.json())
       .then((res) => {
       .then((res) => {
         if (res.ok) {
         if (res.ok) {
           const newState = {
           const newState = {

+ 5 - 2
website/src/docs/server.md

@@ -265,6 +265,10 @@ app.use(uppy.app({
 
 
 The default value simply returns `filename`, so all files will be uploaded to the root of the bucket as their original file name.
 The default value simply returns `filename`, so all files will be uploaded to the root of the bucket as their original file name.
 
 
+### Run in Kubernetes
+
+We have [a detailed guide on running Uppy Server in Kubernetes](https://github.com/transloadit/uppy-server/blob/master/KUBERNETES.md) for you, that’s how we currently run our example server at http://server.uppy.io.
+
 ### Adding Custom Providers
 ### Adding Custom Providers
 
 
 As of now, Uppy Server supports **Google Drive**, **Dropbox**, **Instagram**, and **Url** (remote urls) out of the box, but you may also choose to add your custom providers. You can do this by passing the `customProviders` option when calling the uppy `app` method. The custom provider is expected to support Oauth 1 or 2 for authentication/authorization.
 As of now, Uppy Server supports **Google Drive**, **Dropbox**, **Instagram**, and **Url** (remote urls) out of the box, but you may also choose to add your custom providers. You can do this by passing the `customProviders` option when calling the uppy `app` method. The custom provider is expected to support Oauth 1 or 2 for authentication/authorization.
@@ -329,7 +333,6 @@ npm run start:dev
 
 
 This would get the Uppy Server running on `http://localhost:3020`.
 This would get the Uppy Server running on `http://localhost:3020`.
 
 
-## Running example
+## Live example
 
 
 An example server is running at http://server.uppy.io, which is deployed with [Kubernetes](https://github.com/transloadit/uppy-server/blob/master/KUBERNETES.md)
 An example server is running at http://server.uppy.io, which is deployed with [Kubernetes](https://github.com/transloadit/uppy-server/blob/master/KUBERNETES.md)
-