Url.jsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import { h } from 'preact'
  2. import { UIPlugin } from '@uppy/core'
  3. import { RequestClient } from '@uppy/companion-client'
  4. import toArray from '@uppy/utils/lib/toArray'
  5. import UrlUI from './UrlUI.jsx'
  6. import forEachDroppedOrPastedUrl from './utils/forEachDroppedOrPastedUrl.js'
  7. import packageJson from '../package.json'
  8. import locale from './locale.js'
  9. function UrlIcon () {
  10. return (
  11. <svg aria-hidden="true" focusable="false" width="32" height="32" viewBox="0 0 32 32">
  12. <g fill="none" fillRule="evenodd">
  13. <rect className="uppy-ProviderIconBg" fill="#FF753E" width="32" height="32" rx="16" />
  14. <path d="M22.788 15.389l-2.199 2.19a3.184 3.184 0 0 1-.513.437c-.806.584-1.686.876-2.638.876a4.378 4.378 0 0 1-3.519-1.752c-.22-.292-.146-.802.147-1.021.293-.22.806-.146 1.026.146.953 1.313 2.785 1.532 4.105.583a.571.571 0 0 0 .293-.292l2.199-2.189c1.1-1.167 1.1-2.992-.073-4.086a2.976 2.976 0 0 0-4.105 0l-1.246 1.24a.71.71 0 0 1-1.026 0 .703.703 0 0 1 0-1.022l1.246-1.24a4.305 4.305 0 0 1 6.083 0c1.833 1.605 1.906 4.451.22 6.13zm-7.183 5.035l-1.246 1.24a2.976 2.976 0 0 1-4.105 0c-1.172-1.094-1.172-2.991-.073-4.086l2.2-2.19.292-.291c.66-.438 1.393-.657 2.2-.584.805.146 1.465.51 1.905 1.168.22.292.733.365 1.026.146.293-.22.367-.73.147-1.022-.733-.949-1.76-1.532-2.859-1.678-1.1-.22-2.272.073-3.225.802l-.44.438-2.199 2.19c-1.686 1.75-1.612 4.524.074 6.202.88.803 1.979 1.241 3.078 1.241 1.1 0 2.199-.438 3.079-1.24l1.246-1.241a.703.703 0 0 0 0-1.022c-.294-.292-.807-.365-1.1-.073z" fill="#FFF" fillRule="nonzero" />
  15. </g>
  16. </svg>
  17. )
  18. }
  19. function addProtocolToURL (url) {
  20. const protocolRegex = /^[a-z0-9]+:\/\//
  21. const defaultProtocol = 'http://'
  22. if (protocolRegex.test(url)) {
  23. return url
  24. }
  25. return defaultProtocol + url
  26. }
  27. function canHandleRootDrop (e) {
  28. const items = toArray(e.dataTransfer.items)
  29. const urls = items.filter((item) => item.kind === 'string'
  30. && item.type === 'text/uri-list')
  31. return urls.length > 0
  32. }
  33. function checkIfCorrectURL (url) {
  34. if (!url) return false
  35. const protocol = url.match(/^([a-z0-9]+):\/\//)[1]
  36. if (protocol !== 'http' && protocol !== 'https') {
  37. return false
  38. }
  39. return true
  40. }
  41. function getFileNameFromUrl (url) {
  42. const { pathname } = new URL(url)
  43. return pathname.substring(pathname.lastIndexOf('/') + 1)
  44. }
  45. /**
  46. * Url
  47. *
  48. */
  49. export default class Url extends UIPlugin {
  50. static VERSION = packageJson.version
  51. constructor (uppy, opts) {
  52. super(uppy, opts)
  53. this.id = this.opts.id || 'Url'
  54. this.title = this.opts.title || 'Link'
  55. this.type = 'acquirer'
  56. this.icon = () => <UrlIcon />
  57. // Set default options and locale
  58. this.defaultLocale = locale
  59. const defaultOptions = {}
  60. this.opts = { ...defaultOptions, ...opts }
  61. this.i18nInit()
  62. this.hostname = this.opts.companionUrl
  63. if (!this.hostname) {
  64. throw new Error('Companion hostname is required, please consult https://uppy.io/docs/companion')
  65. }
  66. // Bind all event handlers for referencability
  67. this.getMeta = this.getMeta.bind(this)
  68. this.addFile = this.addFile.bind(this)
  69. this.handleRootDrop = this.handleRootDrop.bind(this)
  70. this.handleRootPaste = this.handleRootPaste.bind(this)
  71. this.client = new RequestClient(uppy, {
  72. companionUrl: this.opts.companionUrl,
  73. companionHeaders: this.opts.companionHeaders,
  74. companionCookiesRule: this.opts.companionCookiesRule,
  75. })
  76. }
  77. getMeta (url) {
  78. return this.client.post('url/meta', { url })
  79. .then((res) => {
  80. if (res.error) {
  81. this.uppy.log('[URL] Error:')
  82. this.uppy.log(res.error)
  83. throw new Error('Failed to fetch the file')
  84. }
  85. return res
  86. })
  87. }
  88. async addFile (protocollessUrl) {
  89. const url = this.addProtocolToURL(protocollessUrl)
  90. if (!this.checkIfCorrectURL(url)) {
  91. this.uppy.log(`[URL] Incorrect URL entered: ${url}`)
  92. this.uppy.info(this.i18n('enterCorrectUrl'), 'error', 4000)
  93. return undefined
  94. }
  95. try {
  96. const meta = await this.getMeta(url)
  97. const tagFile = {
  98. source: this.id,
  99. name: this.getFileNameFromUrl(url),
  100. type: meta.type,
  101. data: {
  102. size: meta.size,
  103. },
  104. isRemote: true,
  105. body: {
  106. url,
  107. },
  108. remote: {
  109. companionUrl: this.opts.companionUrl,
  110. url: `${this.hostname}/url/get`,
  111. body: {
  112. fileId: url,
  113. url,
  114. },
  115. providerOptions: this.client.opts,
  116. },
  117. }
  118. this.uppy.log('[Url] Adding remote file')
  119. try {
  120. return this.uppy.addFile(tagFile)
  121. } catch (err) {
  122. if (!err.isRestriction) {
  123. this.uppy.log(err)
  124. }
  125. return err
  126. }
  127. } catch (err) {
  128. this.uppy.log(err)
  129. this.uppy.info({
  130. message: this.i18n('failedToFetch'),
  131. details: err,
  132. }, 'error', 4000)
  133. return err
  134. }
  135. }
  136. handleRootDrop (e) {
  137. forEachDroppedOrPastedUrl(e.dataTransfer, 'drop', (url) => {
  138. this.uppy.log(`[URL] Adding file from dropped url: ${url}`)
  139. this.addFile(url)
  140. })
  141. }
  142. handleRootPaste (e) {
  143. forEachDroppedOrPastedUrl(e.clipboardData, 'paste', (url) => {
  144. this.uppy.log(`[URL] Adding file from pasted url: ${url}`)
  145. this.addFile(url)
  146. })
  147. }
  148. render () {
  149. return <UrlUI i18n={this.i18n} addFile={this.addFile} />
  150. }
  151. install () {
  152. const { target } = this.opts
  153. if (target) {
  154. this.mount(target, this)
  155. }
  156. }
  157. uninstall () {
  158. this.unmount()
  159. }
  160. }
  161. // TODO: remove from prototype in the next major.
  162. Url.prototype.addProtocolToURL = addProtocolToURL
  163. Url.prototype.canHandleRootDrop = canHandleRootDrop
  164. Url.prototype.checkIfCorrectURL = checkIfCorrectURL
  165. Url.prototype.getFileNameFromUrl = getFileNameFromUrl