@strapi/upload 5.33.1 → 5.33.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/admin/package.json.js +7 -7
  2. package/dist/admin/package.json.mjs +7 -7
  3. package/dist/admin/pages/App/App.js +1 -1
  4. package/dist/admin/pages/App/App.js.map +1 -1
  5. package/dist/admin/pages/App/App.mjs +1 -1
  6. package/dist/admin/pages/App/App.mjs.map +1 -1
  7. package/dist/admin/pages/App/{MediaLibrary/MediaLibrary.js → MediaLibrary.js} +26 -26
  8. package/dist/admin/pages/App/MediaLibrary.js.map +1 -0
  9. package/dist/admin/pages/App/{MediaLibrary/MediaLibrary.mjs → MediaLibrary.mjs} +26 -26
  10. package/dist/admin/pages/App/MediaLibrary.mjs.map +1 -0
  11. package/dist/admin/pages/App/{MediaLibrary/components → components}/BulkActions.js +3 -3
  12. package/dist/admin/pages/App/components/BulkActions.js.map +1 -0
  13. package/dist/admin/pages/App/{MediaLibrary/components → components}/BulkActions.mjs +3 -3
  14. package/dist/admin/pages/App/components/BulkActions.mjs.map +1 -0
  15. package/dist/admin/pages/App/{MediaLibrary/components → components}/BulkDeleteButton.js +1 -1
  16. package/dist/admin/pages/App/components/BulkDeleteButton.js.map +1 -0
  17. package/dist/admin/pages/App/{MediaLibrary/components → components}/BulkDeleteButton.mjs +1 -1
  18. package/dist/admin/pages/App/components/BulkDeleteButton.mjs.map +1 -0
  19. package/dist/admin/pages/App/{MediaLibrary/components → components}/BulkMoveButton.js +1 -1
  20. package/dist/admin/pages/App/components/BulkMoveButton.js.map +1 -0
  21. package/dist/admin/pages/App/{MediaLibrary/components → components}/BulkMoveButton.mjs +1 -1
  22. package/dist/admin/pages/App/components/BulkMoveButton.mjs.map +1 -0
  23. package/dist/admin/pages/App/{MediaLibrary/components → components}/EmptyOrNoPermissions.js +4 -4
  24. package/dist/admin/pages/App/components/EmptyOrNoPermissions.js.map +1 -0
  25. package/dist/admin/pages/App/{MediaLibrary/components → components}/EmptyOrNoPermissions.mjs +4 -4
  26. package/dist/admin/pages/App/components/EmptyOrNoPermissions.mjs.map +1 -0
  27. package/dist/admin/pages/App/{MediaLibrary/components → components}/Filters.js +6 -6
  28. package/dist/admin/pages/App/components/Filters.js.map +1 -0
  29. package/dist/admin/pages/App/{MediaLibrary/components → components}/Filters.mjs +6 -6
  30. package/dist/admin/pages/App/components/Filters.mjs.map +1 -0
  31. package/dist/admin/pages/App/{MediaLibrary/components → components}/Header.js +4 -4
  32. package/dist/admin/pages/App/components/Header.js.map +1 -0
  33. package/dist/admin/pages/App/{MediaLibrary/components → components}/Header.mjs +4 -4
  34. package/dist/admin/pages/App/components/Header.mjs.map +1 -0
  35. package/dist/admin/src/pages/App/components/BulkActions.d.ts +1 -1
  36. package/dist/admin/src/pages/App/components/BulkDeleteButton.d.ts +1 -4
  37. package/dist/admin/src/pages/App/components/BulkMoveButton.d.ts +4 -4
  38. package/dist/admin/src/pages/App/components/EmptyOrNoPermissions.d.ts +1 -2
  39. package/dist/admin/src/pages/App/components/Header.d.ts +12 -3
  40. package/dist/server/controllers/admin-upload.js +6 -36
  41. package/dist/server/controllers/admin-upload.js.map +1 -1
  42. package/dist/server/controllers/admin-upload.mjs +7 -37
  43. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  44. package/dist/server/controllers/content-api.js +13 -11
  45. package/dist/server/controllers/content-api.js.map +1 -1
  46. package/dist/server/controllers/content-api.mjs +13 -11
  47. package/dist/server/controllers/content-api.mjs.map +1 -1
  48. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  49. package/dist/server/src/controllers/content-api.d.ts.map +1 -1
  50. package/dist/server/src/utils/mime-validation.d.ts +8 -0
  51. package/dist/server/src/utils/mime-validation.d.ts.map +1 -1
  52. package/dist/server/utils/mime-validation.js +44 -0
  53. package/dist/server/utils/mime-validation.js.map +1 -1
  54. package/dist/server/utils/mime-validation.mjs +44 -1
  55. package/dist/server/utils/mime-validation.mjs.map +1 -1
  56. package/package.json +7 -7
  57. package/dist/admin/pages/App/MediaLibrary/MediaLibrary.js.map +0 -1
  58. package/dist/admin/pages/App/MediaLibrary/MediaLibrary.mjs.map +0 -1
  59. package/dist/admin/pages/App/MediaLibrary/components/BulkActions.js.map +0 -1
  60. package/dist/admin/pages/App/MediaLibrary/components/BulkActions.mjs.map +0 -1
  61. package/dist/admin/pages/App/MediaLibrary/components/BulkDeleteButton.js.map +0 -1
  62. package/dist/admin/pages/App/MediaLibrary/components/BulkDeleteButton.mjs.map +0 -1
  63. package/dist/admin/pages/App/MediaLibrary/components/BulkMoveButton.js.map +0 -1
  64. package/dist/admin/pages/App/MediaLibrary/components/BulkMoveButton.mjs.map +0 -1
  65. package/dist/admin/pages/App/MediaLibrary/components/EmptyOrNoPermissions.js.map +0 -1
  66. package/dist/admin/pages/App/MediaLibrary/components/EmptyOrNoPermissions.mjs.map +0 -1
  67. package/dist/admin/pages/App/MediaLibrary/components/Filters.js.map +0 -1
  68. package/dist/admin/pages/App/MediaLibrary/components/Filters.mjs.map +0 -1
  69. package/dist/admin/pages/App/MediaLibrary/components/Header.js.map +0 -1
  70. package/dist/admin/pages/App/MediaLibrary/components/Header.mjs.map +0 -1
  71. package/dist/admin/src/pages/App/MediaLibrary/components/BulkActions.d.ts +0 -15
  72. package/dist/admin/src/pages/App/MediaLibrary/components/BulkDeleteButton.d.ts +0 -7
  73. package/dist/admin/src/pages/App/MediaLibrary/components/BulkMoveButton.d.ts +0 -15
  74. package/dist/admin/src/pages/App/MediaLibrary/components/EmptyOrNoPermissions.d.ts +0 -7
  75. package/dist/admin/src/pages/App/MediaLibrary/components/Filters.d.ts +0 -1
  76. package/dist/admin/src/pages/App/MediaLibrary/components/Header.d.ts +0 -22
  77. /package/dist/admin/src/pages/App/{MediaLibrary/MediaLibrary.d.ts → MediaLibrary.d.ts} +0 -0
@@ -5,7 +5,7 @@ var React = require('react');
5
5
  var designSystem = require('@strapi/design-system');
6
6
  var icons = require('@strapi/icons');
7
7
  var reactIntl = require('react-intl');
8
- var BulkMoveDialog = require('../../../../components/BulkMoveDialog/BulkMoveDialog.js');
8
+ var BulkMoveDialog = require('../../../components/BulkMoveDialog/BulkMoveDialog.js');
9
9
 
