@strapi/upload 5.0.3 → 5.0.5

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 (74) hide show
  1. package/dist/_chunks/{index-Cze6kL5_.mjs → index-BJfZXhbz.mjs} +5 -2
  2. package/dist/_chunks/index-BJfZXhbz.mjs.map +1 -0
  3. package/dist/_chunks/{index-BwMdH-eI.js → index-C7Nj9ETW.js} +356 -290
  4. package/dist/_chunks/index-C7Nj9ETW.js.map +1 -0
  5. package/dist/_chunks/{index-DzHgwpS9.mjs → index-CuzWuOXx.mjs} +357 -291
  6. package/dist/_chunks/index-CuzWuOXx.mjs.map +1 -0
  7. package/dist/_chunks/{index-DK1he91f.js → index-DIjfjBzf.js} +5 -2
  8. package/dist/_chunks/index-DIjfjBzf.js.map +1 -0
  9. package/dist/_chunks/{index-C5E7jC83.js → index-Dztfplsk.js} +8 -8
  10. package/dist/_chunks/index-Dztfplsk.js.map +1 -0
  11. package/dist/_chunks/{index-ox-PAURv.js → index-boOjOtd3.js} +2 -2
  12. package/dist/_chunks/{index-ox-PAURv.js.map → index-boOjOtd3.js.map} +1 -1
  13. package/dist/_chunks/{index-Cw3NiaYP.mjs → index-moi5JZRr.mjs} +2 -2
  14. package/dist/_chunks/{index-Cw3NiaYP.mjs.map → index-moi5JZRr.mjs.map} +1 -1
  15. package/dist/_chunks/{index-CCEHGHWW.mjs → index-uOSk9F1z.mjs} +8 -8
  16. package/dist/_chunks/index-uOSk9F1z.mjs.map +1 -0
  17. package/dist/admin/index.js +4 -1
  18. package/dist/admin/index.js.map +1 -1
  19. package/dist/admin/index.mjs +4 -1
  20. package/dist/admin/index.mjs.map +1 -1
  21. package/dist/admin/src/components/SelectTree/utils/flattenTree.d.ts +14 -0
  22. package/dist/admin/src/hooks/useAssets.d.ts +14 -0
  23. package/dist/admin/src/hooks/useBulkMove.d.ts +78 -0
  24. package/dist/admin/src/hooks/useBulkRemove.d.ts +73 -0
  25. package/dist/admin/src/hooks/useConfig.d.ts +5 -0
  26. package/dist/admin/src/hooks/useCropImg.d.ts +9 -0
  27. package/dist/admin/src/hooks/useEditAsset.d.ts +119 -0
  28. package/dist/admin/src/hooks/useEditFolder.d.ts +71 -0
  29. package/dist/admin/src/hooks/useFolder.d.ts +7 -0
  30. package/dist/admin/src/hooks/useFolderStructure.d.ts +12 -0
  31. package/dist/admin/src/hooks/useFolders.d.ts +11 -0
  32. package/dist/admin/src/hooks/useMediaLibraryPermissions.d.ts +3 -0
  33. package/dist/admin/src/hooks/useModalQueryParams.d.ts +21 -0
  34. package/dist/admin/src/hooks/usePersistentState.d.ts +1 -0
  35. package/dist/admin/src/hooks/useRemoveAsset.d.ts +66 -0
  36. package/dist/admin/src/hooks/useUpload.d.ts +12 -0
  37. package/dist/admin/src/hooks/utils/rename-keys.d.ts +6 -0
  38. package/dist/admin/src/newConstants.d.ts +45 -0
  39. package/dist/admin/src/pluginId.d.ts +2 -0
  40. package/dist/admin/src/utils/appendSearchParamsToUrl.d.ts +6 -0
  41. package/dist/admin/src/utils/containsAssetFilter.d.ts +2 -0
  42. package/dist/admin/src/utils/createAssetUrl.d.ts +2 -0
  43. package/dist/admin/src/utils/displayedFilters.d.ts +22 -0
  44. package/dist/admin/src/utils/downloadFile.d.ts +1 -0
  45. package/dist/admin/src/utils/findRecursiveFolderByValue.d.ts +10 -0
  46. package/dist/admin/src/utils/formatBytes.d.ts +1 -0
  47. package/dist/admin/src/utils/formatDuration.d.ts +1 -0
  48. package/dist/admin/src/utils/getAPIInnerErrors.d.ts +14 -0
  49. package/dist/admin/src/utils/getAllowedFiles.d.ts +15 -0
  50. package/dist/admin/src/utils/getBreadcrumbDataCM.d.ts +19 -0
  51. package/dist/admin/src/utils/getBreadcrumbDataML.d.ts +18 -0
  52. package/dist/admin/src/utils/getFileExtension.d.ts +1 -0
  53. package/dist/admin/src/utils/getFolderParents.d.ts +10 -0
  54. package/dist/admin/src/utils/getFolderURL.d.ts +5 -0
  55. package/dist/admin/src/utils/getTrad.d.ts +1 -0
  56. package/dist/admin/src/utils/index.d.ts +25 -0
  57. package/dist/admin/src/utils/moveElement.d.ts +1 -0
  58. package/dist/admin/src/utils/normalizeAPIError.d.ts +22 -0
  59. package/dist/admin/src/utils/prefixFileUrlWithBackendUrl.d.ts +1 -0
  60. package/dist/admin/src/utils/prefixPluginTranslations.d.ts +5 -0
  61. package/dist/admin/src/utils/rawFileToAsset.d.ts +14 -0
  62. package/dist/admin/src/utils/toSingularTypes.d.ts +2 -0
  63. package/dist/admin/src/utils/typeFromMime.d.ts +2 -0
  64. package/dist/admin/src/utils/urlYupSchema.d.ts +8 -0
  65. package/dist/admin/src/utils/urlsToAssets.d.ts +10 -0
  66. package/dist/shared/contracts/files.d.ts +46 -6
  67. package/dist/shared/contracts/folders.d.ts +5 -6
  68. package/package.json +8 -7
  69. package/dist/_chunks/index-BwMdH-eI.js.map +0 -1
  70. package/dist/_chunks/index-C5E7jC83.js.map +0 -1
  71. package/dist/_chunks/index-CCEHGHWW.mjs.map +0 -1
  72. package/dist/_chunks/index-Cze6kL5_.mjs.map +0 -1
  73. package/dist/_chunks/index-DK1he91f.js.map +0 -1
  74. package/dist/_chunks/index-DzHgwpS9.mjs.map +0 -1
@@ -1,19 +1,19 @@
1
1
  import { ChevronUp, ChevronDown, Cross, CaretDown, Link, FilePdf, File as File$1, Check, Trash, Download, Crop, Pencil, Folder, Eye, CaretUp, Plus, Filter, ChevronLeft, ChevronRight, Search, List, GridFour, PlusCircle, Images } from "@strapi/icons";
2
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import { useEffect, useState, useMemo, useRef, useCallback, forwardRef, createElement, createContext, useContext, useLayoutEffect } from "react";
4
+ import { useMemo, useState, useEffect, useCallback, forwardRef, useRef, createElement, createContext, useContext, useLayoutEffect } from "react";
5
5
  import PropTypes from "prop-types";
6
- import { useNotification, useFetchClient, useRBAC, useTracking, ConfirmDialog, useQueryParams, Layouts, Page, translatedErrors, useField } from "@strapi/admin/strapi-admin";
6
+ import { translatedErrors, useNotification, useFetchClient, useRBAC, useTracking, ConfirmDialog, useQueryParams, Layouts, Page, useField } from "@strapi/admin/strapi-admin";
7
7
  import { useNotifyAT, Box, Grid, Flex, Typography, Modal, IconButton, ProgressBar, Dialog, Badge, Menu, FocusTrap, Button, VisuallyHidden, Loader, Field, TextInput, CardAction, Card as Card$1, CardHeader, CardCheckbox, CardBody, CardContent, CardTitle, CardSubtitle, CardBadge, CardAsset as CardAsset$2, CardTimer, KeyboardNavigable, CrumbSimpleMenu, MenuItem, Breadcrumbs as Breadcrumbs$1, CrumbLink, Crumb, SingleSelect, SingleSelectOption, Avatar, Tbody, Tr, Td, Checkbox, Table, Thead, Th, Tooltip, Tag, DateTimePicker, Popover, SearchForm, Searchbar, Divider, Tabs, Textarea, CarouselActions, CarouselInput, CarouselSlide } from "@strapi/design-system";
8
8
  import { useIntl } from "react-intl";
9
9
  import { styled, useTheme } from "styled-components";
10
10
  import byteSize from "byte-size";
11
11
  import { intervalToDuration } from "date-fns";
12
12
  import { stringify } from "qs";
13
+ import * as yup from "yup";
13
14
  import { useQuery, useMutation, useQueryClient } from "react-query";
14
15
  import { Formik, Form } from "formik";
15
16
  import isEqual from "lodash/isEqual";
16
- import * as yup from "yup";
17
17
  import ReactSelect, { components } from "react-select";
18
18
  import Cropper from "cropperjs";
19
19
  import "cropperjs/dist/cropper.css";
@@ -32,7 +32,7 @@ const __variableDynamicImportRuntimeHelper = (glob, path) => {
32
32
  });
33
33
  };
34
34
  const name$1 = "@strapi/upload";
35
- const version = "5.0.2";
35
+ const version = "5.0.4";
36
36
  const description = "Makes it easy to upload images and files to your Strapi Application.";
37
37
  const license = "SEE LICENSE IN LICENSE";