10
10
  function _interopNamespaceDefault(e) {
11
11
  var n = Object.create(null);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BulkMoveButton.js","sources":["../../../../../admin/src/pages/App/components/BulkMoveButton.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Button, Modal } from '@strapi/design-system';\nimport { Folder } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { BulkMoveDialog } from '../../../components/BulkMoveDialog/BulkMoveDialog';\n\nimport type { File } from '../../../../../shared/contracts/files';\nimport type { Folder as FolderDefinition } from '../../../../../shared/contracts/folders';\n\ninterface FolderWithType extends FolderDefinition {\n type: string;\n}\n\ninterface FileWithType extends File {\n type: string;\n}\n\nexport interface BulkMoveButtonProps {\n onSuccess: () => void;\n currentFolder?: FolderWithType;\n selected?: Array<FolderWithType | FileWithType>;\n}\n\nexport const BulkMoveButton = ({\n selected = [],\n onSuccess,\n currentFolder,\n}: BulkMoveButtonProps) => {\n const { formatMessage } = useIntl();\n const [showConfirmDialog, setShowConfirmDialog] = React.useState(false);\n\n const handleConfirmMove = () => {\n setShowConfirmDialog(false);\n onSuccess();\n };\n\n return (\n <Modal.Root open={showConfirmDialog} onOpenChange={setShowConfirmDialog}>\n <Modal.Trigger>\n <Button variant=\"secondary\" size=\"S\" startIcon={<Folder />}>\n {formatMessage({ id: 'global.move', defaultMessage: 'Move' })}\n </Button>\n </Modal.Trigger>\n <BulkMoveDialog\n currentFolder={currentFolder}\n onClose={handleConfirmMove}\n selected={selected}\n />\n </Modal.Root>\n );\n};\n"],"names":["BulkMoveButton","selected","onSuccess","currentFolder","formatMessage","useIntl","showConfirmDialog","setShowConfirmDialog","React","useState","handleConfirmMove","_jsxs","Modal","Root","open","onOpenChange","_jsx","Trigger","Button","variant","size","startIcon","Folder","id","defaultMessage","BulkMoveDialog","onClose"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBO,MAAMA,cAAiB,GAAA,CAAC,EAC7BC,QAAAA,GAAW,EAAE,EACbC,SAAS,EACTC,aAAa,EACO,GAAA;IACpB,MAAM,EAAEC,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;AAC1B,IAAA,MAAM,CAACC,iBAAmBC,EAAAA,oBAAAA,CAAqB,GAAGC,gBAAAA,CAAMC,QAAQ,CAAC,KAAA,CAAA;AAEjE,IAAA,MAAMC,iBAAoB,GAAA,IAAA;QACxBH,oBAAqB,CAAA,KAAA,CAAA;AACrBL,QAAAA,SAAAA,EAAAA;AACF,KAAA;IAEA,qBACES,eAAA,CAACC,mBAAMC,IAAI,EAAA;QAACC,IAAMR,EAAAA,iBAAAA;QAAmBS,YAAcR,EAAAA,oBAAAA;;AACjD,0BAAAS,cAAA,CAACJ,mBAAMK,OAAO,EAAA;AACZ,gBAAA,QAAA,gBAAAD,cAACE,CAAAA,mBAAAA,EAAAA;oBAAOC,OAAQ,EAAA,WAAA;oBAAYC,IAAK,EAAA,GAAA;AAAIC,oBAAAA,SAAAA,gBAAWL,cAACM,CAAAA,YAAAA,EAAAA,EAAAA,CAAAA;8BAC9ClB,aAAc,CAAA;wBAAEmB,EAAI,EAAA,aAAA;wBAAeC,cAAgB,EAAA;AAAO,qBAAA;;;0BAG/DR,cAACS,CAAAA,6BAAAA,EAAAA;gBACCtB,aAAeA,EAAAA,aAAAA;gBACfuB,OAAShB,EAAAA,iBAAAA;gBACTT,QAAUA,EAAAA;;;;AAIlB;;;;"}
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { Modal, Button } from '@strapi/design-system';
4
4
  import { Folder } from '@strapi/icons';
5
5
  import { useIntl } from 'react-intl';
6
- import { BulkMoveDialog } from '../../../../components/BulkMoveDialog/BulkMoveDialog.mjs';
6
+ import { BulkMoveDialog } from '../../../components/BulkMoveDialog/BulkMoveDialog.mjs';
7
7
 
8
8
  const BulkMoveButton = ({ selected = [], onSuccess, currentFolder })=>{
9
9
  const { formatMessage } = useIntl();
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BulkMoveButton.mjs","sources":["../../../../../admin/src/pages/App/components/BulkMoveButton.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Button, Modal } from '@strapi/design-system';\nimport { Folder } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { BulkMoveDialog } from '../../../components/BulkMoveDialog/BulkMoveDialog';\n\nimport type { File } from '../../../../../shared/contracts/files';\nimport type { Folder as FolderDefinition } from '../../../../../shared/contracts/folders';\n\ninterface FolderWithType extends FolderDefinition {\n type: string;\n}\n\ninterface FileWithType extends File {\n type: string;\n}\n\nexport interface BulkMoveButtonProps {\n onSuccess: () => void;\n currentFolder?: FolderWithType;\n selected?: Array<FolderWithType | FileWithType>;\n}\n\nexport const BulkMoveButton = ({\n selected = [],\n onSuccess,\n currentFolder,\n}: BulkMoveButtonProps) => {\n const { formatMessage } = useIntl();\n const [showConfirmDialog, setShowConfirmDialog] = React.useState(false);\n\n const handleConfirmMove = () => {\n setShowConfirmDialog(false);\n onSuccess();\n };\n\n return (\n <Modal.Root open={showConfirmDialog} onOpenChange={setShowConfirmDialog}>\n <Modal.Trigger>\n <Button variant=\"secondary\" size=\"S\" startIcon={<Folder />}>\n {formatMessage({ id: 'global.move', defaultMessage: 'Move' })}\n </Button>\n </Modal.Trigger>\n <BulkMoveDialog\n currentFolder={currentFolder}\n onClose={handleConfirmMove}\n selected={selected}\n />\n </Modal.Root>\n );\n};\n"],"names":["BulkMoveButton","selected","onSuccess","currentFolder","formatMessage","useIntl","showConfirmDialog","setShowConfirmDialog","React","useState","handleConfirmMove","_jsxs","Modal","Root","open","onOpenChange","_jsx","Trigger","Button","variant","size","startIcon","Folder","id","defaultMessage","BulkMoveDialog","onClose"],"mappings":";;;;;;;AAyBO,MAAMA,cAAiB,GAAA,CAAC,EAC7BC,QAAAA,GAAW,EAAE,EACbC,SAAS,EACTC,aAAa,EACO,GAAA;IACpB,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;AAC1B,IAAA,MAAM,CAACC,iBAAmBC,EAAAA,oBAAAA,CAAqB,GAAGC,KAAAA,CAAMC,QAAQ,CAAC,KAAA,CAAA;AAEjE,IAAA,MAAMC,iBAAoB,GAAA,IAAA;QACxBH,oBAAqB,CAAA,KAAA,CAAA;AACrBL,QAAAA,SAAAA,EAAAA;AACF,KAAA;IAEA,qBACES,IAAA,CAACC,MAAMC,IAAI,EAAA;QAACC,IAAMR,EAAAA,iBAAAA;QAAmBS,YAAcR,EAAAA,oBAAAA;;AACjD,0BAAAS,GAAA,CAACJ,MAAMK,OAAO,EAAA;AACZ,gBAAA,QAAA,gBAAAD,GAACE,CAAAA,MAAAA,EAAAA;oBAAOC,OAAQ,EAAA,WAAA;oBAAYC,IAAK,EAAA,GAAA;AAAIC,oBAAAA,SAAAA,gBAAWL,GAACM,CAAAA,MAAAA,EAAAA,EAAAA,CAAAA;8BAC9ClB,aAAc,CAAA;wBAAEmB,EAAI,EAAA,aAAA;wBAAeC,cAAgB,EAAA;AAAO,qBAAA;;;0BAG/DR,GAACS,CAAAA,cAAAA,EAAAA;gBACCtB,aAAeA,EAAAA,aAAAA;gBACfuB,OAAShB,EAAAA,iBAAAA;gBACTT,QAAUA,EAAAA;;;;AAIlB;;;;"}
@@ -5,13 +5,13 @@ var designSystem = require('@strapi/design-system');
5
5
  var icons = require('@strapi/icons');
6
6
  var symbols = require('@strapi/icons/symbols');
7
7
  var reactIntl = require('react-intl');
8
- var EmptyAssets = require('../../../../components/EmptyAssets/EmptyAssets.js');
8
+ var EmptyAssets = require('../../../components/EmptyAssets/EmptyAssets.js');
9
9
  require('byte-size');
10
10
  require('date-fns');
11
- var getTrad = require('../../../../utils/getTrad.js');
11
+ var getTrad = require('../../../utils/getTrad.js');
12
12
  require('qs');
13
- require('../../../../utils/typeFromMime.js');
14
- require('../../../../utils/urlYupSchema.js');
13
+ require('../../../utils/typeFromMime.js');
14
+ require('../../../utils/urlYupSchema.js');
15
15
 
16
16
  const getContentIntlMessage = ({ isFiltering, canCreate, canRead })=>{
17
17
  if (isFiltering) {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmptyOrNoPermissions.js","sources":["../../../../../admin/src/pages/App/components/EmptyOrNoPermissions.tsx"],"sourcesContent":["import { Button } from '@strapi/design-system';\nimport { Plus } from '@strapi/icons';\nimport { EmptyPermissions } from '@strapi/icons/symbols';\nimport { useIntl } from 'react-intl';\n\nimport { EmptyAssets } from '../../../components/EmptyAssets/EmptyAssets';\nimport { getTrad } from '../../../utils';\n\nexport interface EmptyOrNoPermissionsProps {\n canCreate: boolean;\n canRead: boolean;\n isFiltering: boolean;\n onActionClick: () => void;\n}\n\nconst getContentIntlMessage = ({\n isFiltering,\n canCreate,\n canRead,\n}: Omit<EmptyOrNoPermissionsProps, 'onActionClick'>) => {\n if (isFiltering) {\n return {\n id: 'list.assets-empty.title-withSearch',\n defaultMessage: 'There are no elements with the applied filters',\n };\n }\n\n if (canRead) {\n if (canCreate) {\n return {\n id: 'list.assets.empty-upload',\n defaultMessage: 'Upload your first assets...',\n };\n }\n\n return {\n id: 'list.assets.empty',\n defaultMessage: 'Media Library is empty',\n };\n }\n\n return {\n id: 'header.actions.no-permissions',\n defaultMessage: 'No permissions to view',\n };\n};\n\nexport const EmptyOrNoPermissions = ({\n canCreate,\n isFiltering,\n canRead,\n onActionClick,\n}: EmptyOrNoPermissionsProps) => {\n const { formatMessage } = useIntl();\n const content = getContentIntlMessage({ isFiltering, canCreate, canRead });\n\n return (\n <EmptyAssets\n icon={!canRead ? EmptyPermissions : undefined}\n action={\n canCreate &&\n !isFiltering && (\n <Button variant=\"secondary\" startIcon={<Plus />} onClick={onActionClick}>\n {formatMessage({\n id: getTrad('header.actions.add-assets'),\n defaultMessage: 'Add new assets',\n })}\n </Button>\n )\n }\n content={formatMessage({\n ...content,\n id: getTrad(content.id),\n })}\n />\n );\n};\n"],"names":["getContentIntlMessage","isFiltering","canCreate","canRead","id","defaultMessage","EmptyOrNoPermissions","onActionClick","formatMessage","useIntl","content","_jsx","EmptyAssets","icon","EmptyPermissions","undefined","action","Button","variant","startIcon","Plus","onClick","getTrad"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAMA,qBAAAA,GAAwB,CAAC,EAC7BC,WAAW,EACXC,SAAS,EACTC,OAAO,EAC0C,GAAA;AACjD,IAAA,IAAIF,WAAa,EAAA;QACf,OAAO;YACLG,EAAI,EAAA,oCAAA;YACJC,cAAgB,EAAA;AAClB,SAAA;AACF;AAEA,IAAA,IAAIF,OAAS,EAAA;AACX,QAAA,IAAID,SAAW,EAAA;YACb,OAAO;gBACLE,EAAI,EAAA,0BAAA;gBACJC,cAAgB,EAAA;AAClB,aAAA;AACF;QAEA,OAAO;YACLD,EAAI,EAAA,mBAAA;YACJC,cAAgB,EAAA;AAClB,SAAA;AACF;IAEA,OAAO;QACLD,EAAI,EAAA,+BAAA;QACJC,cAAgB,EAAA;AAClB,KAAA;AACF,CAAA;AAEO,MAAMC,oBAAuB,GAAA,CAAC,EACnCJ,SAAS,EACTD,WAAW,EACXE,OAAO,EACPI,aAAa,EACa,GAAA;IAC1B,MAAM,EAAEC,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;AAC1B,IAAA,MAAMC,UAAUV,qBAAsB,CAAA;AAAEC,QAAAA,WAAAA;AAAaC,QAAAA,SAAAA;AAAWC,QAAAA;AAAQ,KAAA,CAAA;AAExE,IAAA,qBACEQ,cAACC,CAAAA,uBAAAA,EAAAA;QACCC,IAAM,EAAA,CAACV,UAAUW,wBAAmBC,GAAAA,SAAAA;QACpCC,MACEd,EAAAA,SAAAA,IACA,CAACD,WAAAA,kBACCU,cAACM,CAAAA,mBAAAA,EAAAA;YAAOC,OAAQ,EAAA,WAAA;AAAYC,YAAAA,SAAAA,gBAAWR,cAACS,CAAAA,UAAAA,EAAAA,EAAAA,CAAAA;YAASC,OAASd,EAAAA,aAAAA;sBACvDC,aAAc,CAAA;AACbJ,gBAAAA,EAAAA,EAAIkB,eAAQ,CAAA,2BAAA,CAAA;gBACZjB,cAAgB,EAAA;AAClB,aAAA;;AAINK,QAAAA,OAAAA,EAASF,aAAc,CAAA;AACrB,YAAA,GAAGE,OAAO;YACVN,EAAIkB,EAAAA,eAAAA,CAAQZ,QAAQN,EAAE;AACxB,SAAA;;AAGN;;;;"}
@@ -3,13 +3,13 @@ import { Button } from '@strapi/design-system';
3
3
  import { Plus } from '@strapi/icons';
4
4
  import { EmptyPermissions } from '@strapi/icons/symbols';
5
5
  import { useIntl } from 'react-intl';
6
- import { EmptyAssets } from '../../../../components/EmptyAssets/EmptyAssets.mjs';
6
+ import { EmptyAssets } from '../../../components/EmptyAssets/EmptyAssets.mjs';
7
7
  import 'byte-size';
8
8
  import 'date-fns';
9
- import { getTrad } from '../../../../utils/getTrad.mjs';
9
+ import { getTrad } from '../../../utils/getTrad.mjs';
10
10
  import 'qs';
11
- import '../../../../utils/typeFromMime.mjs';
12
- import '../../../../utils/urlYupSchema.mjs';
11
+ import '../../../utils/typeFromMime.mjs';
12
+ import '../../../utils/urlYupSchema.mjs';
13
13
 
14
14
  const getContentIntlMessage = ({ isFiltering, canCreate, canRead })=>{
15
15
  if (isFiltering) {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmptyOrNoPermissions.mjs","sources":["../../../../../admin/src/pages/App/components/EmptyOrNoPermissions.tsx"],"sourcesContent":["import { Button } from '@strapi/design-system';\nimport { Plus } from '@strapi/icons';\nimport { EmptyPermissions } from '@strapi/icons/symbols';\nimport { useIntl } from 'react-intl';\n\nimport { EmptyAssets } from '../../../components/EmptyAssets/EmptyAssets';\nimport { getTrad } from '../../../utils';\n\nexport interface EmptyOrNoPermissionsProps {\n canCreate: boolean;\n canRead: boolean;\n isFiltering: boolean;\n onActionClick: () => void;\n}\n\nconst getContentIntlMessage = ({\n isFiltering,\n canCreate,\n canRead,\n}: Omit<EmptyOrNoPermissionsProps, 'onActionClick'>) => {\n if (isFiltering) {\n return {\n id: 'list.assets-empty.title-withSearch',\n defaultMessage: 'There are no elements with the applied filters',\n };\n }\n\n if (canRead) {\n if (canCreate) {\n return {\n id: 'list.assets.empty-upload',\n defaultMessage: 'Upload your first assets...',\n };\n }\n\n return {\n id: 'list.assets.empty',\n defaultMessage: 'Media Library is empty',\n };\n }\n\n return {\n id: 'header.actions.no-permissions',\n defaultMessage: 'No permissions to view',\n };\n};\n\nexport const EmptyOrNoPermissions = ({\n canCreate,\n isFiltering,\n canRead,\n onActionClick,\n}: EmptyOrNoPermissionsProps) => {\n const { formatMessage } = useIntl();\n const content = getContentIntlMessage({ isFiltering, canCreate, canRead });\n\n return (\n <EmptyAssets\n icon={!canRead ? EmptyPermissions : undefined}\n action={\n canCreate &&\n !isFiltering && (\n <Button variant=\"secondary\" startIcon={<Plus />} onClick={onActionClick}>\n {formatMessage({\n id: getTrad('header.actions.add-assets'),\n defaultMessage: 'Add new assets',\n })}\n </Button>\n )\n }\n content={formatMessage({\n ...content,\n id: getTrad(content.id),\n })}\n />\n );\n};\n"],"names":["getContentIntlMessage","isFiltering","canCreate","canRead","id","defaultMessage","EmptyOrNoPermissions","onActionClick","formatMessage","useIntl","content","_jsx","EmptyAssets","icon","EmptyPermissions","undefined","action","Button","variant","startIcon","Plus","onClick","getTrad"],"mappings":";;;;;;;;;;;;;AAeA,MAAMA,qBAAAA,GAAwB,CAAC,EAC7BC,WAAW,EACXC,SAAS,EACTC,OAAO,EAC0C,GAAA;AACjD,IAAA,IAAIF,WAAa,EAAA;QACf,OAAO;YACLG,EAAI,EAAA,oCAAA;YACJC,cAAgB,EAAA;AAClB,SAAA;AACF;AAEA,IAAA,IAAIF,OAAS,EAAA;AACX,QAAA,IAAID,SAAW,EAAA;YACb,OAAO;gBACLE,EAAI,EAAA,0BAAA;gBACJC,cAAgB,EAAA;AAClB,aAAA;AACF;QAEA,OAAO;YACLD,EAAI,EAAA,mBAAA;YACJC,cAAgB,EAAA;AAClB,SAAA;AACF;IAEA,OAAO;QACLD,EAAI,EAAA,+BAAA;QACJC,cAAgB,EAAA;AAClB,KAAA;AACF,CAAA;AAEO,MAAMC,oBAAuB,GAAA,CAAC,EACnCJ,SAAS,EACTD,WAAW,EACXE,OAAO,EACPI,aAAa,EACa,GAAA;IAC1B,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;AAC1B,IAAA,MAAMC,UAAUV,qBAAsB,CAAA;AAAEC,QAAAA,WAAAA;AAAaC,QAAAA,SAAAA;AAAWC,QAAAA;AAAQ,KAAA,CAAA;AAExE,IAAA,qBACEQ,GAACC,CAAAA,WAAAA,EAAAA;QACCC,IAAM,EAAA,CAACV,UAAUW,gBAAmBC,GAAAA,SAAAA;QACpCC,MACEd,EAAAA,SAAAA,IACA,CAACD,WAAAA,kBACCU,GAACM,CAAAA,MAAAA,EAAAA;YAAOC,OAAQ,EAAA,WAAA;AAAYC,YAAAA,SAAAA,gBAAWR,GAACS,CAAAA,IAAAA,EAAAA,EAAAA,CAAAA;YAASC,OAASd,EAAAA,aAAAA;sBACvDC,aAAc,CAAA;AACbJ,gBAAAA,EAAAA,EAAIkB,OAAQ,CAAA,2BAAA,CAAA;gBACZjB,cAAgB,EAAA;AAClB,aAAA;;AAINK,QAAAA,OAAAA,EAASF,aAAc,CAAA;AACrB,YAAA,GAAGE,OAAO;YACVN,EAAIkB,EAAAA,OAAAA,CAAQZ,QAAQN,EAAE;AACxB,SAAA;;AAGN;;;;"}
@@ -6,15 +6,15 @@ var strapiAdmin = require('@strapi/admin/strapi-admin');
6
6
  var designSystem = require('@strapi/design-system');
7
7
  var icons = require('@strapi/icons');
8
8
  var reactIntl = require('react-intl');
9
- var FilterList = require('../../../../components/FilterList/FilterList.js');
10
- var FilterPopover = require('../../../../components/FilterPopover/FilterPopover.js');
11
- var useTracking = require('../../../../hooks/useTracking.js');
12
- var displayedFilters = require('../../../../utils/displayedFilters.js');
9
+ var FilterList = require('../../../components/FilterList/FilterList.js');
10
+ var FilterPopover = require('../../../components/FilterPopover/FilterPopover.js');
11
+ var useTracking = require('../../../hooks/useTracking.js');
12
+ var displayedFilters = require('../../../utils/displayedFilters.js');
13
13
  require('byte-size');
14
14
  require('date-fns');
15
15
  require('qs');
16
- require('../../../../utils/typeFromMime.js');
17
- require('../../../../utils/urlYupSchema.js');
16
+ require('../../../utils/typeFromMime.js');
17
+ require('../../../utils/urlYupSchema.js');
18
18
 
19
19
  function _interopNamespaceDefault(e) {
20
20
  var n = Object.create(null);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Filters.js","sources":["../../../../../admin/src/pages/App/components/Filters.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\nimport { Button, Popover } from '@strapi/design-system';\nimport { Filter } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { FilterList } from '../../../components/FilterList/FilterList';\nimport { FilterPopover } from '../../../components/FilterPopover/FilterPopover';\nimport { useTracking } from '../../../hooks/useTracking';\nimport { displayedFilters } from '../../../utils';\n\nimport type { Query } from '../../../../../shared/contracts/files';\nimport type { FilterListProps } from '../../../components/FilterList/FilterList';\nimport type { FilterPopoverProps } from '../../../components/FilterPopover/FilterPopover';\n\nexport const Filters = () => {\n const [open, setOpen] = React.useState(false);\n const { formatMessage } = useIntl();\n const { trackUsage } = useTracking();\n const [{ query }, setQuery] = useQueryParams<Query>();\n const filters = query?.filters?.$and || [];\n\n const handleRemoveFilter: FilterListProps['onRemoveFilter'] = (nextFilters) => {\n setQuery({ filters: { $and: nextFilters }, page: 1 } as Query);\n };\n\n const handleSubmit: FilterPopoverProps['onSubmit'] = (filters) => {\n trackUsage('didFilterMediaLibraryElements', {\n location: 'content-manager',\n filter: Object.keys(filters[filters.length - 1])[0],\n });\n setQuery({ filters: { $and: filters }, page: 1 } as Query);\n };\n\n return (\n <Popover.Root open={open} onOpenChange={setOpen}>\n <Popover.Trigger>\n <Button variant=\"tertiary\" startIcon={<Filter />} size=\"S\">\n {formatMessage({ id: 'app.utils.filters', defaultMessage: 'Filters' })}\n </Button>\n </Popover.Trigger>\n <FilterPopover\n displayedFilters={displayedFilters}\n filters={filters}\n onSubmit={handleSubmit}\n onToggle={setOpen as FilterPopoverProps['onToggle']}\n />\n <FilterList\n appliedFilters={filters as FilterListProps['appliedFilters']}\n filtersSchema={displayedFilters}\n onRemoveFilter={handleRemoveFilter}\n />\n </Popover.Root>\n );\n};\n"],"names":["Filters","open","setOpen","React","useState","formatMessage","useIntl","trackUsage","useTracking","query","setQuery","useQueryParams","filters","$and","handleRemoveFilter","nextFilters","page","handleSubmit","location","filter","Object","keys","length","_jsxs","Popover","Root","onOpenChange","_jsx","Trigger","Button","variant","startIcon","Filter","size","id","defaultMessage","FilterPopover","displayedFilters","onSubmit","onToggle","FilterList","appliedFilters","filtersSchema","onRemoveFilter"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAgBaA,OAAU,GAAA,IAAA;AACrB,IAAA,MAAM,CAACC,IAAMC,EAAAA,OAAAA,CAAQ,GAAGC,gBAAAA,CAAMC,QAAQ,CAAC,KAAA,CAAA;IACvC,MAAM,EAAEC,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;IAC1B,MAAM,EAAEC,UAAU,EAAE,GAAGC,uBAAAA,EAAAA;AACvB,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,EAAEC,SAAS,GAAGC,0BAAAA,EAAAA;AAC9B,IAAA,MAAMC,OAAUH,GAAAA,KAAAA,EAAOG,OAASC,EAAAA,IAAAA,IAAQ,EAAE;AAE1C,IAAA,MAAMC,qBAAwD,CAACC,WAAAA,GAAAA;QAC7DL,QAAS,CAAA;YAAEE,OAAS,EAAA;gBAAEC,IAAME,EAAAA;AAAY,aAAA;YAAGC,IAAM,EAAA;AAAE,SAAA,CAAA;AACrD,KAAA;AAEA,IAAA,MAAMC,eAA+C,CAACL,OAAAA,GAAAA;AACpDL,QAAAA,UAAAA,CAAW,+BAAiC,EAAA;YAC1CW,QAAU,EAAA,iBAAA;YACVC,MAAQC,EAAAA,MAAAA,CAAOC,IAAI,CAACT,OAAO,CAACA,OAAQU,CAAAA,MAAM,GAAG,CAAA,CAAE,CAAC,CAAC,CAAE;AACrD,SAAA,CAAA;QACAZ,QAAS,CAAA;YAAEE,OAAS,EAAA;gBAAEC,IAAMD,EAAAA;AAAQ,aAAA;YAAGI,IAAM,EAAA;AAAE,SAAA,CAAA;AACjD,KAAA;IAEA,qBACEO,eAAA,CAACC,qBAAQC,IAAI,EAAA;QAACxB,IAAMA,EAAAA,IAAAA;QAAMyB,YAAcxB,EAAAA,OAAAA;;AACtC,0BAAAyB,cAAA,CAACH,qBAAQI,OAAO,EAAA;AACd,gBAAA,QAAA,gBAAAD,cAACE,CAAAA,mBAAAA,EAAAA;oBAAOC,OAAQ,EAAA,UAAA;AAAWC,oBAAAA,SAAAA,gBAAWJ,cAACK,CAAAA,YAAAA,EAAAA,EAAAA,CAAAA;oBAAWC,IAAK,EAAA,GAAA;8BACpD5B,aAAc,CAAA;wBAAE6B,EAAI,EAAA,mBAAA;wBAAqBC,cAAgB,EAAA;AAAU,qBAAA;;;0BAGxER,cAACS,CAAAA,2BAAAA,EAAAA;gBACCC,gBAAkBA,EAAAA,iCAAAA;gBAClBzB,OAASA,EAAAA,OAAAA;gBACT0B,QAAUrB,EAAAA,YAAAA;gBACVsB,QAAUrC,EAAAA;;0BAEZyB,cAACa,CAAAA,qBAAAA,EAAAA;gBACCC,cAAgB7B,EAAAA,OAAAA;gBAChB8B,aAAeL,EAAAA,iCAAAA;gBACfM,cAAgB7B,EAAAA;;;;AAIxB;;;;"}
@@ -4,15 +4,15 @@ import { useQueryParams } from '@strapi/admin/strapi-admin';
4
4
  import { Popover, Button } from '@strapi/design-system';
5
5
  import { Filter } from '@strapi/icons';
6
6
  import { useIntl } from 'react-intl';
7
- import { FilterList } from '../../../../components/FilterList/FilterList.mjs';
8
- import { FilterPopover } from '../../../../components/FilterPopover/FilterPopover.mjs';
9
- import { useTracking } from '../../../../hooks/useTracking.mjs';
10
- import { displayedFilters } from '../../../../utils/displayedFilters.mjs';
7
+ import { FilterList } from '../../../components/FilterList/FilterList.mjs';
8
+ import { FilterPopover } from '../../../components/FilterPopover/FilterPopover.mjs';
9
+ import { useTracking } from '../../../hooks/useTracking.mjs';
10
+ import { displayedFilters } from '../../../utils/displayedFilters.mjs';
11
11
  import 'byte-size';
12
12
  import 'date-fns';
13
13
  import 'qs';
14
- import '../../../../utils/typeFromMime.mjs';
15
- import '../../../../utils/urlYupSchema.mjs';
14
+ import '../../../utils/typeFromMime.mjs';
15
+ import '../../../utils/urlYupSchema.mjs';
16
16
 
17
17
  const Filters = ()=>{
18
18
  const [open, setOpen] = React.useState(false);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Filters.mjs","sources":["../../../../../admin/src/pages/App/components/Filters.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\nimport { Button, Popover } from '@strapi/design-system';\nimport { Filter } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { FilterList } from '../../../components/FilterList/FilterList';\nimport { FilterPopover } from '../../../components/FilterPopover/FilterPopover';\nimport { useTracking } from '../../../hooks/useTracking';\nimport { displayedFilters } from '../../../utils';\n\nimport type { Query } from '../../../../../shared/contracts/files';\nimport type { FilterListProps } from '../../../components/FilterList/FilterList';\nimport type { FilterPopoverProps } from '../../../components/FilterPopover/FilterPopover';\n\nexport const Filters = () => {\n const [open, setOpen] = React.useState(false);\n const { formatMessage } = useIntl();\n const { trackUsage } = useTracking();\n const [{ query }, setQuery] = useQueryParams<Query>();\n const filters = query?.filters?.$and || [];\n\n const handleRemoveFilter: FilterListProps['onRemoveFilter'] = (nextFilters) => {\n setQuery({ filters: { $and: nextFilters }, page: 1 } as Query);\n };\n\n const handleSubmit: FilterPopoverProps['onSubmit'] = (filters) => {\n trackUsage('didFilterMediaLibraryElements', {\n location: 'content-manager',\n filter: Object.keys(filters[filters.length - 1])[0],\n });\n setQuery({ filters: { $and: filters }, page: 1 } as Query);\n };\n\n return (\n <Popover.Root open={open} onOpenChange={setOpen}>\n <Popover.Trigger>\n <Button variant=\"tertiary\" startIcon={<Filter />} size=\"S\">\n {formatMessage({ id: 'app.utils.filters', defaultMessage: 'Filters' })}\n </Button>\n </Popover.Trigger>\n <FilterPopover\n displayedFilters={displayedFilters}\n filters={filters}\n onSubmit={handleSubmit}\n onToggle={setOpen as FilterPopoverProps['onToggle']}\n />\n <FilterList\n appliedFilters={filters as FilterListProps['appliedFilters']}\n filtersSchema={displayedFilters}\n onRemoveFilter={handleRemoveFilter}\n />\n </Popover.Root>\n );\n};\n"],"names":["Filters","open","setOpen","React","useState","formatMessage","useIntl","trackUsage","useTracking","query","setQuery","useQueryParams","filters","$and","handleRemoveFilter","nextFilters","page","handleSubmit","location","filter","Object","keys","length","_jsxs","Popover","Root","onOpenChange","_jsx","Trigger","Button","variant","startIcon","Filter","size","id","defaultMessage","FilterPopover","displayedFilters","onSubmit","onToggle","FilterList","appliedFilters","filtersSchema","onRemoveFilter"],"mappings":";;;;;;;;;;;;;;;;MAgBaA,OAAU,GAAA,IAAA;AACrB,IAAA,MAAM,CAACC,IAAMC,EAAAA,OAAAA,CAAQ,GAAGC,KAAAA,CAAMC,QAAQ,CAAC,KAAA,CAAA;IACvC,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;IAC1B,MAAM,EAAEC,UAAU,EAAE,GAAGC,WAAAA,EAAAA;AACvB,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,EAAEC,SAAS,GAAGC,cAAAA,EAAAA;AAC9B,IAAA,MAAMC,OAAUH,GAAAA,KAAAA,EAAOG,OAASC,EAAAA,IAAAA,IAAQ,EAAE;AAE1C,IAAA,MAAMC,qBAAwD,CAACC,WAAAA,GAAAA;QAC7DL,QAAS,CAAA;YAAEE,OAAS,EAAA;gBAAEC,IAAME,EAAAA;AAAY,aAAA;YAAGC,IAAM,EAAA;AAAE,SAAA,CAAA;AACrD,KAAA;AAEA,IAAA,MAAMC,eAA+C,CAACL,OAAAA,GAAAA;AACpDL,QAAAA,UAAAA,CAAW,+BAAiC,EAAA;YAC1CW,QAAU,EAAA,iBAAA;YACVC,MAAQC,EAAAA,MAAAA,CAAOC,IAAI,CAACT,OAAO,CAACA,OAAQU,CAAAA,MAAM,GAAG,CAAA,CAAE,CAAC,CAAC,CAAE;AACrD,SAAA,CAAA;QACAZ,QAAS,CAAA;YAAEE,OAAS,EAAA;gBAAEC,IAAMD,EAAAA;AAAQ,aAAA;YAAGI,IAAM,EAAA;AAAE,SAAA,CAAA;AACjD,KAAA;IAEA,qBACEO,IAAA,CAACC,QAAQC,IAAI,EAAA;QAACxB,IAAMA,EAAAA,IAAAA;QAAMyB,YAAcxB,EAAAA,OAAAA;;AACtC,0BAAAyB,GAAA,CAACH,QAAQI,OAAO,EAAA;AACd,gBAAA,QAAA,gBAAAD,GAACE,CAAAA,MAAAA,EAAAA;oBAAOC,OAAQ,EAAA,UAAA;AAAWC,oBAAAA,SAAAA,gBAAWJ,GAACK,CAAAA,MAAAA,EAAAA,EAAAA,CAAAA;oBAAWC,IAAK,EAAA,GAAA;8BACpD5B,aAAc,CAAA;wBAAE6B,EAAI,EAAA,mBAAA;wBAAqBC,cAAgB,EAAA;AAAU,qBAAA;;;0BAGxER,GAACS,CAAAA,aAAAA,EAAAA;gBACCC,gBAAkBA,EAAAA,gBAAAA;gBAClBzB,OAASA,EAAAA,OAAAA;gBACT0B,QAAUrB,EAAAA,YAAAA;gBACVsB,QAAUrC,EAAAA;;0BAEZyB,GAACa,CAAAA,UAAAA,EAAAA;gBACCC,cAAgB7B,EAAAA,OAAAA;gBAChB8B,aAAeL,EAAAA,gBAAAA;gBACfM,cAAgB7B,EAAAA;;;;AAIxB;;;;"}
@@ -7,12 +7,12 @@ var icons = require('@strapi/icons');
7
7
  var qs = require('qs');
8
8
  var reactIntl = require('react-intl');
9
9
  var reactRouterDom = require('react-router-dom');
10
- var Breadcrumbs = require('../../../../components/Breadcrumbs/Breadcrumbs.js');
10
+ var Breadcrumbs = require('../../../components/Breadcrumbs/Breadcrumbs.js');
11
11
  require('byte-size');
12
12
  require('date-fns');
13
- var getTrad = require('../../../../utils/getTrad.js');
14
- require('../../../../utils/typeFromMime.js');
15
- require('../../../../utils/urlYupSchema.js');
13
+ var getTrad = require('../../../utils/getTrad.js');
14
+ require('../../../utils/typeFromMime.js');
15
+ require('../../../utils/urlYupSchema.js');
16
16
 
17
17
  const Header = ({ breadcrumbs = null, canCreate, folder = null, onToggleEditFolderDialog, onToggleUploadAssetDialog })=>{
18
18
  const { formatMessage } = reactIntl.useIntl();
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Header.js","sources":["../../../../../admin/src/pages/App/components/Header.tsx"],"sourcesContent":["import { useQueryParams, Layouts } from '@strapi/admin/strapi-admin';\nimport { Button, Flex, Link } from '@strapi/design-system';\nimport { ArrowLeft, Plus } from '@strapi/icons';\nimport { stringify } from 'qs';\nimport { useIntl } from 'react-intl';\nimport { useLocation, NavLink } from 'react-router-dom';\n\nimport { Breadcrumbs } from '../../../components/Breadcrumbs/Breadcrumbs';\nimport { getTrad } from '../../../utils';\n\nimport type { Folder } from '../../../../../shared/contracts/folders';\nimport type { CrumbDefinition } from '../../../components/Breadcrumbs/Breadcrumbs';\n\ninterface FolderDefinition extends Omit<Folder, 'children' | 'files' | 'parent'> {\n children: {\n count: number;\n };\n files: {\n count: number;\n };\n parent?: FolderDefinition;\n}\n\nexport interface HeaderProps {\n breadcrumbs?: Array<CrumbDefinition> | null;\n canCreate: boolean;\n folder?: FolderDefinition | null;\n onToggleEditFolderDialog: (args?: { created?: boolean }) => void;\n onToggleUploadAssetDialog: () => void;\n}\n\nexport const Header = ({\n breadcrumbs = null,\n canCreate,\n folder = null,\n onToggleEditFolderDialog,\n onToggleUploadAssetDialog,\n}: HeaderProps) => {\n const { formatMessage } = useIntl();\n const { pathname } = useLocation();\n const [{ query }] = useQueryParams();\n const backQuery = {\n ...query,\n folder:\n folder?.parent && typeof folder.parent !== 'number' && folder.parent.id\n ? folder.parent.id\n : undefined,\n folderPath:\n folder?.parent && typeof folder.parent !== 'number' && folder.parent.path\n ? folder.parent.path\n : undefined,\n };\n\n return (\n <Layouts.Header\n title={formatMessage({\n id: getTrad('plugin.name'),\n defaultMessage: `Media Library`,\n })}\n subtitle={\n breadcrumbs &&\n typeof breadcrumbs !== 'boolean' &&\n folder && (\n <Breadcrumbs\n label={formatMessage({\n id: getTrad('header.breadcrumbs.nav.label'),\n defaultMessage: 'Folders navigation',\n })}\n breadcrumbs={breadcrumbs}\n currentFolderId={folder?.id}\n />\n )\n }\n navigationAction={\n folder && (\n <Link\n tag={NavLink}\n startIcon={<ArrowLeft />}\n to={`${pathname}?${stringify(backQuery, { encode: false })}`}\n >\n {formatMessage({\n id: getTrad('header.actions.folder-level-up'),\n defaultMessage: 'Back',\n })}\n </Link>\n )\n }\n primaryAction={\n canCreate && (\n <Flex gap={2}>\n <Button startIcon={<Plus />} variant=\"secondary\" onClick={onToggleEditFolderDialog}>\n {formatMessage({\n id: getTrad('header.actions.add-folder'),\n defaultMessage: 'Add new folder',\n })}\n </Button>\n\n <Button startIcon={<Plus />} onClick={onToggleUploadAssetDialog}>\n {formatMessage({\n id: getTrad('header.actions.add-assets'),\n defaultMessage: 'Add new assets',\n })}\n </Button>\n </Flex>\n )\n }\n />\n );\n};\n"],"names":["Header","breadcrumbs","canCreate","folder","onToggleEditFolderDialog","onToggleUploadAssetDialog","formatMessage","useIntl","pathname","useLocation","query","useQueryParams","backQuery","parent","id","undefined","folderPath","path","_jsx","Layouts","title","getTrad","defaultMessage","subtitle","Breadcrumbs","label","currentFolderId","navigationAction","Link","tag","NavLink","startIcon","ArrowLeft","to","stringify","encode","primaryAction","_jsxs","Flex","gap","Button","Plus","variant","onClick"],"mappings":";;;;;;;;;;;;;;;;AA+BaA,MAAAA,MAAAA,GAAS,CAAC,EACrBC,cAAc,IAAI,EAClBC,SAAS,EACTC,SAAS,IAAI,EACbC,wBAAwB,EACxBC,yBAAyB,EACb,GAAA;IACZ,MAAM,EAAEC,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;IAC1B,MAAM,EAAEC,QAAQ,EAAE,GAAGC,0BAAAA,EAAAA;AACrB,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,0BAAAA,EAAAA;AACpB,IAAA,MAAMC,SAAY,GAAA;AAChB,QAAA,GAAGF,KAAK;AACRP,QAAAA,MAAAA,EACEA,QAAQU,MAAU,IAAA,OAAOV,MAAOU,CAAAA,MAAM,KAAK,QAAYV,IAAAA,MAAAA,CAAOU,MAAM,CAACC,EAAE,GACnEX,MAAAA,CAAOU,MAAM,CAACC,EAAE,GAChBC,SAAAA;AACNC,QAAAA,UAAAA,EACEb,QAAQU,MAAU,IAAA,OAAOV,MAAOU,CAAAA,MAAM,KAAK,QAAYV,IAAAA,MAAAA,CAAOU,MAAM,CAACI,IAAI,GACrEd,MAAAA,CAAOU,MAAM,CAACI,IAAI,GAClBF;AACR,KAAA;IAEA,qBACEG,cAAA,CAACC,oBAAQnB,MAAM,EAAA;AACboB,QAAAA,KAAAA,EAAOd,aAAc,CAAA;AACnBQ,YAAAA,EAAAA,EAAIO,eAAQ,CAAA,aAAA,CAAA;YACZC,cAAgB,EAAA,CAAC,aAAa;AAChC,SAAA,CAAA;AACAC,QAAAA,QAAAA,EACEtB,WACA,IAAA,OAAOA,WAAgB,KAAA,SAAA,IACvBE,wBACEe,cAACM,CAAAA,uBAAAA,EAAAA;AACCC,YAAAA,KAAAA,EAAOnB,aAAc,CAAA;AACnBQ,gBAAAA,EAAAA,EAAIO,eAAQ,CAAA,8BAAA,CAAA;gBACZC,cAAgB,EAAA;AAClB,aAAA,CAAA;YACArB,WAAaA,EAAAA,WAAAA;AACbyB,YAAAA,eAAAA,EAAiBvB,MAAQW,EAAAA;;AAI/Ba,QAAAA,gBAAAA,EACExB,wBACEe,cAACU,CAAAA,iBAAAA,EAAAA;YACCC,GAAKC,EAAAA,sBAAAA;AACLC,YAAAA,SAAAA,gBAAWb,cAACc,CAAAA,eAAAA,EAAAA,EAAAA,CAAAA;AACZC,YAAAA,EAAAA,EAAI,CAAGzB,EAAAA,QAAAA,CAAS,CAAC,EAAE0B,aAAUtB,SAAW,EAAA;gBAAEuB,MAAQ,EAAA;aAAU,CAAA,CAAA,CAAA;sBAE3D7B,aAAc,CAAA;AACbQ,gBAAAA,EAAAA,EAAIO,eAAQ,CAAA,gCAAA,CAAA;gBACZC,cAAgB,EAAA;AAClB,aAAA;;AAINc,QAAAA,aAAAA,EACElC,2BACEmC,eAACC,CAAAA,iBAAAA,EAAAA;YAAKC,GAAK,EAAA,CAAA;;8BACTrB,cAACsB,CAAAA,mBAAAA,EAAAA;AAAOT,oBAAAA,SAAAA,gBAAWb,cAACuB,CAAAA,UAAAA,EAAAA,EAAAA,CAAAA;oBAASC,OAAQ,EAAA,WAAA;oBAAYC,OAASvC,EAAAA,wBAAAA;8BACvDE,aAAc,CAAA;AACbQ,wBAAAA,EAAAA,EAAIO,eAAQ,CAAA,2BAAA,CAAA;wBACZC,cAAgB,EAAA;AAClB,qBAAA;;8BAGFJ,cAACsB,CAAAA,mBAAAA,EAAAA;AAAOT,oBAAAA,SAAAA,gBAAWb,cAACuB,CAAAA,UAAAA,EAAAA,EAAAA,CAAAA;oBAASE,OAAStC,EAAAA,yBAAAA;8BACnCC,aAAc,CAAA;AACbQ,wBAAAA,EAAAA,EAAIO,eAAQ,CAAA,2BAAA,CAAA;wBACZC,cAAgB,EAAA;AAClB,qBAAA;;;;;AAOd;;;;"}
@@ -5,12 +5,12 @@ import { ArrowLeft, Plus } from '@strapi/icons';
5
5
  import { stringify } from 'qs';
6
6
  import { useIntl } from 'react-intl';
7
7
  import { useLocation, NavLink } from 'react-router-dom';
8
- import { Breadcrumbs } from '../../../../components/Breadcrumbs/Breadcrumbs.mjs';
8
+ import { Breadcrumbs } from '../../../components/Breadcrumbs/Breadcrumbs.mjs';
9
9
  import 'byte-size';
10
10
  import 'date-fns';
11
- import { getTrad } from '../../../../utils/getTrad.mjs';
12
- import '../../../../utils/typeFromMime.mjs';
13
- import '../../../../utils/urlYupSchema.mjs';
11
+ import { getTrad } from '../../../utils/getTrad.mjs';
12
+ import '../../../utils/typeFromMime.mjs';
13
+ import '../../../utils/urlYupSchema.mjs';
14
14
 
15
15
  const Header = ({ breadcrumbs = null, canCreate, folder = null, onToggleEditFolderDialog, onToggleUploadAssetDialog })=>{
16
16
  const { formatMessage } = useIntl();
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Header.mjs","sources":["../../../../../admin/src/pages/App/components/Header.tsx"],"sourcesContent":["import { useQueryParams, Layouts } from '@strapi/admin/strapi-admin';\nimport { Button, Flex, Link } from '@strapi/design-system';\nimport { ArrowLeft, Plus } from '@strapi/icons';\nimport { stringify } from 'qs';\nimport { useIntl } from 'react-intl';\nimport { useLocation, NavLink } from 'react-router-dom';\n\nimport { Breadcrumbs } from '../../../components/Breadcrumbs/Breadcrumbs';\nimport { getTrad } from '../../../utils';\n\nimport type { Folder } from '../../../../../shared/contracts/folders';\nimport type { CrumbDefinition } from '../../../components/Breadcrumbs/Breadcrumbs';\n\ninterface FolderDefinition extends Omit<Folder, 'children' | 'files' | 'parent'> {\n children: {\n count: number;\n };\n files: {\n count: number;\n };\n parent?: FolderDefinition;\n}\n\nexport interface HeaderProps {\n breadcrumbs?: Array<CrumbDefinition> | null;\n canCreate: boolean;\n folder?: FolderDefinition | null;\n onToggleEditFolderDialog: (args?: { created?: boolean }) => void;\n onToggleUploadAssetDialog: () => void;\n}\n\nexport const Header = ({\n breadcrumbs = null,\n canCreate,\n folder = null,\n onToggleEditFolderDialog,\n onToggleUploadAssetDialog,\n}: HeaderProps) => {\n const { formatMessage } = useIntl();\n const { pathname } = useLocation();\n const [{ query }] = useQueryParams();\n const backQuery = {\n ...query,\n folder:\n folder?.parent && typeof folder.parent !== 'number' && folder.parent.id\n ? folder.parent.id\n : undefined,\n folderPath:\n folder?.parent && typeof folder.parent !== 'number' && folder.parent.path\n ? folder.parent.path\n : undefined,\n };\n\n return (\n <Layouts.Header\n title={formatMessage({\n id: getTrad('plugin.name'),\n defaultMessage: `Media Library`,\n })}\n subtitle={\n breadcrumbs &&\n typeof breadcrumbs !== 'boolean' &&\n folder && (\n <Breadcrumbs\n label={formatMessage({\n id: getTrad('header.breadcrumbs.nav.label'),\n defaultMessage: 'Folders navigation',\n })}\n breadcrumbs={breadcrumbs}\n currentFolderId={folder?.id}\n />\n )\n }\n navigationAction={\n folder && (\n <Link\n tag={NavLink}\n startIcon={<ArrowLeft />}\n to={`${pathname}?${stringify(backQuery, { encode: false })}`}\n >\n {formatMessage({\n id: getTrad('header.actions.folder-level-up'),\n defaultMessage: 'Back',\n })}\n </Link>\n )\n }\n primaryAction={\n canCreate && (\n <Flex gap={2}>\n <Button startIcon={<Plus />} variant=\"secondary\" onClick={onToggleEditFolderDialog}>\n {formatMessage({\n id: getTrad('header.actions.add-folder'),\n defaultMessage: 'Add new folder',\n })}\n </Button>\n\n <Button startIcon={<Plus />} onClick={onToggleUploadAssetDialog}>\n {formatMessage({\n id: getTrad('header.actions.add-assets'),\n defaultMessage: 'Add new assets',\n })}\n </Button>\n </Flex>\n )\n }\n />\n );\n};\n"],"names":["Header","breadcrumbs","canCreate","folder","onToggleEditFolderDialog","onToggleUploadAssetDialog","formatMessage","useIntl","pathname","useLocation","query","useQueryParams","backQuery","parent","id","undefined","folderPath","path","_jsx","Layouts","title","getTrad","defaultMessage","subtitle","Breadcrumbs","label","currentFolderId","navigationAction","Link","tag","NavLink","startIcon","ArrowLeft","to","stringify","encode","primaryAction","_jsxs","Flex","gap","Button","Plus","variant","onClick"],"mappings":";;;;;;;;;;;;;;AA+BaA,MAAAA,MAAAA,GAAS,CAAC,EACrBC,cAAc,IAAI,EAClBC,SAAS,EACTC,SAAS,IAAI,EACbC,wBAAwB,EACxBC,yBAAyB,EACb,GAAA;IACZ,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;IAC1B,MAAM,EAAEC,QAAQ,EAAE,GAAGC,WAAAA,EAAAA;AACrB,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,cAAAA,EAAAA;AACpB,IAAA,MAAMC,SAAY,GAAA;AAChB,QAAA,GAAGF,KAAK;AACRP,QAAAA,MAAAA,EACEA,QAAQU,MAAU,IAAA,OAAOV,MAAOU,CAAAA,MAAM,KAAK,QAAYV,IAAAA,MAAAA,CAAOU,MAAM,CAACC,EAAE,GACnEX,MAAAA,CAAOU,MAAM,CAACC,EAAE,GAChBC,SAAAA;AACNC,QAAAA,UAAAA,EACEb,QAAQU,MAAU,IAAA,OAAOV,MAAOU,CAAAA,MAAM,KAAK,QAAYV,IAAAA,MAAAA,CAAOU,MAAM,CAACI,IAAI,GACrEd,MAAAA,CAAOU,MAAM,CAACI,IAAI,GAClBF;AACR,KAAA;IAEA,qBACEG,GAAA,CAACC,QAAQnB,MAAM,EAAA;AACboB,QAAAA,KAAAA,EAAOd,aAAc,CAAA;AACnBQ,YAAAA,EAAAA,EAAIO,OAAQ,CAAA,aAAA,CAAA;YACZC,cAAgB,EAAA,CAAC,aAAa;AAChC,SAAA,CAAA;AACAC,QAAAA,QAAAA,EACEtB,WACA,IAAA,OAAOA,WAAgB,KAAA,SAAA,IACvBE,wBACEe,GAACM,CAAAA,WAAAA,EAAAA;AACCC,YAAAA,KAAAA,EAAOnB,aAAc,CAAA;AACnBQ,gBAAAA,EAAAA,EAAIO,OAAQ,CAAA,8BAAA,CAAA;gBACZC,cAAgB,EAAA;AAClB,aAAA,CAAA;YACArB,WAAaA,EAAAA,WAAAA;AACbyB,YAAAA,eAAAA,EAAiBvB,MAAQW,EAAAA;;AAI/Ba,QAAAA,gBAAAA,EACExB,wBACEe,GAACU,CAAAA,IAAAA,EAAAA;YACCC,GAAKC,EAAAA,OAAAA;AACLC,YAAAA,SAAAA,gBAAWb,GAACc,CAAAA,SAAAA,EAAAA,EAAAA,CAAAA;AACZC,YAAAA,EAAAA,EAAI,CAAGzB,EAAAA,QAAAA,CAAS,CAAC,EAAE0B,UAAUtB,SAAW,EAAA;gBAAEuB,MAAQ,EAAA;aAAU,CAAA,CAAA,CAAA;sBAE3D7B,aAAc,CAAA;AACbQ,gBAAAA,EAAAA,EAAIO,OAAQ,CAAA,gCAAA,CAAA;gBACZC,cAAgB,EAAA;AAClB,aAAA;;AAINc,QAAAA,aAAAA,EACElC,2BACEmC,IAACC,CAAAA,IAAAA,EAAAA;YAAKC,GAAK,EAAA,CAAA;;8BACTrB,GAACsB,CAAAA,MAAAA,EAAAA;AAAOT,oBAAAA,SAAAA,gBAAWb,GAACuB,CAAAA,IAAAA,EAAAA,EAAAA,CAAAA;oBAASC,OAAQ,EAAA,WAAA;oBAAYC,OAASvC,EAAAA,wBAAAA;8BACvDE,aAAc,CAAA;AACbQ,wBAAAA,EAAAA,EAAIO,OAAQ,CAAA,2BAAA,CAAA;wBACZC,cAAgB,EAAA;AAClB,qBAAA;;8BAGFJ,GAACsB,CAAAA,MAAAA,EAAAA;AAAOT,oBAAAA,SAAAA,gBAAWb,GAACuB,CAAAA,IAAAA,EAAAA,EAAAA,CAAAA;oBAASE,OAAStC,EAAAA,yBAAAA;8BACnCC,aAAc,CAAA;AACbQ,wBAAAA,EAAAA,EAAIO,OAAQ,CAAA,2BAAA,CAAA;wBACZC,cAAgB,EAAA;AAClB,qBAAA;;;;;AAOd;;;;"}
@@ -6,7 +6,7 @@ interface FolderWithType extends FolderInitial {
6
6
  export interface FileWithType extends File {
7
7
  type: string;
8
8
  }
9
- interface BulkActionsProps {
9
+ export interface BulkActionsProps {
10
10
  selected: Array<FileWithType | FolderDefinition> | Array<FolderWithType | FileWithType>;
11
11
  onSuccess: () => void;
12
12
  currentFolder?: FolderWithType;
@@ -1,8 +1,5 @@
1
- import type { File } from '../../../../../shared/contracts/files';
2
1
  import type { FolderDefinition } from '../../../../../shared/contracts/folders';
3
- export interface FileWithType extends File {
4
- type: string;
5
- }
2
+ import type { FileWithType } from '../../../hooks/useBulkRemove';
6
3
  export interface BulkDeleteButtonProps {
7
4
  selected: Array<FileWithType | FolderDefinition>;
8
5
  onSuccess: () => void;
@@ -1,15 +1,15 @@
1
1
  import type { File } from '../../../../../shared/contracts/files';
2
2
  import type { Folder as FolderDefinition } from '../../../../../shared/contracts/folders';
3
- interface FileWithType extends File {
3
+ interface FolderWithType extends FolderDefinition {
4
4
  type: string;
5
5
  }
6
- interface FolderWithType extends FolderDefinition {
6
+ interface FileWithType extends File {
7
7
  type: string;
8
8
  }
9
9
  export interface BulkMoveButtonProps {
10
10
  onSuccess: () => void;
11
11
  currentFolder?: FolderWithType;
12
- selected: Array<FolderWithType | FileWithType>;
12
+ selected?: Array<FolderWithType | FileWithType>;
13
13
  }
14
- export declare const BulkMoveButton: ({ selected, onSuccess, currentFolder }: BulkMoveButtonProps) => import("react/jsx-runtime").JSX.Element;
14
+ export declare const BulkMoveButton: ({ selected, onSuccess, currentFolder, }: BulkMoveButtonProps) => import("react/jsx-runtime").JSX.Element;
15
15
  export {};
@@ -1,8 +1,7 @@
1
- interface EmptyOrNoPermissionsProps {
1
+ export interface EmptyOrNoPermissionsProps {
2
2
  canCreate: boolean;
3
3
  canRead: boolean;
4
4
  isFiltering: boolean;
5
5
  onActionClick: () => void;
6
6
  }
7
7
  export declare const EmptyOrNoPermissions: ({ canCreate, isFiltering, canRead, onActionClick, }: EmptyOrNoPermissionsProps) => import("react/jsx-runtime").JSX.Element;
8
- export {};
@@ -1,10 +1,19 @@
1
1
  import type { Folder } from '../../../../../shared/contracts/folders';
2
2
  import type { CrumbDefinition } from '../../../components/Breadcrumbs/Breadcrumbs';
3
- interface HeaderProps {
3
+ interface FolderDefinition extends Omit<Folder, 'children' | 'files' | 'parent'> {
4
+ children: {
5
+ count: number;
6
+ };
7
+ files: {
8
+ count: number;
9
+ };
10
+ parent?: FolderDefinition;
11
+ }
12
+ export interface HeaderProps {
4
13
  breadcrumbs?: Array<CrumbDefinition> | null;
5
14
  canCreate: boolean;
6
- folder?: Folder | null;
7
- onToggleEditFolderDialog: ({ created }?: {
15
+ folder?: FolderDefinition | null;
16
+ onToggleEditFolderDialog: (args?: {
8
17
  created?: boolean;
9
18
  }) => void;
10
19
  onToggleUploadAssetDialog: () => void;
@@ -49,20 +49,11 @@ var adminUpload = {
49
49
  if (Array.isArray(files)) {
50
50
  throw new utils.errors.ApplicationError('Cannot replace a file with multiple ones');
51
51
  }
52
- const securityResults = await mimeValidation.enforceUploadSecurity(files, strapi);
53
- if (securityResults.errors.length > 0) {
54
- const { error } = securityResults.errors[0];
55
- switch(error.code){
56
- case 'MIME_TYPE_NOT_ALLOWED':
57
- throw new utils.errors.ValidationError(error.message, error.details);
58
- default:
59
- throw new utils.errors.ApplicationError(error.message, error.details);
60
- }
61
- }
62
- const data = await upload.validateUploadBody(body);
52
+ const { validFiles, filteredBody } = await mimeValidation.prepareUploadRequest(files, body, strapi);
53
+ const data = await upload.validateUploadBody(filteredBody);
63
54
  const replacedFile = await uploadService.replace(id, {
64
55
  data,
65
- file: securityResults.validFiles[0]
56
+ file: validFiles[0]
66
57
  }, {
67
58
  user
68
59
  });
@@ -83,31 +74,10 @@ var adminUpload = {
83
74
  if (!pm.isAllowed) {
84
75
  return ctx.forbidden();
85
76
  }
86
- const securityResults = await mimeValidation.enforceUploadSecurity(files, strapi);
87
- if (securityResults.validFiles.length === 0) {
88
- throw new utils.errors.ValidationError(securityResults.errors[0].error.message, securityResults.errors[0].error.details);
89
- }
90
- let filteredBody = body;
91
- if (body?.fileInfo && Array.isArray(body.fileInfo)) {
92
- const filteredFileInfo = body.fileInfo.filter((fi)=>{
93
- const info = typeof fi === 'string' ? JSON.parse(fi) : fi;
94
- return securityResults.validFileNames.includes(info.name);
95
- });
96
- if (filteredFileInfo.length === 1) {
97
- filteredBody = {
98
- ...body,
99
- fileInfo: filteredFileInfo[0]
100
- };
101
- } else {
102
- filteredBody = {
103
- ...body,
104
- fileInfo: filteredFileInfo
105
- };
106
- }
107
- }
108
- const isMultipleFiles = Array.isArray(filteredBody.fileInfo) && filteredBody.fileInfo.length > 1;
77
+ const { validFiles, filteredBody } = await mimeValidation.prepareUploadRequest(files, body, strapi);
78
+ const isMultipleFiles = validFiles.length > 1;
109
79
  const data = await upload.validateUploadBody(filteredBody, isMultipleFiles);
110
- let filesArray = securityResults.validFiles;
80
+ let filesArray = validFiles;
111
81
  if (data.fileInfo && Array.isArray(data.fileInfo) && filesArray.length === data.fileInfo.length) {
112
82
  // Reorder filesArray to match data.fileInfo order
113
83
  const alignedFilesArray = data.fileInfo.map((info)=>{
@@ -1 +1 @@
1
- {"version":3,"file":"admin-upload.js","sources":["../../../server/src/controllers/admin-upload.ts"],"sourcesContent":["import _ from 'lodash';\nimport { errors, async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { FileInfo } from '../types';\nimport { enforceUploadSecurity } from '../utils/mime-validation';\n\nexport default {\n async bulkUpdateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const { updates } = await validateBulkUpdateBody(body);\n const uploadService = getService('upload');\n\n const results = await async.map(\n updates,\n async ({ id, fileInfo }: { id: number; fileInfo: FileInfo }) => {\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const updated = await uploadService.updateFileInfo(id, fileInfo as any, { user });\n return pm.sanitizeOutput(updated, { action: ACTIONS.read });\n }\n );\n\n ctx.body = results;\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const data = await validateUploadBody(body);\n\n const file = await uploadService.updateFileInfo(id, data.fileInfo as any, { user });\n\n ctx.body = await pm.sanitizeOutput(file, { action: ACTIONS.read });\n },\n\n async replaceFile(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body, files: { files } = {} },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n if (Array.isArray(files)) {\n throw new errors.ApplicationError('Cannot replace a file with multiple ones');\n }\n\n const securityResults = await enforceUploadSecurity(files, strapi);\n\n if (securityResults.errors.length > 0) {\n const { error } = securityResults.errors[0];\n switch (error.code) {\n case 'MIME_TYPE_NOT_ALLOWED':\n throw new errors.ValidationError(error.message, error.details);\n default:\n throw new errors.ApplicationError(error.message, error.details);\n }\n }\n\n const data = (await validateUploadBody(body)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(\n id,\n { data, file: securityResults.validFiles[0] },\n { user }\n );\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(replacedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async uploadFiles(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const securityResults = await enforceUploadSecurity(files, strapi);\n\n if (securityResults.validFiles.length === 0) {\n throw new errors.ValidationError(\n securityResults.errors[0].error.message,\n securityResults.errors[0].error.details\n );\n }\n\n let filteredBody = body;\n if (body?.fileInfo && Array.isArray(body.fileInfo)) {\n const filteredFileInfo = body.fileInfo.filter((fi: string) => {\n const info = typeof fi === 'string' ? JSON.parse(fi) : fi;\n return securityResults.validFileNames.includes(info.name);\n });\n\n if (filteredFileInfo.length === 1) {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo[0],\n };\n } else {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo,\n };\n }\n }\n\n const isMultipleFiles =\n Array.isArray(filteredBody.fileInfo) && filteredBody.fileInfo.length > 1;\n\n const data = await validateUploadBody(filteredBody, isMultipleFiles);\n\n let filesArray = securityResults.validFiles;\n\n if (\n data.fileInfo &&\n Array.isArray(data.fileInfo) &&\n filesArray.length === data.fileInfo.length\n ) {\n // Reorder filesArray to match data.fileInfo order\n const alignedFilesArray = data.fileInfo\n .map((info) => {\n return filesArray.find((file) => file.originalFilename === info.name);\n })\n .filter(Boolean) as any[];\n\n filesArray = alignedFilesArray;\n }\n\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\n if (uploadedFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // AFTER upload - use thumbnail versions for AI processing\n if (await aiMetadataService.isEnabled()) {\n try {\n // Use thumbnail URLs instead of original files\n const thumbnailFiles = uploadedFiles.map(\n (file) =>\n ({\n filepath: file.formats?.thumbnail?.url || file.url, // Use thumbnail if available\n mimetype: file.mime,\n originalFilename: file.name,\n size: file.formats?.thumbnail?.size || file.size,\n provider: file.provider,\n }) as unknown as any\n );\n\n const metadataResults = await aiMetadataService.processFiles(thumbnailFiles);\n\n // Update the uploaded files with AI metadata\n await Promise.all(\n uploadedFiles.map(async (uploadedFile, index) => {\n const aiMetadata = metadataResults[index];\n if (aiMetadata) {\n await uploadService.updateFileInfo(\n uploadedFile.id,\n {\n alternativeText: aiMetadata.altText,\n caption: aiMetadata.caption,\n },\n { user }\n );\n\n uploadedFiles[index].alternativeText = aiMetadata.altText;\n uploadedFiles[index].caption = aiMetadata.caption;\n }\n })\n );\n } catch (error) {\n strapi.log.warn('AI metadata generation failed, proceeding without AI enhancements', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFiles = await async.map(uploadedFiles, getService('file').signFileUrls);\n\n ctx.body = await pm.sanitizeOutput(signedFiles, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new errors.ApplicationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n};\n"],"names":["bulkUpdateFileInfo","ctx","state","userAbility","user","request","body","updates","validateBulkUpdateBody","uploadService","getService","results","async","map","id","fileInfo","pm","findEntityAndCheckPermissions","ACTIONS","update","FILE_MODEL_UID","updated","updateFileInfo","sanitizeOutput","action","read","query","errors","ValidationError","data","validateUploadBody","file","replaceFile","files","Array","isArray","ApplicationError","securityResults","enforceUploadSecurity","strapi","length","error","code","message","details","replacedFile","replace","validFiles","signedFile","signFileUrls","uploadFiles","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","filteredBody","filteredFileInfo","filter","fi","info","JSON","parse","validFileNames","includes","name","isMultipleFiles","filesArray","alignedFilesArray","find","originalFilename","Boolean","uploadedFiles","upload","some","mime","startsWith","trackUsage","aiMetadataService","isEnabled","thumbnailFiles","filepath","formats","thumbnail","url","mimetype","size","provider","metadataResults","processFiles","Promise","all","uploadedFile","index","aiMetadata","alternativeText","altText","caption","log","warn","Error","String","signedFiles","status","_","isEmpty"],"mappings":"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"}
1
+ {"version":3,"file":"admin-upload.js","sources":["../../../server/src/controllers/admin-upload.ts"],"sourcesContent":["import _ from 'lodash';\nimport { errors, async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { FileInfo } from '../types';\nimport { prepareUploadRequest } from '../utils/mime-validation';\n\nexport default {\n async bulkUpdateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const { updates } = await validateBulkUpdateBody(body);\n const uploadService = getService('upload');\n\n const results = await async.map(\n updates,\n async ({ id, fileInfo }: { id: number; fileInfo: FileInfo }) => {\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const updated = await uploadService.updateFileInfo(id, fileInfo as any, { user });\n return pm.sanitizeOutput(updated, { action: ACTIONS.read });\n }\n );\n\n ctx.body = results;\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const data = await validateUploadBody(body);\n\n const file = await uploadService.updateFileInfo(id, data.fileInfo as any, { user });\n\n ctx.body = await pm.sanitizeOutput(file, { action: ACTIONS.read });\n },\n\n async replaceFile(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body, files: { files } = {} },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n if (Array.isArray(files)) {\n throw new errors.ApplicationError('Cannot replace a file with multiple ones');\n }\n\n const { validFiles, filteredBody } = await prepareUploadRequest(files, body, strapi);\n\n const data = (await validateUploadBody(filteredBody)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(id, { data, file: validFiles[0] }, { user });\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(replacedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async uploadFiles(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const { validFiles, filteredBody } = await prepareUploadRequest(files, body, strapi);\n\n const isMultipleFiles = validFiles.length > 1;\n const data = await validateUploadBody(filteredBody, isMultipleFiles);\n\n let filesArray = validFiles;\n\n if (\n data.fileInfo &&\n Array.isArray(data.fileInfo) &&\n filesArray.length === data.fileInfo.length\n ) {\n // Reorder filesArray to match data.fileInfo order\n const alignedFilesArray = data.fileInfo\n .map((info) => {\n return filesArray.find((file) => file.originalFilename === info.name);\n })\n .filter(Boolean) as any[];\n\n filesArray = alignedFilesArray;\n }\n\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\n if (uploadedFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // AFTER upload - use thumbnail versions for AI processing\n if (await aiMetadataService.isEnabled()) {\n try {\n // Use thumbnail URLs instead of original files\n const thumbnailFiles = uploadedFiles.map(\n (file) =>\n ({\n filepath: file.formats?.thumbnail?.url || file.url, // Use thumbnail if available\n mimetype: file.mime,\n originalFilename: file.name,\n size: file.formats?.thumbnail?.size || file.size,\n provider: file.provider,\n }) as unknown as any\n );\n\n const metadataResults = await aiMetadataService.processFiles(thumbnailFiles);\n\n // Update the uploaded files with AI metadata\n await Promise.all(\n uploadedFiles.map(async (uploadedFile, index) => {\n const aiMetadata = metadataResults[index];\n if (aiMetadata) {\n await uploadService.updateFileInfo(\n uploadedFile.id,\n {\n alternativeText: aiMetadata.altText,\n caption: aiMetadata.caption,\n },\n { user }\n );\n\n uploadedFiles[index].alternativeText = aiMetadata.altText;\n uploadedFiles[index].caption = aiMetadata.caption;\n }\n })\n );\n } catch (error) {\n strapi.log.warn('AI metadata generation failed, proceeding without AI enhancements', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFiles = await async.map(uploadedFiles, getService('file').signFileUrls);\n\n ctx.body = await pm.sanitizeOutput(signedFiles, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new errors.ApplicationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n};\n"],"names":["bulkUpdateFileInfo","ctx","state","userAbility","user","request","body","updates","validateBulkUpdateBody","uploadService","getService","results","async","map","id","fileInfo","pm","findEntityAndCheckPermissions","ACTIONS","update","FILE_MODEL_UID","updated","updateFileInfo","sanitizeOutput","action","read","query","errors","ValidationError","data","validateUploadBody","file","replaceFile","files","Array","isArray","ApplicationError","validFiles","filteredBody","prepareUploadRequest","strapi","replacedFile","replace","signedFile","signFileUrls","uploadFiles","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","isMultipleFiles","length","filesArray","alignedFilesArray","info","find","originalFilename","name","filter","Boolean","uploadedFiles","upload","some","mime","startsWith","trackUsage","aiMetadataService","isEnabled","thumbnailFiles","filepath","formats","thumbnail","url","mimetype","size","provider","metadataResults","processFiles","Promise","all","uploadedFile","index","aiMetadata","alternativeText","altText","caption","error","log","warn","Error","message","String","signedFiles","status","_","isEmpty"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { getService } from '../utils/index.mjs';
4
4
  import { ACTIONS, FILE_MODEL_UID } from '../constants.mjs';
5
5
  import { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload.mjs';
6
6
  import { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions.mjs';
7
- import { enforceUploadSecurity } from '../utils/mime-validation.mjs';
7
+ import { prepareUploadRequest } from '../utils/mime-validation.mjs';
8
8
 
9
9
  var adminUpload = {
10
10
  async bulkUpdateFileInfo (ctx) {
@@ -47,20 +47,11 @@ var adminUpload = {
47
47
  if (Array.isArray(files)) {
48
48
  throw new errors.ApplicationError('Cannot replace a file with multiple ones');
49
49
  }
50
- const securityResults = await enforceUploadSecurity(files, strapi);
51
- if (securityResults.errors.length > 0) {
52
- const { error } = securityResults.errors[0];
53
- switch(error.code){
54
- case 'MIME_TYPE_NOT_ALLOWED':
55
- throw new errors.ValidationError(error.message, error.details);
56
- default:
57
- throw new errors.ApplicationError(error.message, error.details);
58
- }
59
- }
60
- const data = await validateUploadBody(body);
50
+ const { validFiles, filteredBody } = await prepareUploadRequest(files, body, strapi);
51
+ const data = await validateUploadBody(filteredBody);
61
52
  const replacedFile = await uploadService.replace(id, {
62
53
  data,
63
- file: securityResults.validFiles[0]
54
+ file: validFiles[0]
64
55
  }, {
65
56
  user
66
57
  });
@@ -81,31 +72,10 @@ var adminUpload = {
81
72
  if (!pm.isAllowed) {
82
73
  return ctx.forbidden();
83
74
  }
84
- const securityResults = await enforceUploadSecurity(files, strapi);
85
- if (securityResults.validFiles.length === 0) {
86
- throw new errors.ValidationError(securityResults.errors[0].error.message, securityResults.errors[0].error.details);
87
- }
88
- let filteredBody = body;
89
- if (body?.fileInfo && Array.isArray(body.fileInfo)) {
90
- const filteredFileInfo = body.fileInfo.filter((fi)=>{
91
- const info = typeof fi === 'string' ? JSON.parse(fi) : fi;
92
- return securityResults.validFileNames.includes(info.name);
93
- });
94
- if (filteredFileInfo.length === 1) {
95
- filteredBody = {
96
- ...body,
97
- fileInfo: filteredFileInfo[0]
98
- };
99
- } else {
100
- filteredBody = {
101
- ...body,
102
- fileInfo: filteredFileInfo
103
- };
104
- }
105
- }
106
- const isMultipleFiles = Array.isArray(filteredBody.fileInfo) && filteredBody.fileInfo.length > 1;
75
+ const { validFiles, filteredBody } = await prepareUploadRequest(files, body, strapi);
76
+ const isMultipleFiles = validFiles.length > 1;
107
77
  const data = await validateUploadBody(filteredBody, isMultipleFiles);
108
- let filesArray = securityResults.validFiles;
78
+ let filesArray = validFiles;
109
79
  if (data.fileInfo && Array.isArray(data.fileInfo) && filesArray.length === data.fileInfo.length) {
110
80
  // Reorder filesArray to match data.fileInfo order
111
81
  const alignedFilesArray = data.fileInfo.map((info)=>{