38
38
  const author = {
@@ -119,6 +119,7 @@ const devDependencies = {
119
119
  "@testing-library/dom": "10.1.0",
120
120
  "@testing-library/react": "15.0.7",
121
121
  "@testing-library/user-event": "14.5.2",
122
+ "@types/byte-size": "8.1.2",
122
123
  "@types/fs-extra": "11.0.4",
123
124
  "@types/koa": "2.13.4",
124
125
  "@types/koa-range": "0.3.5",
@@ -140,7 +141,7 @@ const peerDependencies = {
140
141
  "styled-components": "^6.0.0"
141
142
  };
142
143
  const engines = {
143
- node: ">=18.0.0 <=20.x.x",
144
+ node: ">=18.0.0 <=22.x.x",
144
145
  npm: ">=6.0.0"
145
146
  };
146
147
  const strapi = {
@@ -172,7 +173,7 @@ const appendSearchParamsToUrl = ({ url, params }) => {
172
173
  }
173
174
  const urlObj = new URL(url, window.strapi.backendURL);
174
175
  Object.entries(params).forEach(([key, value]) => {
175
- if (value !== void 0) {
176
+ if (value !== void 0 && value !== null) {
176
177
  urlObj.searchParams.append(key, value);
177
178
  }
178
179
  });
@@ -201,6 +202,43 @@ const createAssetUrl = (asset, forThumbnail = true) => {
201
202
  const assetUrl = forThumbnail ? asset?.formats?.thumbnail?.url || asset.url : asset.url;
202
203
  return prefixFileUrlWithBackendUrl(assetUrl);
203
204
  };
205
+ const displayedFilters = [
206
+ {
207
+ name: "createdAt",
208
+ fieldSchema: {
209
+ type: "date"
210
+ },
211
+ metadatas: { label: "createdAt" }
212
+ },
213
+ {
214
+ name: "updatedAt",
215
+ fieldSchema: {
216
+ type: "date"
217
+ },
218
+ metadatas: { label: "updatedAt" }
219
+ },
220
+ {
221
+ name: "mime",
222
+ fieldSchema: {
223
+ type: "enumeration",
224
+ options: [
225
+ { label: "audio", value: "audio" },
226
+ { label: "file", value: "file" },
227
+ { label: "image", value: "image" },
228
+ { label: "video", value: "video" }
229
+ ]
230
+ },
231
+ metadatas: { label: "type" }
232
+ }
233
+ ];
234
+ const downloadFile = async (url, fileName) => {
235
+ const fileBlob = await fetch(url).then((res) => res.blob());
236
+ const urlDownload = window.URL.createObjectURL(fileBlob);
237
+ const link = document.createElement("a");
238
+ link.href = urlDownload;
239
+ link.setAttribute("download", fileName);
240
+ link.click();
241
+ };
204
242
  function findRecursiveFolderByValue(data, value) {
205
243
  let result;
206
244
  function iter(a) {
@@ -214,7 +252,8 @@ function findRecursiveFolderByValue(data, value) {
214
252
  return result;
215
253
  }
216
254
  function formatBytes(receivedBytes, decimals = 0) {
217
- const { value, unit } = byteSize(receivedBytes * 1e3, { precision: decimals });
255
+ const realBytes = typeof receivedBytes === "string" ? Number(receivedBytes) : receivedBytes;
256
+ const { value, unit } = byteSize(realBytes * 1e3, { precision: decimals });
218
257
  if (!unit) {
219
258
  return "0B";
220
259
  }
@@ -225,9 +264,81 @@ const formatDuration = (durationInSecond) => {
225
264
  const duration = intervalToDuration({ start: 0, end: durationInSecond * 1e3 });
226
265
  return `${zeroPad(duration.hours)}:${zeroPad(duration.minutes)}:${zeroPad(duration.seconds)}`;
227
266
  };
267
+ const toSingularTypes = (types) => {
268
+ if (!types) {
269
+ return [];
270
+ }
271
+ return types.map((type) => type.substring(0, type.length - 1));
272
+ };
273
+ const getAllowedFiles = (pluralTypes, files2) => {
274
+ const singularTypes = toSingularTypes(pluralTypes);
275
+ const allowedFiles = files2.filter((file) => {
276
+ const fileType = file?.mime?.split("/")[0];
277
+ if (!fileType) {
278
+ return false;
279
+ }
280
+ if (singularTypes.includes("file") && !["video", "image", "audio"].includes(fileType)) {
281
+ return true;
282
+ }
283
+ return singularTypes.includes(fileType);
284
+ });
285
+ return allowedFiles;
286
+ };
287
+ function getPrefixedId(message, callback) {
288
+ const prefixedMessage = `apiError.${message}`;
289
+ if (typeof callback === "function") {
290
+ return callback(prefixedMessage);
291
+ }
292
+ return prefixedMessage;
293
+ }
294
+ function normalizeError(error, { name: name2, intlMessagePrefixCallback }) {
295
+ const { message } = error;
296
+ const normalizedError = {
297
+ id: getPrefixedId(message, intlMessagePrefixCallback),
298
+ defaultMessage: message,
299
+ name: error.name ?? name2,
300
+ values: {}
301
+ };
302
+ if ("path" in error) {
303
+ normalizedError.values = { path: error.path.join(".") };
304
+ }
305
+ return normalizedError;
306
+ }
307
+ const validateErrorIsYupValidationError = (err) => typeof err.details === "object" && err.details !== null && "errors" in err.details;
308
+ function normalizeAPIError(apiError, intlMessagePrefixCallback) {
309
+ const error = apiError.response?.data.error;
310
+ if (error) {
311
+ if (validateErrorIsYupValidationError(error)) {
312
+ return {
313
+ name: error.name,
314
+ message: error?.message || null,
315
+ errors: error.details.errors.map(
316
+ (err) => normalizeError(err, { name: error.name, intlMessagePrefixCallback })
317
+ )
318
+ };
319
+ }
320
+ return normalizeError(error, { intlMessagePrefixCallback });
321
+ }
322
+ return null;
323
+ }
324
+ function getAPIInnerErrors(error, { getTrad: getTrad2 }) {
325
+ const normalizedError = normalizeAPIError(error, getTrad2);
326
+ if (normalizedError && "errors" in normalizedError) {
327
+ return normalizedError.errors.reduce((acc, error2) => {
328
+ if ("path" in error2.values) {
329
+ acc[error2.values.path] = {
330
+ id: error2.id,
331
+ defaultMessage: error2.defaultMessage
332
+ };
333
+ }
334
+ return acc;
335
+ }, {});
336
+ }
337
+ return normalizedError?.defaultMessage;
338
+ }
228
339
  const pluginId = pluginPkg.name.replace(/^@strapi\//i, "");
229
340
  const getTrad = (id2) => `${pluginId}.${id2}`;
230
- const getBreadcrumbDataML = (folder) => {
341
+ const getBreadcrumbDataCM = (folder) => {
231
342
  let data = [
232
343
  {
233
344
  id: null,
@@ -265,7 +376,8 @@ const getFolderURL = (pathname, currentQuery, { folder, folderPath } = {}) => {
265
376
  );
266
377
  return `${pathname}${queryParamsString ? `?${queryParamsString}` : ""}`;
267
378
  };
268
- function flattenTree(tree, parent, depth = 0) {
379
+ const getFileExtension = (ext) => ext && ext[0] === "." ? ext.substring(1) : ext;
380
+ function flattenTree(tree, parent = null, depth = 0) {
269
381
  return tree.flatMap(
270
382
  (item) => item.children ? [{ ...item, parent: parent?.value, depth }, ...flattenTree(item.children, item, depth + 1)] : { ...item, depth, parent: parent?.value }
271
383
  );
@@ -280,70 +392,178 @@ const getFolderParents = (folders, currentFolderId) => {
280
392
  let { parent } = currentFolder;
281
393
  while (parent !== void 0) {
282
394
  let parentToStore = flatFolders.find(({ value }) => value === parent);
283
- parents.push({ id: parentToStore.value, label: parentToStore.label });
284
- parent = parentToStore.parent;
395
+ parents.push({ id: parentToStore?.value, label: parentToStore?.label });
396
+ parent = parentToStore?.parent;
285
397
  }
286
398
  return parents.reverse();
287
399
  };
288
- const toSingularTypes = (types) => {
289
- if (!types) {
290
- return [];
400
+ const move = (array, oldIndex, newIndex) => {
401
+ if (newIndex >= array.length) {
402
+ newIndex = array.length - 1;
291
403
  }
292
- return types.map((type) => type.substring(0, type.length - 1));
404
+ array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
405
+ return array;
293
406
  };
294
- const getFileExtension = (ext) => ext && ext[0] === "." ? ext.substring(1) : ext;
295
- function getPrefixedId(message, callback) {
296
- const prefixedMessage = `apiError.${message}`;
297
- if (typeof callback === "function") {
298
- return callback(prefixedMessage);
407
+ const moveElement = (array, index2, offset) => {
408
+ const newIndex = index2 + offset;
409
+ return move(array, index2, newIndex);
410
+ };
411
+ const prefixPluginTranslations = (trad, pluginId2) => {
412
+ if (!pluginId2) {
413
+ throw new TypeError("pluginId can't be empty");
299
414
  }
300
- return prefixedMessage;
301
- }
302
- function normalizeError(error, { name: name2, intlMessagePrefixCallback }) {
303
- const { message } = error;
304
- const normalizedError = {
305
- id: getPrefixedId(message, intlMessagePrefixCallback),
306
- defaultMessage: message,
307
- name: error.name ?? name2,
308
- values: {}
309
- };
310
- if ("path" in error) {
311
- normalizedError.values = { path: error.path.join(".") };
415
+ return Object.keys(trad).reduce((acc, current) => {
416
+ acc[`${pluginId2}.${current}`] = trad[current];
417
+ return acc;
418
+ }, {});
419
+ };
420
+ var AssetType$1 = /* @__PURE__ */ ((AssetType2) => {
421
+ AssetType2["Video"] = "video";
422
+ AssetType2["Image"] = "image";
423
+ AssetType2["Document"] = "doc";
424
+ AssetType2["Audio"] = "audio";
425
+ return AssetType2;
426
+ })(AssetType$1 || {});
427
+ var AssetSource$1 = /* @__PURE__ */ ((AssetSource2) => {
428
+ AssetSource2["Url"] = "url";
429
+ AssetSource2["Computer"] = "computer";
430
+ return AssetSource2;
431
+ })(AssetSource$1 || {});
432
+ const PERMISSIONS$1 = {
433
+ // This permission regards the main component (App) and is used to tell
434
+ // If the plugin link should be displayed in the menu
435
+ // And also if the plugin is accessible. This use case is found when a user types the url of the
436
+ // plugin directly in the browser
437
+ main: [
438
+ { action: "plugin::upload.read", subject: null },
439
+ {
440
+ action: "plugin::upload.assets.create",
441
+ subject: null
442
+ },
443
+ {
444
+ action: "plugin::upload.assets.update",
445
+ subject: null
446
+ }
447
+ ],
448
+ copyLink: [
449
+ {
450
+ action: "plugin::upload.assets.copy-link",
451
+ subject: null
452
+ }
453
+ ],
454
+ create: [
455
+ {
456
+ action: "plugin::upload.assets.create",
457
+ subject: null
458
+ }
459
+ ],
460
+ download: [
461
+ {
462
+ action: "plugin::upload.assets.download",
463
+ subject: null
464
+ }
465
+ ],
466
+ read: [{ action: "plugin::upload.read", subject: null }],
467
+ configureView: [{ action: "plugin::upload.configure-view", subject: null }],
468
+ settings: [{ action: "plugin::upload.settings.read", subject: null }],
469
+ update: [{ action: "plugin::upload.assets.update", subject: null, fields: null }]
470
+ };
471
+ const typeFromMime = (mime) => {
472
+ if (mime.includes(AssetType$1.Image)) {
473
+ return AssetType$1.Image;
312
474
  }
313
- return normalizedError;
475
+ if (mime.includes(AssetType$1.Video)) {
476
+ return AssetType$1.Video;
477
+ }
478
+ if (mime.includes(AssetType$1.Audio)) {
479
+ return AssetType$1.Audio;
480
+ }
481
+ return AssetType$1.Document;
482
+ };
483
+ const rawFileToAsset = (rawFile, assetSource) => {
484
+ return {
485
+ size: rawFile.size / 1e3,
486
+ createdAt: new Date(rawFile.lastModified).toISOString(),
487
+ name: rawFile.name,
488
+ source: assetSource,
489
+ type: typeFromMime(rawFile.type),
490
+ url: URL.createObjectURL(rawFile),
491
+ ext: rawFile.name.split(".").pop(),
492
+ mime: rawFile.type,
493
+ rawFile,
494
+ isLocal: true
495
+ };
496
+ };
497
+ function getFilenameFromURL(url) {
498
+ return new URL(url).pathname.split("/").pop();
314
499
  }
315
- const validateErrorIsYupValidationError = (err) => typeof err.details === "object" && err.details !== null && "errors" in err.details;
316
- function normalizeAPIError(apiError, intlMessagePrefixCallback) {
317
- const error = apiError.response?.data.error;
318
- if (error) {
319
- if (validateErrorIsYupValidationError(error)) {
500
+ const urlsToAssets = async (urls) => {
501
+ const assetPromises = urls.map(
502
+ (url) => fetch(url).then(async (res) => {
503
+ const blob = await res.blob();
504
+ const loadedFile = new File([blob], getFilenameFromURL(res.url), {
505
+ type: res.headers.get("content-type") || void 0
506
+ });
320
507
  return {
321
- name: error.name,
322
- message: error?.message || null,
323
- errors: error.details.errors.map(
324
- (err) => normalizeError(err, { name: error.name, intlMessagePrefixCallback })
325
- )
508
+ name: loadedFile.name,
509
+ url: res.url,
510
+ mime: res.headers.get("content-type"),
511
+ rawFile: loadedFile
326
512
  };
327
- }
328
- return normalizeError(error, { intlMessagePrefixCallback });
329
- }
330
- return null;
331
- }
332
- function getAPIInnerErrors(error, { getTrad: getTrad2 }) {
333
- const normalizedError = normalizeAPIError(error, getTrad2);
334
- if (normalizedError && "errors" in normalizedError) {
335
- return normalizedError.errors.reduce((acc, error2) => {
336
- if ("path" in error2.values) {
337
- acc[error2.values.path] = {
338
- id: error2.id,
339
- defaultMessage: error2.defaultMessage
340
- };
513
+ })
514
+ );
515
+ const assetsResults = await Promise.all(assetPromises);
516
+ const assets = assetsResults.map((fullFilledAsset) => ({
517
+ source: AssetSource$1.Url,
518
+ name: fullFilledAsset.name,
519
+ type: typeFromMime(fullFilledAsset.mime),
520
+ url: fullFilledAsset.url,
521
+ ext: fullFilledAsset.url.split(".").pop(),
522
+ mime: fullFilledAsset.mime,
523
+ rawFile: fullFilledAsset.rawFile
524
+ }));
525
+ return assets;
526
+ };
527
+ const urlSchema = yup.object().shape({
528
+ urls: yup.string().test({
529
+ name: "isUrlValid",
530
+ // eslint-disable-next-line no-template-curly-in-string
531
+ message: "${path}",
532
+ test(values = "") {
533
+ const urls = values.split(/\r?\n/);
534
+ if (urls.length === 0) {
535
+ return this.createError({
536
+ path: this.path,
537
+ message: translatedErrors.min.id
538
+ });
341
539
  }
342
- return acc;
343
- }, {});
344
- }
345
- return normalizedError?.defaultMessage;
346
- }
540
+ if (urls.length > 20) {
541
+ return this.createError({
542
+ path: this.path,
543
+ message: translatedErrors.max.id
544
+ });
545
+ }
546
+ const filtered = urls.filter((val) => {
547
+ try {
548
+ new URL(val);
549
+ return false;
550
+ } catch (err) {
551
+ return true;
552
+ }
553
+ });
554
+ const filteredLength = filtered.length;
555
+ if (filteredLength === 0) {
556
+ return true;
557
+ }
558
+ const errorMessage = filteredLength > 1 ? "form.upload-url.error.url.invalids" : "form.upload-url.error.url.invalid";
559
+ return this.createError({
560
+ path: this.path,
561
+ message: getTrad(errorMessage),
562
+ params: { number: filtered.length }
563
+ });
564
+ }
565
+ })
566
+ });
347
567
  const AssetType = {
348
568
  Video: "video",
349
569
  Image: "image",
@@ -592,7 +812,7 @@ const useAssets = ({ skipWhen = false, query = {} } = {}) => {
592
812
  }
593
813
  }
594
814
  );
595
- useEffect(() => {
815
+ React.useEffect(() => {
596
816
  if (data) {
597
817
  notifyStatus(
598
818
  formatMessage({
@@ -602,7 +822,7 @@ const useAssets = ({ skipWhen = false, query = {} } = {}) => {
602
822
  );
603
823
  }
604
824
  }, [data, formatMessage, notifyStatus]);
605
- useEffect(() => {
825
+ React.useEffect(() => {
606
826
  if (error) {
607
827
  toggleNotification({
608
828
  type: "danger",
@@ -679,7 +899,7 @@ const useFolders = ({ enabled = true, query = {} } = {}) => {
679
899
  }, [data, formatMessage, notifyStatus]);
680
900
  return { data, error, isLoading };
681
901
  };
682
- const { main, ...restPermissions } = PERMISSIONS;
902
+ const { main, ...restPermissions } = PERMISSIONS$1;
683
903
  const useMediaLibraryPermissions = () => {
684
904
  const { allowedActions, isLoading } = useRBAC(restPermissions);
685
905
  return { ...allowedActions, isLoading };
@@ -707,7 +927,7 @@ const useConfig = () => {
707
927
  /**
708
928
  * We're cementing that we always expect an object to be returned.
709
929
  */
710
- select: (data) => !data ? {} : data
930
+ select: (data) => data || {}
711
931
  }
712
932
  );
713
933
  const putMutation = useMutation(
@@ -737,7 +957,7 @@ const useModalQueryParams = (initialState) => {
737
957
  const {
738
958
  config: { data: config }
739
959
  } = useConfig();
740
- const [queryObject, setQueryObject] = useState({
960
+ const [queryObject, setQueryObject] = React.useState({
741
961
  page: 1,
742
962
  sort: "updatedAt:DESC",
743
963
  pageSize: 10,
@@ -746,8 +966,8 @@ const useModalQueryParams = (initialState) => {
746
966
  },
747
967
  ...initialState
748
968
  });
749
- useEffect(() => {
750
- if (config) {
969
+ React.useEffect(() => {
970
+ if (config && "sort" in config && "pageSize" in config) {
751
971
  setQueryObject((prevQuery) => ({
752
972
  ...prevQuery,
753
973
  sort: config.sort,
@@ -756,24 +976,32 @@ const useModalQueryParams = (initialState) => {
756
976
  }
757
977
  }, [config]);
758
978
  const handleChangeFilters = (nextFilters) => {
759
- trackUsage("didFilterMediaLibraryElements", {
760
- location: "content-manager",
761
- filter: Object.keys(nextFilters[nextFilters.length - 1])[0]
762
- });
763
- setQueryObject((prev) => ({ ...prev, page: 1, filters: { $and: nextFilters } }));
979
+ if (nextFilters) {
980
+ trackUsage("didFilterMediaLibraryElements", {
981
+ location: "content-manager",
982
+ filter: Object.keys(nextFilters[nextFilters.length - 1])[0]
983
+ });
984
+ setQueryObject((prev) => ({ ...prev, page: 1, filters: { $and: nextFilters } }));
985
+ }
764
986
  };
765
987
  const handleChangePageSize = (pageSize) => {
766
- setQueryObject((prev) => ({ ...prev, pageSize: parseInt(pageSize, 10), page: 1 }));
988
+ setQueryObject((prev) => ({
989
+ ...prev,
990
+ pageSize: typeof pageSize === "string" ? parseInt(pageSize, 10) : pageSize,
991
+ page: 1
992
+ }));
767
993
  };
768
994
  const handeChangePage = (page) => {
769
995
  setQueryObject((prev) => ({ ...prev, page }));
770
996
  };
771
997
  const handleChangeSort = (sort) => {
772
- trackUsage("didSortMediaLibraryElements", {
773
- location: "content-manager",
774
- sort
775
- });
776
- setQueryObject((prev) => ({ ...prev, sort }));
998
+ if (sort) {
999
+ trackUsage("didSortMediaLibraryElements", {
1000
+ location: "content-manager",
1001
+ sort
1002
+ });
1003
+ setQueryObject((prev) => ({ ...prev, sort }));
1004
+ }
777
1005
  };
778
1006
  const handleChangeSearch = (_q) => {
779
1007
  if (_q) {
@@ -862,28 +1090,6 @@ const useSelectionState = (keys, initialValue) => {
862
1090
  { selectOne, selectAll, selectOnly, selectMultiple, deselectMultiple, setSelections }
863
1091
  ];
864
1092
  };
865
- const getAllowedFiles = (pluralTypes, files2) => {
866
- const singularTypes = toSingularTypes(pluralTypes);
867
- const allowedFiles = files2.filter((file) => {
868
- const fileType = file.mime.split("/")[0];
869
- if (singularTypes.includes("file") && !["video", "image", "audio"].includes(fileType)) {
870
- return true;
871
- }
872
- return singularTypes.includes(fileType);
873
- });
874
- return allowedFiles;
875
- };
876
- const move = (array, oldIndex, newIndex) => {
877
- if (newIndex >= array.length) {
878
- newIndex = array.length - 1;
879
- }
880
- array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
881
- return array;
882
- };
883
- const moveElement = (array, index2, offset) => {
884
- const newIndex = index2 + offset;
885
- return move(array, index2, newIndex);
886
- };
887
1093
  const editAssetRequest = (asset, file, signal, onProgress, post) => {
888
1094
  const endpoint2 = `/${pluginId}?id=${asset.id}`;
889
1095
  const formData = new FormData();
@@ -904,33 +1110,30 @@ const editAssetRequest = (asset, file, signal, onProgress, post) => {
904
1110
  }).then((res) => res.data);
905
1111
  };
906
1112
  const useEditAsset = () => {
907
- const [progress, setProgress] = useState(0);
1113
+ const [progress, setProgress] = React.useState(0);
908
1114
  const { formatMessage } = useIntl();
909
1115
  const { toggleNotification } = useNotification();
910
1116
  const queryClient = useQueryClient();
911
1117
  const abortController = new AbortController();
912
1118
  const signal = abortController.signal;
913
1119
  const { post } = useFetchClient();
914
- const mutation = useMutation(
915
- ({ asset, file }) => editAssetRequest(asset, file, signal, setProgress, post),
916
- {
917
- onSuccess() {
918
- queryClient.refetchQueries([pluginId, "assets"], { active: true });
919
- queryClient.refetchQueries([pluginId, "asset-count"], { active: true });
920
- queryClient.refetchQueries([pluginId, "folders"], { active: true });
921
- },
922
- onError(reason) {
923
- if (reason.response.status === 403) {
924
- toggleNotification({
925
- type: "info",
926
- message: formatMessage({ id: getTrad("permissions.not-allowed.update") })
927
- });
928
- } else {
929
- toggleNotification({ type: "danger", message: reason.message });
930
- }
1120
+ const mutation = useMutation(({ asset, file }) => editAssetRequest(asset, file, signal, setProgress, post), {
1121
+ onSuccess() {
1122
+ queryClient.refetchQueries([pluginId, "assets"], { active: true });
1123
+ queryClient.refetchQueries([pluginId, "asset-count"], { active: true });
1124
+ queryClient.refetchQueries([pluginId, "folders"], { active: true });
1125
+ },
1126
+ onError(reason) {
1127
+ if (reason?.response?.status === 403) {
1128
+ toggleNotification({
1129
+ type: "info",
1130
+ message: formatMessage({ id: getTrad("permissions.not-allowed.update") })
1131
+ });
1132
+ } else {
1133
+ toggleNotification({ type: "danger", message: reason?.message });
931
1134
  }
932
1135
  }
933
- );
1136
+ });
934
1137
  const editAsset = (asset, file) => mutation.mutateAsync({ asset, file });
935
1138
  const cancel = () => abortController.abort();
936
1139
  return { ...mutation, cancel, editAsset, progress, status: mutation.status };
@@ -952,7 +1155,9 @@ const useFolderStructure = ({ enabled = true } = {}) => {
952
1155
  const {
953
1156
  data: { data: data2 }
954
1157
  } = await get("/upload/folder-structure");
955
- const children = data2.map((f) => recursiveRenameKeys(f, (key) => FIELD_MAPPING?.[key] ?? key));
1158
+ const children = data2.map(
1159
+ (f) => recursiveRenameKeys(f, (key) => FIELD_MAPPING?.[key] ?? key)
1160
+ );
956
1161
  return [
957
1162
  {
958
1163
  value: null,
@@ -1322,10 +1527,10 @@ const DialogHeader = () => {
1322
1527
  };
1323
1528
  const QUALITY = 1;
1324
1529
  const useCropImg = () => {
1325
- const cropperRef = useRef();
1326
- const [isCropping, setIsCropping] = useState(false);
1327
- const [size, setSize] = useState({ width: void 0, height: void 0 });
1328
- useEffect(() => {
1530
+ const cropperRef = React.useRef();
1531
+ const [isCropping, setIsCropping] = React.useState(false);
1532
+ const [size, setSize] = React.useState({ width: void 0, height: void 0 });
1533
+ React.useEffect(() => {
1329
1534
  return () => {
1330
1535
  if (cropperRef.current) {
1331
1536
  cropperRef.current.destroy();
@@ -1372,7 +1577,7 @@ const useCropImg = () => {
1372
1577
  resolve(
1373
1578
  new File([blob], name2, {
1374
1579
  type: mimeType,
1375
- lastModifiedDate
1580
+ lastModified: new Date(lastModifiedDate).getTime()
1376
1581
  })
1377
1582
  );
1378
1583
  },
@@ -1409,7 +1614,7 @@ const uploadAsset = (asset, folderId, signal, onProgress, post) => {
1409
1614
  }).then((res) => res.data);
1410
1615
  };
1411
1616
  const useUpload = () => {
1412
- const [progress, setProgress] = useState(0);
1617
+ const [progress, setProgress] = React.useState(0);
1413
1618
  const queryClient = useQueryClient();
1414
1619
  const abortController = new AbortController();
1415
1620
  const signal = abortController.signal;
@@ -1435,14 +1640,6 @@ const useUpload = () => {
1435
1640
  status: mutation.status
1436
1641
  };
1437
1642
  };
1438
- const downloadFile = async (url, fileName) => {
1439
- const fileBlob = await fetch(url).then((res) => res.blob());
1440
- const urlDownload = window.URL.createObjectURL(fileBlob);
1441
- const link = document.createElement("a");
1442
- link.href = urlDownload;
1443
- link.setAttribute("download", fileName);
1444
- link.click();
1445
- };
1446
1643
  const useClipboard = () => {
1447
1644
  const copy = useCallback(async (value) => {
1448
1645
  try {
@@ -1554,23 +1751,26 @@ const useRemoveAsset = (onSuccess) => {
1554
1751
  const { formatMessage } = useIntl();
1555
1752
  const queryClient = useQueryClient();
1556
1753
  const { del } = useFetchClient();
1557
- const mutation = useMutation((assetId) => del(`/upload/files/${assetId}`), {
1558
- onSuccess() {
1559
- queryClient.refetchQueries([pluginId, "assets"], { active: true });
1560
- queryClient.refetchQueries([pluginId, "asset-count"], { active: true });
1561
- toggleNotification({
1562
- type: "success",
1563
- message: formatMessage({
1564
- id: "modal.remove.success-label",
1565
- defaultMessage: "Elements have been successfully deleted."
1566
- })
1567
- });
1568
- onSuccess();
1569
- },
1570
- onError(error) {
1571
- toggleNotification({ type: "danger", message: error.message });
1754
+ const mutation = useMutation(
1755
+ (assetId) => del(`/upload/files/${assetId}`),
1756
+ {
1757
+ onSuccess() {
1758
+ queryClient.refetchQueries([pluginId, "assets"], { active: true });
1759
+ queryClient.refetchQueries([pluginId, "asset-count"], { active: true });
1760
+ toggleNotification({
1761
+ type: "success",
1762
+ message: formatMessage({
1763
+ id: "modal.remove.success-label",
1764
+ defaultMessage: "Elements have been successfully deleted."
1765
+ })
1766
+ });
1767
+ onSuccess();
1768
+ },
1769
+ onError(error) {
1770
+ toggleNotification({ type: "danger", message: error.message });
1771
+ }
1572
1772
  }
1573
- });
1773
+ );
1574
1774
  const removeAsset = async (assetId) => {
1575
1775
  await mutation.mutateAsync(assetId);
1576
1776
  };
@@ -2365,7 +2565,7 @@ const useBulkRemove = () => {
2365
2565
  });
2366
2566
  },
2367
2567
  onError(error) {
2368
- toggleNotification({ type: "danger", message: error.message });
2568
+ toggleNotification({ type: "danger", message: error?.message });
2369
2569
  }
2370
2570
  });
2371
2571
  const remove = (...args) => mutation.mutateAsync(...args);
@@ -3809,35 +4009,6 @@ TableList.propTypes = {
3809
4009
  shouldDisableBulkSelect: PropTypes.bool,
3810
4010
  sortQuery: PropTypes.string
3811
4011
  };
3812
- const displayedFilters = [
3813
- {
3814
- name: "createdAt",
3815
- fieldSchema: {
3816
- type: "date"
3817
- },
3818
- metadatas: { label: "createdAt" }
3819
- },
3820
- {
3821
- name: "updatedAt",
3822
- fieldSchema: {
3823
- type: "date"
3824
- },
3825
- metadatas: { label: "updatedAt" }
3826
- },
3827
- {
3828
- name: "mime",
3829
- fieldSchema: {
3830
- type: "enumeration",
3831
- options: [
3832
- { label: "audio", value: "audio" },
3833
- { label: "file", value: "file" },
3834
- { label: "image", value: "image" },
3835
- { label: "video", value: "video" }
3836
- ]
3837
- },
3838
- metadatas: { label: "type" }
3839
- }
3840
- ];
3841
4012
  const FilterTag = ({ attribute, filter, onClick, operator, value }) => {
3842
4013
  const { formatMessage, formatDate, formatTime } = useIntl();
3843
4014
  const handleClick = () => {
@@ -4697,7 +4868,7 @@ const BrowseStep = ({
4697
4868
  isSelectable: isSelectable(singularTypes, asset?.mime),
4698
4869
  type: "asset"
4699
4870
  }));
4700
- const breadcrumbs = !isCurrentFolderLoading && getBreadcrumbDataML(currentFolder);
4871
+ const breadcrumbs = !isCurrentFolderLoading && getBreadcrumbDataCM(currentFolder);
4701
4872
  const allAllowedAsset = getAllowedFiles(allowedTypes, assets);
4702
4873
  const areAllAssetSelected = allAllowedAsset.length > 0 && selectedAssets.length > 0 && allAllowedAsset.every(
4703
4874
  (asset) => selectedAssets.findIndex((currAsset) => currAsset.id === asset.id) !== -1
@@ -5291,32 +5462,6 @@ const TabsRoot = styled(Tabs.Root)`
5291
5462
  flex-direction: column;
5292
5463
  overflow: hidden;
5293
5464
  `;
5294
- const typeFromMime = (mime) => {
5295
- if (mime.includes(AssetType.Image)) {
5296
- return AssetType.Image;
5297
- }
5298
- if (mime.includes(AssetType.Video)) {
5299
- return AssetType.Video;
5300
- }
5301
- if (mime.includes(AssetType.Audio)) {
5302
- return AssetType.Audio;
5303
- }
5304
- return AssetType.Document;
5305
- };
5306
- const rawFileToAsset = (rawFile, assetSource) => {
5307
- return {
5308
- size: rawFile.size / 1e3,
5309
- createdAt: new Date(rawFile.lastModified).toISOString(),
5310
- name: rawFile.name,
5311
- source: assetSource,
5312
- type: typeFromMime(rawFile.type),
5313
- url: URL.createObjectURL(rawFile),
5314
- ext: rawFile.name.split(".").pop(),
5315
- mime: rawFile.type,
5316
- rawFile,
5317
- isLocal: true
5318
- };
5319
- };
5320
5465
  const Wrapper = styled(Flex)`
5321
5466
  flex-direction: column;
5322
5467
  `;
@@ -5439,76 +5584,6 @@ FromComputerForm.propTypes = {
5439
5584
  onAddAssets: PropTypes.func.isRequired,
5440
5585
  trackedLocation: PropTypes.string
5441
5586
  };
5442
- function getFilenameFromURL(url) {
5443
- return new URL(url).pathname.split("/").pop();
5444
- }
5445
- const urlsToAssets = async (urls) => {
5446
- const assetPromises = urls.map(
5447
- (url) => fetch(url).then(async (res) => {
5448
- const blob = await res.blob();
5449
- const loadedFile = new File([blob], getFilenameFromURL(res.url), {
5450
- type: res.headers.get("content-type")
5451
- });
5452
- return {
5453
- name: loadedFile.name,
5454
- url: res.url,
5455
- mime: res.headers.get("content-type"),
5456
- rawFile: loadedFile
5457
- };
5458
- })
5459
- );
5460
- const assetsResults = await Promise.all(assetPromises);
5461
- const assets = assetsResults.map((fullFilledAsset) => ({
5462
- source: AssetSource.Url,
5463
- name: fullFilledAsset.name,
5464
- type: typeFromMime(fullFilledAsset.mime),
5465
- url: fullFilledAsset.url,
5466
- ext: fullFilledAsset.url.split(".").pop(),
5467
- mime: fullFilledAsset.mime,
5468
- rawFile: fullFilledAsset.rawFile
5469
- }));
5470
- return assets;
5471
- };
5472
- const urlSchema = yup.object().shape({
5473
- urls: yup.string().test({
5474
- name: "isUrlValid",
5475
- // eslint-disable-next-line no-template-curly-in-string
5476
- message: "${path}",
5477
- test(values = "") {
5478
- const urls = values.split(/\r?\n/);
5479
- if (urls.length === 0) {
5480
- return this.createError({
5481
- path: this.path,
5482
- message: translatedErrors.min.id
5483
- });
5484
- }
5485
- if (urls.length > 20) {
5486
- return this.createError({
5487
- path: this.path,
5488
- message: translatedErrors.max.id
5489
- });
5490
- }
5491
- const filtered = urls.filter((val) => {
5492
- try {
5493
- new URL(val);
5494
- return false;
5495
- } catch (err) {
5496
- return true;
5497
- }
5498
- });
5499
- const filteredLength = filtered.length;
5500
- if (filteredLength === 0) {
5501
- return true;
5502
- }
5503
- const errorMessage = filteredLength > 1 ? "form.upload-url.error.url.invalids" : "form.upload-url.error.url.invalid";
5504
- return this.createError({
5505
- path: this.path,
5506
- message: getTrad(errorMessage),
5507
- params: { number: filtered.length }
5508
- });
5509
- }
5510
- })
5511
- });
5512
5587
  const FromUrlForm = ({ onClose, onAddAsset, trackedLocation }) => {
5513
5588
  const [loading, setLoading] = useState(false);
5514
5589
  const [error, setError] = useState(void 0);
@@ -6541,15 +6616,6 @@ MediaLibraryInput.propTypes = {
6541
6616
  name: PropTypes.string.isRequired,
6542
6617
  required: PropTypes.bool
6543
6618
  };
6544
- const prefixPluginTranslations = (trad, pluginId2) => {
6545
- if (!pluginId2) {
6546
- throw new TypeError("pluginId can't be empty");
6547
- }
6548
- return Object.keys(trad).reduce((acc, current) => {
6549
- acc[`${pluginId2}.${current}`] = trad[current];
6550
- return acc;
6551
- }, {});
6552
- };
6553
6619
  const name = pluginPkg.strapi.name;
6554
6620
  const index = {
6555
6621
  register(app) {
@@ -6561,7 +6627,7 @@ const index = {
6561
6627
  defaultMessage: "Media Library"
6562
6628
  },
6563
6629
  permissions: PERMISSIONS.main,
6564
- Component: () => import("./index-CCEHGHWW.mjs"),
6630
+ Component: () => import("./index-uOSk9F1z.mjs"),
6565
6631
  position: 4
6566
6632
  });
6567
6633
  app.addSettingsLink("global", {
@@ -6571,7 +6637,7 @@ const index = {
6571
6637
  defaultMessage: "Media Library"
6572
6638
  },
6573
6639
  to: "media-library",
6574
- Component: () => import("./index-Cw3NiaYP.mjs"),
6640
+ Component: () => import("./index-moi5JZRr.mjs"),
6575
6641
  permissions: PERMISSIONS.settings
6576
6642
  });
6577
6643
  app.addFields({ type: "media", Component: MediaLibraryInput });
@@ -6642,4 +6708,4 @@ export {
6642
6708
  FolderCardBodyAction as y,
6643
6709
  AssetGridList as z
6644
6710
  };
6645
- //# sourceMappingURL=index-DzHgwpS9.mjs.map
6711
+ //# sourceMappingURL=index-CuzWuOXx.mjs.map