diff --git a/docs/fields/overview.mdx b/docs/fields/overview.mdx index cd68f29656..6aeabf94a7 100644 --- a/docs/fields/overview.mdx +++ b/docs/fields/overview.mdx @@ -155,18 +155,19 @@ Example: In addition to each field's base configuration, you can define specific traits and properties for fields that only have effect on how they are rendered in the Admin panel. The following properties are available for all fields within the `admin` property: -| Option | Description | -| ------------- | -------------| -| `condition` | You can programmatically show / hide fields based on what other fields are doing. [Click here](#conditional-logic) for more info. | -| `components` | All field components can be completely and easily swapped out for custom components that you define. [Click here](#custom-components) for more info. | -| `description` | Helper text to display with the field to provide more information for the editor user. [Click here](#description) for more info. | -| `position` | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. | -| `width` | Restrict the width of a field. you can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. | -| `style` | Attach raw CSS style properties to the root DOM element of a field. | -| `className` | Attach a CSS class name to the root DOM element of a field. | -| `readOnly` | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. | -| `disabled` | If a field is `disabled`, it is completely omitted from the Admin panel. | -| `hidden` | Setting a field's `hidden` property on its `admin` config will transform it into a `hidden` input type. Its value will still submit with the Admin panel's requests, but the field itself will not be visible to editors. | +| Option | Description | +|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `condition` | You can programmatically show / hide fields based on what other fields are doing. [Click here](#conditional-logic) for more info. | +| `components` | All field components can be completely and easily swapped out for custom components that you define. [Click here](#custom-components) for more info. | +| `description` | Helper text to display with the field to provide more information for the editor user. [Click here](#description) for more info. | +| `position` | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. | +| `width` | Restrict the width of a field. you can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. | +| `style` | Attach raw CSS style properties to the root DOM element of a field. | +| `className` | Attach a CSS class name to the root DOM element of a field. | +| `readOnly` | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. | +| `disabled` | If a field is `disabled`, it is completely omitted from the Admin panel. | +| `disableBulkEdit` | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. | +| `hidden` | Setting a field's `hidden` property on its `admin` config will transform it into a `hidden` input type. Its value will still submit with the Admin panel's requests, but the field itself will not be visible to editors. | ### Custom components diff --git a/docs/local-api/overview.mdx b/docs/local-api/overview.mdx index e1c0d26343..eb74f26afd 100644 --- a/docs/local-api/overview.mdx +++ b/docs/local-api/overview.mdx @@ -162,7 +162,7 @@ const result = await payload.findByID({ }); ``` -#### Update +#### Update by ID ```js // Result will be the updated Post document. @@ -193,6 +193,44 @@ const result = await payload.update({ }); ``` +#### Update Many + +```js +// Result will be an object with: +// { +// docs: [], // each document that was updated +// errors: [], // each error also includes the id of the document +// } +const result = await payload.update({ + collection: "posts", // required + where: { + // required + fieldName: { equals: 'value' }, + }, + data: { + // required + title: "sure", + description: "maybe", + }, + depth: 0, + locale: "en", + fallbackLocale: false, + user: dummyUser, + overrideAccess: false, + showHiddenFields: true, + + // If your collection supports uploads, you can upload + // a file directly through the Local API by providing + // its full, absolute file path. + filePath: path.resolve(__dirname, "./path-to-image.jpg"), + + // If you are uploading a file and would like to replace + // the existing file instead of generating a new filename, + // you can set the following property to `true` + overwriteExistingFiles: true, +}); +``` + #### Delete ```js @@ -209,6 +247,29 @@ const result = await payload.delete({ }); ``` +#### Delete Many + +```js +// Result will be an object with: +// { +// docs: [], // each document that is now deleted +// errors: [], // any errors that occurred, including the id of the errored on document +// } +const result = await payload.delete({ + collection: "posts", // required + where: { + // required + fieldName: { equals: 'value' }, + }, + depth: 0, + locale: "en", + fallbackLocale: false, + user: dummyUser, + overrideAccess: false, + showHiddenFields: true, +}); +``` + ## Auth Operations If a collection has [`Authentication`](/docs/authentication/overview) enabled, additional Local API operations will be available: diff --git a/docs/rest-api/overview.mdx b/docs/rest-api/overview.mdx index c6aa962bce..aadca233ae 100644 --- a/docs/rest-api/overview.mdx +++ b/docs/rest-api/overview.mdx @@ -26,13 +26,15 @@ Note: Collection slugs must be formatted in kebab-case **All CRUD operations are exposed as follows:** -| Method | Path | Description | -| -------- | --------------------------- | -------------------------------------- | -| `GET` | `/api/{collection-slug}` | Find paginated documents | -| `GET` | `/api/{collection-slug}/:id` | Find a specific document by ID | -| `POST` | `/api/{collection-slug}` | Create a new document | -| `PATCH` | `/api/{collection-slug}/:id` | Update a document by ID | -| `DELETE` | `/api/{collection-slug}/:id` | Delete an existing document by ID | +| Method | Path | Description | +|----------|-------------------------------|--------------------------------------------------| +| `GET` | `/api/{collection-slug}` | Find paginated documents | +| `GET` | `/api/{collection-slug}/:id` | Find a specific document by ID | +| `POST` | `/api/{collection-slug}` | Create a new document | +| `PATCH` | `/api/{collection-slug}` | Update all documents matching the `where` query | +| `PATCH` | `/api/{collection-slug}` | Update a document by ID | +| `DELETE` | `/api/{collection-slug}` | Delete all documents matching the `where` query | +| `DELETE` | `/api/{collection-sldug}/:id` | Delete an existing document by ID | ##### Additional `find` query parameters diff --git a/src/admin/components/Routes.tsx b/src/admin/components/Routes.tsx index 598b58a00a..56c44e67cf 100644 --- a/src/admin/components/Routes.tsx +++ b/src/admin/components/Routes.tsx @@ -189,12 +189,10 @@ const Routes = () => { render={(routeProps) => { if (permissions?.collections?.[collection.slug]?.read?.permission) { return ( - - - + ); } diff --git a/src/admin/components/elements/ColumnSelector/index.tsx b/src/admin/components/elements/ColumnSelector/index.tsx index 763f12621b..950a4d0c58 100644 --- a/src/admin/components/elements/ColumnSelector/index.tsx +++ b/src/admin/components/elements/ColumnSelector/index.tsx @@ -1,4 +1,4 @@ -import React, { useId } from 'react'; +import React, { useId, useState } from 'react'; import { useTranslation } from 'react-i18next'; import Pill from '../Pill'; import Plus from '../../icons/Plus'; @@ -49,6 +49,8 @@ const ColumnSelector: React.FC = (props) => { name, } = col; + if (col.accessor === '_select') return null; + return ( = (props) => { + const { + resetParams, + collection: { + slug, + labels: { + plural, + }, + } = {}, + } = props; + + const { permissions } = useAuth(); + const { serverURL, routes: { api } } = useConfig(); + const { toggleModal } = useModal(); + const { selectAll, count, getQueryParams, toggleAll } = useSelection(); + const { t, i18n } = useTranslation('general'); + const [deleting, setDeleting] = useState(false); + + const collectionPermissions = permissions?.collections?.[slug]; + const hasDeletePermission = collectionPermissions?.delete?.permission; + + const modalSlug = `delete-${slug}`; + + const addDefaultError = useCallback(() => { + toast.error(t('error:unknown')); + }, [t]); + + const handleDelete = useCallback(() => { + setDeleting(true); + requests.delete(`${serverURL}${api}/${slug}${getQueryParams()}`, { + headers: { + 'Content-Type': 'application/json', + 'Accept-Language': i18n.language, + }, + }).then(async (res) => { + try { + const json = await res.json(); + toggleModal(modalSlug); + if (res.status < 400) { + toast.success(json.message || t('deletedSuccessfully'), { autoClose: 3000 }); + toggleAll(); + resetParams({ page: selectAll ? 1 : undefined }); + return null; + } + + if (json.errors) { + toast.error(json.message); + } else { + addDefaultError(); + } + return false; + } catch (e) { + return addDefaultError(); + } + }); + }, [addDefaultError, api, getQueryParams, i18n.language, modalSlug, resetParams, selectAll, serverURL, slug, t, toggleAll, toggleModal]); + + if (selectAll === SelectAllStatus.None || !hasDeletePermission) { + return null; + } + + return ( + + { + setDeleting(false); + toggleModal(modalSlug); + }} + > + {t('delete')} + + + +

{t('confirmDeletion')}

+

+ {t('aboutToDeleteCount', { label: getTranslation(plural, i18n), count })} +

+ + +
+
+
+ ); +}; + +export default DeleteMany; diff --git a/src/admin/components/elements/DeleteMany/types.ts b/src/admin/components/elements/DeleteMany/types.ts new file mode 100644 index 0000000000..b246998b8c --- /dev/null +++ b/src/admin/components/elements/DeleteMany/types.ts @@ -0,0 +1,8 @@ +import { SanitizedCollectionConfig } from '../../../../collections/config/types'; +import type { Props as ListProps } from '../../views/collections/List/types'; + +export type Props = { + collection: SanitizedCollectionConfig, + title?: string, + resetParams: ListProps['resetParams'], +} diff --git a/src/admin/components/elements/EditMany/index.scss b/src/admin/components/elements/EditMany/index.scss new file mode 100644 index 0000000000..ad48942d98 --- /dev/null +++ b/src/admin/components/elements/EditMany/index.scss @@ -0,0 +1,190 @@ +@import '../../../scss/styles.scss'; + +.edit-many { + + &__toggle { + font-size: 1rem; + line-height: base(1); + display: inline-flex; + background: var(--theme-elevation-150); + color: var(--theme-elevation-800); + border-radius: $style-radius-s; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + border: 0; + padding: 0 base(.25); + align-items: center; + cursor: pointer; + text-decoration: none; + + &:active, + &:focus { + outline: none; + } + + &:hover { + background: var(--theme-elevation-100); + } + + &:active { + background: var(--theme-elevation-100); + } + } + + &__form { + height: 100%; + } + + &__main { + width: calc(100% - #{base(15)}); + display: flex; + flex-direction: column; + min-height: 100%; + } + + &__header { + display: flex; + margin-top: base(2.5); + margin-bottom: base(1); + width: 100%; + + &__title { + margin: 0; + flex-grow: 1; + } + + &__close { + border: 0; + background-color: transparent; + padding: 0; + cursor: pointer; + overflow: hidden; + width: base(1); + height: base(1); + + svg { + width: base(2.75); + height: base(2.75); + position: relative; + left: base(-.825); + top: base(-.825); + + .stroke { + stroke-width: 2px; + vector-effect: non-scaling-stroke; + } + } + } + } + + &__edit { + padding-top: base(1); + padding-bottom: base(2); + flex-grow: 1; + } + + &__sidebar-wrap { + position: fixed; + width: base(15); + height: 100%; + top: 0; + right: 0; + overflow: visible; + border-left: 1px solid var(--theme-elevation-100); + } + + &__sidebar { + width: 100%; + height: 100%; + overflow-y: auto; + } + + &__sidebar-sticky-wrap { + display: flex; + flex-direction: column; + min-height: 100%; + } + + &__collection-actions, + &__meta, + &__sidebar-fields { + padding-left: base(1.5); + } + + &__document-actions { + padding-right: $baseline; + position: sticky; + top: 0; + z-index: var(--z-nav); + + > * { + position: relative; + z-index: 1; + } + + @include mid-break { + @include blur-bg; + } + } + + &__document-actions { + display: flex; + flex-wrap: wrap; + padding: base(1); + gap: base(.5); + + .form-submit { + width: calc(50% - #{base(1)}); + + @include mid-break { + width: auto; + flex-grow: 1; + } + + .btn { + width: 100%; + padding-left: base(.5); + padding-right: base(.5); + margin-bottom: 0; + } + } + } + + @include mid-break { + &__main { + width: 100%; + min-height: initial; + } + + &__sidebar-wrap { + position: static; + width: 100%; + height: initial; + } + + &__form { + display: block; + } + + &__edit { + padding-top: 0; + padding-bottom: 0; + } + + &__document-actions { + position: fixed; + bottom: 0; + left: 0; + right: 0; + top: auto; + z-index: var(--z-nav); + } + + &__document-actions, + &__sidebar-fields { + padding-left: var(--gutter-h); + padding-right: var(--gutter-h); + } + } +} diff --git a/src/admin/components/elements/EditMany/index.tsx b/src/admin/components/elements/EditMany/index.tsx new file mode 100644 index 0000000000..76739b592a --- /dev/null +++ b/src/admin/components/elements/EditMany/index.tsx @@ -0,0 +1,204 @@ +import React, { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useModal } from '@faceless-ui/modal'; +import { useConfig } from '../../utilities/Config'; +import { Drawer, DrawerToggler } from '../Drawer'; +import { Props } from './types'; +import { SelectAllStatus, useSelection } from '../../views/collections/List/SelectionProvider'; +import { getTranslation } from '../../../../utilities/getTranslation'; +import { useAuth } from '../../utilities/Auth'; +import { FieldSelect } from '../FieldSelect'; +import FormSubmit from '../../forms/Submit'; +import Form from '../../forms/Form'; +import { useForm } from '../../forms/Form/context'; +import RenderFields from '../../forms/RenderFields'; +import { OperationContext } from '../../utilities/OperationProvider'; +import fieldTypes from '../../forms/field-types'; +import X from '../../icons/X'; + +import './index.scss'; + +const baseClass = 'edit-many'; + +const Submit: React.FC<{disabled: boolean, action: string}> = ({ action, disabled }) => { + const { submit } = useForm(); + const { t } = useTranslation('general'); + + const save = useCallback(() => { + submit({ + skipValidation: true, + method: 'PATCH', + action, + }); + }, [action, submit]); + + return ( + + {t('save')} + + ); +}; +const Publish: React.FC<{disabled: boolean, action: string}> = ({ action, disabled }) => { + const { submit } = useForm(); + const { t } = useTranslation('version'); + + const save = useCallback(() => { + submit({ + skipValidation: true, + method: 'PATCH', + overrides: { + _status: 'published', + }, + action, + }); + }, [action, submit]); + + return ( + + {t('publishChanges')} + + ); +}; +const SaveDraft: React.FC<{disabled: boolean, action: string}> = ({ action, disabled }) => { + const { submit } = useForm(); + const { t } = useTranslation('version'); + + const save = useCallback(() => { + submit({ + skipValidation: true, + method: 'PATCH', + overrides: { + _status: 'draft', + }, + action, + }); + }, [action, submit]); + + return ( + + {t('saveDraft')} + + ); +}; +const EditMany: React.FC = (props) => { + const { + resetParams, + collection, + collection: { + slug, + labels: { + plural, + }, + fields, + } = {}, + } = props; + + const { permissions } = useAuth(); + const { closeModal } = useModal(); + const { serverURL, routes: { api } } = useConfig(); + const { selectAll, count, getQueryParams } = useSelection(); + const { t, i18n } = useTranslation('general'); + const [selected, setSelected] = useState([]); + + const collectionPermissions = permissions?.collections?.[slug]; + const hasUpdatePermission = collectionPermissions?.update?.permission; + + const drawerSlug = `edit-${slug}`; + + if (selectAll === SelectAllStatus.None || !hasUpdatePermission) { + return null; + } + + const onSuccess = () => { + resetParams({ page: selectAll === SelectAllStatus.AllAvailable ? 1 : undefined }); + }; + + return ( +
+ { + setSelected([]); + }} + > + {t('edit')} + + + +
+
+
+

+ {t('editingLabel', { label: getTranslation(plural, i18n), count })} +

+ +
+ + +
+
+
+
+ + { collection.versions && ( + + + + + )} +
+
+
+
+
+
+
+
+
+ ); +}; + +export default EditMany; diff --git a/src/admin/components/elements/EditMany/types.ts b/src/admin/components/elements/EditMany/types.ts new file mode 100644 index 0000000000..0ef8386b53 --- /dev/null +++ b/src/admin/components/elements/EditMany/types.ts @@ -0,0 +1,7 @@ +import { SanitizedCollectionConfig } from '../../../../collections/config/types'; +import type { Props as ListProps } from '../../views/collections/List/types'; + +export type Props = { + collection: SanitizedCollectionConfig, + resetParams: ListProps['resetParams'], +} diff --git a/src/admin/components/elements/FieldSelect/index.scss b/src/admin/components/elements/FieldSelect/index.scss new file mode 100644 index 0000000000..92980a30d9 --- /dev/null +++ b/src/admin/components/elements/FieldSelect/index.scss @@ -0,0 +1,5 @@ +@import '../../../scss/styles.scss'; + +.field-select { + margin-bottom: base(1); +} diff --git a/src/admin/components/elements/FieldSelect/index.tsx b/src/admin/components/elements/FieldSelect/index.tsx new file mode 100644 index 0000000000..4f1212abfa --- /dev/null +++ b/src/admin/components/elements/FieldSelect/index.tsx @@ -0,0 +1,101 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Field, fieldAffectsData, + fieldHasSubFields, FieldWithPath, + tabHasName, +} from '../../../../fields/config/types'; +import ReactSelect from '../ReactSelect'; +import { getTranslation } from '../../../../utilities/getTranslation'; +import Label from '../../forms/Label'; +import { useForm } from '../../forms/Form/context'; +import { createNestedFieldPath } from '../../forms/Form/createNestedFieldPath'; + +import './index.scss'; + +const baseClass = 'field-select'; + +type Props = { + fields: Field[]; + setSelected: (fields: FieldWithPath[]) => void +} + +const combineLabel = (prefix, field, i18n): string => ( + `${prefix === '' ? '' : `${prefix} > `}${getTranslation(field.label || field.name, i18n) || ''}` +); +const reduceFields = (fields: Field[], i18n, path = '', labelPrefix = ''): {label: string, value: FieldWithPath}[] => ( + fields.reduce((fieldsToUse, field) => { + // escape for a variety of reasons + if (fieldAffectsData(field) && (field.admin?.disableBulkEdit || field.unique || field.hidden || field.admin?.hidden || field.admin?.readOnly)) { + return fieldsToUse; + } + if (!(field.type === 'array' || field.type === 'blocks') && fieldHasSubFields(field)) { + return [ + ...fieldsToUse, + ...reduceFields(field.fields, i18n, createNestedFieldPath(path, field), combineLabel(labelPrefix, field, i18n)), + ]; + } + if (field.type === 'tabs') { + return [ + ...fieldsToUse, + ...field.tabs.reduce((tabFields, tab) => { + return [ + ...tabFields, + ...(reduceFields(tab.fields, i18n, tabHasName(tab) ? createNestedFieldPath(path, field) : path, combineLabel(labelPrefix, field, i18n))), + ]; + }, []), + ]; + } + const formattedField = { + label: combineLabel(labelPrefix, field, i18n), + value: { + ...field, + path: createNestedFieldPath(path, field), + }, + }; + + return [ + ...fieldsToUse, + formattedField, + ]; + }, [])); +export const FieldSelect: React.FC = ({ + fields, + setSelected, +}) => { + const { t, i18n } = useTranslation('general'); + const [options] = useState(() => reduceFields(fields, i18n)); + const { getFields, dispatchFields } = useForm(); + const handleChange = (selected) => { + const activeFields = getFields(); + if (selected === null) { + setSelected([]); + } else { + setSelected(selected.map(({ value }) => value)); + } + // remove deselected values from form state + if (selected === null || Object.keys(activeFields).length > selected.length) { + Object.keys(activeFields).forEach((path) => { + if (selected === null || !selected.find((field) => { + return field.value.path === path; + })) { + dispatchFields({ + type: 'REMOVE', + path, + }); + } + }); + } + }; + + return ( +
+
+ ); +}; diff --git a/src/admin/components/elements/ListControls/index.scss b/src/admin/components/elements/ListControls/index.scss index 0b6e05eba9..561e5d5e76 100644 --- a/src/admin/components/elements/ListControls/index.scss +++ b/src/admin/components/elements/ListControls/index.scss @@ -5,6 +5,8 @@ &__wrap { display: flex; + align-items: center; + background-color: var(--theme-elevation-50); } .search-filter { @@ -21,24 +23,28 @@ &__buttons-wrap { display: flex; - margin-left: - base(.5); - margin-right: - base(.5); - width: calc(100% + #{base(1)}); + align-items: center; + margin-right: base(.5); + + .btn, .pill { + margin: 0 0 0 base(.5); + } .btn { - margin: 0 base(.5); + background-color: var(--theme-elevation-100); + cursor: pointer; + padding: 0 base(.25); + border-radius: $style-radius-s; + + &:hover { + background-color: var(--theme-elevation-200); + } } } - &__toggle-columns, - &__toggle-where, - &__toggle-sort { - min-width: 140px; - - &.btn--style-primary { - svg { - transform: rotate(180deg); - } + &__buttons-active { + svg { + transform: rotate(180deg); } } @@ -48,25 +54,10 @@ margin-top: base(1); } - @include mid-break { - &__buttons { - margin-left: base(.5); - } - - &__buttons-wrap { - margin-left: - base(.25); - margin-right: - base(.25); - width: calc(100% + #{base(0.5)}); - - .btn { - margin: 0 base(.25); - } - } - } - @include small-break { &__wrap { flex-wrap: wrap; + background-color: unset; } .search-filter { @@ -74,11 +65,23 @@ width: 100%; } - &__buttons { - margin: 0; + &__buttons-wrap { + margin-left: - base(.25); + margin-right: - base(.25); + width: calc(100% + #{base(0.5)}); + + .pill { + margin: 0 base(.25); + padding: base(.5) base(1); + + svg { + margin-left: auto; + } + } } &__buttons { + margin: 0; width: 100%; } diff --git a/src/admin/components/elements/ListControls/index.tsx b/src/admin/components/elements/ListControls/index.tsx index b29c4fcc25..791e262c92 100644 --- a/src/admin/components/elements/ListControls/index.tsx +++ b/src/admin/components/elements/ListControls/index.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import AnimateHeight from 'react-animate-height'; import { useTranslation } from 'react-i18next'; +import { useWindowInfo } from '@faceless-ui/window-info'; import { fieldAffectsData } from '../../../../fields/config/types'; import SearchFilter from '../SearchFilter'; import ColumnSelector from '../ColumnSelector'; @@ -13,6 +14,12 @@ import validateWhereQuery from '../WhereBuilder/validateWhereQuery'; import flattenFields from '../../../../utilities/flattenTopLevelFields'; import { getTextFieldsToBeSearched } from './getTextFieldsToBeSearched'; import { getTranslation } from '../../../../utilities/getTranslation'; +import Pill from '../Pill'; +import Chevron from '../../icons/Chevron'; +import EditMany from '../EditMany'; +import DeleteMany from '../DeleteMany'; +import PublishMany from '../PublishMany'; +import UnpublishMany from '../UnpublishMany'; import './index.scss'; @@ -26,6 +33,7 @@ const ListControls: React.FC = (props) => { handleSortChange, handleWhereChange, modifySearchQuery = true, + resetParams, collection: { fields, admin: { @@ -45,6 +53,7 @@ const ListControls: React.FC = (props) => { const [textFieldsToBeSearched] = useState(getTextFieldsToBeSearched(listSearchableFields, fields)); const [visibleDrawer, setVisibleDrawer] = useState<'where' | 'sort' | 'columns'>(shouldInitializeWhereOpened ? 'where' : undefined); const { t, i18n } = useTranslation('general'); + const { breakpoints: { s: smallBreak } } = useWindowInfo(); return (
@@ -58,26 +67,44 @@ const ListControls: React.FC = (props) => { />
+ { !smallBreak && ( + + + + + + + )} {enableColumns && ( - + )} - + {enableSort && ( + + ) } +
+ ); +}; + +export default ListSelection; diff --git a/src/admin/components/elements/PublishMany/index.scss b/src/admin/components/elements/PublishMany/index.scss new file mode 100644 index 0000000000..14c2144d3c --- /dev/null +++ b/src/admin/components/elements/PublishMany/index.scss @@ -0,0 +1,17 @@ +@import '../../../scss/styles.scss'; + +.publish-many { + @include blur-bg; + display: flex; + align-items: center; + height: 100%; + + &__template { + z-index: 1; + position: relative; + } + + .btn { + margin-right: $baseline; + } +} diff --git a/src/admin/components/elements/PublishMany/index.tsx b/src/admin/components/elements/PublishMany/index.tsx new file mode 100644 index 0000000000..9d4100b272 --- /dev/null +++ b/src/admin/components/elements/PublishMany/index.tsx @@ -0,0 +1,123 @@ +import React, { useState, useCallback } from 'react'; +import { toast } from 'react-toastify'; +import { Modal, useModal } from '@faceless-ui/modal'; +import { useTranslation } from 'react-i18next'; +import { useConfig } from '../../utilities/Config'; +import Button from '../Button'; +import MinimalTemplate from '../../templates/Minimal'; +import { requests } from '../../../api'; +import { Props } from './types'; +import { SelectAllStatus, useSelection } from '../../views/collections/List/SelectionProvider'; +import { getTranslation } from '../../../../utilities/getTranslation'; +import Pill from '../Pill'; +import { useAuth } from '../../utilities/Auth'; + +import './index.scss'; + +const baseClass = 'publish-many'; + +const PublishMany: React.FC = (props) => { + const { + resetParams, + collection: { + slug, + labels: { + plural, + }, + versions, + } = {}, + } = props; + + const { serverURL, routes: { api } } = useConfig(); + const { permissions } = useAuth(); + const { toggleModal } = useModal(); + const { t, i18n } = useTranslation('version'); + const { selectAll, count, getQueryParams } = useSelection(); + const [submitted, setSubmitted] = useState(false); + + const collectionPermissions = permissions?.collections?.[slug]; + const hasPermission = collectionPermissions?.update?.permission; + + const modalSlug = `publish-${slug}`; + + const addDefaultError = useCallback(() => { + toast.error(t('error:unknown')); + }, [t]); + + const handlePublish = useCallback(() => { + setSubmitted(true); + requests.patch(`${serverURL}${api}/${slug}${getQueryParams({ _status: { not_equals: 'published' } })}`, { + body: JSON.stringify({ + _status: 'published', + }), + headers: { + 'Content-Type': 'application/json', + 'Accept-Language': i18n.language, + }, + }).then(async (res) => { + try { + const json = await res.json(); + toggleModal(modalSlug); + if (res.status < 400) { + toast.success(t('general:updatedSuccessfully')); + resetParams({ page: selectAll ? 1 : undefined }); + return null; + } + + if (json.errors) { + json.errors.forEach((error) => toast.error(error.message)); + } else { + addDefaultError(); + } + return false; + } catch (e) { + return addDefaultError(); + } + }); + }, [addDefaultError, api, getQueryParams, i18n.language, modalSlug, resetParams, selectAll, serverURL, slug, t, toggleModal]); + + if (!(versions?.drafts) || (selectAll === SelectAllStatus.None || !hasPermission)) { + return null; + } + + return ( + + { + setSubmitted(false); + toggleModal(modalSlug); + }} + > + {t('publish')} + + + +

{t('confirmPublish')}

+

+ {t('aboutToPublishSelection', { label: getTranslation(plural, i18n) })} +

+ + +
+
+
+ ); +}; + +export default PublishMany; diff --git a/src/admin/components/elements/PublishMany/types.ts b/src/admin/components/elements/PublishMany/types.ts new file mode 100644 index 0000000000..0ef8386b53 --- /dev/null +++ b/src/admin/components/elements/PublishMany/types.ts @@ -0,0 +1,7 @@ +import { SanitizedCollectionConfig } from '../../../../collections/config/types'; +import type { Props as ListProps } from '../../views/collections/List/types'; + +export type Props = { + collection: SanitizedCollectionConfig, + resetParams: ListProps['resetParams'], +} diff --git a/src/admin/components/elements/SearchFilter/index.scss b/src/admin/components/elements/SearchFilter/index.scss index faab08d3d5..4d2f6ae5da 100644 --- a/src/admin/components/elements/SearchFilter/index.scss +++ b/src/admin/components/elements/SearchFilter/index.scss @@ -7,10 +7,20 @@ position: absolute; top: 50%; transform: translateY(-50%); - right: base(.5); + left: base(.5); } &__input { @include formInput; + box-shadow: none; + padding-left: base(2); + background-color: var(--theme-elevation-50); + border: none; + + &:not(:disabled) { + &:hover, &:focus { + box-shadow: none; + } + } } } diff --git a/src/admin/components/elements/TableColumns/buildColumns.tsx b/src/admin/components/elements/TableColumns/buildColumns.tsx index a86d58f013..fd948f26c4 100644 --- a/src/admin/components/elements/TableColumns/buildColumns.tsx +++ b/src/admin/components/elements/TableColumns/buildColumns.tsx @@ -1,53 +1,25 @@ import React from 'react'; -import type { TFunction } from 'react-i18next'; import Cell from '../../views/collections/List/Cell'; import SortColumn from '../SortColumn'; import { SanitizedCollectionConfig } from '../../../../collections/config/types'; import { Column } from '../Table/types'; -import { Field, fieldIsPresentationalOnly } from '../../../../fields/config/types'; +import { fieldIsPresentationalOnly } from '../../../../fields/config/types'; import flattenFields from '../../../../utilities/flattenTopLevelFields'; import { Props as CellProps } from '../../views/collections/List/Cell/types'; +import SelectAll from '../../views/collections/List/SelectAll'; +import SelectRow from '../../views/collections/List/SelectRow'; const buildColumns = ({ collection, columns, - t, cellProps, }: { collection: SanitizedCollectionConfig, columns: Pick[], - t: TFunction, cellProps: Partial[] }): Column[] => { - // only insert each base field if it doesn't already exist in the collection - const baseFields: Field[] = [ - { - name: 'id', - type: 'text', - label: 'ID', - }, - { - name: 'updatedAt', - type: 'date', - label: t('updatedAt'), - }, - { - name: 'createdAt', - type: 'date', - label: t('createdAt'), - }, - ]; - - const combinedFields = baseFields.reduce((acc, field) => { - // if the field already exists in the collection, don't add it - if (acc.find((f) => 'name' in f && 'name' in field && f.name === field.name)) return acc; - return [...acc, field]; - }, collection.fields); - - const flattenedFields = flattenFields(combinedFields, true); - // sort the fields to the order of activeColumns - const sortedFields = flattenedFields.sort((a, b) => { + const sortedFields = flattenFields(collection.fields, true).sort((a, b) => { const aIndex = columns.findIndex((column) => column.accessor === a.name); const bIndex = columns.findIndex((column) => column.accessor === b.name); if (aIndex === -1 && bIndex === -1) return 0; @@ -97,6 +69,23 @@ const buildColumns = ({ }; }); + cols.unshift({ + active: true, + label: null, + name: '', + accessor: '_select', + components: { + Heading: ( + + ), + renderCell: (rowData) => ( + + ), + }, + }); + return cols; }; diff --git a/src/admin/components/elements/TableColumns/columnReducer.ts b/src/admin/components/elements/TableColumns/columnReducer.ts index e2ee6a72da..51abb2f3c3 100644 --- a/src/admin/components/elements/TableColumns/columnReducer.ts +++ b/src/admin/components/elements/TableColumns/columnReducer.ts @@ -1,4 +1,3 @@ -import { TFunction } from 'react-i18next'; import { SanitizedCollectionConfig } from '../../../../collections/config/types'; import { Column } from '../Table/types'; import buildColumns from './buildColumns'; @@ -9,7 +8,6 @@ type TOGGLE = { type: 'toggle', payload: { column: string - t: TFunction collection: SanitizedCollectionConfig cellProps: Partial[] } @@ -19,7 +17,6 @@ type SET = { type: 'set', payload: { columns: Pick[] - t: TFunction collection: SanitizedCollectionConfig cellProps: Partial[] } @@ -30,7 +27,6 @@ type MOVE = { payload: { fromIndex: number toIndex: number - t: TFunction collection: SanitizedCollectionConfig cellProps: Partial[] } @@ -43,7 +39,6 @@ export const columnReducer = (state: Column[], action: Action): Column[] => { case 'toggle': { const { column, - t, collection, cellProps, } = action.payload; @@ -62,7 +57,6 @@ export const columnReducer = (state: Column[], action: Action): Column[] => { return buildColumns({ columns: withToggledColumn, collection, - t, cellProps, }); } @@ -70,7 +64,6 @@ export const columnReducer = (state: Column[], action: Action): Column[] => { const { fromIndex, toIndex, - t, collection, cellProps, } = action.payload; @@ -82,14 +75,12 @@ export const columnReducer = (state: Column[], action: Action): Column[] => { return buildColumns({ columns: withMovedColumn, collection, - t, cellProps, }); } case 'set': { const { columns, - t, collection, cellProps, } = action.payload; @@ -97,7 +88,6 @@ export const columnReducer = (state: Column[], action: Action): Column[] => { return buildColumns({ columns, collection, - t, cellProps, }); } diff --git a/src/admin/components/elements/TableColumns/index.tsx b/src/admin/components/elements/TableColumns/index.tsx index 8c1c975f56..976ae1c5c6 100644 --- a/src/admin/components/elements/TableColumns/index.tsx +++ b/src/admin/components/elements/TableColumns/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useReducer, createContext, useContext, useRef } from 'react'; +import React, { useCallback, useEffect, useReducer, createContext, useContext, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { SanitizedCollectionConfig } from '../../../../collections/config/types'; import { usePreferences } from '../../utilities/Preferences'; @@ -8,6 +8,8 @@ import buildColumns from './buildColumns'; import { Action, columnReducer } from './columnReducer'; import getInitialColumnState from './getInitialColumns'; import { Props as CellProps } from '../../views/collections/List/Cell/types'; +import formatFields from '../../views/collections/List/formatFields'; +import { Field } from '../../../../fields/config/types'; export interface ITableColumns { columns: Column[] @@ -33,21 +35,22 @@ export const TableColumnsProvider: React.FC<{ cellProps, collection, collection: { - fields, admin: { useAsTitle, defaultColumns, }, }, }) => { - const { t } = useTranslation('general'); const preferenceKey = `${collection.slug}-list`; const prevCollection = useRef(); const hasInitialized = useRef(false); const { getPreference, setPreference } = usePreferences(); + const { t } = useTranslation(); + const [formattedFields] = useState(() => formatFields(collection, t)); const [tableColumns, dispatchTableColumns] = useReducer(columnReducer, {}, () => { - const initialColumns = getInitialColumnState(fields, useAsTitle, defaultColumns); + const initialColumns = getInitialColumnState(formattedFields, useAsTitle, defaultColumns); + return buildColumns({ collection, columns: initialColumns.map((column) => ({ @@ -55,7 +58,6 @@ export const TableColumnsProvider: React.FC<{ active: true, })), cellProps, - t, }); }); @@ -72,7 +74,7 @@ export const TableColumnsProvider: React.FC<{ const currentPreferences = await getPreference(preferenceKey); prevCollection.current = collection.slug; - const initialColumns = getInitialColumnState(fields, useAsTitle, defaultColumns); + const initialColumns = getInitialColumnState(formattedFields, useAsTitle, defaultColumns); const newCols = currentPreferences?.columns || initialColumns; dispatchTableColumns({ @@ -89,8 +91,7 @@ export const TableColumnsProvider: React.FC<{ } return column; }), - t, - collection, + collection: { ...collection, fields: formatFields(collection, t) }, cellProps, }, }); @@ -100,7 +101,7 @@ export const TableColumnsProvider: React.FC<{ }; sync(); - }, [preferenceKey, setPreference, fields, tableColumns, getPreference, useAsTitle, defaultColumns, t, collection, cellProps]); + }, [preferenceKey, setPreference, tableColumns, getPreference, useAsTitle, defaultColumns, collection, cellProps, formattedFields, t]); // ///////////////////////////////////// // Set preferences on column change @@ -130,12 +131,11 @@ export const TableColumnsProvider: React.FC<{ dispatchTableColumns({ type: 'set', payload: { - collection, + collection: { ...collection, fields: formatFields(collection, t) }, columns: columns.map((column) => ({ accessor: column, active: true, })), - t, // onSelect, cellProps, }, @@ -153,8 +153,7 @@ export const TableColumnsProvider: React.FC<{ payload: { fromIndex, toIndex, - collection, - t, + collection: { ...collection, fields: formatFields(collection, t) }, cellProps, }, }); @@ -165,8 +164,7 @@ export const TableColumnsProvider: React.FC<{ type: 'toggle', payload: { column, - collection, - t, + collection: { ...collection, fields: formatFields(collection, t) }, cellProps, }, }); diff --git a/src/admin/components/elements/UnpublishMany/index.scss b/src/admin/components/elements/UnpublishMany/index.scss new file mode 100644 index 0000000000..14ceda2300 --- /dev/null +++ b/src/admin/components/elements/UnpublishMany/index.scss @@ -0,0 +1,17 @@ +@import '../../../scss/styles.scss'; + +.unpublish-many { + @include blur-bg; + display: flex; + align-items: center; + height: 100%; + + &__template { + z-index: 1; + position: relative; + } + + .btn { + margin-right: $baseline; + } +} diff --git a/src/admin/components/elements/UnpublishMany/index.tsx b/src/admin/components/elements/UnpublishMany/index.tsx new file mode 100644 index 0000000000..0ddbc36753 --- /dev/null +++ b/src/admin/components/elements/UnpublishMany/index.tsx @@ -0,0 +1,123 @@ +import React, { useState, useCallback } from 'react'; +import { toast } from 'react-toastify'; +import { Modal, useModal } from '@faceless-ui/modal'; +import { useTranslation } from 'react-i18next'; +import { useConfig } from '../../utilities/Config'; +import Button from '../Button'; +import MinimalTemplate from '../../templates/Minimal'; +import { requests } from '../../../api'; +import { Props } from './types'; +import { SelectAllStatus, useSelection } from '../../views/collections/List/SelectionProvider'; +import { getTranslation } from '../../../../utilities/getTranslation'; +import Pill from '../Pill'; +import { useAuth } from '../../utilities/Auth'; + +import './index.scss'; + +const baseClass = 'unpublish-many'; + +const UnpublishMany: React.FC = (props) => { + const { + resetParams, + collection: { + slug, + labels: { + plural, + }, + versions, + } = {}, + } = props; + + const { serverURL, routes: { api } } = useConfig(); + const { permissions } = useAuth(); + const { toggleModal } = useModal(); + const { t, i18n } = useTranslation('version'); + const { selectAll, count, getQueryParams } = useSelection(); + const [submitted, setSubmitted] = useState(false); + + const collectionPermissions = permissions?.collections?.[slug]; + const hasPermission = collectionPermissions?.update?.permission; + + const modalSlug = `unpublish-${slug}`; + + const addDefaultError = useCallback(() => { + toast.error(t('error:unknown')); + }, [t]); + + const handleUnpublish = useCallback(() => { + setSubmitted(true); + requests.patch(`${serverURL}${api}/${slug}${getQueryParams({ _status: { not_equals: 'draft' } })}`, { + body: JSON.stringify({ + _status: 'draft', + }), + headers: { + 'Content-Type': 'application/json', + 'Accept-Language': i18n.language, + }, + }).then(async (res) => { + try { + const json = await res.json(); + toggleModal(modalSlug); + if (res.status < 400) { + toast.success(t('general:updatedSuccessfully')); + resetParams({ page: selectAll ? 1 : undefined }); + return null; + } + + if (json.errors) { + json.errors.forEach((error) => toast.error(error.message)); + } else { + addDefaultError(); + } + return false; + } catch (e) { + return addDefaultError(); + } + }); + }, [addDefaultError, api, getQueryParams, i18n.language, modalSlug, resetParams, selectAll, serverURL, slug, t, toggleModal]); + + if (!(versions?.drafts) || (selectAll === SelectAllStatus.None || !hasPermission)) { + return null; + } + + return ( + + { + setSubmitted(false); + toggleModal(modalSlug); + }} + > + {t('unpublish')} + + + +

{t('confirmUnpublish')}

+

+ {t('aboutToUnpublishSelection', { label: getTranslation(plural, i18n) })} +

+ + +
+
+
+ ); +}; + +export default UnpublishMany; diff --git a/src/admin/components/elements/UnpublishMany/types.ts b/src/admin/components/elements/UnpublishMany/types.ts new file mode 100644 index 0000000000..0ef8386b53 --- /dev/null +++ b/src/admin/components/elements/UnpublishMany/types.ts @@ -0,0 +1,7 @@ +import { SanitizedCollectionConfig } from '../../../../collections/config/types'; +import type { Props as ListProps } from '../../views/collections/List/types'; + +export type Props = { + collection: SanitizedCollectionConfig, + resetParams: ListProps['resetParams'], +} diff --git a/src/admin/components/elements/UploadGallery/index.scss b/src/admin/components/elements/UploadGallery/index.scss deleted file mode 100644 index f63956edab..0000000000 --- a/src/admin/components/elements/UploadGallery/index.scss +++ /dev/null @@ -1,32 +0,0 @@ -@import '../../../scss/styles.scss'; - -.upload-gallery { - list-style: none; - padding: 0; - margin: base(2) -#{base(.5)}; - width: calc(100% + #{$baseline}); - display: flex; - flex-wrap: wrap; - - li { - min-width: 0; - width: 16.66%; - } - - .thumbnail-card { - margin: base(.5); - max-width: initial; - } - - @include mid-break { - li { - width: 33.33%; - } - } - - @include small-break { - li { - width: 50%; - } - } -} diff --git a/src/admin/components/elements/UploadGallery/index.tsx b/src/admin/components/elements/UploadGallery/index.tsx deleted file mode 100644 index fdccc6c040..0000000000 --- a/src/admin/components/elements/UploadGallery/index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import { Props } from './types'; -import { ThumbnailCard } from '../ThumbnailCard'; - -import './index.scss'; - -const baseClass = 'upload-gallery'; - -const UploadGallery: React.FC = (props) => { - const { docs, onCardClick, collection } = props; - - if (docs && docs.length > 0) { - return ( -
    - {docs.map((doc) => ( -
  • - onCardClick(doc)} - /> -
  • - ))} -
- ); - } - - return null; -}; - -export default UploadGallery; diff --git a/src/admin/components/elements/UploadGallery/types.ts b/src/admin/components/elements/UploadGallery/types.ts deleted file mode 100644 index 14c9078a9f..0000000000 --- a/src/admin/components/elements/UploadGallery/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { SanitizedCollectionConfig } from '../../../../collections/config/types'; - -export type Props = { - docs?: Record[], - collection: SanitizedCollectionConfig, - onCardClick: (doc) => void, -} diff --git a/src/admin/components/icons/Line/index.scss b/src/admin/components/icons/Line/index.scss new file mode 100644 index 0000000000..24bdec61ec --- /dev/null +++ b/src/admin/components/icons/Line/index.scss @@ -0,0 +1,11 @@ +@import '../../../scss/styles'; + +.icon--line { + width: $baseline; + height: $baseline; + + .stroke { + stroke: var(--theme-elevation-800); + stroke-width: $style-stroke-width; + } +} diff --git a/src/admin/components/icons/Line/index.tsx b/src/admin/components/icons/Line/index.tsx new file mode 100644 index 0000000000..97d9563d40 --- /dev/null +++ b/src/admin/components/icons/Line/index.tsx @@ -0,0 +1,21 @@ +import React from 'react'; + +import './index.scss'; + +const Line: React.FC = () => ( + + + +); + +export default Line; diff --git a/src/admin/components/views/collections/List/Cell/field-types/Upload/index.scss b/src/admin/components/views/collections/List/Cell/field-types/Upload/index.scss new file mode 100644 index 0000000000..046b54a1c8 --- /dev/null +++ b/src/admin/components/views/collections/List/Cell/field-types/Upload/index.scss @@ -0,0 +1,17 @@ +@import '../../../../../../../scss/styles.scss'; + +.upload-cell { + display: flex; + flex-wrap: nowrap; + margin: base(-.25) 0; + + .thumbnail { + max-width: base(3); + height: base(3); + } + + &__filename { + align-self: center; + margin-left: base(1); + } +} diff --git a/src/admin/components/views/collections/List/Cell/field-types/Upload/index.tsx b/src/admin/components/views/collections/List/Cell/field-types/Upload/index.tsx index e69de29bb2..64158f0c55 100644 --- a/src/admin/components/views/collections/List/Cell/field-types/Upload/index.tsx +++ b/src/admin/components/views/collections/List/Cell/field-types/Upload/index.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import Thumbnail from '../../../../../../elements/Thumbnail'; +import type { Props } from './types'; +import { useConfig } from '../../../../../../utilities/Config'; + +import './index.scss'; + +const baseClass = 'upload-cell'; + +const UploadCell:React.FC = ({ rowData, cellData, collection }: Props) => { + const { routes: { admin } } = useConfig(); + + return ( + + + { String(cellData) } + + ); +}; + +export default UploadCell; diff --git a/src/admin/components/views/collections/List/Cell/field-types/Upload/types.ts b/src/admin/components/views/collections/List/Cell/field-types/Upload/types.ts new file mode 100644 index 0000000000..50b58ee18b --- /dev/null +++ b/src/admin/components/views/collections/List/Cell/field-types/Upload/types.ts @@ -0,0 +1,6 @@ +import { SanitizedCollectionConfig } from '../../../../../../../../collections/config/types'; +import { Props as CellProps } from '../../types'; + +export type Props = CellProps & { + collection: SanitizedCollectionConfig +} diff --git a/src/admin/components/views/collections/List/Default.tsx b/src/admin/components/views/collections/List/Default.tsx index 90684653de..66af3543ce 100644 --- a/src/admin/components/views/collections/List/Default.tsx +++ b/src/admin/components/views/collections/List/Default.tsx @@ -1,11 +1,10 @@ import React, { Fragment } from 'react'; -import { useHistory } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { useConfig } from '../../../utilities/Config'; -import UploadGallery from '../../../elements/UploadGallery'; +import { useWindowInfo } from '@faceless-ui/window-info'; import Eyebrow from '../../../elements/Eyebrow'; import Paginator from '../../../elements/Paginator'; import ListControls from '../../../elements/ListControls'; +import ListSelection from '../../../elements/ListSelection'; import Pill from '../../../elements/Pill'; import Button from '../../../elements/Button'; import { Table } from '../../../elements/Table'; @@ -17,6 +16,11 @@ import { Gutter } from '../../../elements/Gutter'; import { RelationshipProvider } from './RelationshipProvider'; import { getTranslation } from '../../../../../utilities/getTranslation'; import { StaggeredShimmers } from '../../../elements/ShimmerEffect'; +import { SelectionProvider } from './SelectionProvider'; +import EditMany from '../../../elements/EditMany'; +import DeleteMany from '../../../elements/DeleteMany'; +import PublishMany from '../../../elements/PublishMany'; +import UnpublishMany from '../../../elements/UnpublishMany'; import './index.scss'; @@ -26,8 +30,6 @@ const DefaultList: React.FC = (props) => { const { collection, collection: { - upload, - slug, labels: { singular: singularLabel, plural: pluralLabel, @@ -42,17 +44,15 @@ const DefaultList: React.FC = (props) => { hasCreatePermission, disableEyebrow, modifySearchParams, - disableCardLink, - onCardClick, handleSortChange, handleWhereChange, handlePageChange, handlePerPageChange, customHeader, + resetParams, } = props; - const { routes: { admin } } = useConfig(); - const history = useHistory(); + const { breakpoints: { s: smallBreak } } = useWindowInfo(); const { t, i18n } = useTranslation('general'); return ( @@ -60,117 +60,135 @@ const DefaultList: React.FC = (props) => { - {!disableEyebrow && ( - - )} - -
- {customHeader && customHeader} - {!customHeader && ( - -

- {getTranslation(pluralLabel, i18n)} -

- {hasCreatePermission && ( - - {t('createNew')} - - )} - {description && ( -
- -
- )} -
- )} -
- - {!data.docs && ( - + {!disableEyebrow && ( + + )} + +
+ {customHeader && customHeader} + {!customHeader && ( + +

+ {getTranslation(pluralLabel, i18n)} +

+ {hasCreatePermission && ( + + {t('createNew')} + + )} + {!smallBreak && ( + + )} + {description && ( +
+ +
+ )} +
+ )} +
+ - )} - {(data.docs && data.docs.length > 0) && ( - - {!upload && ( - - - - )} - {upload && ( - { - if (typeof onCardClick === 'function') onCardClick(doc); - if (!disableCardLink) history.push(`${admin}/collections/${slug}/${doc.id}`); - }} - /> - )} - - )} - {data.docs && data.docs.length === 0 && ( -
-

- {t('noResults', { label: getTranslation(pluralLabel, i18n) })} -

- {hasCreatePermission && newDocumentURL && ( - + {!data.docs && ( + + )} + {(data.docs && data.docs.length > 0) && ( + +
+ + )} + {data.docs && data.docs.length === 0 && ( +
+

+ {t('noResults', { label: getTranslation(pluralLabel, i18n) })} +

+ {hasCreatePermission && newDocumentURL && ( + + )} +
+ )} +
+ + {data?.totalDocs > 0 && ( + +
+ {(data.page * data.limit) - (data.limit - 1)} + - + {data.totalPages > 1 && data.totalPages !== data.page ? (data.limit * data.page) : data.totalDocs} + {' '} + {t('of')} + {' '} + {data.totalDocs} +
+ +
+ {smallBreak && ( + + +
+ + + + +
+
+ )} +
+
)}
- )} -
- - {data?.totalDocs > 0 && ( - -
- {(data.page * data.limit) - (data.limit - 1)} - - - {data.totalPages > 1 && data.totalPages !== data.page ? (data.limit * data.page) : data.totalDocs} - {' '} - {t('of')} - {' '} - {data.totalDocs} -
- -
- )} -
- + + ); }; diff --git a/src/admin/components/views/collections/List/SelectAll/index.scss b/src/admin/components/views/collections/List/SelectAll/index.scss new file mode 100644 index 0000000000..cf52b8dfc9 --- /dev/null +++ b/src/admin/components/views/collections/List/SelectAll/index.scss @@ -0,0 +1,30 @@ +@import '../../../../../scss/styles.scss'; + +.select-all { + button { + @extend %btn-reset; + display: flex; + align-items: center; + cursor: pointer; + + &:focus, + &:active { + outline: none; + } + + &:hover { + svg { + opacity: .2; + } + } + } + + &__input { + @include formInput; + padding: 0; + line-height: 0; + position: relative; + width: $baseline; + height: $baseline; + } +} diff --git a/src/admin/components/views/collections/List/SelectAll/index.tsx b/src/admin/components/views/collections/List/SelectAll/index.tsx new file mode 100644 index 0000000000..18b0b3dcb3 --- /dev/null +++ b/src/admin/components/views/collections/List/SelectAll/index.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { SelectAllStatus, useSelection } from '../SelectionProvider'; +import Check from '../../../../icons/Check'; +import Line from '../../../../icons/Line'; + +import './index.scss'; + +const baseClass = 'select-all'; + +const SelectAll: React.FC = () => { + const { selectAll, toggleAll } = useSelection(); + + return ( +
+ +
+ ); +}; + +export default SelectAll; diff --git a/src/admin/components/views/collections/List/SelectRow/index.scss b/src/admin/components/views/collections/List/SelectRow/index.scss new file mode 100644 index 0000000000..e6051e3776 --- /dev/null +++ b/src/admin/components/views/collections/List/SelectRow/index.scss @@ -0,0 +1,44 @@ +@import '../../../../../scss/styles.scss'; + +.select-row { + button { + @extend %btn-reset; + display: flex; + align-items: center; + cursor: pointer; + + &:focus, + &:active { + outline: none; + } + + &:hover { + svg { + opacity: .2; + } + } + } + + &__input { + @include formInput; + padding: 0; + line-height: 0; + position: relative; + width: $baseline; + height: $baseline; + + svg { + opacity: 0; + } + } + + &--checked { + button { + .select-row__input { + svg { + opacity: 1; + } + } + } + } +} diff --git a/src/admin/components/views/collections/List/SelectRow/index.tsx b/src/admin/components/views/collections/List/SelectRow/index.tsx new file mode 100644 index 0000000000..acae082fe7 --- /dev/null +++ b/src/admin/components/views/collections/List/SelectRow/index.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { useSelection } from '../SelectionProvider'; +import Check from '../../../../icons/Check'; + +import './index.scss'; + +const baseClass = 'select-row'; + +const SelectRow: React.FC<{ id: string | number }> = ({ id }) => { + const { selected, setSelection } = useSelection(); + + return ( +
+ +
+ ); +}; + +export default SelectRow; diff --git a/src/admin/components/views/collections/List/SelectionProvider/index.tsx b/src/admin/components/views/collections/List/SelectionProvider/index.tsx new file mode 100644 index 0000000000..07ee8e63b4 --- /dev/null +++ b/src/admin/components/views/collections/List/SelectionProvider/index.tsx @@ -0,0 +1,157 @@ +import React, { + createContext, + useCallback, + useContext, + useEffect, + useRef, + useState, +} from 'react'; +import { useHistory } from 'react-router-dom'; +import queryString from 'qs'; +import { Where } from '../../../../../../types'; + +export enum SelectAllStatus { + AllAvailable = 'allAvailable', + AllInPage = 'allInPage', + Some = 'some', + None = 'none', +} + +type SelectionContext = { + selected: Record + setSelection: (id: string | number) => void + selectAll: SelectAllStatus + toggleAll: (allAvailable?: boolean) => void + totalDocs: number + count: number + getQueryParams: (additionalParams?: Where) => string +} + +const Context = createContext({} as SelectionContext); + +type Props = { + children: React.ReactNode + docs: any[] + totalDocs: number +} +export const SelectionProvider: React.FC = ({ children, docs = [], totalDocs }) => { + const contextRef = useRef({} as SelectionContext); + + const history = useHistory(); + const [selected, setSelected] = useState({}); + const [selectAll, setSelectAll] = useState(SelectAllStatus.None); + const [count, setCount] = useState(0); + + const toggleAll = useCallback((allAvailable = false) => { + const rows = {}; + if (allAvailable) { + setSelectAll(SelectAllStatus.AllAvailable); + docs.forEach(({ id }) => { + rows[id] = true; + }); + } else if (selectAll === SelectAllStatus.AllAvailable || selectAll === SelectAllStatus.AllInPage) { + setSelectAll(SelectAllStatus.None); + docs.forEach(({ id }) => { + rows[id] = false; + }); + } else { + docs.forEach(({ id }) => { + rows[id] = selectAll !== SelectAllStatus.Some; + }); + } + setSelected(rows); + }, [docs, selectAll]); + + const setSelection = useCallback((id) => { + const isSelected = !selected[id]; + const newSelected = { + ...selected, + [id]: isSelected, + }; + if (!isSelected) { + setSelectAll(SelectAllStatus.Some); + } + setSelected(newSelected); + }, [selected]); + + const getQueryParams = useCallback((additionalParams?: Where): string => { + let where: Where; + if (selectAll === SelectAllStatus.AllAvailable) { + const params = queryString.parse(history.location.search, { ignoreQueryPrefix: true }).where as Where; + where = params || { + id: { not_equals: '' }, + }; + } else { + where = { + id: { + in: Object.keys(selected).filter((id) => selected[id]).map((id) => id), + }, + }; + } + if (additionalParams) { + where = { + and: [ + { ...additionalParams }, + where, + ], + }; + } + return queryString.stringify({ + where, + }, { addQueryPrefix: true }); + }, [history.location.search, selectAll, selected]); + + useEffect(() => { + if (selectAll === SelectAllStatus.AllAvailable) { + return; + } + let some = false; + let all = true; + Object.values(selected).forEach((val) => { + all = all && val; + some = some || val; + }); + + if (all) { + setSelectAll(SelectAllStatus.AllInPage); + } else if (some) { + setSelectAll(SelectAllStatus.Some); + } else { + setSelectAll(SelectAllStatus.None); + } + }, [docs, selectAll, selected]); + + useEffect(() => { + const rows = {}; + if (docs.length) { + docs.forEach(({ id }) => { + rows[id] = false; + }); + setSelected(rows); + } + setSelectAll(SelectAllStatus.None); + }, [docs, history]); + + useEffect(() => { + const newCount = selectAll === SelectAllStatus.AllAvailable ? totalDocs : Object.keys(selected).filter((id) => selected[id]).length; + setCount(newCount); + }, [selectAll, selected, totalDocs]); + + contextRef.current = { + selectAll, + toggleAll, + selected, + setSelection, + totalDocs, + count, + getQueryParams, + }; + + return ( + + {children} + + ); +}; + +export const useSelection = (): SelectionContext => useContext(Context); diff --git a/src/admin/components/views/collections/List/formatFields.tsx b/src/admin/components/views/collections/List/formatFields.tsx index ad2506aedf..fd13de2692 100644 --- a/src/admin/components/views/collections/List/formatFields.tsx +++ b/src/admin/components/views/collections/List/formatFields.tsx @@ -1,42 +1,70 @@ import { TFunction } from 'react-i18next'; +import React from 'react'; import { SanitizedCollectionConfig } from '../../../../../collections/config/types'; import { Field, fieldAffectsData, fieldIsPresentationalOnly } from '../../../../../fields/config/types'; +import UploadCell from './Cell/field-types/Upload'; +import { Props } from './Cell/types'; const formatFields = (config: SanitizedCollectionConfig, t: TFunction): Field[] => { const hasID = config.fields.findIndex((field) => fieldAffectsData(field) && field.name === 'id') > -1; - let fields: Field[] = config.fields.reduce((formatted, field) => { + const fields: Field[] = config.fields.reduce((formatted, field) => { if (!fieldIsPresentationalOnly(field) && (field.hidden === true || field?.admin?.disabled === true)) { return formatted; } + if (config.upload && fieldAffectsData(field) && field.name === 'filename') { + const Cell: React.FC = (props) => ( + + ); + return [ + ...formatted, + { + ...field, + admin: { + ...field.admin, + components: { + ...field.admin?.components || {}, + Cell: field.admin?.components?.Cell || Cell, + }, + }, + }, + ]; + } + return [ ...formatted, field, ]; - }, hasID ? [] : [{ name: 'id', label: 'ID', type: 'text' }]); + }, hasID ? [] : [{ + name: 'id', + label: 'ID', + type: 'text', + admin: { + disableBulkEdit: true, + }, + }]); if (config.timestamps) { - fields = fields.concat([ + fields.push( { name: 'createdAt', label: t('general:createdAt'), type: 'date', + admin: { + disableBulkEdit: true, + }, }, { name: 'updatedAt', label: t('general:updatedAt'), type: 'date', + admin: { + disableBulkEdit: true, + }, }, - ]); - } - - if (config.upload) { - fields = fields.concat([ - { - name: 'filename', - label: t('upload:fileName'), - type: 'text', - }, - ]); + ); } return fields; diff --git a/src/admin/components/views/collections/List/index.scss b/src/admin/components/views/collections/List/index.scss index 93a4ce97f9..dfeaea3834 100644 --- a/src/admin/components/views/collections/List/index.scss +++ b/src/admin/components/views/collections/List/index.scss @@ -37,6 +37,12 @@ table { width: 100%; overflow: auto; + + #heading-_select, + .cell-_select { + min-width: unset; + width: auto; + } } } @@ -55,31 +61,40 @@ margin-left: auto; } - &__shimmer { - margin-top: base(1.75); - } + &__list-selection { + position: fixed; + bottom: 0; + z-index: 10; + padding: base(.75) 0; + width: 100%; + background-color: var(--theme-bg); - &__shimmer--rows { - >div { - margin-top: 8px; + .btn { + margin: 0 0 0 base(.5); + } + + .btn { + background-color: var(--theme-elevation-100); + cursor: pointer; + padding: 0 base(.25); + border-radius: $style-radius-s; + + &:hover { + background-color: var(--theme-elevation-200); + } } } - &__shimmer--uploads { - // match upload cards - margin: base(2) -#{base(.5)}; - width: calc(100% + #{$baseline}); + &__list-selection-actions { display: flex; - flex-wrap: wrap; + gap: base(.25); + } + &__shimmer { + margin-top: base(1.75); + width: 100%; >div { - min-width: 0; - width: calc(16.66%); - - >div { - margin: base(.5); - padding-bottom: 110%; - } + margin-top: 8px; } } @@ -111,19 +126,9 @@ width: 100%; margin-bottom: $baseline; } - - &__shimmer--uploads { - >div { - width: 33.33%; - } - } } @include small-break { - &__shimmer--uploads { - >div { - width: 50%; - } - } + margin-bottom: base(3); } } diff --git a/src/admin/components/views/collections/List/index.tsx b/src/admin/components/views/collections/List/index.tsx index 2fd667c308..5ea89e5174 100644 --- a/src/admin/components/views/collections/List/index.tsx +++ b/src/admin/components/views/collections/List/index.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useState } from 'react'; +import { v4 as uuid } from 'uuid'; +import React, { useEffect, useState, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; import queryString from 'qs'; import { useTranslation } from 'react-i18next'; @@ -9,10 +10,11 @@ import DefaultList from './Default'; import RenderCustomComponent from '../../../utilities/RenderCustomComponent'; import { useStepNav } from '../../../elements/StepNav'; import formatFields from './formatFields'; -import { ListIndexProps, ListPreferences } from './types'; +import { Props, ListIndexProps, ListPreferences } from './types'; import { usePreferences } from '../../../utilities/Preferences'; import { useSearchParams } from '../../../utilities/SearchParams'; -import { Field } from '../../../../../fields/config/types'; +import { TableColumnsProvider } from '../../../elements/TableColumns'; +import type { Field } from '../../../../../fields/config/types'; const ListView: React.FC = (props) => { const { @@ -48,7 +50,7 @@ const ListView: React.FC = (props) => { const collectionPermissions = permissions?.collections?.[slug]; const hasCreatePermission = collectionPermissions?.create?.permission; const newDocumentURL = `${admin}/collections/${slug}/create`; - const [{ data }, { setParams: setFetchParams }] = usePayloadAPI(fetchURL, { initialParams: { page: 1 } }); + const [{ data }, { setParams }] = usePayloadAPI(fetchURL, { initialParams: { page: 1 } }); useEffect(() => { setStepNav([ @@ -62,27 +64,31 @@ const ListView: React.FC = (props) => { // Set up Payload REST API query params // ///////////////////////////////////// - useEffect(() => { - const params = { + const resetParams = useCallback((overrides = {}) => { + const params: Record = { depth: 0, draft: 'true', - page: undefined, - sort: undefined, - where: undefined, + page: overrides?.page, + sort: overrides?.sort, + where: overrides?.where, limit, }; if (page) params.page = page; if (sort) params.sort = sort; if (where) params.where = where; + params.invoke = uuid(); + setParams(params); + }, [limit, page, setParams, sort, where]); + + useEffect(() => { // Performance enhancement // Setting the Fetch URL this way // prevents a double-fetch setFetchURL(`${serverURL}${api}/${slug}`); - - setFetchParams(params); - }, [setFetchParams, page, sort, where, collection, limit, serverURL, api, slug]); + resetParams(); + }, [api, resetParams, serverURL, slug]); // ///////////////////////////////////// // Fetch preferences on first load @@ -128,18 +134,41 @@ const ListView: React.FC = (props) => { })(); }, [sort, limit, preferenceKey, setPreference, getPreference]); + // ///////////////////////////////////// + // Prevent going beyond page limit + // ///////////////////////////////////// + + useEffect(() => { + if (data?.totalDocs && data.pagingCounter > data.totalDocs) { + const params = queryString.parse(history.location.search, { + ignoreQueryPrefix: true, + depth: 0, + }); + const newSearchQuery = queryString.stringify({ + ...params, + page: data.totalPages, + }, { addQueryPrefix: true }); + history.replace({ + search: newSearchQuery, + }); + } + }, [data, history, resetParams]); + return ( - + + + ); }; diff --git a/src/admin/components/views/collections/List/types.ts b/src/admin/components/views/collections/List/types.ts index 76e3b0991c..01c4cfe312 100644 --- a/src/admin/components/views/collections/List/types.ts +++ b/src/admin/components/views/collections/List/types.ts @@ -1,3 +1,4 @@ +import { Where } from '../../../../../types'; import { SanitizedCollectionConfig } from '../../../../../collections/config/types'; import { PaginatedDocs } from '../../../../../mongoose/types'; import { Props as ListControlsProps } from '../../../elements/ListControls/types'; @@ -11,15 +12,16 @@ export type Props = { setListControls: (controls: unknown) => void setSort: (sort: string) => void toggleColumn: (column: string) => void + resetParams: (overrides?: { page?: number, sort?: string, where?: Where }) => void hasCreatePermission: boolean setLimit: (limit: number) => void limit: number disableEyebrow?: boolean modifySearchParams?: boolean onCardClick?: (doc: any) => void - disableCardLink?: boolean handleSortChange?: ListControlsProps['handleSortChange'] handleWhereChange?: ListControlsProps['handleWhereChange'] + handleDelete?: () => void handlePageChange?: PaginatorProps['onChange'] handlePerPageChange?: PerPageProps['handleChange'] onCreateNewClick?: () => void diff --git a/src/collections/buildEndpoints.ts b/src/collections/buildEndpoints.ts index 0b5dcfcfc0..ab53035dfa 100644 --- a/src/collections/buildEndpoints.ts +++ b/src/collections/buildEndpoints.ts @@ -16,9 +16,11 @@ import findVersionByID from './requestHandlers/findVersionByID'; import restoreVersion from './requestHandlers/restoreVersion'; import deleteHandler from './requestHandlers/delete'; import findByID from './requestHandlers/findByID'; -import update, { deprecatedUpdate } from './requestHandlers/update'; +import update from './requestHandlers/update'; +import updateByID, { deprecatedUpdate } from './requestHandlers/updateByID'; import logoutHandler from '../auth/requestHandlers/logout'; import docAccessRequestHandler from './requestHandlers/docAccess'; +import deleteByID from './requestHandlers/deleteByID'; const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => { let { endpoints } = collection; @@ -131,10 +133,15 @@ const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => { handler: deprecatedUpdate, }, { - path: '/:id', + path: '/', method: 'patch', handler: update, }, + { + path: '/:id', + method: 'patch', + handler: updateByID, + }, { path: '/:id', method: 'get', @@ -143,6 +150,11 @@ const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => { { path: '/:id', method: 'delete', + handler: deleteByID, + }, + { + path: '/', + method: 'delete', handler: deleteHandler, }, ]); diff --git a/src/collections/config/types.ts b/src/collections/config/types.ts index 0bf7324ed6..0cf4304b38 100644 --- a/src/collections/config/types.ts +++ b/src/collections/config/types.ts @@ -9,6 +9,7 @@ import { PayloadRequest } from '../../express/types'; import { IncomingAuthType, Auth } from '../../auth/types'; import { IncomingUploadType, Upload } from '../../uploads/types'; import { IncomingCollectionVersions, SanitizedCollectionVersions } from '../../versions/types'; +import { Config as GeneratedTypes } from '../../generated-types'; type Register = (doc: T, password: string) => T; @@ -101,13 +102,13 @@ export type AfterReadHook = (args: { export type BeforeDeleteHook = (args: { req: PayloadRequest; - id: string; + id: string | number; }) => any; export type AfterDeleteHook = (args: { doc: T; req: PayloadRequest; - id: string; + id: string | number; }) => any; export type AfterErrorHook = (err: Error, res: unknown) => { response: any, status: number } | void; @@ -320,6 +321,14 @@ export type Collection = { } }; +export type BulkOperationResult = { + docs: GeneratedTypes['collections'][TSlug][], + errors: { + message: string + id: GeneratedTypes['collections'][TSlug]['id'] + }[] +} + export type AuthCollection = { Model: AuthCollectionModel; config: SanitizedCollectionConfig; diff --git a/src/collections/graphql/resolvers/delete.ts b/src/collections/graphql/resolvers/delete.ts index 09709ca912..d3fbde7d2b 100644 --- a/src/collections/graphql/resolvers/delete.ts +++ b/src/collections/graphql/resolvers/delete.ts @@ -3,7 +3,7 @@ import { Config as GeneratedTypes } from 'payload/generated-types'; import { Response } from 'express'; import { PayloadRequest } from '../../../express/types'; import { Collection } from '../../config/types'; -import deleteOperation from '../../operations/delete'; +import deleteByID from '../../operations/deleteByID'; export type Resolver = ( _: unknown, @@ -31,7 +31,7 @@ export default function getDeleteResolver = (_: unknown, args: { @@ -35,7 +35,7 @@ export default function updateResolver(options); + const result = await updateByID(options); return result; } diff --git a/src/collections/operations/delete.ts b/src/collections/operations/delete.ts index aba87898ae..26110ad321 100644 --- a/src/collections/operations/delete.ts +++ b/src/collections/operations/delete.ts @@ -1,23 +1,21 @@ -import fs from 'fs'; -import path from 'path'; - import { Config as GeneratedTypes } from 'payload/generated-types'; +import httpStatus from 'http-status'; +import { AccessResult } from '../../config/types'; import { PayloadRequest } from '../../express/types'; import sanitizeInternalFields from '../../utilities/sanitizeInternalFields'; -import { NotFound, Forbidden, ErrorDeletingFile } from '../../errors'; +import { APIError } from '../../errors'; import executeAccess from '../../auth/executeAccess'; import { BeforeOperationHook, Collection } from '../config/types'; -import { Document, Where } from '../../types'; +import { Where } from '../../types'; import { hasWhereAccessResult } from '../../auth/types'; -import { FileData } from '../../uploads/types'; -import fileExists from '../../uploads/fileExists'; import { afterRead } from '../../fields/hooks/afterRead'; import { deleteCollectionVersions } from '../../versions/deleteCollectionVersions'; +import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'; export type Arguments = { depth?: number collection: Collection - id: string + where: Where req: PayloadRequest overrideAccess?: boolean showHiddenFields?: boolean @@ -25,7 +23,13 @@ export type Arguments = { async function deleteOperation( incomingArgs: Arguments, -): Promise { +): Promise<{ + docs: GeneratedTypes['collections'][TSlug][], + errors: { + message: string + id: GeneratedTypes['collections'][TSlug]['id'] + }[] +}> { let args = incomingArgs; // ///////////////////////////////////// @@ -47,7 +51,7 @@ async function deleteOperation { - await priorHook; - - return hook({ - req, - id, - }); - }, Promise.resolve()); - - // ///////////////////////////////////// - // Retrieve document - // ///////////////////////////////////// - - const queryToBuild: { - where: Where - } = { + const queryToBuild: { where?: Where } = { where: { - and: [ - { - id: { - equals: id, - }, - }, - ], + and: [], }, }; - if (hasWhereAccessResult(accessResults)) { - (queryToBuild.where.and as Where[]).push(accessResults); + if (where) { + queryToBuild.where = { + and: [], + ...where, + }; + + if (Array.isArray(where.AND)) { + queryToBuild.where.and = [ + ...queryToBuild.where.and, + ...where.AND, + ]; + } + } + + let accessResult: AccessResult; + + if (!overrideAccess) { + accessResult = await executeAccess({ req }, collectionConfig.access.delete); + + if (hasWhereAccessResult(accessResult)) { + queryToBuild.where.and.push(accessResult); + } } const query = await Model.buildQuery(queryToBuild, locale); - const docToDelete = await Model.findOne(query); - - if (!docToDelete && !hasWhereAccess) throw new NotFound(t); - if (!docToDelete && hasWhereAccess) throw new Forbidden(t); - - const resultToDelete = docToDelete.toJSON({ virtuals: true }); - // ///////////////////////////////////// - // Delete any associated files + // Retrieve documents // ///////////////////////////////////// - if (collectionConfig.upload) { - const { staticDir } = collectionConfig.upload; + const docs = await Model.find(query, {}, { lean: true }); - const staticPath = path.resolve(config.paths.configDir, staticDir); + const errors = []; - const fileToDelete = `${staticPath}/${resultToDelete.filename}`; + /* eslint-disable no-param-reassign */ + const promises = docs.map(async (doc) => { + let result; - if (await fileExists(fileToDelete)) { - fs.unlink(fileToDelete, (err) => { - if (err) { - throw new ErrorDeletingFile(t); - } + // custom id type reset + doc.id = doc._id; + doc = JSON.stringify(doc); + doc = JSON.parse(doc); + doc = sanitizeInternalFields(doc); + + const { id } = doc; + + try { + // ///////////////////////////////////// + // beforeDelete - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.beforeDelete.reduce(async (priorHook, hook) => { + await priorHook; + + return hook({ + req, + id, + }); + }, Promise.resolve()); + + await deleteAssociatedFiles({ config, collectionConfig, doc, t, overrideDelete: true }); + + // ///////////////////////////////////// + // Delete document + // ///////////////////////////////////// + + await Model.deleteOne({ _id: id }, { lean: true }); + + // ///////////////////////////////////// + // Delete versions + // ///////////////////////////////////// + + if (collectionConfig.versions) { + deleteCollectionVersions({ + payload, + id, + slug: collectionConfig.slug, + }); + } + + // ///////////////////////////////////// + // afterDelete - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.afterDelete.reduce(async (priorHook, hook) => { + await priorHook; + + result = await hook({ + req, + id, + doc, + }) || doc; + }, Promise.resolve()); + + + // ///////////////////////////////////// + // afterRead - Fields + // ///////////////////////////////////// + + result = await afterRead({ + depth, + doc: result || doc, + entityConfig: collectionConfig, + overrideAccess, + req, + showHiddenFields, + }); + + // ///////////////////////////////////// + // afterRead - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => { + await priorHook; + + result = await hook({ + req, + doc: result || doc, + }) || result; + }, Promise.resolve()); + + // ///////////////////////////////////// + // 8. Return results + // ///////////////////////////////////// + + return result; + } catch (error) { + errors.push({ + message: error.message, + id: doc.id, }); } + return null; + }); - if (resultToDelete.sizes) { - Object.values(resultToDelete.sizes).forEach(async (size: FileData) => { - const sizeToDelete = `${staticPath}/${size.filename}`; - if (await fileExists(sizeToDelete)) { - fs.unlink(sizeToDelete, (err) => { - if (err) { - throw new ErrorDeletingFile(t); - } - }); - } - }); - } - } - - // ///////////////////////////////////// - // Delete document - // ///////////////////////////////////// - - const doc = await Model.findOneAndDelete({ _id: id }); - - let result: Document = doc.toJSON({ virtuals: true }); - - // custom id type reset - result.id = result._id; - result = JSON.stringify(result); - result = JSON.parse(result); - result = sanitizeInternalFields(result); + const awaitedDocs = await Promise.all(promises); // ///////////////////////////////////// // Delete Preferences // ///////////////////////////////////// if (collectionConfig.auth) { - await preferences.Model.deleteMany({ user: id, userCollection: collectionConfig.slug }); - } - await preferences.Model.deleteMany({ key: `collection-${collectionConfig.slug}-${id}` }); - - // ///////////////////////////////////// - // Delete versions - // ///////////////////////////////////// - - if (collectionConfig.versions) { - deleteCollectionVersions({ - payload, - id, - slug: collectionConfig.slug, + preferences.Model.deleteMany({ + user: { in: docs.map(({ id }) => id) }, + userCollection: collectionConfig.slug, }); } + preferences.Model.deleteMany({ key: { in: docs.map(({ id }) => `collection-${collectionConfig.slug}-${id}`) } }); - // ///////////////////////////////////// - // afterDelete - Collection - // ///////////////////////////////////// - - await collectionConfig.hooks.afterDelete.reduce(async (priorHook, hook) => { - await priorHook; - - result = await hook({ req, id, doc: result }) || result; - }, Promise.resolve()); - - // ///////////////////////////////////// - // afterRead - Fields - // ///////////////////////////////////// - - result = await afterRead({ - depth, - doc: result, - entityConfig: collectionConfig, - overrideAccess, - req, - showHiddenFields, - }); - - // ///////////////////////////////////// - // afterRead - Collection - // ///////////////////////////////////// - - await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => { - await priorHook; - - result = await hook({ - req, - doc: result, - }) || result; - }, Promise.resolve()); - - // ///////////////////////////////////// - // 8. Return results - // ///////////////////////////////////// - - return result; + return { + docs: awaitedDocs.filter(Boolean), + errors, + }; } export default deleteOperation; diff --git a/src/collections/operations/deleteByID.ts b/src/collections/operations/deleteByID.ts new file mode 100644 index 0000000000..392481760e --- /dev/null +++ b/src/collections/operations/deleteByID.ts @@ -0,0 +1,191 @@ +import { Config as GeneratedTypes } from 'payload/generated-types'; +import { PayloadRequest } from '../../express/types'; +import sanitizeInternalFields from '../../utilities/sanitizeInternalFields'; +import { NotFound, Forbidden } from '../../errors'; +import executeAccess from '../../auth/executeAccess'; +import { BeforeOperationHook, Collection } from '../config/types'; +import { Document, Where } from '../../types'; +import { hasWhereAccessResult } from '../../auth/types'; +import { afterRead } from '../../fields/hooks/afterRead'; +import { deleteCollectionVersions } from '../../versions/deleteCollectionVersions'; +import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'; + +export type Arguments = { + depth?: number + collection: Collection + id: string | number + req: PayloadRequest + overrideAccess?: boolean + showHiddenFields?: boolean +} + +async function deleteByID(incomingArgs: Arguments): Promise { + let args = incomingArgs; + + // ///////////////////////////////////// + // beforeOperation - Collection + // ///////////////////////////////////// + + await args.collection.config.hooks.beforeOperation.reduce(async (priorHook: BeforeOperationHook | Promise, hook: BeforeOperationHook) => { + await priorHook; + + args = (await hook({ + args, + operation: 'delete', + })) || args; + }, Promise.resolve()); + + const { + depth, + collection: { + Model, + config: collectionConfig, + }, + id, + req, + req: { + t, + locale, + payload, + payload: { + config, + preferences, + }, + }, + overrideAccess, + showHiddenFields, + } = args; + + // ///////////////////////////////////// + // Access + // ///////////////////////////////////// + + const accessResults = !overrideAccess ? await executeAccess({ req, id }, collectionConfig.access.delete) : true; + const hasWhereAccess = hasWhereAccessResult(accessResults); + + // ///////////////////////////////////// + // beforeDelete - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.beforeDelete.reduce(async (priorHook, hook) => { + await priorHook; + + return hook({ + req, + id, + }); + }, Promise.resolve()); + + // ///////////////////////////////////// + // Retrieve document + // ///////////////////////////////////// + + const queryToBuild: { + where: Where + } = { + where: { + and: [ + { + id: { + equals: id, + }, + }, + ], + }, + }; + + if (hasWhereAccessResult(accessResults)) { + (queryToBuild.where.and as Where[]).push(accessResults); + } + + const query = await Model.buildQuery(queryToBuild, locale); + + const docToDelete = await Model.findOne(query); + + if (!docToDelete && !hasWhereAccess) throw new NotFound(t); + if (!docToDelete && hasWhereAccess) throw new Forbidden(t); + + const resultToDelete = docToDelete.toJSON({ virtuals: true }); + + await deleteAssociatedFiles({ config, collectionConfig, doc: resultToDelete, t, overrideDelete: true }); + + // ///////////////////////////////////// + // Delete document + // ///////////////////////////////////// + + const doc = await Model.findOneAndDelete({ _id: id }); + + let result: Document = doc.toJSON({ virtuals: true }); + + // custom id type reset + result.id = result._id; + result = JSON.stringify(result); + result = JSON.parse(result); + result = sanitizeInternalFields(result); + + // ///////////////////////////////////// + // Delete Preferences + // ///////////////////////////////////// + + if (collectionConfig.auth) { + await preferences.Model.deleteMany({ user: id, userCollection: collectionConfig.slug }); + } + await preferences.Model.deleteMany({ key: `collection-${collectionConfig.slug}-${id}` }); + + // ///////////////////////////////////// + // Delete versions + // ///////////////////////////////////// + + if (collectionConfig.versions) { + deleteCollectionVersions({ + payload, + id, + slug: collectionConfig.slug, + }); + } + + // ///////////////////////////////////// + // afterDelete - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.afterDelete.reduce(async (priorHook, hook) => { + await priorHook; + + result = await hook({ req, id, doc: result }) || result; + }, Promise.resolve()); + + + // ///////////////////////////////////// + // afterRead - Fields + // ///////////////////////////////////// + + result = await afterRead({ + depth, + doc: result, + entityConfig: collectionConfig, + overrideAccess, + req, + showHiddenFields, + }); + + // ///////////////////////////////////// + // afterRead - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => { + await priorHook; + + result = await hook({ + req, + doc: result, + }) || result; + }, Promise.resolve()); + + // ///////////////////////////////////// + // 8. Return results + // ///////////////////////////////////// + + return result; +} + +export default deleteByID; diff --git a/src/collections/operations/local/delete.ts b/src/collections/operations/local/delete.ts index 9e80388e6e..5011e1a8ec 100644 --- a/src/collections/operations/local/delete.ts +++ b/src/collections/operations/local/delete.ts @@ -1,15 +1,17 @@ -import { Config as GeneratedTypes } from 'payload/generated-types'; -import { Document } from '../../../types'; +import { Config as GeneratedTypes } from '../../../generated-types'; +import { Document, Where } from '../../../types'; import { PayloadRequest } from '../../../express/types'; import { Payload } from '../../../payload'; import deleteOperation from '../delete'; +import deleteByID from '../deleteByID'; import { getDataLoader } from '../../dataloader'; import i18n from '../../../translations/init'; import { APIError } from '../../../errors'; +import { BulkOperationResult } from '../../config/types'; -export type Options = { +export type BaseOptions = { collection: T - id: string + id: string | number depth?: number locale?: string fallbackLocale?: string @@ -18,14 +20,26 @@ export type Options = { showHiddenFields?: boolean } -export default async function deleteLocal( - payload: Payload, - options: Options, -): Promise { +export type ByIDOptions = BaseOptions & { + where?: never +} + +export type ManyOptions = BaseOptions & { + where: Where + id?: never +} + +export type Options = ByIDOptions | ManyOptions + +async function deleteLocal(payload: Payload, options: ByIDOptions): Promise +async function deleteLocal(payload: Payload, options: ManyOptions): Promise> +async function deleteLocal(payload: Payload, options: Options): Promise> +async function deleteLocal(payload: Payload, options: Options): Promise> { const { collection: collectionSlug, depth, id, + where, locale = null, fallbackLocale = null, user, @@ -53,12 +67,20 @@ export default async function deleteLocal({ + const args = { depth, id, + where, collection, overrideAccess, showHiddenFields, req, - }); + }; + + if (options.id) { + return deleteByID(args); + } + return deleteOperation(args); } + +export default deleteLocal; diff --git a/src/collections/operations/local/index.ts b/src/collections/operations/local/index.ts index c6cc8fe5f4..b9f4a432fe 100644 --- a/src/collections/operations/local/index.ts +++ b/src/collections/operations/local/index.ts @@ -1,18 +1,32 @@ +import { Config as GeneratedTypes } from 'payload/generated-types'; import find from './find'; import findByID from './findByID'; import create from './create'; -import update from './update'; -import localDelete from './delete'; +import update, { ByIDOptions as UpdateByIDOptions, ManyOptions as UpdateManyOptions, Options as UpdateOptions } from './update'; +import deleteLocal, { ByIDOptions as DeleteByIDOptions, ManyOptions as DeleteManyOptions, Options as DeleteOptions } from './delete'; import auth from '../../../auth/operations/local'; import findVersionByID from './findVersionByID'; import findVersions from './findVersions'; import restoreVersion from './restoreVersion'; +import { BulkOperationResult } from '../../config/types'; + +async function localUpdate (options: UpdateByIDOptions): Promise +async function localUpdate (options: UpdateManyOptions): Promise> +async function localUpdate (options: UpdateOptions): Promise> { + return update(this, options); +} + +async function localDelete (options: DeleteByIDOptions): Promise +async function localDelete (options: DeleteManyOptions): Promise> +async function localDelete (options: DeleteOptions): Promise> { + return deleteLocal(this, options); +} export default { find, findByID, create, - update, + update: localUpdate, localDelete, auth, findVersionByID, diff --git a/src/collections/operations/local/update.ts b/src/collections/operations/local/update.ts index 979a56c3ef..2c5cc4e380 100644 --- a/src/collections/operations/local/update.ts +++ b/src/collections/operations/local/update.ts @@ -1,6 +1,6 @@ import { Config as GeneratedTypes } from 'payload/generated-types'; import { Payload } from '../../../payload'; -import { Document } from '../../../types'; +import { Document, Where } from '../../../types'; import getFileByPath from '../../../uploads/getFileByPath'; import update from '../update'; import { PayloadRequest } from '../../../express/types'; @@ -8,10 +8,11 @@ import { getDataLoader } from '../../dataloader'; import { File } from '../../../uploads/types'; import i18nInit from '../../../translations/init'; import { APIError } from '../../../errors'; +import updateByID from '../updateByID'; +import { BulkOperationResult } from '../../config/types'; -export type Options = { +export type BaseOptions = { collection: TSlug - id: string | number data: Partial depth?: number locale?: string @@ -26,17 +27,28 @@ export type Options = { autosave?: boolean } -export default async function updateLocal( - payload: Payload, - options: Options, -): Promise { +export type ByIDOptions = BaseOptions & { + id: string | number + where?: never +} + +export type ManyOptions = BaseOptions & { + where: Where + id?: never +} + +export type Options = ByIDOptions | ManyOptions + +async function updateLocal(payload: Payload, options: ByIDOptions): Promise +async function updateLocal(payload: Payload, options: ManyOptions): Promise> +async function updateLocal(payload: Payload, options: Options): Promise> +async function updateLocal(payload: Payload, options: Options): Promise> { const { collection: collectionSlug, depth, locale = null, fallbackLocale = null, data, - id, user, overrideAccess = true, showHiddenFields, @@ -45,6 +57,8 @@ export default async function updateLocal(args); + } return update(args); } + +export default updateLocal; diff --git a/src/collections/operations/update.ts b/src/collections/operations/update.ts index 9da2fedcfe..7828900d33 100644 --- a/src/collections/operations/update.ts +++ b/src/collections/operations/update.ts @@ -1,13 +1,10 @@ -import fs from 'fs'; -import { promisify } from 'util'; -import path from 'path'; import httpStatus from 'http-status'; import { Config as GeneratedTypes } from 'payload/generated-types'; -import { Where, Document } from '../../types'; -import { Collection } from '../config/types'; +import { Document, Where } from '../../types'; +import { BulkOperationResult, Collection } from '../config/types'; import sanitizeInternalFields from '../../utilities/sanitizeInternalFields'; import executeAccess from '../../auth/executeAccess'; -import { NotFound, Forbidden, APIError, ValidationError, ErrorDeletingFile } from '../../errors'; +import { APIError, ValidationError } from '../../errors'; import { PayloadRequest } from '../../express/types'; import { hasWhereAccessResult } from '../../auth/types'; import { saveVersion } from '../../versions/saveVersion'; @@ -17,17 +14,15 @@ import { beforeValidate } from '../../fields/hooks/beforeValidate'; import { afterChange } from '../../fields/hooks/afterChange'; import { afterRead } from '../../fields/hooks/afterRead'; import { generateFileData } from '../../uploads/generateFileData'; -import { getLatestCollectionVersion } from '../../versions/getLatestCollectionVersion'; -import { mapAsync } from '../../utilities/mapAsync'; -import fileExists from '../../uploads/fileExists'; -import { FileData } from '../../uploads/types'; - -const unlinkFile = promisify(fs.unlink); +import { AccessResult } from '../../config/types'; +import { queryDrafts } from '../../versions/drafts/queryDrafts'; +import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'; +import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'; export type Arguments = { collection: Collection req: PayloadRequest - id: string | number + where: Where data: Partial depth?: number disableVerificationEmail?: boolean @@ -35,12 +30,10 @@ export type Arguments showHiddenFields?: boolean overwriteExistingFiles?: boolean draft?: boolean - autosave?: boolean } - async function update( incomingArgs: Arguments, -): Promise { +): Promise> { let args = incomingArgs; // ///////////////////////////////////// @@ -63,7 +56,7 @@ async function update( Model, config: collectionConfig, }, - id, + where, req, req: { t, @@ -77,77 +70,76 @@ async function update( showHiddenFields, overwriteExistingFiles = false, draft: draftArg = false, - autosave = false, } = args; - if (!id) { - throw new APIError('Missing ID of document to update.', httpStatus.BAD_REQUEST); + if (!where) { + throw new APIError('Missing \'where\' query of documents to update.', httpStatus.BAD_REQUEST); } let { data } = args; - const { password } = data; const shouldSaveDraft = Boolean(draftArg && collectionConfig.versions.drafts); - const shouldSavePassword = Boolean(password && collectionConfig.auth && !shouldSaveDraft); - const lean = !shouldSavePassword; // ///////////////////////////////////// // Access // ///////////////////////////////////// - const accessResults = !overrideAccess ? await executeAccess({ req, id, data }, collectionConfig.access.update) : true; - const hasWherePolicy = hasWhereAccessResult(accessResults); - - // ///////////////////////////////////// - // Retrieve document - // ///////////////////////////////////// - - const queryToBuild: { where: Where } = { + const queryToBuild: { where?: Where } = { where: { - and: [ - { - id: { - equals: id, - }, - }, - ], + and: [], }, }; - if (hasWhereAccessResult(accessResults)) { - (queryToBuild.where.and as Where[]).push(accessResults); + if (where) { + queryToBuild.where = { + and: [], + ...where, + }; + + if (Array.isArray(where.AND)) { + queryToBuild.where.and = [ + ...queryToBuild.where.and, + ...where.AND, + ]; + } + } + + let accessResult: AccessResult; + + if (!overrideAccess) { + accessResult = await executeAccess({ req }, collectionConfig.access.update); + + if (hasWhereAccessResult(accessResult)) { + queryToBuild.where.and.push(accessResult); + } } const query = await Model.buildQuery(queryToBuild, locale); - const doc = await getLatestCollectionVersion({ - payload, - Model, - config: collectionConfig, - id, - query, - lean, - }); + // ///////////////////////////////////// + // Retrieve documents + // ///////////////////////////////////// + let docs; - if (!doc && !hasWherePolicy) throw new NotFound(t); - if (!doc && hasWherePolicy) throw new Forbidden(t); - - let docWithLocales: Document = JSON.stringify(lean ? doc : doc.toJSON({ virtuals: true })); - docWithLocales = JSON.parse(docWithLocales); - - const originalDoc = await afterRead({ - depth: 0, - doc: docWithLocales, - entityConfig: collectionConfig, - req, - overrideAccess: true, - showHiddenFields: true, - }); + if (collectionConfig.versions?.drafts && shouldSaveDraft) { + docs = await queryDrafts({ + accessResult, + collection, + locale, + payload, + where: query, + }); + } else { + docs = await Model.find(query, {}, { lean: true }); + } // ///////////////////////////////////// // Generate data for all files and sizes // ///////////////////////////////////// - const { data: newFileData, files: filesToUpload } = await generateFileData({ + const { + data: newFileData, + files: filesToUpload, + } = await generateFileData({ config, collection, req, @@ -158,229 +150,217 @@ async function update( data = newFileData; - // ///////////////////////////////////// - // beforeValidate - Fields - // ///////////////////////////////////// + const errors = []; - data = await beforeValidate({ - data, - doc: originalDoc, - entityConfig: collectionConfig, - id, - operation: 'update', - overrideAccess, - req, - }); + const promises = docs.map(async (doc) => { + let docWithLocales: Document = JSON.stringify(doc); + docWithLocales = JSON.parse(docWithLocales); - // ///////////////////////////////////// - // beforeValidate - Collection - // ///////////////////////////////////// + const id = docWithLocales._id; - await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => { - await priorHook; - - data = (await hook({ - data, - req, - operation: 'update', - originalDoc, - })) || data; - }, Promise.resolve()); - - // ///////////////////////////////////// - // Write files to local storage - // ///////////////////////////////////// - - if (!collectionConfig.upload.disableLocalStorage) { - await uploadFiles(payload, filesToUpload, t); - } - - // ///////////////////////////////////// - // beforeChange - Collection - // ///////////////////////////////////// - - await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => { - await priorHook; - - data = (await hook({ - data, - req, - originalDoc, - operation: 'update', - })) || data; - }, Promise.resolve()); - - // ///////////////////////////////////// - // beforeChange - Fields - // ///////////////////////////////////// - - let result = await beforeChange({ - data, - doc: originalDoc, - docWithLocales, - entityConfig: collectionConfig, - id, - operation: 'update', - req, - skipValidation: shouldSaveDraft || data._status === 'draft', - }); - - // ///////////////////////////////////// - // Handle potential password update - // ///////////////////////////////////// - - if (shouldSavePassword) { - await doc.setPassword(password); - await doc.save(); - delete data.password; - delete result.password; - } - - // ///////////////////////////////////// - // Update - // ///////////////////////////////////// - - if (!shouldSaveDraft) { try { - result = await Model.findByIdAndUpdate( - { _id: id }, - result, - { new: true }, - ); - } catch (error) { - // Handle uniqueness error from MongoDB - throw error.code === 11000 && error.keyValue - ? new ValidationError([{ message: 'Value must be unique', field: Object.keys(error.keyValue)[0] }], t) - : error; - } - } - - result = JSON.parse(JSON.stringify(result)); - result.id = result._id as string | number; - result = sanitizeInternalFields(result); - - // ///////////////////////////////////// - // Delete any associated files - // ///////////////////////////////////// - - if (collectionConfig.upload && !overwriteExistingFiles && filesToUpload && filesToUpload.length > 0) { - const { staticDir } = collectionConfig.upload; - - const staticPath = path.resolve(config.paths.configDir, staticDir); - - const fileToDelete = `${staticPath}/${doc.filename}`; - - if (await fileExists(fileToDelete)) { - fs.unlink(fileToDelete, (err) => { - if (err) { - throw new ErrorDeletingFile(t); - } + const originalDoc = await afterRead({ + depth: 0, + doc: docWithLocales, + entityConfig: collectionConfig, + req, + overrideAccess: true, + showHiddenFields: true, }); - } - if (doc.sizes) { - Object.values(doc.sizes).forEach(async (size: FileData) => { - const sizeToDelete = `${staticPath}/${size.filename}`; - if (await fileExists(sizeToDelete)) { - fs.unlink(sizeToDelete, (err) => { - if (err) { - throw new ErrorDeletingFile(t); - } - }); - } + await deleteAssociatedFiles({ config, collectionConfig, files: filesToUpload, doc: docWithLocales, t, overrideDelete: false }); + + // ///////////////////////////////////// + // beforeValidate - Fields + // ///////////////////////////////////// + + data = await beforeValidate({ + data, + doc: originalDoc, + entityConfig: collectionConfig, + id, + operation: 'update', + overrideAccess, + req, }); - } - } - // ///////////////////////////////////// - // Create version - // ///////////////////////////////////// + // ///////////////////////////////////// + // beforeValidate - Collection + // ///////////////////////////////////// - if (collectionConfig.versions) { - result = await saveVersion({ - payload, - collection: collectionConfig, - req, - docWithLocales: { - ...result, - createdAt: docWithLocales.createdAt, - }, - id, - autosave, - draft: shouldSaveDraft, - }); - } + await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => { + await priorHook; - // ///////////////////////////////////// - // afterRead - Fields - // ///////////////////////////////////// + data = (await hook({ + data, + req, + operation: 'update', + originalDoc, + })) || data; + }, Promise.resolve()); - result = await afterRead({ - depth, - doc: result, - entityConfig: collectionConfig, - req, - overrideAccess, - showHiddenFields, - }); + // ///////////////////////////////////// + // Write files to local storage + // ///////////////////////////////////// - // ///////////////////////////////////// - // afterRead - Collection - // ///////////////////////////////////// - - await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => { - await priorHook; - - result = await hook({ - req, - doc: result, - }) || result; - }, Promise.resolve()); - - // ///////////////////////////////////// - // afterChange - Fields - // ///////////////////////////////////// - - result = await afterChange({ - data, - doc: result, - previousDoc: originalDoc, - entityConfig: collectionConfig, - operation: 'update', - req, - }); - - // ///////////////////////////////////// - // afterChange - Collection - // ///////////////////////////////////// - - await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => { - await priorHook; - - result = await hook({ - doc: result, - previousDoc: originalDoc, - req, - operation: 'update', - }) || result; - }, Promise.resolve()); - - // Remove temp files if enabled, as express-fileupload does not do this automatically - if (config.upload?.useTempFiles && collectionConfig.upload) { - const { files } = req; - const fileArray = Array.isArray(files) ? files : [files]; - await mapAsync(fileArray, async ({ file }) => { - // Still need this check because this will not be populated if using local API - if (file.tempFilePath) { - await unlinkFile(file.tempFilePath); + if (!collectionConfig.upload.disableLocalStorage) { + await uploadFiles(payload, filesToUpload, t); } - }); - } - // ///////////////////////////////////// - // Return results - // ///////////////////////////////////// - return result; + // ///////////////////////////////////// + // beforeChange - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => { + await priorHook; + + data = (await hook({ + data, + req, + originalDoc, + operation: 'update', + })) || data; + }, Promise.resolve()); + + // ///////////////////////////////////// + // beforeChange - Fields + // ///////////////////////////////////// + + let result = await beforeChange({ + data, + doc: originalDoc, + docWithLocales, + entityConfig: collectionConfig, + id, + operation: 'update', + req, + skipValidation: shouldSaveDraft || data._status === 'draft', + }); + + // ///////////////////////////////////// + // Update + // ///////////////////////////////////// + + if (!shouldSaveDraft) { + try { + result = await Model.findByIdAndUpdate( + { _id: id }, + result, + { new: true }, + ); + } catch (error) { + // Handle uniqueness error from MongoDB + throw error.code === 11000 && error.keyValue + ? new ValidationError([{ + message: 'Value must be unique', + field: Object.keys(error.keyValue)[0], + }], t) + : error; + } + } + + result = JSON.parse(JSON.stringify(result)); + result.id = result._id as string | number; + result = sanitizeInternalFields(result); + + // ///////////////////////////////////// + // Create version + // ///////////////////////////////////// + + if (collectionConfig.versions) { + result = await saveVersion({ + payload, + collection: collectionConfig, + req, + docWithLocales: { + ...result, + createdAt: docWithLocales.createdAt, + }, + id, + draft: shouldSaveDraft, + }); + } + + // ///////////////////////////////////// + // afterRead - Fields + // ///////////////////////////////////// + + result = await afterRead({ + depth, + doc: result, + entityConfig: collectionConfig, + req, + overrideAccess, + showHiddenFields, + }); + + // ///////////////////////////////////// + // afterRead - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => { + await priorHook; + + result = await hook({ + req, + doc: result, + }) || result; + }, Promise.resolve()); + + // ///////////////////////////////////// + // afterChange - Fields + // ///////////////////////////////////// + + result = await afterChange({ + data, + doc: result, + previousDoc: originalDoc, + entityConfig: collectionConfig, + operation: 'update', + req, + }); + + // ///////////////////////////////////// + // afterChange - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => { + await priorHook; + + result = await hook({ + doc: result, + previousDoc: originalDoc, + req, + operation: 'update', + }) || result; + }, Promise.resolve()); + + await unlinkTempFiles({ + req, + config, + collectionConfig, + }); + + // ///////////////////////////////////// + // Return results + // ///////////////////////////////////// + + return result; + } catch (error) { + errors.push({ + message: error.message, + id, + }); + } + return null; + }); + + const awaitedDocs = await Promise.all(promises); + + return { + docs: awaitedDocs.filter(Boolean), + errors, + }; } export default update; diff --git a/src/collections/operations/updateByID.ts b/src/collections/operations/updateByID.ts new file mode 100644 index 0000000000..b69e4c116d --- /dev/null +++ b/src/collections/operations/updateByID.ts @@ -0,0 +1,348 @@ +import httpStatus from 'http-status'; +import { Config as GeneratedTypes } from 'payload/generated-types'; +import { Where, Document } from '../../types'; +import { Collection } from '../config/types'; +import sanitizeInternalFields from '../../utilities/sanitizeInternalFields'; +import executeAccess from '../../auth/executeAccess'; +import { NotFound, Forbidden, APIError, ValidationError } from '../../errors'; +import { PayloadRequest } from '../../express/types'; +import { hasWhereAccessResult } from '../../auth/types'; +import { saveVersion } from '../../versions/saveVersion'; +import { uploadFiles } from '../../uploads/uploadFiles'; +import { beforeChange } from '../../fields/hooks/beforeChange'; +import { beforeValidate } from '../../fields/hooks/beforeValidate'; +import { afterChange } from '../../fields/hooks/afterChange'; +import { afterRead } from '../../fields/hooks/afterRead'; +import { generateFileData } from '../../uploads/generateFileData'; +import { getLatestCollectionVersion } from '../../versions/getLatestCollectionVersion'; +import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'; +import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'; + +export type Arguments = { + collection: Collection + req: PayloadRequest + id: string | number + data: Partial + depth?: number + disableVerificationEmail?: boolean + overrideAccess?: boolean + showHiddenFields?: boolean + overwriteExistingFiles?: boolean + draft?: boolean + autosave?: boolean +} + +async function updateByID( + incomingArgs: Arguments, +): Promise { + let args = incomingArgs; + + // ///////////////////////////////////// + // beforeOperation - Collection + // ///////////////////////////////////// + + await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => { + await priorHook; + + args = (await hook({ + args, + operation: 'update', + })) || args; + }, Promise.resolve()); + + const { + depth, + collection, + collection: { + Model, + config: collectionConfig, + }, + id, + req, + req: { + t, + locale, + payload, + payload: { + config, + }, + }, + overrideAccess, + showHiddenFields, + overwriteExistingFiles = false, + draft: draftArg = false, + autosave = false, + } = args; + + if (!id) { + throw new APIError('Missing ID of document to update.', httpStatus.BAD_REQUEST); + } + + let { data } = args; + const { password } = data; + const shouldSaveDraft = Boolean(draftArg && collectionConfig.versions.drafts); + const shouldSavePassword = Boolean(password && collectionConfig.auth && !shouldSaveDraft); + const lean = !shouldSavePassword; + + // ///////////////////////////////////// + // Access + // ///////////////////////////////////// + + const accessResults = !overrideAccess ? await executeAccess({ req, id, data }, collectionConfig.access.update) : true; + const hasWherePolicy = hasWhereAccessResult(accessResults); + + // ///////////////////////////////////// + // Retrieve document + // ///////////////////////////////////// + + const queryToBuild: { where: Where } = { + where: { + and: [ + { + id: { + equals: id, + }, + }, + ], + }, + }; + + if (hasWhereAccessResult(accessResults)) { + (queryToBuild.where.and as Where[]).push(accessResults); + } + + const query = await Model.buildQuery(queryToBuild, locale); + + const doc = await getLatestCollectionVersion({ + payload, + Model, + config: collectionConfig, + id, + query, + lean, + }); + + if (!doc && !hasWherePolicy) throw new NotFound(t); + if (!doc && hasWherePolicy) throw new Forbidden(t); + + let docWithLocales: Document = JSON.stringify(lean ? doc : doc.toJSON({ virtuals: true })); + docWithLocales = JSON.parse(docWithLocales); + + const originalDoc = await afterRead({ + depth: 0, + doc: docWithLocales, + entityConfig: collectionConfig, + req, + overrideAccess: true, + showHiddenFields: true, + }); + + // ///////////////////////////////////// + // Generate data for all files and sizes + // ///////////////////////////////////// + + const { data: newFileData, files: filesToUpload } = await generateFileData({ + config, + collection, + req, + data, + throwOnMissingFile: false, + overwriteExistingFiles, + }); + + data = newFileData; + + // ///////////////////////////////////// + // Delete any associated files + // ///////////////////////////////////// + + await deleteAssociatedFiles({ config, collectionConfig, files: filesToUpload, doc, t, overrideDelete: false }); + + // ///////////////////////////////////// + // beforeValidate - Fields + // ///////////////////////////////////// + + data = await beforeValidate({ + data, + doc: originalDoc, + entityConfig: collectionConfig, + id, + operation: 'update', + overrideAccess, + req, + }); + + // ///////////////////////////////////// + // beforeValidate - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => { + await priorHook; + + data = (await hook({ + data, + req, + operation: 'update', + originalDoc, + })) || data; + }, Promise.resolve()); + + // ///////////////////////////////////// + // Write files to local storage + // ///////////////////////////////////// + + if (!collectionConfig.upload.disableLocalStorage) { + await uploadFiles(payload, filesToUpload, t); + } + + // ///////////////////////////////////// + // beforeChange - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => { + await priorHook; + + data = (await hook({ + data, + req, + originalDoc, + operation: 'update', + })) || data; + }, Promise.resolve()); + + // ///////////////////////////////////// + // beforeChange - Fields + // ///////////////////////////////////// + + let result = await beforeChange({ + data, + doc: originalDoc, + docWithLocales, + entityConfig: collectionConfig, + id, + operation: 'update', + req, + skipValidation: shouldSaveDraft || data._status === 'draft', + }); + + // ///////////////////////////////////// + // Handle potential password update + // ///////////////////////////////////// + + if (shouldSavePassword) { + await doc.setPassword(password); + await doc.save(); + delete data.password; + delete result.password; + } + + // ///////////////////////////////////// + // Update + // ///////////////////////////////////// + + if (!shouldSaveDraft) { + try { + result = await Model.findByIdAndUpdate( + { _id: id }, + result, + { new: true }, + ); + } catch (error) { + // Handle uniqueness error from MongoDB + throw error.code === 11000 && error.keyValue + ? new ValidationError([{ message: 'Value must be unique', field: Object.keys(error.keyValue)[0] }], t) + : error; + } + } + + result = JSON.parse(JSON.stringify(result)); + result.id = result._id as string | number; + result = sanitizeInternalFields(result); + + // ///////////////////////////////////// + // Create version + // ///////////////////////////////////// + + if (collectionConfig.versions) { + result = await saveVersion({ + payload, + collection: collectionConfig, + req, + docWithLocales: { + ...result, + createdAt: docWithLocales.createdAt, + }, + id, + autosave, + draft: shouldSaveDraft, + }); + } + + // ///////////////////////////////////// + // afterRead - Fields + // ///////////////////////////////////// + + result = await afterRead({ + depth, + doc: result, + entityConfig: collectionConfig, + req, + overrideAccess, + showHiddenFields, + }); + + // ///////////////////////////////////// + // afterRead - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => { + await priorHook; + + result = await hook({ + req, + doc: result, + }) || result; + }, Promise.resolve()); + + // ///////////////////////////////////// + // afterChange - Fields + // ///////////////////////////////////// + + result = await afterChange({ + data, + doc: result, + previousDoc: originalDoc, + entityConfig: collectionConfig, + operation: 'update', + req, + }); + + // ///////////////////////////////////// + // afterChange - Collection + // ///////////////////////////////////// + + await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => { + await priorHook; + + result = await hook({ + doc: result, + previousDoc: originalDoc, + req, + operation: 'update', + }) || result; + }, Promise.resolve()); + + await unlinkTempFiles({ + req, + config, + collectionConfig, + }); + + // ///////////////////////////////////// + // Return results + // ///////////////////////////////////// + + return result; +} + +export default updateByID; diff --git a/src/collections/requestHandlers/delete.ts b/src/collections/requestHandlers/delete.ts index 61aa3cdf84..99012a7b65 100644 --- a/src/collections/requestHandlers/delete.ts +++ b/src/collections/requestHandlers/delete.ts @@ -1,9 +1,10 @@ import { Response, NextFunction } from 'express'; import httpStatus from 'http-status'; import { PayloadRequest } from '../../express/types'; -import { NotFound } from '../../errors'; -import { Document } from '../../types'; +import { Document, Where } from '../../types'; import deleteOperation from '../operations/delete'; +import formatSuccessResponse from '../../express/responses/formatSuccess'; +import { getTranslation } from '../../utilities/getTranslation'; export type DeleteResult = { message: string; @@ -12,18 +13,36 @@ export type DeleteResult = { export default async function deleteHandler(req: PayloadRequest, res: Response, next: NextFunction): Promise | void> { try { - const doc = await deleteOperation({ + const result = await deleteOperation({ req, collection: req.collection, - id: req.params.id, + where: req.query.where as Where, depth: parseInt(String(req.query.depth), 10), }); - if (!doc) { - return res.status(httpStatus.NOT_FOUND).json(new NotFound(req.t)); + if (result.errors.length === 0) { + const message = req.t('general:deletedCountSuccessfully', { + count: result.docs.length, + label: getTranslation(req.collection.config.labels[result.docs.length > 1 ? 'plural' : 'singular'], req.i18n), + }); + + return res.status(httpStatus.OK).json({ + ...formatSuccessResponse(message, 'message'), + ...result, + }); } - return res.status(httpStatus.OK).send(doc); + const total = result.docs.length + result.errors.length; + const message = req.t('error:unableToDeleteCount', { + count: result.errors.length, + total, + label: getTranslation(req.collection.config.labels[total > 1 ? 'plural' : 'singular'], req.i18n), + }); + + return res.status(httpStatus.BAD_REQUEST).json({ + message, + ...result, + }); } catch (error) { return next(error); } diff --git a/src/collections/requestHandlers/deleteByID.ts b/src/collections/requestHandlers/deleteByID.ts new file mode 100644 index 0000000000..c56523d369 --- /dev/null +++ b/src/collections/requestHandlers/deleteByID.ts @@ -0,0 +1,30 @@ +import { Response, NextFunction } from 'express'; +import httpStatus from 'http-status'; +import { PayloadRequest } from '../../express/types'; +import { NotFound } from '../../errors'; +import { Document } from '../../types'; +import deleteByID from '../operations/deleteByID'; + +export type DeleteResult = { + message: string; + doc: Document; +} + +export default async function deleteByIDHandler(req: PayloadRequest, res: Response, next: NextFunction): Promise | void> { + try { + const doc = await deleteByID({ + req, + collection: req.collection, + id: req.params.id, + depth: parseInt(String(req.query.depth), 10), + }); + + if (!doc) { + return res.status(httpStatus.NOT_FOUND).json(new NotFound(req.t)); + } + + return res.status(httpStatus.OK).send(doc); + } catch (error) { + return next(error); + } +} diff --git a/src/collections/requestHandlers/update.ts b/src/collections/requestHandlers/update.ts index deb72fc936..4af136cc02 100644 --- a/src/collections/requestHandlers/update.ts +++ b/src/collections/requestHandlers/update.ts @@ -1,44 +1,51 @@ import { Response, NextFunction } from 'express'; import httpStatus from 'http-status'; +import { Where, Document } from '../../types'; import { PayloadRequest } from '../../express/types'; import formatSuccessResponse from '../../express/responses/formatSuccess'; -import { Document } from '../../types'; import update from '../operations/update'; +import { getTranslation } from '../../utilities/getTranslation'; export type UpdateResult = { message: string doc: Document }; -export async function deprecatedUpdate(req: PayloadRequest, res: Response, next: NextFunction): Promise | void> { - req.payload.logger.warn('The PUT method is deprecated and will no longer be supported in a future release. Please use the PATCH method for update requests.'); - - return updateHandler(req, res, next); -} - export default async function updateHandler(req: PayloadRequest, res: Response, next: NextFunction): Promise | void> { try { const draft = req.query.draft === 'true'; - const autosave = req.query.autosave === 'true'; - const doc = await update({ + const result = await update({ req, collection: req.collection, - id: req.params.id, + where: req.query.where as Where, data: req.body, depth: parseInt(String(req.query.depth), 10), draft, - autosave, }); - let message = req.t('general:updatedSuccessfully'); + if (result.errors.length === 0) { + const message = req.t('general:updatedCountSuccessfully', { + count: result.docs.length, + label: getTranslation(req.collection.config.labels[result.docs.length > 1 ? 'plural' : 'singular'], req.i18n), + }); - if (draft) message = req.t('version:draftSavedSuccessfully'); - if (autosave) message = req.t('version:autosavedSuccessfully'); + return res.status(httpStatus.OK).json({ + ...formatSuccessResponse(message, 'message'), + ...result, + }); + } - return res.status(httpStatus.OK).json({ + const total = result.docs.length + result.errors.length; + const message = req.t('error:unableToUpdateCount', { + count: result.errors.length, + total, + label: getTranslation(req.collection.config.labels[total > 1 ? 'plural' : 'singular'], req.i18n), + }); + + return res.status(httpStatus.BAD_REQUEST).json({ ...formatSuccessResponse(message, 'message'), - doc, + ...result, }); } catch (error) { return next(error); diff --git a/src/collections/requestHandlers/updateByID.ts b/src/collections/requestHandlers/updateByID.ts new file mode 100644 index 0000000000..e88ecb3e3d --- /dev/null +++ b/src/collections/requestHandlers/updateByID.ts @@ -0,0 +1,45 @@ +import { Response, NextFunction } from 'express'; +import httpStatus from 'http-status'; +import { PayloadRequest } from '../../express/types'; +import formatSuccessResponse from '../../express/responses/formatSuccess'; +import updateByID from '../operations/updateByID'; + +export type UpdateResult = { + message: string + doc: Document +}; + +export async function deprecatedUpdate(req: PayloadRequest, res: Response, next: NextFunction): Promise | void> { + req.payload.logger.warn('The PUT method is deprecated and will no longer be supported in a future release. Please use the PATCH method for update requests.'); + + return updateByIDHandler(req, res, next); +} + +export default async function updateByIDHandler(req: PayloadRequest, res: Response, next: NextFunction): Promise | void> { + try { + const draft = req.query.draft === 'true'; + const autosave = req.query.autosave === 'true'; + + const doc = await updateByID({ + req, + collection: req.collection, + id: req.params.id, + data: req.body, + depth: parseInt(String(req.query.depth), 10), + draft, + autosave, + }); + + let message = req.t('general:updatedSuccessfully'); + + if (draft) message = req.t('version:draftSavedSuccessfully'); + if (autosave) message = req.t('version:autosavedSuccessfully'); + + return res.status(httpStatus.OK).json({ + ...formatSuccessResponse(message, 'message'), + doc, + }); + } catch (error) { + return next(error); + } +} diff --git a/src/fields/config/schema.ts b/src/fields/config/schema.ts index c2231ccfaa..78011974a4 100644 --- a/src/fields/config/schema.ts +++ b/src/fields/config/schema.ts @@ -21,6 +21,7 @@ export const baseAdminFields = joi.object().keys({ initCollapsed: joi.boolean().default(false), hidden: joi.boolean().default(false), disabled: joi.boolean().default(false), + disableBulkEdit: joi.boolean().default(false), condition: joi.func(), components: baseAdminComponentFields, }); diff --git a/src/fields/config/types.ts b/src/fields/config/types.ts index 3d7e0d9bd6..b517e1c03b 100644 --- a/src/fields/config/types.ts +++ b/src/fields/config/types.ts @@ -70,6 +70,7 @@ type Admin = { Cell?: React.ComponentType; Field?: React.ComponentType; } + disableBulkEdit?: boolean hidden?: boolean } @@ -232,6 +233,7 @@ export type UIField = { position?: string width?: string condition?: Condition + disableBulkEdit?: boolean components?: { Filter?: React.ComponentType; Cell?: React.ComponentType; diff --git a/src/fields/hooks/afterChange/promise.ts b/src/fields/hooks/afterChange/promise.ts index 8258ae866e..d7e3f462fb 100644 --- a/src/fields/hooks/afterChange/promise.ts +++ b/src/fields/hooks/afterChange/promise.ts @@ -65,7 +65,7 @@ export const promise = async ({ fields: field.fields, operation, req, - siblingData: siblingData[field.name] as Record || {}, + siblingData: siblingData?.[field.name] as Record || {}, siblingDoc: siblingDoc[field.name] as Record, }); @@ -82,11 +82,11 @@ export const promise = async ({ data, doc, previousDoc, - previousSiblingDoc: previousDoc[field.name]?.[i] || {} as Record, + previousSiblingDoc: previousDoc?.[field.name]?.[i] || {} as Record, fields: field.fields, operation, req, - siblingData: siblingData[field.name]?.[i] || {}, + siblingData: siblingData?.[field.name]?.[i] || {}, siblingDoc: { ...row } || {}, })); }); @@ -108,11 +108,11 @@ export const promise = async ({ data, doc, previousDoc, - previousSiblingDoc: previousDoc[field.name]?.[i] || {} as Record, + previousSiblingDoc: previousDoc?.[field.name]?.[i] || {} as Record, fields: block.fields, operation, req, - siblingData: siblingData[field.name]?.[i] || {}, + siblingData: siblingData?.[field.name]?.[i] || {}, siblingDoc: { ...row } || {}, })); } diff --git a/src/index.ts b/src/index.ts index 7d5e7451c0..325d1cab9d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,5 @@ import { Config as GeneratedTypes } from 'payload/generated-types'; -import { - InitOptions, -} from './config/types'; +import { InitOptions } from './config/types'; import { initHTTP } from './initHTTP'; import { Payload as LocalPayload, BasePayload } from './payload'; diff --git a/src/payload.ts b/src/payload.ts index aa87b818d4..3ce6bcfddf 100644 --- a/src/payload.ts +++ b/src/payload.ts @@ -29,8 +29,6 @@ import { Preferences } from './preferences/types'; import { Options as CreateOptions } from './collections/operations/local/create'; import { Options as FindOptions } from './collections/operations/local/find'; import { Options as FindByIDOptions } from './collections/operations/local/findByID'; -import { Options as UpdateOptions } from './collections/operations/local/update'; -import { Options as DeleteOptions } from './collections/operations/local/delete'; import { Options as FindVersionsOptions } from './collections/operations/local/findVersions'; import { Options as FindVersionByIDOptions } from './collections/operations/local/findVersionByID'; import { Options as RestoreVersionOptions } from './collections/operations/local/restoreVersion'; @@ -249,23 +247,18 @@ export class BasePayload { } /** - * @description Update document + * @description Update one or more documents * @param options - * @returns Updated document + * @returns Updated document(s) */ - update = async ( - options: UpdateOptions, - ): Promise => { - const { update } = localOperations; - return update(this, options); - } + update = localOperations.update - delete = async ( - options: DeleteOptions, - ): Promise => { - const { localDelete } = localOperations; - return localDelete(this, options); - } + /** + * @description delete one or more documents + * @param options + * @returns Updated document(s) + */ + delete = localOperations.localDelete; /** * @description Find versions with criteria diff --git a/src/translations/cs.json b/src/translations/cs.json index 9c24574ee4..fe8087451e 100644 --- a/src/translations/cs.json +++ b/src/translations/cs.json @@ -82,6 +82,8 @@ "problemUploadingFile": "Při nahrávání souboru došlo k chybě.", "tokenInvalidOrExpired": "Token je neplatný nebo vypršel.", "unPublishingDocument": "Při zrušení publikování tohoto dokumentu došlo k chybě.", + "unableToDeleteCount": "Nelze smazat {{count}} z {{total}} {{label}}", + "unableToUpdateCount": "Nelze aktualizovat {{count}} z {{total}} {{label}}.", "unauthorized": "Neautorizováno, pro zadání tohoto požadavku musíte být přihlášeni.", "unknown": "Došlo k neznámé chybě.", "unspecific": "Došlo k chybě.", @@ -124,6 +126,7 @@ "saveChanges": "Uložit změny", "searchForBlock": "Hledat blok", "selectExistingLabel": "Vybrat existující {{label}}", + "selectFieldsToEdit": "Vyberte pole, která chcete upravit", "showAll": "Zobrazit vše", "swapUpload": "Vyměnit nahrání", "textToDisplay": "Text k zobrazení", @@ -132,6 +135,9 @@ }, "general": { "aboutToDelete": "Chystáte se odstranit {{label}} <1>{{title}}. Jste si jisti?", + "aboutToDeleteCount_many": "Chystáte se smazat {{count}} {{label}}", + "aboutToDeleteCount_one": "Chystáte se smazat {{count}} {{label}}", + "aboutToDeleteCount_other": "Chystáte se smazat {{count}} {{label}}", "addBelow": "Přidat pod", "addFilter": "Přidat filtr", "adminTheme": "Motiv administračního rozhraní", @@ -159,6 +165,7 @@ "dark": "Tmavé", "dashboard": "Nástěnka", "delete": "Odstranit", + "deletedCountSuccessfully": "Úspěšně smazáno {{count}} {{label}}.", "deletedSuccessfully": "Úspěšně odstraněno.", "deleting": "Odstraňování...", "descending": "Sestupně", @@ -167,6 +174,9 @@ "edit": "Upravit", "editLabel": "Upravit {{label}}", "editing": "Úpravy", + "editingLabel_many": "Úprava {{count}} {{label}}", + "editingLabel_one": "Úprava {{count}} {{label}}", + "editingLabel_other": "Úprava {{count}} {{label}}", "email": "E-mail", "emailAddress": "E-mailová adresa", "enterAValue": "Zadejte hodnotu", @@ -205,7 +215,9 @@ "save": "Uložit", "saving": "Ukládání...", "searchBy": "Vyhledat podle {{label}}", + "selectAll": "Vybrat vše {{count}} {{label}}", "selectValue": "Vyberte hodnotu", + "selectedCount": "Vybráno {{count}} {{label}}", "sorryNotFound": "Je nám líto, ale neexistuje nic, co by odpovídalo vašemu požadavku.", "sort": "Třídit", "stayOnThisPage": "Zůstat na této stránce", @@ -219,6 +231,7 @@ "unsavedChangesDuplicate": "Máte neuložené změny. Chtěli byste pokračovat v duplikování?", "untitled": "Bez názvu", "updatedAt": "Aktualizováno v", + "updatedCountSuccessfully": "Úspěšně aktualizováno {{count}} {{label}}.", "updatedSuccessfully": "Úspěšně aktualizováno.", "updating": "Aktualizace", "uploading": "Nahrávání", @@ -273,15 +286,18 @@ "validUploadID": "Toto pole není platné ID pro odeslání." }, "version": { + "aboutToPublishSelection": "Chystáte se publikovat všechny {{label}} ve výběru. Jsi si jistá?", "aboutToRestore": "Chystáte se obnovit tento {{label}} dokument do stavu, v jakém byl {{versionDate}}.", "aboutToRestoreGlobal": "Chystáte se obnovit globální {{label}} do stavu, v jakém byl {{versionDate}}.", "aboutToRevertToPublished": "Chystáte se vrátit změny tohoto dokumentu do jeho publikovaného stavu. Jste si jisti?", "aboutToUnpublish": "Chystáte se zrušit publikování tohoto dokumentu. Jste si jisti?", + "aboutToUnpublishSelection": "Chystáte se zrušit publikování všech {{label}} ve výběru. Jsi si jistá?", "autosave": "Automatické uložení", "autosavedSuccessfully": "Úspěšně uloženo automaticky.", "autosavedVersion": "Verze automatického uložení", "changed": "Změněno", "compareVersion": "Porovnat verzi s:", + "confirmPublish": "Potvrďte publikování", "confirmRevertToSaved": "Potvrdit vrácení k uloženému", "confirmUnpublish": "Potvrdit zrušení publikování", "confirmVersionRestoration": "Potvrdit obnovení verze", @@ -293,6 +309,7 @@ "noRowsFound": "Nenalezen {{label}}", "preview": "Náhled", "problemRestoringVersion": "Při obnovování této verze došlo k problému", + "publish": "Publikovat", "publishChanges": "Publikovat změny", "published": "Publikováno", "restoreThisVersion": "Obnovit tuto verzi", @@ -303,8 +320,8 @@ "saveDraft": "Uložit koncept", "selectLocales": "Vyberte místní verze pro zobrazení", "selectVersionToCompare": "Vyberte verzi pro porovnání", - "showingVersionsFor": "Zobrazují se verze pro:", "showLocales": "Zobrazit místní verze:", + "showingVersionsFor": "Zobrazují se verze pro:", "status": "Stav", "type": "Typ", "unpublish": "Zrušit publikování", diff --git a/src/translations/de.json b/src/translations/de.json index 08cf82d277..2aef1b7015 100644 --- a/src/translations/de.json +++ b/src/translations/de.json @@ -60,6 +60,7 @@ "accountAlreadyActivated": "Dieses Konto wurde bereits aktiviert", "autosaving": "Es gab ein Problem während der automatischen Speicherung für dieses Dokument", "correctInvalidFields": "Bitte ungültige Felder korrigieren.", + "deletingFile": "Beim Löschen der Datei ist ein Fehler aufgetreten.", "deletingTitle": "Es gab ein Problem während der Löschung von {{title}}. Bitte überprüfe deine Verbindung und versuche es erneut.", "emailOrPasswordIncorrect": "Die E-Mail-Adresse oder das Passwort sind nicht korrekt.", "followingFieldsInvalid_many": "Die folgenden Felder sind nicht korrekt:", @@ -81,6 +82,8 @@ "problemUploadingFile": "Es gab ein Problem während des Hochladens der Datei.", "tokenInvalidOrExpired": "Token ist entweder ungültig oder abgelaufen.", "unPublishingDocument": "Es gab ein Problem, dieses Dokument auf Entwurf zu setzen.", + "unableToDeleteCount": "{{count}} von {{total}} {{label}} konnte nicht gelöscht werden.", + "unableToUpdateCount": "{{count}} von {{total}} {{label}} konnte nicht aktualisiert werden.", "unauthorized": "Nicht autorisiert - du musst angemeldet sein, um diese Anfrage zu stellen.", "unknown": "Ein unbekannter Fehler ist aufgetreten.", "unspecific": "Ein Fehler ist aufgetreten.", @@ -89,24 +92,24 @@ "verificationTokenInvalid": "Verifizierungs-Token ist nicht korrekt." }, "fields": { - "block": "Block", - "blocks": "Blöcke", "addLabel": "{{label}} hinzufügen", "addLink": "Link Hinzufügen", "addNew": "Neu erstellen", "addNewLabel": "{{label}} erstellen", "addRelationship": "Verknüpfung Hinzufügen", "addUpload": "Hochladen Hinzufügen", + "block": "Block", "blockType": "Block-Typ", + "blocks": "Blöcke", "chooseBetweenCustomTextOrDocument": "Wähle zwischen einer eigenen Text-URL oder verlinke zu einem anderen Dokument.", "chooseDocumentToLink": "Wähle ein Dokument zum Verlinken", "chooseFromExisting": "Aus vorhandenen auswählen", "chooseLabel": "{{label}} auswählen", "collapseAll": "Alle einklappen", "customURL": "Eigene URL", + "editLabelData": "{{label}} bearbeiten", "editLink": "Bearbeite Link", "editRelationship": "Beziehung Hinzufügen", - "editLabelData": "{{label}} bearbeiten", "enterURL": "URL eingeben", "internalLink": "Interner Link", "itemsAndMore": "{{items}} und {{count}} mehr", @@ -125,6 +128,7 @@ "saveChanges": "Änderungen speichern", "searchForBlock": "Nach Block suchen", "selectExistingLabel": "{{label}} auswählen (vorhandene)", + "selectFieldsToEdit": "Wählen Sie die zu bearbeitenden Felder aus", "showAll": "Alle anzeigen", "swapRelationship": "Beziehung Tauschen", "swapUpload": "Datei Austauschen", @@ -134,10 +138,14 @@ }, "general": { "aboutToDelete": "Du bist dabei {{label}} <1>{{title}} zu löschen. Bist du dir sicher?", + "aboutToDeleteCount_many": "Sie sind dabei, {{count}} {{label}} zu löschen", + "aboutToDeleteCount_one": "Sie sind dabei, {{count}} {{label}} zu löschen", + "aboutToDeleteCount_other": "Sie sind dabei, {{count}} {{label}} zu löschen", "addBelow": "Darunter hinzufügen", "addFilter": "Filter hinzufügen", "adminTheme": "Admin-Farbthema", "and": "Und", + "ascending": "Aufsteigend", "automatic": "Automatisch", "backToDashboard": "Zurück zur Übersicht", "cancel": "Abbrechen", @@ -160,6 +168,7 @@ "dark": "Dunkel", "dashboard": "Übersicht", "delete": "Löschen", + "deletedCountSuccessfully": "{{count}} {{label}} erfolgreich gelöscht.", "deletedSuccessfully": "Erfolgreich gelöscht.", "deleting": "Lösche...", "descending": "Absteigend", @@ -168,6 +177,9 @@ "edit": "Bearbeiten", "editLabel": "{{label}} bearbeiten", "editing": "Bearbeite", + "editingLabel_many": "Bearbeiten von {{count}} {{label}}", + "editingLabel_one": "Bearbeiten von {{count}} {{label}}", + "editingLabel_other": "Bearbeiten von {{count}} {{label}}", "email": "E-Mail", "emailAddress": "E-Mail-Adresse", "enterAValue": "Gib einen Wert ein", @@ -206,7 +218,9 @@ "save": "Speichern", "saving": "Speichert...", "searchBy": "Suche nach {{label}}", + "selectAll": "Alle auswählen {{count}} {{label}}", "selectValue": "Wert auswählen", + "selectedCount": "{{count}} {{label}} ausgewählt", "sorryNotFound": "Entschuldige, es entspricht nichts deiner Anfrage", "sort": "Sortieren", "stayOnThisPage": "Auf dieser Seite bleiben", @@ -220,6 +234,7 @@ "unsavedChangesDuplicate": "Du hast ungespeicherte Änderungen, möchtest du mit dem Duplizieren fortfahren?", "untitled": "ohne Titel", "updatedAt": "Aktualisiert am", + "updatedCountSuccessfully": "{{count}} {{label}} erfolgreich aktualisiert.", "updatedSuccessfully": "Erfolgreich aktualisiert.", "updating": "Aktualisierung", "uploading": "Hochladen", @@ -228,20 +243,21 @@ "welcome": "Willkommen" }, "operators": { + "contains": "enthält", "equals": "gleich", - "isNotEqualTo": "ist nicht gleich", - "isIn": "ist drin", - "isNotIn": "ist nicht drin", "exists": "existiert", "isGreaterThan": "ist größer als", + "isGreaterThanOrEqualTo": "ist größer oder gleich", + "isIn": "ist drin", "isLessThan": "ist kleiner als", "isLessThanOrEqualTo": "ist kleiner oder gleich", - "isGreaterThanOrEqualTo": "ist größer oder gleich", - "near": "in der Nähe", "isLike": "ist wie", - "contains": "enthält" + "isNotEqualTo": "ist nicht gleich", + "isNotIn": "ist nicht drin", + "near": "in der Nähe" }, "upload": { + "dragAndDrop": "Ziehen Sie eine Datei per Drag-and-Drop", "dragAndDropHere": "oder ziehe eine Datei hier", "fileName": "Dateiname", "fileSize": "Dateigröße", @@ -250,7 +266,6 @@ "moreInfo": "Mehr Info", "selectCollectionToBrowse": "Wähle eine Sammlung zum Durchsuchen aus", "selectFile": "Datei auswählen", - "dragAndDrop": "Ziehen Sie eine Datei per Drag-and-Drop", "sizes": "Größen", "width": "Breite" }, @@ -264,25 +279,28 @@ "invalidSelections": "'Dieses Feld enthält die folgenden inkorrekten Auswahlen:'", "lessThanMin": "\"{{value}}\" ist weniger als der minimale erlaubte Wert von {{min}}.", "longerThanMin": "Dieser Wert muss länger als die minimale Länge von {{minLength}} Zeichen sein.", - "requiresNoMoreThan": "Dieses Feld kann nicht mehr als {{count}} {{label}} enthalten.", "notValidDate": "\"{{value}}\" ist kein gültiges Datum.", "required": "Pflichtfeld", "requiresAtLeast": "Dieses Feld muss mindestens {{count}} {{label}} enthalten.", + "requiresNoMoreThan": "Dieses Feld kann nicht mehr als {{count}} {{label}} enthalten.", "requiresTwoNumbers": "Dieses Feld muss zwei Nummern enthalten.", "shorterThanMax": "Dieser Wert muss kürzer als die maximale Länge von {{maxLength}} sein.", "trueOrFalse": "Dieses Feld kann nur wahr oder falsch sein.", "validUploadID": "'Dieses Feld enthält keine valide Upload-ID.'" }, "version": { + "aboutToPublishSelection": "Sie sind dabei, alle {{label}} in der Auswahl zu veröffentlichen. Bist du dir sicher?", "aboutToRestore": "Du bist dabei, {{label}} auf den Stand vom {{versionDate}} zurücksetzen.", "aboutToRestoreGlobal": "Du bist dabei, das Globale Dokument {{label}} auf den Stand vom {{versionDate}} zurückzusetzen.", "aboutToRevertToPublished": "Du bist dabei, dieses Dokument auf den Stand des ersten Veröffentlichungsdatums zurückzusetzen - Bist du sicher?", "aboutToUnpublish": "Du bist dabei dieses Dokument auf Entwurf zu setzen - bist du dir sicher?", + "aboutToUnpublishSelection": "Sie sind dabei, die Veröffentlichung aller {{label}} in der Auswahl aufzuheben. Bist du dir sicher?", "autosave": "Automatische Speicherung", "autosavedSuccessfully": "Erfolgreich automatisch gespeichert.", "autosavedVersion": "Automatisch gespeicherte Version", "changed": "Geändert", "compareVersion": "Vergleiche Version zu:", + "confirmPublish": "Veröffentlichung bestätigen", "confirmRevertToSaved": "Zurücksetzen auf die letzte Speicherung bestätigen", "confirmUnpublish": "Setzen auf Entwurf bestätigen", "confirmVersionRestoration": " Wiederherstellung der Version bestätigen", @@ -294,6 +312,7 @@ "noRowsFound": "Kein {{label}} gefunden", "preview": "Vorschau", "problemRestoringVersion": "Es gab ein Problem bei der Wiederherstellung dieser Version", + "publish": "Veröffentlichen", "publishChanges": "Änderungen veröffentlichen", "published": "Veröffentlicht", "restoreThisVersion": "Diese Version wiederherstellen", @@ -305,6 +324,7 @@ "selectLocales": "Wähle anzuzeigende Sprachumgebungen", "selectVersionToCompare": "Wähle Version zum Vergleich", "showLocales": "Sprachumgebungen anzeigen:", + "showingVersionsFor": "Versionen anzeigen für:", "status": "Status", "type": "Typ", "unpublish": "Auf Entwurf setzen", @@ -313,6 +333,7 @@ "versionCount_many": "{{count}} Versionen gefunden", "versionCount_none": "Keine Versionen gefunden", "versionCount_one": "{{count}} Version gefunden", + "versionCount_other": "{{count}} Versionen gefunden", "versionCreatedOn": "{{version}} erstellt am:", "versionID": "Version ID", "versions": "Versionen", diff --git a/src/translations/en.json b/src/translations/en.json index f1851998ff..1444008606 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -82,6 +82,8 @@ "problemUploadingFile": "There was a problem while uploading the file.", "tokenInvalidOrExpired": "Token is either invalid or has expired.", "unPublishingDocument": "There was a problem while un-publishing this document.", + "unableToDeleteCount": "Unable to delete {{count}} out of {{total}} {{label}}.", + "unableToUpdateCount": "Unable to update {{count}} out of {{total}} {{label}}.", "unauthorized": "Unauthorized, you must be logged in to make this request.", "unknown": "An unknown error has occurred.", "unspecific": "An error has occurred.", @@ -126,6 +128,7 @@ "saveChanges": "Save changes", "searchForBlock": "Search for a block", "selectExistingLabel": "Select existing {{label}}", + "selectFieldsToEdit": "Select fields to edit", "showAll": "Show All", "swapRelationship": "Swap Relationship", "swapUpload": "Swap Upload", @@ -135,6 +138,9 @@ }, "general": { "aboutToDelete": "You are about to delete the {{label}} <1>{{title}}. Are you sure?", + "aboutToDeleteCount_many": "You are about to delete {{count}} {{label}}", + "aboutToDeleteCount_one": "You are about to delete {{count}} {{label}}", + "aboutToDeleteCount_other": "You are about to delete {{count}} {{label}}", "addBelow": "Add Below", "addFilter": "Add Filter", "adminTheme": "Admin Theme", @@ -162,6 +168,7 @@ "dark": "Dark", "dashboard": "Dashboard", "delete": "Delete", + "deletedCountSuccessfully": "Deleted {{count}} {{label}} successfully.", "deletedSuccessfully": "Deleted successfully.", "deleting": "Deleting...", "descending": "Descending", @@ -170,6 +177,9 @@ "edit": "Edit", "editLabel": "Edit {{label}}", "editing": "Editing", + "editingLabel_many": "Editing {{count}} {{label}}", + "editingLabel_one": "Editing {{count}} {{label}}", + "editingLabel_other": "Editing {{count}} {{label}}", "email": "Email", "emailAddress": "Email Address", "enterAValue": "Enter a value", @@ -208,7 +218,9 @@ "save": "Save", "saving": "Saving...", "searchBy": "Search by {{label}}", + "selectAll": "Select all {{count}} {{label}}", "selectValue": "Select a value", + "selectedCount": "{{count}} {{label}} selected", "sorryNotFound": "Sorry—there is nothing to correspond with your request.", "sort": "Sort", "stayOnThisPage": "Stay on this page", @@ -222,6 +234,7 @@ "unsavedChangesDuplicate": "You have unsaved changes. Would you like to continue to duplicate?", "untitled": "Untitled", "updatedAt": "Updated At", + "updatedCountSuccessfully": "Updated {{count}} {{label}} successfully.", "updatedSuccessfully": "Updated successfully.", "updating": "Updating", "uploading": "Uploading", @@ -230,20 +243,21 @@ "welcome": "Welcome" }, "operators": { + "contains": "contains", "equals": "equals", - "isNotEqualTo": "is not equal to", - "isIn": "is in", - "isNotIn": "is not in", "exists": "exists", "isGreaterThan": "is greater than", + "isGreaterThanOrEqualTo": "is greater than or equal to", + "isIn": "is in", "isLessThan": "is less than", "isLessThanOrEqualTo": "is less than or equal to", - "isGreaterThanOrEqualTo": "is greater than or equal to", - "near": "near", "isLike": "is like", - "contains": "contains" + "isNotEqualTo": "is not equal to", + "isNotIn": "is not in", + "near": "near" }, "upload": { + "dragAndDrop": "Drag and drop a file", "dragAndDropHere": "or drag and drop a file here", "fileName": "File Name", "fileSize": "File Size", @@ -252,7 +266,6 @@ "moreInfo": "More info", "selectCollectionToBrowse": "Select a Collection to Browse", "selectFile": "Select a file", - "dragAndDrop": "Drag and drop a file", "sizes": "Sizes", "width": "Width" }, @@ -276,15 +289,18 @@ "validUploadID": "This field is not a valid upload ID." }, "version": { + "aboutToPublishSelection": "You are about to publish all {{label}} in the selection. Are you sure?", "aboutToRestore": "You are about to restore this {{label}} document to the state that it was in on {{versionDate}}.", "aboutToRestoreGlobal": "You are about to restore the global {{label}} to the state that it was in on {{versionDate}}.", "aboutToRevertToPublished": "You are about to revert this document's changes to its published state. Are you sure?", "aboutToUnpublish": "You are about to unpublish this document. Are you sure?", + "aboutToUnpublishSelection": "You are about to unpublish all {{label}} in the selection. Are you sure?", "autosave": "Autosave", "autosavedSuccessfully": "Autosaved successfully.", "autosavedVersion": "Autosaved version", "changed": "Changed", "compareVersion": "Compare version against:", + "confirmPublish": "Confirm publish", "confirmRevertToSaved": "Confirm revert to saved", "confirmUnpublish": "Confirm unpublish", "confirmVersionRestoration": "Confirm version Restoration", @@ -296,6 +312,7 @@ "noRowsFound": "No {{label}} found", "preview": "Preview", "problemRestoringVersion": "There was a problem restoring this version", + "publish": "Publish", "publishChanges": "Publish changes", "published": "Published", "restoreThisVersion": "Restore this version", @@ -306,8 +323,8 @@ "saveDraft": "Save Draft", "selectLocales": "Select locales to display", "selectVersionToCompare": "Select a version to compare", - "showingVersionsFor": "Showing versions for:", "showLocales": "Show locales:", + "showingVersionsFor": "Showing versions for:", "status": "Status", "type": "Type", "unpublish": "Unpublish", diff --git a/src/translations/es.json b/src/translations/es.json index 900617342c..3d64ec62bc 100644 --- a/src/translations/es.json +++ b/src/translations/es.json @@ -82,6 +82,8 @@ "problemUploadingFile": "Ocurrió un problema al subir el archivo.", "tokenInvalidOrExpired": "El token es inválido o ya expiró.", "unPublishingDocument": "Ocurrió un error al despublicar este documento.", + "unableToDeleteCount": "No se pudo eliminar {{count}} de {{total}} {{label}}.", + "unableToUpdateCount": "No se puede actualizar {{count}} de {{total}} {{label}}.", "unauthorized": "No autorizado, debes iniciar sesión para realizar esta solicitud.", "unknown": "Ocurrió un error desconocido.", "unspecific": "Ocurrió un error.", @@ -126,6 +128,7 @@ "saveChanges": "Guardar cambios", "searchForBlock": "Buscar bloque", "selectExistingLabel": "Seleccionar {{label}} existente", + "selectFieldsToEdit": "Seleccionar campos para editar", "showAll": "Mostrar Todo", "swapRelationship": "Cambiar Relación", "swapUpload": "Cambiar carga", @@ -135,6 +138,9 @@ }, "general": { "aboutToDelete": "Estás por eliminar el {{label}} <1>{{title}}. ¿Estás seguro?", + "aboutToDeleteCount_many": "Está a punto de eliminar {{count}} {{label}}", + "aboutToDeleteCount_one": "Está a punto de eliminar {{count}} {{label}}", + "aboutToDeleteCount_other": "Está a punto de eliminar {{count}} {{label}}", "addBelow": "Agrega abajo", "addFilter": "Añadir filtro", "adminTheme": "Tema del admin", @@ -162,6 +168,7 @@ "dark": "Oscuro", "dashboard": "Tablero", "delete": "Eliminar", + "deletedCountSuccessfully": "Se eliminó {{count}} {{label}} con éxito.", "deletedSuccessfully": "Borrado exitosamente.", "deleting": "Eliminando...", "descending": "Descendente", @@ -170,6 +177,9 @@ "edit": "Editar", "editLabel": "Editar {{label}}", "editing": "Editando", + "editingLabel_many": "Edición de {{count}} {{label}}", + "editingLabel_one": "Editando {{count}} {{label}}", + "editingLabel_other": "Edición de {{count}} {{label}}", "email": "Correo electrónico", "emailAddress": "Dirección de Correo Electrónico", "enterAValue": "Introduce un valor", @@ -208,7 +218,9 @@ "save": "Guardar", "saving": "Guardando...", "searchBy": "Buscar por {{label}}", + "selectAll": "Seleccionar todo {{count}} {{label}}", "selectValue": "Selecciona un valor", + "selectedCount": "{{count}} {{label}} seleccionado", "sorryNotFound": "Lo sentimos. No hay nada que corresponda con tu solicitud.", "sort": "Ordenar", "stayOnThisPage": "Permanecer en esta página", @@ -222,6 +234,7 @@ "unsavedChangesDuplicate": "Tienes cambios sin guardar. ¿Deseas continuar para duplicar?", "untitled": "Sin título", "updatedAt": "Fecha de modificado", + "updatedCountSuccessfully": "{{count}} {{label}} actualizado con éxito.", "updatedSuccessfully": "Actualizado con éxito.", "updating": "Actualizando", "uploading": "Subiendo", @@ -230,20 +243,21 @@ "welcome": "Bienvenido" }, "operators": { + "contains": "contiene", "equals": "igual", - "isNotEqualTo": "no es igual a", - "isIn": "está en", - "isNotIn": "no está en", "exists": "existe", "isGreaterThan": "es mayor que", + "isGreaterThanOrEqualTo": "es mayor o igual que", + "isIn": "está en", "isLessThan": "es menor que", "isLessThanOrEqualTo": "es menor o igual que", - "isGreaterThanOrEqualTo": "es mayor o igual que", - "near": "cerca", "isLike": "es como", - "contains": "contiene" + "isNotEqualTo": "no es igual a", + "isNotIn": "no está en", + "near": "cerca" }, "upload": { + "dragAndDrop": "Arrastra y suelta un archivo", "dragAndDropHere": "o arrastra un archivo aquí", "fileName": "Nombre del archivo", "fileSize": "Tamaño del archivo", @@ -252,7 +266,6 @@ "moreInfo": "Más info", "selectCollectionToBrowse": "Selecciona una Colección", "selectFile": "Selecciona un archivo", - "dragAndDrop": "Arrastra y suelta un archivo", "sizes": "Tamaños", "width": "Ancho" }, @@ -276,15 +289,18 @@ "validUploadID": "'Este campo no es una ID de subida válida.'" }, "version": { + "aboutToPublishSelection": "Está a punto de publicar todas las {{etiquetas}} de la selección. ¿Está seguro?", "aboutToRestore": "Estás a punto de restaurar este documento de {{label}} al estado en el que estaba en la fecha {{versionDate}}.", "aboutToRestoreGlobal": "Estás a punto de restaurar el {{label}} global al estado en el que estaba en la fecha {{versionDate}}.", "aboutToRevertToPublished": "Estás a punto de revertir los cambios de este documento a su estado publicado. ¿Estás seguro?", "aboutToUnpublish": "Estás a punto de despublicar este documento. ¿Estás seguro?", + "aboutToUnpublishSelection": "Está a punto de anular la publicación de todos los {{label}} de la selección. ¿Está seguro?", "autosave": "Autoguardar", "autosavedSuccessfully": "Guardado automáticamente con éxito.", "autosavedVersion": "Versión Autoguardada", "changed": "Modificado", "compareVersion": "Comparar versión con:", + "confirmPublish": "Confirmar publicación", "confirmRevertToSaved": "Confirmar revertir a guardado", "confirmUnpublish": "Confirmar despublicado", "confirmVersionRestoration": "Confirmar restauración de versión", @@ -296,6 +312,7 @@ "noRowsFound": "No encontramos {{label}}", "preview": "Previsualizar", "problemRestoringVersion": "Ocurrió un problema al restaurar esta versión", + "publish": "Publicar", "publishChanges": "Publicar cambios", "published": "Publicado", "restoreThisVersion": "Restaurar esta versión", @@ -306,8 +323,8 @@ "saveDraft": "Guardar Borrador", "selectLocales": "Selecciona idiomas a mostrar", "selectVersionToCompare": "Selecciona versión a comparar", - "showingVersionsFor": "Mostrando versiones para:", "showLocales": "Mostrar idiomas:", + "showingVersionsFor": "Mostrando versiones para:", "status": "Estado", "type": "Tipo", "unpublish": "Despublicar", diff --git a/src/translations/fr.json b/src/translations/fr.json index 027477bfd0..c4a0b217cf 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -82,6 +82,8 @@ "problemUploadingFile": "Il y a eu un problème lors du téléversement du fichier.", "tokenInvalidOrExpired": "Le jeton n'est soit pas valide ou a expiré.", "unPublishingDocument": "Un problème est survenu lors de l'annulation de la publication de ce document.", + "unableToDeleteCount": "Impossible de supprimer {{count}} sur {{total}} {{label}}.", + "unableToUpdateCount": "Impossible de mettre à jour {{count}} sur {{total}} {{label}}.", "unauthorized": "Non autorisé, vous devez être connecté pour effectuer cette demande.", "unknown": "Une erreur inconnue s'est produite.", "unspecific": "Une erreur est survenue.", @@ -90,15 +92,15 @@ "verificationTokenInvalid": "Le jeton de vérification n'est pas valide." }, "fields": { - "block": "bloc", - "blocks": "blocs", "addLabel": "Ajouter {{label}}", "addLink": "Ajouter un Lien", "addNew": "Ajouter nouveau ou nouvelle", "addNewLabel": "Ajouter nouveau ou nouvelle {{label}}", "addRelationship": "Ajouter une relation", "addUpload": "Ajouter le téléchargement", + "block": "bloc", "blockType": "Type de bloc", + "blocks": "blocs", "chooseBetweenCustomTextOrDocument": "Choisissez entre saisir une URL personnalisée ou créer un lien vers un autre document.", "chooseDocumentToLink": "Choisissez un document vers lequel établir un lien", "chooseFromExisting": "Choisir parmi les existant(e)s", @@ -126,6 +128,7 @@ "saveChanges": "Sauvegarder les modifications", "searchForBlock": "Rechercher un bloc", "selectExistingLabel": "Sélectionnez {{label}} existant", + "selectFieldsToEdit": "Sélectionnez les champs à modifier", "showAll": "Afficher tout", "swapRelationship": "Changer de relation", "swapUpload": "Changer de Fichier", @@ -135,10 +138,14 @@ }, "general": { "aboutToDelete": "Vous êtes sur le point de supprimer ce ou cette {{label}} <1>{{title}}. Êtes-vous sûr ?", + "aboutToDeleteCount_many": "Vous êtes sur le point de supprimer {{count}} {{label}}", + "aboutToDeleteCount_one": "Vous êtes sur le point de supprimer {{count}} {{label}}", + "aboutToDeleteCount_other": "Vous êtes sur le point de supprimer {{count}} {{label}}", "addBelow": "Ajoutez ci-dessous", "addFilter": "Ajouter un filtre", "adminTheme": "Thème d'administration", "and": "Et", + "ascending": "Ascendant", "automatic": "Automatique", "backToDashboard": "Retour au tableau de bord", "cancel": "Annuler", @@ -161,6 +168,7 @@ "dark": "Nuit", "dashboard": "Tableau de bord", "delete": "Supprimer", + "deletedCountSuccessfully": "{{count}} {{label}} supprimé avec succès.", "deletedSuccessfully": "Supprimé(e) avec succès.", "deleting": "Suppression en cours...", "descending": "Descendant(e)", @@ -169,6 +177,9 @@ "edit": "Éditer", "editLabel": "Modifier {{label}}", "editing": "Modification en cours", + "editingLabel_many": "Modification des {{count}} {{label}}", + "editingLabel_one": "Modification de {{count}} {{label}}", + "editingLabel_other": "Modification des {{count}} {{label}}", "email": "E-mail", "emailAddress": "Adresse e-mail", "enterAValue": "Entrez une valeur", @@ -207,7 +218,9 @@ "save": "Sauvegarder", "saving": "Sauvegarde en cours...", "searchBy": "Rechercher par {{label}}", + "selectAll": "Tout sélectionner {{count}} {{label}}", "selectValue": "Sélectionnez une valeur", + "selectedCount": "{{count}} {{label}} sélectionné", "sorryNotFound": "Désolé, rien ne correspond à votre demande.", "sort": "Trier", "stayOnThisPage": "Rester sur cette page", @@ -221,6 +234,7 @@ "unsavedChangesDuplicate": "Vous avez des changements non enregistrés. Souhaitez-vous continuer la duplication ?", "untitled": "Sans titre", "updatedAt": "Modifié le", + "updatedCountSuccessfully": "{{count}} {{label}} mis à jour avec succès.", "updatedSuccessfully": "Mis à jour avec succés.", "updating": "Mise à jour", "uploading": "Téléchargement", @@ -229,20 +243,21 @@ "welcome": "Bienvenu(e)" }, "operators": { + "contains": "contient", "equals": "est égal à", - "isNotEqualTo": "n'est pas égal à", - "isIn": "est dans", - "isNotIn": "n'est pas dans", "exists": "existe", "isGreaterThan": "est supérieur à", + "isGreaterThanOrEqualTo": "est supérieur ou égal à", + "isIn": "est dans", "isLessThan": "est inférieur à", "isLessThanOrEqualTo": "est inférieur ou égal à", - "isGreaterThanOrEqualTo": "est supérieur ou égal à", - "near": "proche", "isLike": "est comme", - "contains": "contient" + "isNotEqualTo": "n'est pas égal à", + "isNotIn": "n'est pas dans", + "near": "proche" }, "upload": { + "dragAndDrop": "Glisser-déposer un fichier", "dragAndDropHere": "ou glissez-déposez un fichier ici", "fileName": "Nom du fichier", "fileSize": "Taille du fichier", @@ -251,7 +266,6 @@ "moreInfo": "Plus d'infos", "selectCollectionToBrowse": "Sélectionnez une collection à parcourir", "selectFile": "Sélectionnez un fichier", - "dragAndDrop": "Glisser-déposer un fichier", "sizes": "Tailles", "width": "Largeur" }, @@ -275,15 +289,18 @@ "validUploadID": "Ce champ n'est pas un valide identifiant de fichier." }, "version": { + "aboutToPublishSelection": "Vous êtes sur le point de publier tous les {{label}} de la sélection. Es-tu sûr?", "aboutToRestore": "Vous êtes sur le point de restaurer le document {{label}} à l'état où il se trouvait le {{versionDate}}.", "aboutToRestoreGlobal": "Vous êtes sur le point de restaurer le ou la {{label}} global(e) à l'état où il ou elle se trouvait le {{versionDate}}.", "aboutToRevertToPublished": "Vous êtes sur le point de rétablir les modifications apportées à ce document à la version publiée. Êtes-vous sûr ?", "aboutToUnpublish": "Vous êtes sur le point d'annuler la publication de ce document. Êtes-vous sûr ?", + "aboutToUnpublishSelection": "Vous êtes sur le point de dépublier tous les {{label}} de la sélection. Es-tu sûr?", "autosave": "Enregistrement automatique", "autosavedSuccessfully": "Enregistrement automatique réussi.", "autosavedVersion": "Version enregistrée automatiquement", "changed": "Modifié", "compareVersion": "Comparez cette version à :", + "confirmPublish": "Confirmer la publication", "confirmRevertToSaved": "Confirmer la restauration", "confirmUnpublish": "Confirmer l'annulation", "confirmVersionRestoration": "Confirmer la restauration de la version", @@ -295,6 +312,7 @@ "noRowsFound": "Aucun(e) {{label}} trouvé(e)", "preview": "Aperçu", "problemRestoringVersion": "Un problème est survenu lors de la restauration de cette version", + "publish": "Publier", "publishChanges": "Publier les modifications", "published": "Publié", "restoreThisVersion": "Restaurer cette version", @@ -303,9 +321,10 @@ "revertToPublished": "Republier", "reverting": "Republication en cours...", "saveDraft": "Enregistrer le brouillon", - "selectLocals": "Sélectionnez les paramètres régionaux à afficher", + "selectLocales": "Sélectionnez les paramètres régionaux à afficher", "selectVersionToCompare": "Sélectionnez une version à comparer", "showLocales": "Afficher les paramètres régionaux :", + "showingVersionsFor": "Affichage des versions pour :", "status": "Statut", "type": "Type", "unpublish": "Annuler la publication", @@ -314,6 +333,7 @@ "versionCount_many": "{{count}} versions trouvées", "versionCount_none": "Aucune version trouvée", "versionCount_one": "{{count}} version trouvée", + "versionCount_other": "{{count}} versions trouvées", "versionCreatedOn": "{{version}} créé(e) le :", "versionID": "Identifiant de la version", "versions": "Versions", diff --git a/src/translations/hr.json b/src/translations/hr.json index fa8974ec22..1371696ea5 100644 --- a/src/translations/hr.json +++ b/src/translations/hr.json @@ -82,6 +82,8 @@ "problemUploadingFile": "Pojavio se problem pri učitavanju datoteke.", "tokenInvalidOrExpired": "Token je nevaljan ili je istekao.", "unPublishingDocument": "Pojavio se problem pri poništavanju objave ovog dokumenta.", + "unableToDeleteCount": "Nije moguće izbrisati {{count}} od {{total}} {{label}}.", + "unableToUpdateCount": "Nije moguće ažurirati {{count}} od {{total}} {{label}}.", "unauthorized": "Neovlašten, morate biti prijavljeni da biste uputili ovaj zahtjev.", "unknown": "Došlo je do nepoznate pogreške.", "unspecific": "Došlo je do pogreške.", @@ -126,6 +128,7 @@ "saveChanges": "Spremi promjene", "searchForBlock": "Potraži blok", "selectExistingLabel": "Odaberi postojeće{{label}}", + "selectFieldsToEdit": "Odaberite polja za uređivanje", "showAll": "Pokaži sve", "swapRelationship": "Zamijeni vezu", "swapUpload": "Zamijeni prijenos", @@ -135,6 +138,9 @@ }, "general": { "aboutToDelete": "Izbrisat ćete {{label}} <1>{{title}}. Jeste li sigurni?", + "aboutToDeleteCount_many": "Upravo ćete izbrisati {{count}} {{label}}", + "aboutToDeleteCount_one": "Upravo ćete izbrisati {{count}} {{label}}", + "aboutToDeleteCount_other": "Upravo ćete izbrisati {{count}} {{label}}", "addBelow": "Dodaj ispod", "addFilter": "Dodaj filter", "adminTheme": "Administratorska tema", @@ -162,6 +168,7 @@ "dark": "Tamno", "dashboard": "Nadzorna ploča", "delete": "Obriši", + "deletedCountSuccessfully": "Uspješno izbrisano {{count}} {{label}}.", "deletedSuccessfully": "Uspješno obrisano.", "deleting": "Brisanje...", "descending": "Silazno", @@ -170,6 +177,9 @@ "edit": "Uredi", "editLabel": "Uredi {{label}}", "editing": "Uređivanje", + "editingLabel_many": "Uređivanje {{count}} {{label}}", + "editingLabel_one": "Uređivanje {{count}} {{label}}", + "editingLabel_other": "Uređivanje {{count}} {{label}}", "email": "Email", "emailAddress": "Email adresa", "enterAValue": "Unesi vrijednost", @@ -208,7 +218,9 @@ "save": "Spremi", "saving": "Spremanje...", "searchBy": "Traži po {{label}}", + "selectAll": "Odaberite sve {{count}} {{label}}", "selectValue": "Odaberi vrijednost", + "selectedCount": "{{count}} {{label}} odabrano", "sorryNotFound": "Nažalost, ne postoji ništa što odgovara vašem zahtjevu.", "sort": "Sortiraj", "stayOnThisPage": "Ostani na ovoj stranici", @@ -222,6 +234,7 @@ "unsavedChangesDuplicate": "Imate nespremljene promjene. Želite li nastaviti s dupliciranjem?", "untitled": "Bez naslova", "updatedAt": "Ažurirano u", + "updatedCountSuccessfully": "Uspješno ažurirano {{count}} {{label}}.", "updatedSuccessfully": "Uspješno ažurirano.", "updating": "Ažuriranje", "uploading": "Prijenos", @@ -230,20 +243,21 @@ "welcome": "Dobrodošli" }, "operators": { + "contains": "sadrži", "equals": "jednako", - "isNotEqualTo": "nije jednako", - "isIn": "je u", - "isNotIn": "nije unutra", "exists": "postoji", "isGreaterThan": "je veće od", + "isGreaterThanOrEqualTo": "je veće od ili jednako", + "isIn": "je u", "isLessThan": "manje je od", "isLessThanOrEqualTo": "manje je ili jednako", - "isGreaterThanOrEqualTo": "je veće od ili jednako", - "near": "blizu", "isLike": "je kao", - "contains": "sadrži" + "isNotEqualTo": "nije jednako", + "isNotIn": "nije unutra", + "near": "blizu" }, "upload": { + "dragAndDrop": "Povucite i ispustite datoteku", "dragAndDropHere": "ili povucite i ispustite datoteku ovdje", "fileName": "Ime datoteke", "fileSize": "Veličina datoteke", @@ -252,7 +266,6 @@ "moreInfo": "Više informacija", "selectCollectionToBrowse": "Odaberite kolekciju za pregled", "selectFile": "Odaberite datoteku", - "dragAndDrop": "Povucite i ispustite datoteku", "sizes": "Veličine", "width": "Širina" }, @@ -276,15 +289,18 @@ "validUploadID": "Ovo polje nije valjani ID prijenosa." }, "version": { + "aboutToPublishSelection": "Upravo ćete objaviti sve {{label}} u izboru. Jesi li siguran?", "aboutToRestore": "Vratit ćete {{label}} dokument u stanje u kojem je bio {{versionDate}}", "aboutToRestoreGlobal": "Vratit ćete globalni {{label}} u stanje u kojem je bio {{versionDate}}.", "aboutToRevertToPublished": "Vratit ćete promjene u dokumentu u objavljeno stanje. Jeste li sigurni? ", "aboutToUnpublish": "Poništit ćete objavu ovog dokumenta. Jeste li sigurni?", + "aboutToUnpublishSelection": "Upravo ćete poništiti objavu svih {{label}} u odabiru. Jesi li siguran?", "autosave": "Automatsko spremanje", "autosavedSuccessfully": "Automatsko spremanje uspješno.", "autosavedVersion": "Verzija automatski spremljenog dokumenta", "changed": "Promijenjeno", "compareVersion": "Usporedi verziju sa:", + "confirmPublish": "Potvrdi objavu", "confirmRevertToSaved": "Potvrdite vraćanje na spremljeno", "confirmUnpublish": "Potvrdite poništavanje objave", "confirmVersionRestoration": "Potvrdite vraćanje verzije", @@ -296,6 +312,7 @@ "noRowsFound": "{{label}} nije pronađeno", "preview": "Pregled", "problemRestoringVersion": "Nastao je problem pri vraćanju ove verzije", + "publish": "Objaviti", "publishChanges": "Objavi promjene", "published": "Objavljeno", "restoreThisVersion": "Vrati ovu verziju", @@ -306,8 +323,8 @@ "saveDraft": "Sačuvaj nacrt", "selectLocales": "Odaberite jezike", "selectVersionToCompare": "Odaberite verziju za usporedbu", - "showingVersionsFor": "Pokazujem verzije za:", "showLocales": "Prikaži jezike:", + "showingVersionsFor": "Pokazujem verzije za:", "status": "Status", "type": "Tip", "unpublish": "Poništi objavu", diff --git a/src/translations/hu.json b/src/translations/hu.json index 87ef020eb5..38c8457ca4 100644 --- a/src/translations/hu.json +++ b/src/translations/hu.json @@ -82,6 +82,8 @@ "problemUploadingFile": "Hiba történt a fájl feltöltése közben.", "tokenInvalidOrExpired": "A token érvénytelen vagy lejárt.", "unPublishingDocument": "Hiba történt a dokumentum közzétételének visszavonása közben.", + "unableToDeleteCount": "Nem sikerült törölni {{count}}/{{total}} {{label}}.", + "unableToUpdateCount": "Nem sikerült frissíteni {{count}}/{{total}} {{label}}.", "unauthorized": "Jogosulatlan, a kéréshez be kell jelentkeznie.", "unknown": "Ismeretlen hiba történt.", "unspecific": "Hiba történt.", @@ -126,6 +128,7 @@ "saveChanges": "Módosítások mentése", "searchForBlock": "Blokk keresése", "selectExistingLabel": "Meglévő {{label}} kiválasztása", + "selectFieldsToEdit": "Válassza ki a szerkeszteni kívánt mezőket", "showAll": "Az összes megjelenítése", "swapRelationship": "Kapcsolat csere", "swapUpload": "Feltöltés csere", @@ -135,6 +138,9 @@ }, "general": { "aboutToDelete": "A {{label}} <1>{{title}} törlésére készül. Biztos benne?", + "aboutToDeleteCount_many": "Törölni készül {{count}} {{label}}", + "aboutToDeleteCount_one": "Törölni készül {{count}} {{label}}", + "aboutToDeleteCount_other": "Törölni készül {{count}} {{label}}", "addBelow": "Hozzáadás lent", "addFilter": "Szűrő hozzáadása", "adminTheme": "Admin téma", @@ -162,6 +168,7 @@ "dark": "Sötét", "dashboard": "Irányítópult", "delete": "Törlés", + "deletedCountSuccessfully": "{{count}} {{label}} sikeresen törölve.", "deletedSuccessfully": "Sikeresen törölve.", "deleting": "Törlés...", "descending": "Csökkenő", @@ -170,6 +177,9 @@ "edit": "Szerkesztés", "editLabel": "{{label}} szerkesztése", "editing": "Szerkesztés", + "editingLabel_many": "{{count}} {{label}} szerkesztése", + "editingLabel_one": "{{count}} {{label}} szerkesztése", + "editingLabel_other": "{{count}} {{label}} szerkesztése", "email": "E-mail", "emailAddress": "E-mail cím", "enterAValue": "Adjon meg egy értéket", @@ -208,7 +218,9 @@ "save": "Mentés", "saving": "Mentés...", "searchBy": "Keresés a következő szerint: {{label}}", + "selectAll": "Az összes kijelölése: {{count}} {{label}}", "selectValue": "Válasszon ki egy értéket", + "selectedCount": "{{count}} {{label}} kiválasztva", "sorryNotFound": "Sajnáljuk – nincs semmi, ami megfelelne a kérésének.", "sort": "Rendezés", "stayOnThisPage": "Maradjon ezen az oldalon", @@ -222,6 +234,7 @@ "unsavedChangesDuplicate": "Nem mentett módosításai vannak. Szeretné folytatni a duplikációt?", "untitled": "Névtelen", "updatedAt": "Frissítve:", + "updatedCountSuccessfully": "{{count}} {{label}} sikeresen frissítve.", "updatedSuccessfully": "Sikeresen frissítve.", "updating": "Frissítés", "uploading": "Feltöltés", @@ -276,15 +289,18 @@ "validUploadID": "Ez a mező nem érvényes feltöltési azonosító." }, "version": { + "aboutToPublishSelection": "Arra készül, hogy az összes {{label}} elemet közzétegye a kijelölésben. biztos vagy ebben?", "aboutToRestore": "Arra készül, hogy visszaállítsa ezt a {{label}} dokumentumot arra az állapotra, amelyben {{versionDate}} napon volt.", "aboutToRestoreGlobal": "Arra készül, hogy visszaállítsa a {{label}} arra az állapotra, amelyben {{versionDate}} napon volt.", "aboutToRevertToPublished": "Arra készül, hogy visszaállítsa a dokumentum módosításait a közzétett állapotába. Biztos benne?", "aboutToUnpublish": "A dokumentum közzétételének visszavonására készül. Biztos benne?", + "aboutToUnpublishSelection": "Arra készül, hogy visszavonja a kijelölésben szereplő összes {{label}} közzétételét. biztos vagy ebben?", "autosave": "Automatikus mentés", "autosavedSuccessfully": "Automatikus mentés sikeres.", "autosavedVersion": "Automatikusan mentett verzió", "changed": "Megváltozott", "compareVersion": "Hasonlítsa össze a verziót a következőkkel:", + "confirmPublish": "A közzététel megerősítése", "confirmRevertToSaved": "Erősítse meg a mentett verzióra való visszatérést", "confirmUnpublish": "A közzététel visszavonásának megerősítése", "confirmVersionRestoration": "Verzió-visszaállítás megerősítése", @@ -296,6 +312,7 @@ "noRowsFound": "Nem található {{label}}", "preview": "Előnézet", "problemRestoringVersion": "Hiba történt a verzió visszaállításakor", + "publish": "Közzététel", "publishChanges": "Módosítások közzététele", "published": "Közzétett", "restoreThisVersion": "A verzió visszaállítása", @@ -306,8 +323,8 @@ "saveDraft": "Piszkozat mentése", "selectLocales": "Megjelenítendő nyelvek kiválasztása", "selectVersionToCompare": "Válassza ki az összehasonlítani kívánt verziót", - "showingVersionsFor": "Verziók megjelenítése a következőkhöz:", "showLocales": "Nyelvek megjelenítése:", + "showingVersionsFor": "Verziók megjelenítése a következőkhöz:", "status": "Állapot", "type": "Típus", "unpublish": "Közzététel visszavonása", diff --git a/src/translations/it.json b/src/translations/it.json index 191e124d8d..d50e7b0c3c 100644 --- a/src/translations/it.json +++ b/src/translations/it.json @@ -82,6 +82,8 @@ "problemUploadingFile": "Si è verificato un problema durante il caricamento del file.", "tokenInvalidOrExpired": "Il token non è valido o è scaduto.", "unPublishingDocument": "Si è verificato un problema durante l'annullamento della pubblicazione di questo documento.", + "unableToDeleteCount": "Impossibile eliminare {{count}} su {{total}} {{label}}.", + "unableToUpdateCount": "Impossibile aggiornare {{count}} su {{total}} {{label}}.", "unauthorized": "Non autorizzato, devi essere loggato per effettuare questa richiesta.", "unknown": "Si è verificato un errore sconosciuto.", "unspecific": "Si è verificato un errore.", @@ -90,27 +92,34 @@ "verificationTokenInvalid": "Il token di verifica non è valido." }, "fields": { - "block": "blocco", - "blocks": "blocchi", "addLabel": "Aggiungi {{label}}", "addLink": "Aggiungi Collegamento", "addNew": "Aggiungi nuovo", "addNewLabel": "Aggiungi nuovo {{label}}", "addRelationship": "Aggiungi Relazione", "addUpload": "aggiungi Carica", + "block": "blocco", "blockType": "Tipo di Blocco", + "blocks": "blocchi", + "chooseBetweenCustomTextOrDocument": "Scegli tra l'inserimento di un URL di testo personalizzato o il collegamento a un altro documento.", + "chooseDocumentToLink": "Scegli un documento a cui collegarti", "chooseFromExisting": "Scegli tra esistente", "chooseLabel": "Scegli {{label}}", "collapseAll": "Comprimi tutto", + "customURL": "URL personalizzato", "editLabelData": "Modifica i dati di {{label}}", "editLink": "Modifica Collegamento", "editRelationship": "Modifica Relazione", + "enterURL": "Inserisci un URL", + "internalLink": "Collegamento interno", "itemsAndMore": "{{items}} e altri {{count}}", "labelRelationship": "Relazione {{label}}", "latitude": "Latitudine", + "linkType": "Tipo di collegamento", "linkedTo": "Collegato a <0>{{label}}", "longitude": "Longitudine", "newLabel": "Nuovo {{label}}", + "openInNewTab": "Apri in una nuova scheda", "passwordsDoNotMatch": "Le password non corrispondono.", "relatedDocument": "Documento Correlato", "relationTo": "Correla a", @@ -119,18 +128,24 @@ "saveChanges": "Salva modifiche", "searchForBlock": "Cerca un blocco", "selectExistingLabel": "Seleziona {{label}} esistente", + "selectFieldsToEdit": "Seleziona i campi da modificare", "showAll": "Mostra tutto", "swapRelationship": "Cambia Relationship", "swapUpload": "Cambia Upload", + "textToDisplay": "Testo da visualizzare", "toggleBlock": "Apri/chiudi blocco", "uploadNewLabel": "Carica nuovo {{label}}" }, "general": { "aboutToDelete": "Stai per eliminare {{label}} <1>{{title}}. Sei sicuro?", + "aboutToDeleteCount_many": "Stai per eliminare {{count}} {{label}}", + "aboutToDeleteCount_one": "Stai per eliminare {{count}} {{label}}", + "aboutToDeleteCount_other": "Stai per eliminare {{count}} {{label}}", "addBelow": "Aggiungi sotto", "addFilter": "Aggiungi Filtro", "adminTheme": "Tema Admin", "and": "E", + "ascending": "Ascendente", "automatic": "Automatico", "backToDashboard": "Torna alla Dashboard", "cancel": "Cancella", @@ -153,6 +168,7 @@ "dark": "Scuro", "dashboard": "Dashboard", "delete": "Elimina", + "deletedCountSuccessfully": "{{count}} {{label}} eliminato con successo.", "deletedSuccessfully": "Eliminato con successo.", "deleting": "Sto eliminando...", "descending": "Decrescente", @@ -161,6 +177,9 @@ "edit": "Modificare", "editLabel": "Modifica {{label}}", "editing": "Modifica", + "editingLabel_many": "Modificare {{count}} {{label}}", + "editingLabel_one": "Modifica {{count}} {{label}}", + "editingLabel_other": "Modificare {{count}} {{label}}", "email": "Email", "emailAddress": "Indirizzo Email", "enterAValue": "Inserisci un valore", @@ -199,7 +218,9 @@ "save": "Salva", "saving": "Salvo...", "searchBy": "Cerca per {{label}}", + "selectAll": "Seleziona tutto {{count}} {{label}}", "selectValue": "Seleziona un valore", + "selectedCount": "{{count}} {{label}} selezionato", "sorryNotFound": "Siamo spiacenti, non c'è nulla che corrisponda alla tua richiesta.", "sort": "Ordina", "stayOnThisPage": "Rimani su questa pagina", @@ -213,6 +234,7 @@ "unsavedChangesDuplicate": "Sono presenti modifiche non salvate. Vuoi continuare a duplicare?", "untitled": "Senza titolo", "updatedAt": "Aggiornato il", + "updatedCountSuccessfully": "{{count}} {{label}} aggiornato con successo.", "updatedSuccessfully": "Aggiornato con successo.", "updating": "Aggiornamento", "uploading": "Caricamento", @@ -221,20 +243,21 @@ "welcome": "Benvenuto" }, "operators": { + "contains": "contiene", "equals": "uguale", - "isNotEqualTo": "non è uguale a", - "isIn": "è in", - "isNotIn": "non è in", "exists": "esiste", "isGreaterThan": "è maggiore di", + "isGreaterThanOrEqualTo": "è maggiore o uguale a", + "isIn": "è in", "isLessThan": "è minore di", "isLessThanOrEqualTo": "è minore o uguale a", - "isGreaterThanOrEqualTo": "è maggiore o uguale a", - "near": "vicino", "isLike": "è come", - "contains": "contiene" + "isNotEqualTo": "non è uguale a", + "isNotIn": "non è in", + "near": "vicino" }, "upload": { + "dragAndDrop": "Trascina e rilascia un file", "dragAndDropHere": "oppure trascina e rilascia un file qui", "fileName": "Nome File", "fileSize": "Dimensione File", @@ -243,7 +266,6 @@ "moreInfo": "Più info", "selectCollectionToBrowse": "Seleziona una Collezione da Sfogliare", "selectFile": "Seleziona un file", - "dragAndDrop": "Trascina e rilascia un file", "sizes": "Formati", "width": "Larghezza" }, @@ -267,15 +289,18 @@ "validUploadID": "'Questo campo non è un ID di Upload valido.'" }, "version": { + "aboutToPublishSelection": "Stai per pubblicare tutte le {{label}} nella selezione. Sei sicuro?", "aboutToRestore": "Stai per ripristinare questo documento {{label}} allo stato in cui si trovava il {{versionDate}}.", "aboutToRestoreGlobal": "Stai per ripristinare {{label}} allo stato in cui si trovava il {{versionDate}}.", "aboutToRevertToPublished": "Stai per ripristinare le modifiche di questo documento al suo stato pubblicato. Sei sicuro?", "aboutToUnpublish": "Stai per annullare la pubblicazione di questo documento. Sei sicuro?", + "aboutToUnpublishSelection": "Stai per annullare la pubblicazione di tutte le {{label}} nella selezione. Sei sicuro?", "autosave": "Salvataggio automatico", "autosavedSuccessfully": "Salvataggio automatico riuscito.", "autosavedVersion": "Versione salvata automaticamente", "changed": "Modificato", "compareVersion": "Confronta versione con:", + "confirmPublish": "Conferma la pubblicazione", "confirmRevertToSaved": "Conferma il ripristino dei salvataggi", "confirmUnpublish": "Conferma annullamento della pubblicazione", "confirmVersionRestoration": "Conferma il ripristino della versione", @@ -287,6 +312,7 @@ "noRowsFound": "Nessun {{label}} trovato", "preview": "Anteprima", "problemRestoringVersion": "Si è verificato un problema durante il ripristino di questa versione", + "publish": "Pubblicare", "publishChanges": "Pubblica modifiche", "published": "Pubblicato", "restoreThisVersion": "Ripristina questa versione", @@ -297,8 +323,8 @@ "saveDraft": "Salva Bozza", "selectLocales": "Seleziona le lingue da visualizzare", "selectVersionToCompare": "Seleziona una versione da confrontare", - "showingVersionsFor": "Mostra le versioni per:", "showLocales": "Mostra localizzazioni:", + "showingVersionsFor": "Mostra le versioni per:", "status": "Stato", "type": "Tipo", "unpublish": "Annulla pubblicazione", diff --git a/src/translations/ja.json b/src/translations/ja.json index c26fb14cb3..8a8dd8f83f 100644 --- a/src/translations/ja.json +++ b/src/translations/ja.json @@ -60,6 +60,7 @@ "accountAlreadyActivated": "このアカウントはすでに有効です。", "autosaving": "このデータを自動保存する際に問題が発生しました。", "correctInvalidFields": "無効なフィールドを修正してください。", + "deletingFile": "ファイルの削除中にエラーが発生しました。", "deletingTitle": "{{title}} を削除する際にエラーが発生しました。接続を確認してからもう一度お試しください。", "emailOrPasswordIncorrect": "メールアドレス、または、パスワードが正しくありません。", "followingFieldsInvalid_many": "次のフィールドは無効です:", @@ -81,6 +82,8 @@ "problemUploadingFile": "ファイルのアップロード中に問題が発生しました。", "tokenInvalidOrExpired": "トークンが無効、または、有効期限が切れています。", "unPublishingDocument": "このデータを非公開する際に問題が発生しました。", + "unableToDeleteCount": "{{total}} {{label}} から {{count}} を削除できません。", + "unableToUpdateCount": "{{total}} {{label}} のうち {{count}} 個を更新できません。", "unauthorized": "認証されていません。このリクエストを行うにはログインが必要です。", "unknown": "不明なエラーが発生しました。", "unspecific": "エラーが発生しました。", @@ -89,27 +92,34 @@ "verificationTokenInvalid": "認証トークンが無効です。" }, "fields": { - "block": "ブロック", - "blocks": "ブロック", "addLabel": "{{label}} を追加", "addLink": "リンクを追加", "addNew": "新規追加", "addNewLabel": "{{label}} を新規追加", "addRelationship": "リレーションシップを追加", "addUpload": "アップロードを追加", + "block": "ブロック", "blockType": "ブロックタイプ", + "blocks": "ブロック", + "chooseBetweenCustomTextOrDocument": "", + "chooseDocumentToLink": "", "chooseFromExisting": "既存から選択", "chooseLabel": "{{label}} を選択", "collapseAll": "すべて閉じる", + "customURL": "カスタムURL", "editLabelData": "{{label}} データを編集", "editLink": "リンクを編集", "editRelationship": "リレーションシップを編集", + "enterURL": "URL を入力してください", + "internalLink": "内部リンク", "itemsAndMore": "{{items}} 他{{count}}件", "labelRelationship": "{{label}} リレーションシップ", "latitude": "緯度", + "linkType": "リンクタイプ", "linkedTo": "<0>{{label}} にリンク", "longitude": "経度", "newLabel": "新規 {{label}}", + "openInNewTab": "新しいタブで開く", "passwordsDoNotMatch": "パスワードが一致しません", "relatedDocument": "リレーションデータ", "relationTo": "リレーション", @@ -118,9 +128,11 @@ "saveChanges": "変更を保存", "searchForBlock": "ブロックを検索", "selectExistingLabel": "既存 {{label}} を選択", + "selectFieldsToEdit": "編集するフィールドを選択", "showAll": "すべて開く", "swapRelationship": "スワップ関係", "swapUpload": "差し替え", + "textToDisplay": "", "toggleBlock": "ブロックを切り替え", "uploadNewLabel": "新規 {{label}} アップロード" }, diff --git a/src/translations/my.json b/src/translations/my.json index 2c3318a3ae..274c3a447c 100644 --- a/src/translations/my.json +++ b/src/translations/my.json @@ -60,6 +60,7 @@ "accountAlreadyActivated": "ဤအကောင့်ကို အသက်သွင်းပြီးဖြစ်သည်။", "autosaving": "ဖိုင်ကို အလိုအလျောက်သိမ်းဆည်းရာတွင် ပြဿနာတစ်ခုရှိနေသည်။", "correctInvalidFields": "ကျေးဇူးပြု၍ အချက်အလက်များကို ပြန်လည် စစ်ဆေးပါ။", + "deletingFile": "ဖိုင်ကိုဖျက်ရာတွင် အမှားအယွင်းရှိနေသည်။", "deletingTitle": "{{title}} ကို ဖျက်ရာတွင် အမှားအယွင်းရှိခဲ့သည်။ သင့် အင်တာနက်လိုင်းအား စစ်ဆေးပြီး ထပ်မံကြို့စားကြည့်ပါ။", "emailOrPasswordIncorrect": "ထည့်သွင်းထားသော အီးမေးလ် သို့မဟုတ် စကားဝှက်သည် မမှန်ပါ။", "followingFieldsInvalid_many": "ထည့်သွင်းထားသော အချက်အလက်များသည် မမှန်ကန်ပါ။", @@ -81,6 +82,8 @@ "problemUploadingFile": "ဖိုင်ကို အပ်လုဒ်တင်ရာတွင် ပြဿနာရှိနေသည်။", "tokenInvalidOrExpired": "တိုကင်သည် မမှန်ကန်ပါ သို့မဟုတ် သက်တမ်းကုန်သွားပါပြီ။", "unPublishingDocument": "ဖိုင်ကို ပြန်လည့် သိမ်းဆည်းခြင်းမှာ ပြဿနာရှိနေသည်။", + "unableToDeleteCount": "{{total}} {{label}} မှ {{count}} ကို ဖျက်၍မရပါ။", + "unableToUpdateCount": "{{total}} {{label}} မှ {{count}} ကို အပ်ဒိတ်လုပ်၍မရပါ။", "unauthorized": "အခွင့်မရှိပါ။ ဤတောင်းဆိုချက်ကို လုပ်ဆောင်နိုင်ရန် သင်သည် လော့ဂ်အင်ဝင်ရပါမည်။", "unknown": "ဘာမှန်းမသိသော error တက်သွားပါသည်။", "unspecific": "Error တက်နေပါသည်။", @@ -98,18 +101,25 @@ "block": "ဘလောက်", "blockType": "ဘလောက် အမျိုးအစား", "blocks": "ဘလောက်များ", + "chooseBetweenCustomTextOrDocument": "စိတ်ကြိုက်စာသား URL ကိုထည့်ခြင်း သို့မဟုတ် အခြားစာရွက်စာတမ်းတစ်ခုသို့ လင့်ခ်ချိတ်ခြင်းအကြား ရွေးချယ်ပါ။", + "chooseDocumentToLink": "ချိတ်ဆက်ရန် စာရွက်စာတမ်းကို ရွေးပါ။", "chooseFromExisting": "ရှိပြီးသားထဲကပဲ ရွေးချယ်ပါ။", "chooseLabel": "{{label}} အားရွေးချယ်ပါ။", "collapseAll": "အားလုံးကို ခေါက်သိမ်းပါ။", + "customURL": "စိတ်ကြိုက် URL", "editLabelData": "ဒေတာ {{label}} ကို တည်းဖြတ်ပါ။", "editLink": "လင့်ခ်ကို တည်းဖြတ်ပါ။", "editRelationship": "Relationship ကို တည်းဖြတ်ပါ။", + "enterURL": "URL တစ်ခုထည့်ပါ။", + "internalLink": "Internal Link", "itemsAndMore": "{{items}} နှင့် နောက်ထပ် {{count}} ခု", "labelRelationship": "{{label}} Relationship", "latitude": "vĩ độ", + "linkType": "လင့်အမျိုးအစား", "linkedTo": "<0>{{label}} ချိတ်ဆက်ထားသည်။", "longitude": "kinh độ", "newLabel": "{{label}} အသစ်", + "openInNewTab": "တက်ဘ်အသစ်တွင် ဖွင့်ပါ။", "passwordsDoNotMatch": "စကားဝှက်များနှင့် မကိုက်ညီပါ။", "relatedDocument": "ဆက်စပ် ဖိုင်", "relationTo": "ဆက်စပ်မှု", @@ -118,18 +128,24 @@ "saveChanges": "သိမ်းဆည်းမည်။", "searchForBlock": "ဘလောက်တစ်ခုရှာမည်။", "selectExistingLabel": "ရှိပြီးသား {{label}} ကို ရွေးပါ", + "selectFieldsToEdit": "တည်းဖြတ်ရန် အကွက်များကို ရွေးပါ။", "showAll": "အကုန် ကြည့်မည်။", "swapRelationship": "လဲလှယ်ဆက်ဆံရေး", "swapUpload": "အပ်လုဒ်ဖလှယ်ပါ။", + "textToDisplay": "ပြသရန် စာသား", "toggleBlock": "Toggle block", "uploadNewLabel": "{{label}} အသစ်တင်မည်။" }, "general": { "aboutToDelete": "{{label}} <1>{{title}} ကို ဖျက်ပါတော့မည်။ သေချာပြီလား။ ဖျက်ပြီးရင် ပြန်မရဘူးနော်။", + "aboutToDeleteCount_many": "သင်သည် {{count}} {{label}} ကို ဖျက်ပါတော့မည်။", + "aboutToDeleteCount_one": "သင်သည် {{count}} {{label}} ကို ဖျက်ပါတော့မည်။", + "aboutToDeleteCount_other": "သင်သည် {{count}} {{label}} ကို ဖျက်ပါတော့မည်။", "addBelow": "အောက်တွင်ထည့်ပါ။", "addFilter": "ဇကာထည့်ပါ။", "adminTheme": "Admin Theme", "and": "နှင့်", + "ascending": "တက်နေသည်", "automatic": "အော်တို", "backToDashboard": "ပင်မစာမျက်နှာသို့ ပြန်သွားမည်။", "cancel": "မလုပ်တော့ပါ။", @@ -152,6 +168,7 @@ "dark": "အမှောင်", "dashboard": "ပင်မစာမျက်နှာ", "delete": "ဖျက်မည်။", + "deletedCountSuccessfully": "{{count}} {{label}} ကို အောင်မြင်စွာ ဖျက်လိုက်ပါပြီ။", "deletedSuccessfully": "အောင်မြင်စွာ ဖျက်လိုက်ပါပြီ။", "deleting": "ဖျက်နေဆဲ ...", "descending": "Descending", @@ -160,6 +177,9 @@ "edit": "တည်းဖြတ်ပါ။", "editLabel": "{{label}} ပြင်ဆင်မည်။", "editing": "ပြင်ဆင်နေသည်။", + "editingLabel_many": "တည်းဖြတ်ခြင်း {{count}} {{label}}", + "editingLabel_one": "တည်းဖြတ်ခြင်း {{count}} {{label}}", + "editingLabel_other": "တည်းဖြတ်ခြင်း {{count}} {{label}}", "email": "အီးမေးလ်", "emailAddress": "အီးမေးလ် လိပ်စာ", "enterAValue": "တန်ဖိုးတစ်ခုထည့်ပါ။", @@ -198,7 +218,9 @@ "save": "သိမ်းဆည်းမည်။", "saving": "သိမ်းနေဆဲ ...", "searchBy": "ရှာဖွေပါ။", + "selectAll": "{{count}} {{label}} အားလုံးကို ရွေးပါ", "selectValue": "တစ်ခုခုကို ရွေးချယ်ပါ။", + "selectedCount": "{{count}} {{label}} ကို ရွေးထားသည်။", "sorryNotFound": "ဝမ်းနည်းပါသည်။ သင်ရှာနေတဲ့ဟာ ဒီမှာမရှိပါ။", "sort": "အစဉ်လိုက်", "stayOnThisPage": "ဒီမှာပဲ ဆက်နေမည်။", @@ -212,6 +234,7 @@ "unsavedChangesDuplicate": "သင့်တွင် မသိမ်းဆည်းရသေးသော ပြောင်းလဲမှုများ ရှိနေပါသည်။ ပုံတူပွားမှာ သေချာပြီလား။", "untitled": "ခေါင်းစဥ်မဲ့", "updatedAt": "ပြင်ဆင်ခဲ့သည့်အချိန်", + "updatedCountSuccessfully": "{{count}} {{label}} ကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ခဲ့သည်။", "updatedSuccessfully": "အပ်ဒိတ်လုပ်ပြီးပါပြီ။", "updating": "ပြင်ဆင်ရန်", "uploading": "တင်ပေးနေသည်", @@ -220,20 +243,21 @@ "welcome": "ကြိုဆိုပါတယ်။" }, "operators": { + "contains": "ပါဝင်သည်", "equals": "ညီမျှ", - "isNotEqualTo": "ညီမျှသည်", - "isIn": "ရှိ", - "isNotIn": "မဝင်ပါ", "exists": "တည်ရှိသည်", "isGreaterThan": "ထက်ကြီးသည်", + "isGreaterThanOrEqualTo": "ထက်ကြီးသည် သို့မဟုတ် ညီမျှသည်", + "isIn": "ရှိ", "isLessThan": "ထက်နည်းသည်", "isLessThanOrEqualTo": "ထက်နည်းသည် သို့မဟုတ် ညီမျှသည်", - "isGreaterThanOrEqualTo": "ထက်ကြီးသည် သို့မဟုတ် ညီမျှသည်", - "near": "နီး", "isLike": "တူသည်", - "contains": "ပါဝင်သည်" + "isNotEqualTo": "ညီမျှသည်", + "isNotIn": "မဝင်ပါ", + "near": "နီး" }, "upload": { + "dragAndDrop": "ဖိုင်တစ်ဖိုင်ကို ဆွဲချလိုက်ပါ။", "dragAndDropHere": "သို့မဟုတ် ဖိုင်တစ်ခုကို ဤနေရာတွင် ဆွဲချပါ။", "fileName": "ဖိုင် နာမည်", "fileSize": "ဖိုင် အရွယ်အစား", @@ -242,14 +266,13 @@ "moreInfo": "More info", "selectCollectionToBrowse": "စုစည်းမှု တစ်ခုခုကို ရွေးချယ်ပါ။", "selectFile": "ဖိုင်ရွေးပါ။", - "dragAndDrop": "ဖိုင်တစ်ဖိုင်ကို ဆွဲချလိုက်ပါ။", "sizes": "အရွယ်အစားများ", "width": "အကျယ်" }, "validation": { - "fieldHasNo": "ဤအကွက်တွင် {{label}} မရှိပါ။", "emailAddress": "မှန်ကန်သော အီးမေးလ်လိပ်စာကို ထည့်သွင်းပါ။", "enterNumber": "မှန်ကန်သောနံပါတ်တစ်ခုထည့်ပါ။", + "fieldHasNo": "ဤအကွက်တွင် {{label}} မရှိပါ။", "greaterThanMax": "\"{{value}}\" သည် {{max}} ၏ အများဆုံးခွင့်ပြုထားသော တန်ဖိုးထက် ကြီးသည်။", "invalidInput": "ဤအကွက်တွင် မမှန်ကန်သော ထည့်သွင်းမှုတစ်ခုရှိသည်။", "invalidSelection": "ဤအကွက်တွင် မမှန်ကန်သော ရွေးချယ်မှုတစ်ခုရှိသည်။", @@ -266,15 +289,18 @@ "validUploadID": "'ဤအကွက်သည် မှန်ကန်သော အပ်လုဒ် ID မဟုတ်ပါ။'" }, "version": { + "aboutToPublishSelection": "သင်သည် ရွေးချယ်မှုတွင် {{label}} အားလုံးကို ထုတ်ဝေပါတော့မည်။ သေချာလား?", "aboutToRestore": "သင်သည် ဤ {{label}} စာရွက်စာတမ်းကို {{versionDate}} တွင် ပါရှိသည့် အခြေအနေသို့ ပြန်ယူတော့မည်။", "aboutToRestoreGlobal": "သင်သည် ဂလိုဘယ် {{label}} ကို {{versionDate}} တွင် ပါရှိသည့် အခြေအနေသို့ ပြန်လည်ရောက်ရှိတော့မည်ဖြစ်သည်။", "aboutToRevertToPublished": "သင်သည် အပြောင်းအလဲများကို အများဆိုင် အခြေအနေသို့ ပြန်ပြောင်းပါတော့မည်။ သေချာလား?", "aboutToUnpublish": "အများဆိုင်မှ ပြန်ဖြုတ်တော့မည်။ သေချာလား", + "aboutToUnpublishSelection": "သင်သည် ရွေးချယ်မှုတွင် {{label}} အားလုံးကို ထုတ်ဝေတော့မည် ဖြစ်သည်။ သေချာလား?", "autosave": "အလိုအလျောက်သိမ်းဆည်းပါ။", "autosavedSuccessfully": "အလိုအလျောက် သိမ်းဆည်းပြီးပါပြီ။", "autosavedVersion": "အော်တို ဗားရှင်း", "changed": "ပြောင်းခဲ့သည်။", "compareVersion": "ဗားရှင်းနှင့် နှိုင်းယှဉ်ချက်:", + "confirmPublish": "ထုတ်ဝေအတည်ပြုပါ။", "confirmRevertToSaved": "သိမ်းဆည်းပြီးကြောင်း အတည်ပြုပါ။", "confirmUnpublish": "အများဆိုင်ကို ဖျက်ရန် အတည်ပြုပါ။", "confirmVersionRestoration": "ဗားရှင်းပြန်လည် အသုံးပြုခြင်းကို အတည်ပြုပါ။", @@ -286,6 +312,7 @@ "noRowsFound": "{{label}} အားမတွေ့ပါ။", "preview": "နမူနာပြရန်", "problemRestoringVersion": "ဤဗားရှင်းကို ပြန်လည်ရယူရာတွင် ပြဿနာရှိနေသည်။", + "publish": "ထုတ်ဝေသည်။", "publishChanges": "အပြောင်းအလဲများကို တင်ခဲ့သည်။", "published": "တင်ပြီးပြီ။", "restoreThisVersion": "ဤဗားရှင်းကိုကို ပြန်ယူမည်။", @@ -297,6 +324,7 @@ "selectLocales": "ပြသရန် ဒေသန္တရများကို ရွေးပါ။", "selectVersionToCompare": "နှိုင်းယှဉ်ရန် ဗားရှင်းကို ရွေးပါ။", "showLocales": "ဒေသန္တရများကိုပြပါ။:", + "showingVersionsFor": "အတွက် ဗားရှင်းများကို ပြသနေသည်-", "status": "အခြေအနေ", "type": "အမျိုးအစား", "unpublish": "ပြန်ဖြုတ်မည်။", @@ -305,6 +333,7 @@ "versionCount_many": "{{count}} ဗားရှင်းများကို တွေ့ပါသည်။", "versionCount_none": "ဗားရှင်းရှာဖွေ့ပါ။", "versionCount_one": "{{count}} ဗားရှင်အား တွေ့ပါသည်။", + "versionCount_other": "ဗားရှင်း {{count}} ခု တွေ့ရှိပါသည်။", "versionCreatedOn": "{{version}} အား ဖန်တီးခဲ့သည်။", "versionID": "ဗားရှင်း ID", "versions": "ဗားရှင်းများ", diff --git a/src/translations/nb.json b/src/translations/nb.json index 93d4c316fb..d888352fdc 100644 --- a/src/translations/nb.json +++ b/src/translations/nb.json @@ -82,6 +82,8 @@ "problemUploadingFile": "Det oppstod et problem under opplasting av filen.", "tokenInvalidOrExpired": "Token er enten ugyldig eller har utløpt.", "unPublishingDocument": "Det oppstod et problem under avpublisering av dokumentet.", + "unableToDeleteCount": "Kan ikke slette {{count}} av {{total}} {{label}}.", + "unableToUpdateCount": "Kan ikke oppdatere {{count}} av {{total}} {{label}}.", "unauthorized": "Uautorisert, du må være innlogget for å gjøre denne forespørselen.", "unknown": "En ukjent feil har oppstått.", "unspecific": "En feil har oppstått.", @@ -126,6 +128,7 @@ "saveChanges": "Lagre endringer", "searchForBlock": "Søk etter en blokk", "selectExistingLabel": "Velg eksisterende {{label}}", + "selectFieldsToEdit": "Velg felt som skal redigeres", "showAll": "Vis alle", "swapRelationship": "Bytte Forhold", "swapUpload": "Bytt Opplasting", @@ -135,10 +138,14 @@ }, "general": { "aboutToDelete": "Du er i ferd med å slette {{label}} <1>{{title}}. Er du sikker?", + "aboutToDeleteCount_many": "Du er i ferd med å slette {{count}} {{label}}", + "aboutToDeleteCount_one": "Du er i ferd med å slette {{count}} {{label}}", + "aboutToDeleteCount_other": "Du er i ferd med å slette {{count}} {{label}}", "addBelow": "Legg til under", "addFilter": "Legg til filter", "adminTheme": "Admin-tema", "and": "Og", + "ascending": "Stigende", "automatic": "Automatisk", "backToDashboard": "Tilbake til kontrollpanel", "cancel": "Avbryt", @@ -161,6 +168,7 @@ "dark": "Mørk", "dashboard": "Kontrollpanel", "delete": "Slett", + "deletedCountSuccessfully": "Slettet {{count}} {{label}}.", "deletedSuccessfully": "Slettet.", "deleting": "Sletter...", "descending": "Synkende", @@ -169,6 +177,9 @@ "edit": "Redigere", "editLabel": "Rediger {{label}}", "editing": "Redigerer", + "editingLabel_many": "Redigerer {{count}} {{label}}", + "editingLabel_one": "Redigerer {{count}} {{label}}", + "editingLabel_other": "Redigerer {{count}} {{label}}", "email": "E-post", "emailAddress": "E-postadresse", "enterAValue": "Skriv inn en verdi", @@ -207,7 +218,9 @@ "save": "Lagre", "saving": "Lagrer...", "searchBy": "Søk etter {{label}}", + "selectAll": "Velg alle {{count}} {{label}}", "selectValue": "Velg en verdi", + "selectedCount": "{{count}} {{label}} valgt", "sorryNotFound": "Beklager, det er ingenting som samsvarer med forespørselen din.", "sort": "Sortér", "stayOnThisPage": "Bli på denne siden", @@ -221,6 +234,7 @@ "unsavedChangesDuplicate": "Du har ulagrede endringer. Vil du fortsette å duplisere?", "untitled": "Uten tittel", "updatedAt": "Oppdatert", + "updatedCountSuccessfully": "Oppdaterte {{count}} {{label}} vellykket.", "updatedSuccessfully": "Oppdatert.", "updating": "Oppdatering", "uploading": "Opplasting", @@ -229,20 +243,21 @@ "welcome": "Velkommen" }, "operators": { + "contains": "contains", "equals": "lik", - "isNotEqualTo": "er ikke lik", - "isIn": "er i", - "isNotIn": "er ikke med", "exists": "eksisterer", "isGreaterThan": "er større enn", + "isGreaterThanOrEqualTo": "er større enn eller lik", + "isIn": "er i", "isLessThan": "er mindre enn", "isLessThanOrEqualTo": "er mindre enn eller lik", - "isGreaterThanOrEqualTo": "er større enn eller lik", - "near": "nær", "isLike": "er som", - "contains": "contains" + "isNotEqualTo": "er ikke lik", + "isNotIn": "er ikke med", + "near": "nær" }, "upload": { + "dragAndDrop": "Dra og slipp en fil", "dragAndDropHere": "eller dra og slipp en fil her", "fileName": "Filnavn", "fileSize": "Filstørrelse", @@ -251,7 +266,6 @@ "moreInfo": "Mer info", "selectCollectionToBrowse": "Velg en samling å bla i", "selectFile": "Velg en fil", - "dragAndDrop": "Dra og slipp en fil", "sizes": "Størrelser", "width": "Bredde" }, @@ -275,15 +289,18 @@ "validUploadID": "Dette feltet er ikke en gyldig opplastings-ID." }, "version": { + "aboutToPublishSelection": "Du er i ferd med å publisere alle {{label}} i utvalget. Er du sikker?", "aboutToRestore": "Du er i ferd med å gjenopprette denne {{label}} dokumentet til tilstanden det var i på {{versionDate}}.", "aboutToRestoreGlobal": "Du er i ferd med å gjenopprette den globale variabelen {{label}} til tilstanden det var i på {{versionDate}}.", "aboutToRevertToPublished": "Du er i ferd med å tilbakestille endringene i dette dokumentet til den publiserte tilstanden. Er du sikker?", "aboutToUnpublish": "Du er i ferd med å avpublisere dette dokumentet. Er du sikker?", + "aboutToUnpublishSelection": "Du er i ferd med å oppheve publiseringen av alle {{label}} i utvalget. Er du sikker?", "autosave": "Lagre automatisk", "autosavedSuccessfully": "Lagret automatisk.", "autosavedVersion": "Automatisk lagret versjon", "changed": "Endret", "compareVersion": "Sammenlign versjon mot:", + "confirmPublish": "Bekreft publisering", "confirmRevertToSaved": "Bekreft tilbakestilling til lagret", "confirmUnpublish": "Bekreft avpublisering", "confirmVersionRestoration": "Bekreft versjon-gjenoppretting", @@ -295,6 +312,7 @@ "noRowsFound": "Ingen {{label}} funnet", "preview": "Forhåndsvisning", "problemRestoringVersion": "Det oppstod et problem med gjenoppretting av denne versjonen", + "publish": "Publisere", "publishChanges": "Publiser endringer", "published": "Publisert", "restoreThisVersion": "Gjenopprett denne versjonen", @@ -305,8 +323,8 @@ "saveDraft": "Lagre utkast", "selectLocales": "Velg språk å vise", "selectVersionToCompare": "Velg en versjon å sammenligne", - "showingVersionsFor": "Viser versjoner for:", "showLocales": "Vis språk:", + "showingVersionsFor": "Viser versjoner for:", "status": "Status", "type": "Type", "unpublish": "Avpubliser", diff --git a/src/translations/nl.json b/src/translations/nl.json index 520bf77660..44960802db 100644 --- a/src/translations/nl.json +++ b/src/translations/nl.json @@ -82,6 +82,8 @@ "problemUploadingFile": "Er was een probleem bij het uploaden van het bestand.", "tokenInvalidOrExpired": "Token is ongeldig of verlopen.", "unPublishingDocument": "Er was een probleem met het depubliceren van dit document.", + "unableToDeleteCount": "Kan {{count}} van {{total}} {{label}} niet verwijderen.", + "unableToUpdateCount": "Kan {{count}} van {{total}} {{label}} niet updaten.", "unauthorized": "Ongeautoriseerd, u moet ingelogd zijn om dit verzoek te doen.", "unknown": "Er is een onbekende fout opgetreden.", "unspecific": "Er is een fout opgetreden.", @@ -90,27 +92,34 @@ "verificationTokenInvalid": "Verificatietoken is ongeldig." }, "fields": { - "block": "blok", - "blocks": "blokken", "addLabel": "Voeg {{label}} toe", "addLink": "Voeg een link toe", "addNew": "Nieuw(e)", "addNewLabel": "Nieuw(e) {{label}} toevoegen", "addRelationship": "Nieuwe Relatie", "addUpload": "Upload Toevoegen", + "block": "blok", "blockType": "Bloktype", + "blocks": "blokken", + "chooseBetweenCustomTextOrDocument": "Kies tussen het invoeren van een aangepaste tekst-URL of een koppeling naar een ander document.", + "chooseDocumentToLink": "Kies een document om naar te linken", "chooseFromExisting": "Kies uit bestaande", "chooseLabel": "Kies {{label}}", "collapseAll": "Alles samenvouwen", + "customURL": "Eigen URL", "editLabelData": "Bewerk gegevens van {{label}}", "editLink": "Link bewerken", "editRelationship": "Relatie Relatie", + "enterURL": "Voer een URL in", + "internalLink": "Interne koppeling", "itemsAndMore": "{{items}} en {{count}} meer", "labelRelationship": "{{label}} relatie", "latitude": "Breedtegraad", + "linkType": "Koppelingstype", "linkedTo": "Gekoppeld aan aan <0>{{label}}", "longitude": "Lengtegraad", "newLabel": "Nieuw(e) {{label}}", + "openInNewTab": "Openen in nieuw tabblad", "passwordsDoNotMatch": "Wachtwoorden komen niet overeen.", "relatedDocument": "Gerelateerd document", "relationTo": "Relatie tot", @@ -119,18 +128,24 @@ "saveChanges": "Bewaar aanpassingen", "searchForBlock": "Zoeken naar een blok", "selectExistingLabel": "Selecteer bestaand(e) {{label}}", + "selectFieldsToEdit": "Selecteer velden om te bewerken", "showAll": "Alles tonen", "swapRelationship": "Relatie Wisselen", "swapUpload": "Upload Verwisselen", + "textToDisplay": "Tekst om weer te geven", "toggleBlock": "Blok togglen", "uploadNewLabel": "Upload nieuw(e) {{label}}" }, "general": { "aboutToDelete": "U staat op het punt om {{label}} <1>{{title}} te verwijderen. Weet u het zeker?", + "aboutToDeleteCount_many": "Je staat op het punt {{count}} {{label}} te verwijderen", + "aboutToDeleteCount_one": "Je staat op het punt {{count}} {{label}} te verwijderen", + "aboutToDeleteCount_other": "Je staat op het punt {{count}} {{label}} te verwijderen", "addBelow": "Onderaan toevoegen", "addFilter": "Filter toevoegen", "adminTheme": "Adminthema", "and": "En", + "ascending": "Oplopend", "automatic": "Automatisch", "backToDashboard": "Terug naar dashboard", "cancel": "Annuleren", @@ -153,6 +168,7 @@ "dark": "Donker", "dashboard": "Dashboard", "delete": "Verwijderen", + "deletedCountSuccessfully": "{{count}} {{label}} succesvol verwijderd.", "deletedSuccessfully": "Succesvol verwijderd.", "deleting": "Verwijderen...", "descending": "Aflopend", @@ -161,6 +177,9 @@ "edit": "Bewerk", "editLabel": "Bewerk {{label}}", "editing": "Bewerken", + "editingLabel_many": "Bewerken {{count}} {{label}}", + "editingLabel_one": "Bewerken {{count}} {{label}}", + "editingLabel_other": "Bewerken {{count}} {{label}}", "email": "E-mail", "emailAddress": "E-maildres", "enterAValue": "Waarde invoeren", @@ -199,7 +218,9 @@ "save": "Bewaar", "saving": "Bewaren...", "searchBy": "Zoeken op {{label}}", + "selectAll": "Alles selecteren {{count}} {{label}}", "selectValue": "Selecteer een waarde", + "selectedCount": "{{count}} {{label}} geselecteerd", "sorryNotFound": "Sorry, er is niets dat overeen komt met uw verzoek.", "sort": "Sorteer", "stayOnThisPage": "Blijf op deze pagina", @@ -213,6 +234,7 @@ "unsavedChangesDuplicate": "U heeft onbewaarde wijzigingen. Wilt u doorgaan met dupliceren?", "untitled": "Zonder titel", "updatedAt": "Aangepast op", + "updatedCountSuccessfully": "{{count}} {{label}} succesvol bijgewerkt.", "updatedSuccessfully": "Succesvol aangepast.", "updating": "Bijwerken", "uploading": "Uploaden", @@ -221,20 +243,21 @@ "welcome": "Welkom" }, "operators": { + "contains": "bevat", "equals": "is gelijk aan", - "isNotEqualTo": "is niet gelijk aan", - "isIn": "is binnen", - "isNotIn": "zit er niet in", "exists": "bestaat", "isGreaterThan": "is groter dan", + "isGreaterThanOrEqualTo": "is groter dan of gelijk aan", + "isIn": "is binnen", "isLessThan": "is kleiner dan", "isLessThanOrEqualTo": "is kleiner dan of gelijk aan", - "isGreaterThanOrEqualTo": "is groter dan of gelijk aan", - "near": "nabij", "isLike": "is als", - "contains": "bevat" + "isNotEqualTo": "is niet gelijk aan", + "isNotIn": "zit er niet in", + "near": "nabij" }, "upload": { + "dragAndDrop": "Sleep een bestand", "dragAndDropHere": "of sleep een bestand naar hier", "fileName": "Bestandsnaam", "fileSize": "Bestandsgrootte", @@ -243,7 +266,6 @@ "moreInfo": "Meer info", "selectCollectionToBrowse": "Selecteer een collectie om door te bladeren", "selectFile": "Selecteer een bestand", - "dragAndDrop": "Sleep een bestand", "sizes": "Groottes", "width": "Breedte" }, @@ -267,15 +289,18 @@ "validUploadID": "Dit veld is geen geldige upload-ID." }, "version": { + "aboutToPublishSelection": "Je staat op het punt om alle {{label}} in de selectie te publiceren. Weet je het zeker?", "aboutToRestore": "U staat op het punt dit {{label}} document te herstellen in de staat waarin het zich bevond op {{versionDate}}.", "aboutToRestoreGlobal": "U staat op het punt om de global {{label}} te herstellen in de staat waarin het zich bevond op {{versionDate}}.", "aboutToRevertToPublished": "U staat op het punt om de wijzigingen van dit document terug te draaien naar de gepubliceerde staat. Weet u het zeker?", "aboutToUnpublish": "U staat op het punt om de publicatie van dit document ongedaan te maken. Weet u het zeker?", + "aboutToUnpublishSelection": "You are about to unpublish all {{label}} in the selection. Are you sure?", "autosave": "Automatisch bewaren", "autosavedSuccessfully": "Succesvol automatisch bewaard.", "autosavedVersion": "Automatisch bewaarde versie", "changed": "Gewijzigd", "compareVersion": "Vergelijk versie met:", + "confirmPublish": "Bevestig publiceren", "confirmRevertToSaved": "Bevestig terugdraaien naar bewaarde versie", "confirmUnpublish": "Bevestig depubliceren", "confirmVersionRestoration": "Bevestig te herstellen versie", @@ -287,6 +312,7 @@ "noRowsFound": "Geen {{label}} gevonden", "preview": "Voorbeeld", "problemRestoringVersion": "Er was een probleem bij het herstellen van deze versie", + "publish": "Publiceren", "publishChanges": "Publiceer wijzigingen", "published": "Gepubliceerd", "restoreThisVersion": "Herstel deze versie", @@ -298,6 +324,7 @@ "selectLocales": "Selecteer locales om weer te geven", "selectVersionToCompare": "Selecteer een versie om te vergelijken", "showLocales": "Toon locales:", + "showingVersionsFor": "Versies tonen voor:", "status": "Status", "type": "Type", "unpublish": "Publicatie ongedaan maken", @@ -306,6 +333,7 @@ "versionCount_many": "{{count}} versies gevonden", "versionCount_none": "Geen versies gevonden", "versionCount_one": "{{count}} versie gevonden", + "versionCount_other": "{{count}} versies gevonden", "versionCreatedOn": "{{version}} aangemaakt op:", "versionID": "Versie-ID", "versions": "Versies", diff --git a/src/translations/pl.json b/src/translations/pl.json index 7c725988d3..a297aa2656 100644 --- a/src/translations/pl.json +++ b/src/translations/pl.json @@ -60,6 +60,7 @@ "accountAlreadyActivated": "To konto zostało już aktywowane.", "autosaving": "Wystąpił problem podczas automatycznego zapisywania tego dokumentu.", "correctInvalidFields": "Popraw nieprawidłowe pola.", + "deletingFile": "", "deletingTitle": "Wystąpił błąd podczas usuwania {{title}}. Proszę, sprawdź swoje połączenie i spróbuj ponownie.", "emailOrPasswordIncorrect": "Podany adres e-mail lub hasło jest nieprawidłowe.", "followingFieldsInvalid_many": "Następujące pola są nieprawidłowe:", @@ -81,6 +82,8 @@ "problemUploadingFile": "Wystąpił problem podczas przesyłania pliku.", "tokenInvalidOrExpired": "Token jest nieprawidłowy lub wygasł.", "unPublishingDocument": "Wystąpił problem podczas cofania publikacji tego dokumentu.", + "unableToDeleteCount": "Nie można usunąć {{count}} z {{total}} {{label}}.", + "unableToUpdateCount": "Nie można zaktualizować {{count}} z {{total}} {{label}}.", "unauthorized": "Brak dostępu, musisz być zalogowany.", "unknown": "Wystąpił nieznany błąd.", "unspecific": "Wystąpił błąd", @@ -89,27 +92,34 @@ "verificationTokenInvalid": "Token weryfikacyjny jest nieprawidłowy." }, "fields": { - "block": "Blok", - "blocks": "Bloki", "addLabel": "Dodaj {{label}}", "addLink": "Dodaj Link", "addNew": "Dodaj nowy", "addNewLabel": "Dodaj nowy {{label}}", "addRelationship": "Dodaj Relacje", "addUpload": "Dodaj ładowanie", + "block": "Blok", "blockType": "Typ Bloku", + "blocks": "Bloki", + "chooseBetweenCustomTextOrDocument": "Wybierz między wprowadzeniem niestandardowego tekstowego adresu URL a linkiem do innego dokumentu.", + "chooseDocumentToLink": "Wybierz dokument, do którego chcesz utworzyć łącze", "chooseFromExisting": "Wybierz z istniejących", "chooseLabel": "Wybierz {{label}}", "collapseAll": "Zwiń wszystko", + "customURL": "Niestandardowy adres URL", "editLabelData": "Edytuj dane {{label}}", "editLink": "Edytuj Link", "editRelationship": "Edytuj Relacje", + "enterURL": "Wpisz adres URL", + "internalLink": "Link wewnętrzny", "itemsAndMore": "{{items}} i {{count}} więcej", "labelRelationship": "Relacja {{label}}", "latitude": "Szerokość", + "linkType": "Typ łącza", "linkedTo": "Połączony z <0>{{etykietą}}", "longitude": "Długość geograficzna", "newLabel": "Nowy {{label}}", + "openInNewTab": "Otwórz w nowej karcie", "passwordsDoNotMatch": "Hasła nie pasują", "relatedDocument": "Powiązany dokument", "relationTo": "Powiązany z", @@ -118,18 +128,24 @@ "saveChanges": "Zapisz zmiany", "searchForBlock": "Szukaj bloku", "selectExistingLabel": "Wybierz istniejący {{label}}", + "selectFieldsToEdit": "Wybierz pola do edycji", "showAll": "Pokaż wszystkie", "swapRelationship": "Zamiana Relacji", "swapUpload": "Zamień Wrzucone", + "textToDisplay": "Tekst do wyświetlenia", "toggleBlock": "Przełącz blok", "uploadNewLabel": "Wrzuć nowy {{label}}" }, "general": { "aboutToDelete": "Zamierzasz usunąć {{label}} <1>{{title}}. Jesteś pewien?", + "aboutToDeleteCount_many": "Zamierzasz usunąć {{count}} {{label}}", + "aboutToDeleteCount_one": "Zamierzasz usunąć {{count}} {{label}}", + "aboutToDeleteCount_other": "Zamierzasz usunąć {{count}} {{label}}", "addBelow": "Dodaj boniżej", "addFilter": "Dodaj filtr", "adminTheme": "Motyw administratora", "and": "I", + "ascending": "Rosnąco", "automatic": "Automatyczny", "backToDashboard": "Powrót do panelu", "cancel": "Anuluj", @@ -152,6 +168,7 @@ "dark": "Ciemny", "dashboard": "Panel", "delete": "Usuń", + "deletedCountSuccessfully": "", "deletedSuccessfully": "Skutecznie usunięte.", "deleting": "Usuwanie...", "descending": "Malejąco", @@ -160,6 +177,9 @@ "edit": "Edytować", "editLabel": "Edytuj {{label}}", "editing": "Edycja", + "editingLabel_many": "Edytowanie {{count}} {{label}}", + "editingLabel_one": "Edytowanie {{count}} {{label}}", + "editingLabel_other": "Edytowanie {{count}} {{label}}", "email": "Email", "emailAddress": "Adres email", "enterAValue": "Wpisz wartość", @@ -198,7 +218,9 @@ "save": "Zapisz", "saving": "Zapisywanie...", "searchBy": "Szukaj według", + "selectAll": "Wybierz wszystkie {{liczba}} {{etykieta}}", "selectValue": "Wybierz wartość", + "selectedCount": "Wybrano {{count}} {{label}}", "sorryNotFound": "Przepraszamy — nie ma nic, co odpowiadałoby twojej prośbie.", "sort": "Sortuj", "stayOnThisPage": "Pozostań na stronie", @@ -212,6 +234,7 @@ "unsavedChangesDuplicate": "Masz niezapisane zmiany. Czy chcesz kontynuować duplikowanie?", "untitled": "Bez nazwy", "updatedAt": "Data edycji", + "updatedCountSuccessfully": "Pomyślnie zaktualizowano {{count}} {{label}}.", "updatedSuccessfully": "Aktualizacja zakończona sukcesem.", "updating": "Aktualizacja", "uploading": "Wgrywanie", @@ -220,20 +243,21 @@ "welcome": "Witaj" }, "operators": { + "contains": "zawiera", "equals": "równe", - "isNotEqualTo": "nie jest równe", - "isIn": "jest w", - "isNotIn": "nie ma go w", "exists": "istnieje", "isGreaterThan": "jest większy niż", + "isGreaterThanOrEqualTo": "jest większe lub równe", + "isIn": "jest w", "isLessThan": "jest mniejsze niż", "isLessThanOrEqualTo": "jest mniejsze lub równe", - "isGreaterThanOrEqualTo": "jest większe lub równe", - "near": "blisko", "isLike": "jest jak", - "contains": "zawiera" + "isNotEqualTo": "nie jest równe", + "isNotIn": "nie ma go w", + "near": "blisko" }, "upload": { + "dragAndDrop": "Przeciągnij i upuść plik", "dragAndDropHere": "lub złap i upuść plik tutaj", "fileName": "Nazwa pliku", "fileSize": "Rozmiar pliku", @@ -242,7 +266,6 @@ "moreInfo": "Więcej informacji", "selectCollectionToBrowse": "Wybierz kolekcję aby przejrzeć", "selectFile": "Wybierz plik", - "dragAndDrop": "Przeciągnij i upuść plik", "sizes": "Rozmiary", "width": "Szerokość" }, @@ -266,15 +289,18 @@ "validUploadID": "To pole nie jest prawidłowym identyfikatorem przesyłania." }, "version": { + "aboutToPublishSelection": "Za chwilę opublikujesz wszystkie {{label}} w zaznaczeniu. Jesteś pewny?", "aboutToRestore": "Zamierzasz przywrócić dokument {{label}} do stanu, w jakim znajdował się w dniu {{versionDate}}.", "aboutToRestoreGlobal": "Zamierzasz przywrócić globalny rekord {{label}} do stanu, w którym znajdował się w dniu {{versionDate}}.", "aboutToRevertToPublished": "Zamierzasz przywrócić zmiany w tym dokumencie do stanu opublikowanego. Jesteś pewien?", "aboutToUnpublish": "Zamierzasz cofnąć publikację tego dokumentu. Jesteś pewien?", + "aboutToUnpublishSelection": "Zamierzasz cofnąć publikację wszystkich {{label}} w zaznaczeniu. Jesteś pewny?", "autosave": "Autozapis", "autosavedSuccessfully": "Pomyślnie zapisano automatycznie.", "autosavedVersion": "Wersja zapisana automatycznie", "changed": "Zmieniono", "compareVersion": "Porównaj wersję z:", + "confirmPublish": "Potwierdź publikację", "confirmRevertToSaved": "Potwierdź powrót do zapisanego", "confirmUnpublish": "Potwierdź cofnięcie publikacji", "confirmVersionRestoration": "Potwierdź przywrócenie wersji", @@ -286,6 +312,7 @@ "noRowsFound": "Nie znaleziono {{label}}", "preview": "Podgląd", "problemRestoringVersion": "Wystąpił problem podczas przywracania tej wersji", + "publish": "Publikować", "publishChanges": "Opublikuj zmiany", "published": "Opublikowano", "restoreThisVersion": "Przywróć tę wersję", @@ -297,6 +324,7 @@ "selectLocales": "Wybierz lokalizacje do wyświetlenia", "selectVersionToCompare": "Wybierz wersję do porównania", "showLocales": "Pokaż lokalizacje:", + "showingVersionsFor": "Wyświetlanie wersji dla:", "status": "Status", "type": "Typ", "unpublish": "Cofnij publikację", @@ -305,6 +333,7 @@ "versionCount_many": "Znalezionych wersji: {{count}}", "versionCount_none": "Nie znaleziono wersji", "versionCount_one": "Znaleziono {{count}} wersję", + "versionCount_other": "Znaleziono {{count}} wersji", "versionCreatedOn": "Wersja {{version}} utworzona:", "versionID": "ID wersji", "versions": "Wersje", diff --git a/src/translations/pt.json b/src/translations/pt.json index 41324d40a8..4a8422810a 100644 --- a/src/translations/pt.json +++ b/src/translations/pt.json @@ -60,6 +60,7 @@ "accountAlreadyActivated": "Essa conta já foi ativada.", "autosaving": "Ocorreu um problema ao salvar automaticamente esse documento.", "correctInvalidFields": "Por favor, corrija os campos inválidos.", + "deletingFile": "Ocorreu um erro ao excluir o arquivo.", "deletingTitle": "Ocorreu um erro ao excluir {{title}}. Por favor, verifique sua conexão e tente novamente.", "emailOrPasswordIncorrect": "O email ou senha fornecido está incorreto.", "followingFieldsInvalid_many": "Os campos a seguir estão inválidos:", @@ -81,6 +82,8 @@ "problemUploadingFile": "Ocorreu um problema ao carregar o arquivo.", "tokenInvalidOrExpired": "Token expirado ou inválido.", "unPublishingDocument": "Ocorreu um problema ao despublicar esse documento", + "unableToDeleteCount": "Não é possível excluir {{count}} de {{total}} {{label}}.", + "unableToUpdateCount": "Não foi possível atualizar {{count}} de {{total}} {{label}}.", "unauthorized": "Não autorizado. Você deve estar logado para fazer essa requisição", "unknown": "Ocorreu um erro desconhecido.", "unspecific": "Ocorreu um erro.", @@ -89,27 +92,34 @@ "verificationTokenInvalid": "Token de verificação inválido." }, "fields": { - "block": "bloco", - "blocks": "blocos", "addLabel": "Adicionar {{label}}", "addLink": "Adicionar Link", "addNew": "Adicionar novo", "addNewLabel": "Adicionar novo {{label}}", "addRelationship": "Adicionar Relação", "addUpload": "Adicionar Upload", + "block": "bloco", "blockType": "Tipo de bloco", + "blocks": "blocos", + "chooseBetweenCustomTextOrDocument": "Escolha entre inserir um URL de texto personalizado ou vincular a outro documento.", + "chooseDocumentToLink": "Escolha um documento para vincular", "chooseFromExisting": "Escolher entre os existentes", "chooseLabel": "Escolher {{label}}", "collapseAll": "Recolher todos", + "customURL": "URL personalizado", "editLabelData": "Editar dados de {{label}}", "editLink": "Editar Link", "editRelationship": "Editar Relacionamento", + "enterURL": "Insira um URL", + "internalLink": "Link Interno", "itemsAndMore": "{{items}} e mais {{count}}", "labelRelationship": "Relacionado a {{label}}", "latitude": "Latitude", + "linkType": "Tipo de link", "linkedTo": "Ligado a <0>{{label}}", "longitude": "Longitude", "newLabel": "Novo(a) {{label}}", + "openInNewTab": "Abrir em nova aba", "passwordsDoNotMatch": "Senhas não coincidem.", "relatedDocument": "Documento Relacionado", "relationTo": "Relacionado a", @@ -118,18 +128,24 @@ "saveChanges": "Salvar alterações", "searchForBlock": "Procurar bloco", "selectExistingLabel": "Selecionar {{label}} existente", + "selectFieldsToEdit": "Selecione os campos para editar", "showAll": "Mostrar Tudo", "swapRelationship": "Relação de Troca", "swapUpload": "Substituir Upload", + "textToDisplay": "Texto a ser exibido", "toggleBlock": "Alternar bloco", "uploadNewLabel": "Carregar novo(a) {{label}}" }, "general": { "aboutToDelete": "Você está prestes a excluir o/a {{label}} <1>{{title}}. Tem certeza?", + "aboutToDeleteCount_many": "Você está prestes a deletar {{count}} {{label}}", + "aboutToDeleteCount_one": "Você está prestes a deletar {{count}} {{label}}", + "aboutToDeleteCount_other": "Você está prestes a deletar {{count}} {{label}}", "addBelow": "Adicionar abaixo", "addFilter": "Adicionar Filtro", "adminTheme": "Tema do Admin", "and": "E", + "ascending": "Ascendente", "automatic": "Automático", "backToDashboard": "Voltar para Painel de Controle", "cancel": "Cancelar", @@ -152,6 +168,7 @@ "dark": "Escuro", "dashboard": "Painel de Controle", "delete": "Excluir", + "deletedCountSuccessfully": "Excluído {{count}} {{label}} com sucesso.", "deletedSuccessfully": "Apagado com sucesso.", "deleting": "Excluindo...", "descending": "Decrescente", @@ -160,6 +177,9 @@ "edit": "Editar", "editLabel": "Editar {{label}}", "editing": "Editando", + "editingLabel_many": "Editando {{count}} {{label}}", + "editingLabel_one": "Editando {{count}} {{label}}", + "editingLabel_other": "Editando {{count}} {{label}}", "email": "Email", "emailAddress": "Endereço de Email", "enterAValue": "Insira um valor", @@ -198,7 +218,9 @@ "save": "Salvar", "saving": "Salvando...", "searchBy": "Buscar por {{label}}", + "selectAll": "Selecione tudo {{count}} {{label}}", "selectValue": "Selecione um valor", + "selectedCount": "{{count}} {{label}} selecionado", "sorryNotFound": "Desculpe—não há nada que corresponda à sua requisição.", "sort": "Ordenar", "stayOnThisPage": "Permanecer nessa página", @@ -212,6 +234,7 @@ "unsavedChangesDuplicate": "Você tem mudanças não salvas. Você gostaria de continuar a duplicar?", "untitled": "Sem título", "updatedAt": "Atualizado Em", + "updatedCountSuccessfully": "Atualizado {{count}} {{label}} com sucesso.", "updatedSuccessfully": "Atualizado com sucesso.", "updating": "Atualizando", "uploading": "Fazendo upload", @@ -220,20 +243,21 @@ "welcome": "Boas vindas" }, "operators": { + "contains": "contém", "equals": "igual", - "isNotEqualTo": "não é igual a", - "isIn": "está em", - "isNotIn": "não está em", "exists": "existe", "isGreaterThan": "é maior que", + "isGreaterThanOrEqualTo": "é maior ou igual a", + "isIn": "está em", "isLessThan": "é menor que", "isLessThanOrEqualTo": "é menor ou igual a", - "isGreaterThanOrEqualTo": "é maior ou igual a", - "near": "perto", "isLike": "é como", - "contains": "contém" + "isNotEqualTo": "não é igual a", + "isNotIn": "não está em", + "near": "perto" }, "upload": { + "dragAndDrop": "Arraste e solte um arquivo", "dragAndDropHere": "ou arraste um arquivo aqui", "fileName": "Nome do Arquivo", "fileSize": "Tamanho do Arquivo", @@ -242,7 +266,6 @@ "moreInfo": "Ver mais", "selectCollectionToBrowse": "Selecione uma Coleção para Navegar", "selectFile": "Selecione um arquivo", - "dragAndDrop": "Arraste e solte um arquivo", "sizes": "Tamanhos", "width": "Largura" }, @@ -266,15 +289,18 @@ "validUploadID": "'Esse campo não é um ID de upload válido.'" }, "version": { + "aboutToPublishSelection": "Você está prestes a publicar todos os {{label}} da seleção. Tem certeza?", "aboutToRestore": "Você está prestes a restaurar o documento {{label}} para o estado em que ele se encontrava em {{versionDate}}.", "aboutToRestoreGlobal": "Você está prestes a restaurar o Global {{label}} para o estado em que ele se encontrava em {{versionDate}}.", "aboutToRevertToPublished": "Você está prestes a reverter as alterações desse documento para seu estado de publicação. Tem certeza?", "aboutToUnpublish": "Você está prestes a despublicar esse documento. Tem certeza?", + "aboutToUnpublishSelection": "Você está prestes a cancelar a publicação de todos os {{label}} na seleção. Tem certeza?", "autosave": "Salvamento automático", "autosavedSuccessfully": "Salvamento automático com sucesso.", "autosavedVersion": "Versão de salvamento automático", "changed": "Alterado", "compareVersion": "Comparar versão com:", + "confirmPublish": "Confirmar publicação", "confirmRevertToSaved": "Confirmar a reversão para o salvo", "confirmUnpublish": "Confirmar despublicação", "confirmVersionRestoration": "Confirmar Restauração de versão", @@ -286,6 +312,7 @@ "noRowsFound": "Nenhum(a) {{label}} encontrado(a)", "preview": "Pré-visualização", "problemRestoringVersion": "Ocorreu um problema ao restaurar essa versão", + "publish": "Publicar", "publishChanges": "Publicar alterações", "published": "Publicado", "restoreThisVersion": "Restaurar essa versão", @@ -297,6 +324,7 @@ "selectLocales": "Selecione as localizações para exibir", "selectVersionToCompare": "Selecione uma versão para comparar", "showLocales": "Exibir localizações:", + "showingVersionsFor": "Mostrando versões para:", "status": "Status", "type": "Tipo", "unpublish": "Despublicar", @@ -305,6 +333,7 @@ "versionCount_many": "{{count}} versões encontradas", "versionCount_none": "Nenhuma versão encontrada", "versionCount_one": "{{count}} versão encontrada", + "versionCount_other": "{{count}} versões encontradas", "versionCreatedOn": "{{version}} criada em:", "versionID": "ID da versão", "versions": "Versões", diff --git a/src/translations/ru.json b/src/translations/ru.json index f129ae3893..a1d7030d02 100644 --- a/src/translations/ru.json +++ b/src/translations/ru.json @@ -82,6 +82,8 @@ "problemUploadingFile": "Возникла проблема при загрузке файла.", "tokenInvalidOrExpired": "Токен либо недействителен, либо срок его действия истек.", "unPublishingDocument": "При отмене публикации этого документа возникла проблема.", + "unableToDeleteCount": "Не удалось удалить {{count}} из {{total}} {{label}}.", + "unableToUpdateCount": "Не удалось обновить {{count}} из {{total}} {{label}}.", "unauthorized": "Нет доступа, вы должны войти, чтобы сделать этот запрос.", "unknown": "Произошла неизвестная ошибка.", "unspecific": "Произошла ошибка.", @@ -126,6 +128,7 @@ "saveChanges": "Сохранить изменения", "searchForBlock": "Найти Блок", "selectExistingLabel": "Выберите существующий {{label}}", + "selectFieldsToEdit": "Выберите поля для редактирования", "showAll": "Показать все", "swapRelationship": "Поменять отношения", "swapUpload": "Заменить загруженное", @@ -135,10 +138,14 @@ }, "general": { "aboutToDelete": "Вы собираетесь удалить {{label}} <1>{{title}}. Вы уверены?", + "aboutToDeleteCount_many": "Вы собираетесь удалить {{count}} {{label}}", + "aboutToDeleteCount_one": "Вы собираетесь удалить {{count}} {{label}}", + "aboutToDeleteCount_other": "Вы собираетесь удалить {{count}} {{label}}", "addBelow": "Добавить ниже", "addFilter": "Добавить фильтр", "adminTheme": "Тема Панели", "and": "А также", + "ascending": "Восходящий", "automatic": "Автоматически", "backToDashboard": "Назад к Панели", "cancel": "Отмена", @@ -161,6 +168,7 @@ "dark": "Тёмная", "dashboard": "Панель", "delete": "Удалить", + "deletedCountSuccessfully": "Удалено {{count}} {{label}} успешно.", "deletedSuccessfully": "Удален успешно.", "deleting": "Удаление...", "descending": "Уменьшение", @@ -169,6 +177,9 @@ "edit": "Редактировать", "editLabel": "Редактировать {{label}}", "editing": "Редактирование", + "editingLabel_many": "Редактирование {{count}} {{label}}", + "editingLabel_one": "Редактирование {{count}} {{label}}", + "editingLabel_other": "Редактирование {{count}} {{label}}", "email": "Email", "emailAddress": "Email", "enterAValue": "Введите значение", @@ -207,7 +218,9 @@ "save": "Сохранить", "saving": "Сохранение...", "searchBy": "Искать по", + "selectAll": "Выбрать все {{count}} {{label}}", "selectValue": "Выбрать значение", + "selectedCount": "{{count}} {{label}} выбрано", "sorryNotFound": "К сожалению, ничего подходящего под ваш запрос нет.", "sort": "Сортировать", "stayOnThisPage": "Остаться на этой странице", @@ -221,6 +234,7 @@ "unsavedChangesDuplicate": "У вас есть несохраненные изменения. Вы хотите продолжить дублирование?", "untitled": "Без названия", "updatedAt": "Дата правки", + "updatedCountSuccessfully": "Обновлено {{count}} {{label}} успешно.", "updatedSuccessfully": "Успешно Обновлено.", "updating": "Обновление", "uploading": "Загрузка", @@ -229,20 +243,21 @@ "welcome": "Добро пожаловать" }, "operators": { + "contains": "содержит", "equals": "равно", - "isNotEqualTo": "не равно", - "isIn": "находится", - "isNotIn": "нет в", "exists": "существует", "isGreaterThan": "больше чем", + "isGreaterThanOrEqualTo": "больше или равно", + "isIn": "находится", "isLessThan": "меньше чем", "isLessThanOrEqualTo": "меньше или равно", - "isGreaterThanOrEqualTo": "больше или равно", - "near": "рядом", "isLike": "похоже", - "contains": "содержит" + "isNotEqualTo": "не равно", + "isNotIn": "нет в", + "near": "рядом" }, "upload": { + "dragAndDrop": "Перетащите файл", "dragAndDropHere": "или перетащите файл сюда", "fileName": "Имя файла", "fileSize": "Размер файла", @@ -251,7 +266,6 @@ "moreInfo": "Больше информации", "selectCollectionToBrowse": "Выберите Коллекцию для просмотра", "selectFile": "Выберите файл", - "dragAndDrop": "Перетащите файл", "sizes": "Размеры", "width": "Ширина" }, @@ -265,25 +279,28 @@ "invalidSelections": "'Это поле содержит следующие неправильные варианты:'", "lessThanMin": "\"{{value}}\" меньше минимально допустимого значения {{min}}.", "longerThanMin": "Это значение должно быть больше минимальной длины символов: {{minLength}}.", - "requiresAtLeast": "Это поле требует не менее {{count}} {{label}}", - "requiresNoMoreThan": "Это поле требует не более {{count}} {{label}}", "notValidDate": "\"{{value}}\" это не действительная дата.", "required": "Это обязательное поле.", + "requiresAtLeast": "Это поле требует не менее {{count}} {{label}}", + "requiresNoMoreThan": "Это поле требует не более {{count}} {{label}}", "requiresTwoNumbers": "В этом поле требуется два числа.", "shorterThanMax": "Это значение должно быть короче максимальной длины символов {{maxLength}}.", "trueOrFalse": "Это поле может быть равно только true или false.", "validUploadID": "'Это поле не является действительным ID загрузки.'" }, "version": { + "aboutToPublishSelection": "Вы собираетесь опубликовать все {{label}} в выборе. Вы уверены?", "aboutToRestore": "Вы собираетесь восстановить этот документ {{label}} в состояние, в котором он находился {{versionDate}}.", "aboutToRestoreGlobal": "Вы собираетесь восстановить глобальную запись {{label}} в состояние, в котором она находилась {{versionDate}}.", "aboutToRevertToPublished": "Вы собираетесь вернуть изменения этого документа к его опубликованному состоянию. Вы уверены?", "aboutToUnpublish": "Вы собираетесь отменить публикацию этого документа. Вы уверены?", + "aboutToUnpublishSelection": "Вы собираетесь отменить публикацию всех выбранных {{label}}. Вы уверены?", "autosave": "Автосохранение", "autosavedSuccessfully": "Автосохранение успешно.", "autosavedVersion": "Автоматически сохраненная версия", "changed": "Изменено", "compareVersion": "Сравнить версию с:", + "confirmPublish": "Подтвердить публикацию", "confirmRevertToSaved": "Подтвердить возврат к сохраненному", "confirmUnpublish": "Подтвердить отмену публикации", "confirmVersionRestoration": "Подтвердить восстановление версии", @@ -295,6 +312,7 @@ "noRowsFound": "Не найдено {{label}}", "preview": "Предпросмотр", "problemRestoringVersion": "Возникла проблема с восстановлением этой версии", + "publish": "Публиковать", "publishChanges": "Опубликовать изменения", "published": "Опубликовано", "restoreThisVersion": "Восстановить эту версию", @@ -305,8 +323,8 @@ "saveDraft": "Сохранить черновик", "selectLocales": "Выберите локали для отображения", "selectVersionToCompare": "Выбрать версию для сравнения", - "showingVersionsFor": "Показаны версии для:", "showLocales": "Показать локали:", + "showingVersionsFor": "Показаны версии для:", "status": "Статус", "type": "Тип", "unpublish": "Отменить публикацию", diff --git a/src/translations/sv.json b/src/translations/sv.json index 7c53b4f7e4..e9767b15c2 100644 --- a/src/translations/sv.json +++ b/src/translations/sv.json @@ -82,6 +82,8 @@ "problemUploadingFile": "Det uppstod ett problem när filen laddades upp.", "tokenInvalidOrExpired": "Token är antingen ogiltig eller har löpt ut.", "unPublishingDocument": "Det uppstod ett problem när det här dokumentet skulle avpubliceras.", + "unableToDeleteCount": "Det gick inte att ta bort {{count}} av {{total}} {{label}}.", + "unableToUpdateCount": "Det gick inte att uppdatera {{count}} av {{total}} {{label}}.", "unauthorized": "Obehörig, du måste vara inloggad för att göra denna begäran.", "unknown": "Ett okänt fel har uppstått.", "unspecific": "Ett fel har uppstått.", @@ -90,27 +92,34 @@ "verificationTokenInvalid": "Verifieringstoken är ogiltig." }, "fields": { - "block": "block", - "blocks": "block", "addLabel": "Lägg till {{label}}", "addLink": "Lägg till Länk", "addNew": "Lägg till ny", "addNewLabel": "Lägg till ny {{label}}", "addRelationship": "Lägg till Relation", "addUpload": "Lägg till Uppladdning", + "block": "block", "blockType": "Block Typ", + "blocks": "block", + "chooseBetweenCustomTextOrDocument": "Välj mellan att ange en anpassad text-URL eller länka till ett annat dokument.", + "chooseDocumentToLink": "Välj ett dokument att länka till", "chooseFromExisting": "Välj bland befintliga", "chooseLabel": "Välj {{label}}", "collapseAll": "kollapsa Alla", + "customURL": "Anpassad URL", "editLabelData": "Redigera {{label}} data", "editLink": "Redigera Länk", "editRelationship": "Redigera Relation", + "enterURL": "Ange en URL", + "internalLink": "Intern länk", "itemsAndMore": "{{items}} och {{count}} mer", "labelRelationship": "{{label}} Relation", "latitude": "Latitud", + "linkType": "Länktyp", "linkedTo": "Länkad till <0>{{label}}", "longitude": "Longitud", "newLabel": "Ny {{label}}", + "openInNewTab": "Öppna i ny flik", "passwordsDoNotMatch": "Lösenorden matchar inte.", "relatedDocument": "Relaterat Dokument", "relationTo": "Relation till", @@ -119,18 +128,24 @@ "saveChanges": "Spara ändringar", "searchForBlock": "Sök efter ett block", "selectExistingLabel": "Välj befintlig {{label}}", + "selectFieldsToEdit": "Välj fält att redigera", "showAll": "Visa Alla", "swapRelationship": "Byt Förhållande", "swapUpload": "Byt Uppladdning", + "textToDisplay": "Text att visa", "toggleBlock": "Växla block", "uploadNewLabel": "Ladda upp ny {{label}}" }, "general": { "aboutToDelete": "Du är på väg att ta bort {{label}} <1>{{title}}. Är du säker?", + "aboutToDeleteCount_many": "Du är på väg att ta bort {{count}} {{label}}", + "aboutToDeleteCount_one": "Du är på väg att ta bort {{count}} {{label}}", + "aboutToDeleteCount_other": "Du är på väg att ta bort {{count}} {{label}}", "addBelow": "Lägg Till Nedanför", "addFilter": "Lägg Till Filter", "adminTheme": "Admin Tema", "and": "Och", + "ascending": "Stigande", "automatic": "Automatisk", "backToDashboard": "Tillbaka till Manöverpanelen", "cancel": "Avbryt", @@ -153,6 +168,7 @@ "dark": "Mörk", "dashboard": "Manöverpanel", "delete": "Ta bort", + "deletedCountSuccessfully": "Raderade {{count}} {{label}} framgångsrikt.", "deletedSuccessfully": "Togs bort framgångsrikt.", "deleting": "Tar bort...", "descending": "Fallande", @@ -161,6 +177,9 @@ "edit": "Redigera", "editLabel": "Redigera {{label}}", "editing": "Redigerar", + "editingLabel_many": "Redigerar {{count}} {{label}}", + "editingLabel_one": "Redigerar {{count}} {{label}}", + "editingLabel_other": "Redigerar {{count}} {{label}}", "email": "E-post", "emailAddress": "E-postadress", "enterAValue": "Ange ett värde", @@ -199,7 +218,9 @@ "save": "Spara", "saving": "Sparar...", "searchBy": "Sök efter {{label}}", + "selectAll": "Välj alla {{count}} {{label}}", "selectValue": "Välj ett värde", + "selectedCount": "{{count}} {{label}} har valts", "sorryNotFound": "Tyvärr–det finns inget som motsvarar din begäran.", "sort": "Sortera", "stayOnThisPage": "Stanna på denna sida", @@ -213,6 +234,7 @@ "unsavedChangesDuplicate": "Du har osparade ändringar. Vill du fortsätta att duplicera?", "untitled": "Namnlös", "updatedAt": "Uppdaterades Vid", + "updatedCountSuccessfully": "Uppdaterade {{count}} {{label}} framgångsrikt.", "updatedSuccessfully": "Uppdaterades framgångsrikt.", "updating": "Uppdatering", "uploading": "Uppladdning", @@ -221,20 +243,21 @@ "welcome": "Välkommen" }, "operators": { + "contains": "innehåller", "equals": "likar med", - "isNotEqualTo": "är inte lika med", - "isIn": "är med", - "isNotIn": "är inte med", "exists": "finns", "isGreaterThan": "är större än", + "isGreaterThanOrEqualTo": "är större än eller lika med", + "isIn": "är med", "isLessThan": "är mindre än", "isLessThanOrEqualTo": "är mindre än eller lika med", - "isGreaterThanOrEqualTo": "är större än eller lika med", - "near": "nära", "isLike": "är som", - "contains": "innehåller" + "isNotEqualTo": "är inte lika med", + "isNotIn": "är inte med", + "near": "nära" }, "upload": { + "dragAndDrop": "Dra och släpp en fil", "dragAndDropHere": "eller dra och släpp en fil här", "fileName": "Filnamn", "fileSize": "Filstorlek", @@ -243,7 +266,6 @@ "moreInfo": "Mer info", "selectCollectionToBrowse": "Välj en Samling att Bläddra i", "selectFile": "Välj en fil", - "dragAndDrop": "Dra och släpp en fil", "sizes": "Storlekar", "width": "Bredd" }, @@ -267,15 +289,18 @@ "validUploadID": "Det här fältet är inte ett giltigt uppladdnings-ID" }, "version": { + "aboutToPublishSelection": "Du är på väg att publicera alla {{label}} i urvalet. Är du säker?", "aboutToRestore": "Du är på väg att återställa detta {{label}} dokumentet till det tillståndet som det var den {{versionDate}}.", "aboutToRestoreGlobal": "Du är på väg att återställa det globala {{label}} till det tillståndet som det var den {{versionDate}}.", "aboutToRevertToPublished": "Du är på väg att återställa det här dokumentets ändringar till dess publicerade tillstånd. Är du säker?", "aboutToUnpublish": "Du håller på att avpublicera detta dokumentet. Är du säker?", + "aboutToUnpublishSelection": "Du är på väg att avpublicera alla {{label}} i urvalet. Är du säker?", "autosave": "Automatisk sparning", "autosavedSuccessfully": "Autosparades framgångsrikt.", "autosavedVersion": "Autosparad version", "changed": "Ändrad", "compareVersion": "Jämför version med:", + "confirmPublish": "Bekräfta publicering", "confirmRevertToSaved": "Bekräfta återgång till sparad", "confirmUnpublish": "Bekräfta avpublicering", "confirmVersionRestoration": "Bekräfta Versionsåterställning", @@ -287,6 +312,7 @@ "noRowsFound": "Inga {{label}} hittades", "preview": "Förhandsvisa", "problemRestoringVersion": "Det uppstod ett problem när den här versionen skulle återställas", + "publish": "Publicera", "publishChanges": "Publicera ändringar", "published": "Publicerad", "restoreThisVersion": "Återställ den här versionen", @@ -298,6 +324,7 @@ "selectLocales": "Välj språk att visa", "selectVersionToCompare": "Välj en version att jämföra", "showLocales": "Visa språk:", + "showingVersionsFor": "Visar versioner för:", "status": "Status", "type": "Typ", "unpublish": "Avpublicera", @@ -306,6 +333,7 @@ "versionCount_many": "{{count}} versioner hittades", "versionCount_none": "Inga versioner hittades", "versionCount_one": "{{count}} version hittades", + "versionCount_other": "{{count}} versioner hittades", "versionCreatedOn": "{{version}} skapad den:", "versionID": "Versions-ID", "versions": "Versioner", diff --git a/src/translations/th.json b/src/translations/th.json index f702e4ff80..75d2a82e49 100644 --- a/src/translations/th.json +++ b/src/translations/th.json @@ -82,6 +82,8 @@ "problemUploadingFile": "เกิดปัญหาระหว่างการอัปโหลดไฟล์", "tokenInvalidOrExpired": "Token ไม่ถูกต้องหรือหมดอายุ", "unPublishingDocument": "เกิดปัญหาระหว่างการยกเลิกการเผยแพร่เอกสารนี้", + "unableToDeleteCount": "ไม่สามารถลบ {{count}} จาก {{total}} {{label}}", + "unableToUpdateCount": "ไม่สามารถอัปเดต {{count}} จาก {{total}} {{label}}", "unauthorized": "คุณไม่ได้รับอนุญาต กรุณาเข้าสู่ระบบเพื่อทำคำขอนี้", "unknown": "เกิดปัญหาบางอย่างที่ไม่ทราบสาเหตุ", "unspecific": "เกิดปัญหาบางอย่าง", @@ -91,8 +93,8 @@ }, "fields": { "addLabel": "เพิ่ม {{label}}", - "addNew": "เพิ่ม", "addLink": "เพิ่มลิงค์", + "addNew": "เพิ่ม", "addNewLabel": "เพิ่ม {{label}} ใหม่", "addRelationship": "เพิ่มความสัมพันธ์", "addUpload": "เพิ่มการอัปโหลด", @@ -126,6 +128,7 @@ "saveChanges": "บันทึก", "searchForBlock": "ค้นหา Block", "selectExistingLabel": "เลือก {{label}} ที่มีอยู่", + "selectFieldsToEdit": "เลือกช่องที่จะแก้ไข", "showAll": "แสดงทั้งหมด", "swapRelationship": "สลับความสัมพันธ์", "swapUpload": "สลับอัปโหลด", @@ -135,6 +138,9 @@ }, "general": { "aboutToDelete": "คุณกำลังจะลบ {{label}} <1>{{title}} ต้องการดำเนินการต่อหรือไม่?", + "aboutToDeleteCount_many": "คุณกำลังจะลบ {{count}} {{label}}", + "aboutToDeleteCount_one": "คุณกำลังจะลบ {{count}} {{label}}", + "aboutToDeleteCount_other": "คุณกำลังจะลบ {{count}} {{label}}", "addBelow": "เพิ่มด้านล่าง", "addFilter": "เพิ่มการกรอง", "adminTheme": "ธีมผู้ดูแลระบบ", @@ -162,6 +168,7 @@ "dark": "มืด", "dashboard": "แดชบอร์ด", "delete": "ลบ", + "deletedCountSuccessfully": "Deleted {{count}} {{label}} successfully.", "deletedSuccessfully": "ลบสำเร็จ", "deleting": "กำลังลบ...", "descending": "มากไปน้อย", @@ -170,6 +177,9 @@ "edit": "แก้ไข", "editLabel": "แก้ไข {{label}}", "editing": "แก้ไข", + "editingLabel_many": "กำลังแก้ไข {{count}} {{label}}", + "editingLabel_one": "กำลังแก้ไข {{count}} {{label}}", + "editingLabel_other": "กำลังแก้ไข {{count}} {{label}}", "email": "อีเมล", "emailAddress": "อีเมล", "enterAValue": "ระบุค่า", @@ -208,7 +218,9 @@ "save": "บันทึก", "saving": "กำลังบันทึก...", "searchBy": "ค้นหาด้วย {{label}}", + "selectAll": "เลือกทั้งหมด {{count}} {{label}}", "selectValue": "เลือกค่า", + "selectedCount": "เลือก {{count}} {{label}} แล้ว", "sorryNotFound": "ขออภัย ไม่สามารถทำตามคำขอของคุณได้", "sort": "เรียง", "stayOnThisPage": "อยู่หน้านี้ต่อ", @@ -222,6 +234,7 @@ "unsavedChangesDuplicate": "คุณมีการแก้ไขที่ยังไม่ถูกบันทึก คุณต้องการทำสำเนาต่อหรือไม่?", "untitled": "ไม่มีชื่อ", "updatedAt": "แก้ไขเมื่อ", + "updatedCountSuccessfully": "อัปเดต {{count}} {{label}} เรียบร้อยแล้ว", "updatedSuccessfully": "แก้ไขสำเร็จ", "updating": "กำลังอัปเดต", "uploading": "กำลังอัปโหลด", @@ -230,20 +243,21 @@ "welcome": "ยินดีต้อนรับ" }, "operators": { + "contains": "มี", "equals": "เท่ากับ", - "isNotEqualTo": "ไม่เท่ากับ", - "isIn": "อยู่ใน", - "isNotIn": "ไม่ได้อยู่ใน", "exists": "มีอยู่", "isGreaterThan": "มากกว่า", + "isGreaterThanOrEqualTo": "มากกว่าหรือเท่ากับ", + "isIn": "อยู่ใน", "isLessThan": "น้อยกว่า", "isLessThanOrEqualTo": "น้อยกว่าหรือเท่ากับ", - "isGreaterThanOrEqualTo": "มากกว่าหรือเท่ากับ", - "near": "ใกล้", "isLike": "เหมือน", - "contains": "มี" + "isNotEqualTo": "ไม่เท่ากับ", + "isNotIn": "ไม่ได้อยู่ใน", + "near": "ใกล้" }, "upload": { + "dragAndDrop": "ลากและวางไฟล์", "dragAndDropHere": "หรือลากและวางไฟล์ที่นี่", "fileName": "ชื่อไฟล์", "fileSize": "ขนาดไฟล์", @@ -252,7 +266,6 @@ "moreInfo": "แสดงข้อมูล", "selectCollectionToBrowse": "เลือก Collection ที่ต้องการค้นหา", "selectFile": "เลือกไฟล์", - "dragAndDrop": "ลากและวางไฟล์", "sizes": "ขนาด", "width": "ความกว้าง" }, @@ -276,15 +289,18 @@ "validUploadID": "ไม่ใช่ ID ของการอัปโหลดที่ถูกต้อง" }, "version": { + "aboutToPublishSelection": "คุณกำลังจะเผยแพร่ {{label}} ทั้งหมดในส่วนที่เลือก คุณแน่ใจไหม?", "aboutToRestore": "คุณกำลังจะคืนค่าเอกสาร {{label}} นี้กลับไปอยู่ในเวอร์ชันเมื่อวันที่ {{versionDate}}", "aboutToRestoreGlobal": "คุณกำลังจะคืนค่า global {{label}} กลับไปอยู่ในเวอร์ชันเมื่อวันที่ {{versionDate}}.", "aboutToRevertToPublished": "คุณกำลังจะย้อนการเปลี่ยนแปลงของเอกสารนี้ไปยังเวอร์ชันที่เผยแพร่อยู่ คุณต้องการดำเนินการต่อหรือไม่?", "aboutToUnpublish": "คุณกำลังจะยกเลิกเผยแพร่เอกสารนี้ คุณต้องการดำเนินการต่อหรือไม่?", + "aboutToUnpublishSelection": "คุณกำลังจะเลิกเผยแพร่ {{label}} ทั้งหมดในส่วนที่เลือก คุณแน่ใจไหม?", "autosave": "บันทึกอัตโนมัติ", "autosavedSuccessfully": "บันทึกอัตโนมัติสำเร็จ", "autosavedVersion": "เวอร์ชันบันทึกอัตโนมัติ", "changed": "มีการแก้ไข", "compareVersion": "เปรียบเทียบเวอร์ชันกับ:", + "confirmPublish": "ยืนยันการเผยแพร่", "confirmRevertToSaved": "ยืนยันย้อนการแก้ไข", "confirmUnpublish": "ยืนยันการยกเลิกการเผยแพร่", "confirmVersionRestoration": "ยืนยันการกู้คืนเวอร์ชัน", @@ -296,6 +312,7 @@ "noRowsFound": "ไม่พบ {{label}}", "preview": "ตัวอย่าง", "problemRestoringVersion": "เกิดปัญหาระหว่างการกู้คืนเวอร์ชันนี้", + "publish": "เผยแพร่", "publishChanges": "เผยแพร่การแก้ไข", "published": "เผยแพร่แล้ว", "restoreThisVersion": "กู้คืนเวอร์ชันนี้", @@ -306,8 +323,8 @@ "saveDraft": "บันทึกร่าง", "selectLocales": "เลือกภาษาที่ต้องการแสดง", "selectVersionToCompare": "เลือกเวอร์ชันที่ต้องการเปรียบเทียบ", - "showingVersionsFor": "กำลังแสดงเวอร์ชันของ:", "showLocales": "แสดงภาษา:", + "showingVersionsFor": "กำลังแสดงเวอร์ชันของ:", "status": "สถานะ", "type": "ประเภท", "unpublish": "หยุดเผยแพร่", diff --git a/src/translations/tr.json b/src/translations/tr.json index aeef2a6ebe..f9ea7f44cf 100644 --- a/src/translations/tr.json +++ b/src/translations/tr.json @@ -60,6 +60,7 @@ "accountAlreadyActivated": "Hesap zaten etkinleştirildi.", "autosaving": "Otomatik kaydetme başarısız oldu", "correctInvalidFields": "Lütfen geçersiz alanları düzeltin.", + "deletingFile": "Dosya silinirken bir hatayla karşılaşıldı.", "deletingTitle": "{{title}} silinirken bir sorun yaşandı. Lütfen internet bağlantınızı kontrol edip tekrar deneyin.", "emailOrPasswordIncorrect": "Girilen e-posta veya parola hatalı", "followingFieldsInvalid_many": "Lütfen geçersiz alanları düzeltin:", @@ -81,44 +82,44 @@ "problemUploadingFile": "Dosya yüklenirken bir sorun oluştu.", "tokenInvalidOrExpired": "Geçersiz veya süresi dolmuş token.", "unPublishingDocument": "Geçerli döküman yayından kaldırılırken bir sorun oluştu.", + "unableToDeleteCount": "{{total}} {{label}} içinden {{count}} silinemiyor.", + "unableToUpdateCount": "{{total}} {{label}} içinden {{count}} güncellenemiyor.", "unauthorized": "Bu işlemi gerçekleştirmek için lütfen giriş yapın.", "unknown": "Bilinmeyen bir hata oluştu.", "unspecific": "Bir hata oluştu.", "userLocked": "Hesabınız hatalı giriş denemeleri yüzünden geçici olarak kilitlendi. Lütfen daha sonra tekrar deneyin.", "valueMustBeUnique": "Değer benzersiz olmalıdır", - "verificationTokenInvalid": "Doğrulama tokeni geçersiz.", - "deletingFile": "Dosya silinirken bir hatayla karşılaşıldı." + "verificationTokenInvalid": "Doğrulama tokeni geçersiz." }, "fields": { - "chooseBetweenCustomTextOrDocument": "Choose between entering a custom text URL or linking to another document.", - "chooseDocumentToLink": "Bağlantı verilecek bir döküman seçin.", - "customURL": "Özel URL", - "editLink": "Bağlantıyı Düzenle", - "editRelationship": "İlişkiyi Ekle", - "enterURL": "Bir URL girin", - "internalLink": "İç bağlantı", - "linkType": "Bağlantı türü", - "openInNewTab": "Yeni sekmede aç", - "textToDisplay": "Görüntülenecek metin", - "block": "blok", - "blocks": "blok", "addLabel": "{{label}} ekle", "addLink": "Link Ekle", "addNew": "Yeni", "addNewLabel": "Yeni {{label}}", "addRelationship": "İlişki Ekle", "addUpload": "Yükleme Ekle", + "block": "blok", "blockType": "Blok tipi", + "blocks": "blok", + "chooseBetweenCustomTextOrDocument": "Choose between entering a custom text URL or linking to another document.", + "chooseDocumentToLink": "Bağlantı verilecek bir döküman seçin.", "chooseFromExisting": "Varolanlardan seç", "chooseLabel": "{{label}} seç", "collapseAll": "Tümünü daralt", + "customURL": "Özel URL", "editLabelData": "{{label}} düzenle", + "editLink": "Bağlantıyı Düzenle", + "editRelationship": "İlişkiyi Ekle", + "enterURL": "Bir URL girin", + "internalLink": "İç bağlantı", "itemsAndMore": "{{items}} and {{count}} more", "labelRelationship": "{{label}} Relationship", "latitude": "Enlem", + "linkType": "Bağlantı türü", "linkedTo": "<0>label için bağlantı verildi", "longitude": "Boylam", "newLabel": "Yeni {{label}}", + "openInNewTab": "Yeni sekmede aç", "passwordsDoNotMatch": "Parolalar eşleşmiyor.", "relatedDocument": "İlişkili döküman", "relationTo": "Relation To", @@ -127,22 +128,24 @@ "saveChanges": "Değişiklikleri kaydet", "searchForBlock": "Blok ara", "selectExistingLabel": "Varolan {{label}} seç", + "selectFieldsToEdit": "Düzenlenecek alanları seçin", "showAll": "Tümünü göster", "swapRelationship": "Takas Ilişkisi", "swapUpload": "Karşıya Yüklemeyi Değiştir", + "textToDisplay": "Görüntülenecek metin", "toggleBlock": "Bloğu aç/kapat", "uploadNewLabel": "Karşıya {{label}} yükle" }, "general": { - "addBelow": "Altına ekle", - "filter": "Filtrele", - "none": "Hiç", - "enterAValue": "Değer girin", - "deletedSuccessfully": "Başarıyla silindi.", "aboutToDelete": "<1>{{title}} {{label}} silinmek üzere. Silme işlemine devam etmek istiyor musunuz?", + "aboutToDeleteCount_many": "{{count}} {{label}} silmek üzeresiniz", + "aboutToDeleteCount_one": "{{count}} {{label}} silmek üzeresiniz", + "aboutToDeleteCount_other": "{{count}} {{label}} silmek üzeresiniz", + "addBelow": "Altına ekle", "addFilter": "Filtre ekle", "adminTheme": "Admin arayüzü", "and": "ve", + "ascending": "artan", "automatic": "Otomatik", "backToDashboard": "Anasayfaya geri dön", "cancel": "İptal", @@ -165,6 +168,8 @@ "dark": "Karanlık", "dashboard": "Anasayfa", "delete": "Sil", + "deletedCountSuccessfully": "{{count}} {{label}} başarıyla silindi.", + "deletedSuccessfully": "Başarıyla silindi.", "deleting": "Siliniyor...", "descending": "Azalan", "duplicate": "Çoğalt", @@ -172,9 +177,14 @@ "edit": "Düzenle", "editLabel": "{{label}} düzenle", "editing": "Düzenleniyor", + "editingLabel_many": "{{count}} {{label}} düzenleniyor", + "editingLabel_one": "{{count}} {{label}} düzenleniyor", + "editingLabel_other": "{{count}} {{label}} düzenleniyor", "email": "E-posta", "emailAddress": "E-posta adresi", + "enterAValue": "Değer girin", "fallbackToDefaultLocale": "Varsayılan yerel ayara geri dönme", + "filter": "Filtrele", "filterWhere": "{{label}} filtrele:", "filters": "Filtreler", "globals": "Globaller", @@ -183,8 +193,8 @@ "leaveAnyway": "Yine de ayrıl", "leaveWithoutSaving": "Kaydetmeden ayrıl", "light": "Aydınlık", - "locales": "Diller", "loading": "Yükleniyor", + "locales": "Diller", "moveDown": "Aşağı taşı", "moveUp": "Yukarı taşı", "newPassword": "Yeni parola", @@ -192,6 +202,7 @@ "noLabel": "<{{label}} yok>", "noResults": "{{label}} bulunamadı. Henüz bir {{label}} eklenmemiş olabilir veya seçtiğiniz filtrelerle eşleşen bir sonuç bulunamamış olabilir.", "noValue": "Değer yok", + "none": "Hiç", "notFound": "Bulunamadı", "nothingFound": "Hiçbir şey bulunamadı", "of": "of", @@ -207,7 +218,9 @@ "save": "Kaydet", "saving": "Kaydediliyor...", "searchBy": "Şuna göre sırala: {{label}}", + "selectAll": "Tüm {{count}} {{label}}'ı seçin", "selectValue": "Bir değer seçin", + "selectedCount": "{{count}} {{label}} seçildi", "sorryNotFound": "Üzgünüz, isteğinizle eşleşen bir sonuç bulunamadı.", "sort": "Sırala", "stayOnThisPage": "Bu sayfada kal", @@ -221,6 +234,7 @@ "unsavedChangesDuplicate": "Kaydedilmemiş değişiklikler var. Çoğaltma işlemine devam etmek istiyor musunuz?", "untitled": "Başlıksız", "updatedAt": "Güncellenme tarihi", + "updatedCountSuccessfully": "{{count}} {{label}} başarıyla güncellendi.", "updatedSuccessfully": "Başarıyla güncellendi.", "updating": "Güncelleniyor", "uploading": "Yükleniyor", @@ -229,18 +243,18 @@ "welcome": "Hoşgeldiniz" }, "operators": { + "contains": "içerir", "equals": "eşittir", - "isNotEqualTo": "eşit değildir", - "isIn": "içinde", - "isNotIn": "içinde değil", "exists": "var", "isGreaterThan": "şundan büyüktür", + "isGreaterThanOrEqualTo": "büyüktür veya eşittir", + "isIn": "içinde", "isLessThan": "küçüktür", "isLessThanOrEqualTo": "küçüktür veya eşittir", - "isGreaterThanOrEqualTo": "büyüktür veya eşittir", - "near": "yakın", "isLike": "gibidir", - "contains": "içerir" + "isNotEqualTo": "eşit değildir", + "isNotIn": "içinde değil", + "near": "yakın" }, "upload": { "dragAndDrop": "Bir dosya sürükleyip bırakabilirsiniz", @@ -256,11 +270,9 @@ "width": "Genişlik" }, "validation": { - "fieldHasNo": "Bu alanda {{label}} girili değil.", - "requiresAtLeast": "Bu alan en az {{count}} adet {{label}} gerektirmektedir.", - "requiresNoMoreThan": "Bu alana {{count}} adetten fazla {{label}} girilemez.", "emailAddress": "Lütfen geçerli bir e-posta adresi girin.", "enterNumber": "Lütfen geçerli bir sayı girin.", + "fieldHasNo": "Bu alanda {{label}} girili değil.", "greaterThanMax": "\"{{value}}\", izin verilen maksimum değerden ({{max}}) fazla.", "invalidInput": "Bu alanda geçersiz bir giriş mevcut.", "invalidSelection": "Bu alanda geçersiz bir seçim mevcut.", @@ -269,45 +281,50 @@ "longerThanMin": "Bu değer minimum {{minLength}} karakterden uzun olmalıdır.", "notValidDate": "\"{{value}}\" geçerli bir tarih değil.", "required": "Bu alan gereklidir.", + "requiresAtLeast": "Bu alan en az {{count}} adet {{label}} gerektirmektedir.", + "requiresNoMoreThan": "Bu alana {{count}} adetten fazla {{label}} girilemez.", "requiresTwoNumbers": "Bu alana en az iki rakam girilmesi zorunludur.", "shorterThanMax": "Bu alan {{maxLength}} karakterden daha kısa olmalıdır.", "trueOrFalse": "Bu alan yalnızca doğru ve yanlış olabilir.", "validUploadID": "'Bu alan geçerli bir karşıya yükleme ID'sine sahip değil.'" }, "version": { - "showingVersionsFor": "", - "versionCount_other": "", - "autosavedSuccessfully": "Otomatik kaydetme başarılı", - "draftSavedSuccessfully": "Taslak başarıyla kaydedildi.", - "restoredSuccessfully": "Geri getirme başarılı.", - "selectLocales": "Görüntülenecek yerel ayarları seçin", + "aboutToPublishSelection": "Seçimdeki tüm {{label}}'i yayınlamak üzeresiniz. Emin misin?", "aboutToRestore": "Döküman {{label}}, {{versionDate}} tarihindeki sürümüne geri döndürülecek.", "aboutToRestoreGlobal": "Global {{label}}, {{versionDate}} tarihindeki sürümüne geri döndürülecek.", "aboutToRevertToPublished": "Bu dökümanın değişikliklerini yayınladığı haline geri getirmek üzeresiniz. Devam etmek istiyor musunuz?", "aboutToUnpublish": "Bu dökümanı yayından kaldırmak üzeresiniz. Devam etmek istiyor musunuz?", + "aboutToUnpublishSelection": "Seçimdeki tüm {{label}} yayınını kaldırmak üzeresiniz. Emin misin?", "autosave": "Otomatik kaydet", + "autosavedSuccessfully": "Otomatik kaydetme başarılı", "autosavedVersion": "Otomatik kayıtlı sürüm", "changed": "Değişiklik yapıldı", "compareVersion": "Sürümü şununla karşılaştır:", + "confirmPublish": "Yayınlamayı onayla", "confirmRevertToSaved": "Confirm revert to saved", "confirmUnpublish": "Yayından kaldırmayı onayla", "confirmVersionRestoration": "Sürümü Geri Getirmeyi Onayla", "currentDocumentStatus": "Şu an {{docStatus}} döküman", "draft": "Taslak", + "draftSavedSuccessfully": "Taslak başarıyla kaydedildi.", "lastSavedAgo": "Son kaydedilme: {{distance, relativetime(minutes)}}", "noFurtherVersionsFound": "Başka sürüm bulunamadı.", "noRowsFound": "{{label}} bulunamadı", "preview": "Önizleme", "problemRestoringVersion": "Bu sürüme geri döndürürken bir hatayla karşılaşıldı.", + "publish": "Yayınla", "publishChanges": "Değişiklikleri yayınla", "published": "Yayınlandı", "restoreThisVersion": "Bu sürüme geri döndür", + "restoredSuccessfully": "Geri getirme başarılı.", "restoring": "Geri döndürülüyor...", "revertToPublished": "Yayınlanana geri döndür", "reverting": "Değişiklikler geri alınıyor...", "saveDraft": "Taslağı kaydet", + "selectLocales": "Görüntülenecek yerel ayarları seçin", "selectVersionToCompare": "Karşılaştırılacak bir sürüm seçin", "showLocales": "Yerel ayarları göster:", + "showingVersionsFor": "Şunun için sürümler gösteriliyor:", "status": "Durum", "type": "Tür", "unpublish": "Yayından Kaldır", @@ -316,6 +333,7 @@ "versionCount_many": "{{count}} sürüm bulundu", "versionCount_none": "Sürüm bulunamadı", "versionCount_one": "{{count}} sürüm bulundu", + "versionCount_other": "{{count}} sürüm bulundu", "versionCreatedOn": "{{version}} oluşturma tarihi:", "versionID": "Sürüm ID", "versions": "Sürümler", diff --git a/src/translations/translation-schema.json b/src/translations/translation-schema.json index cefc3179ec..16ddf362aa 100644 --- a/src/translations/translation-schema.json +++ b/src/translations/translation-schema.json @@ -318,6 +318,12 @@ "unspecific": { "type": "string" }, + "unableToDeleteCount": { + "type": "string" + }, + "unableToUpdateCount": { + "type": "string" + }, "userLocked": { "type": "string" }, @@ -357,6 +363,8 @@ "unauthorized", "unknown", "unspecific", + "unableToDeleteCount", + "unableToUpdateCount", "userLocked", "valueMustBeUnique", "verificationTokenInvalid" @@ -477,6 +485,9 @@ "selectExistingLabel": { "type": "string" }, + "selectFieldsToEdit": { + "type": "string" + }, "showAll": { "type": "string" }, @@ -528,6 +539,7 @@ "saveChanges", "searchForBlock", "selectExistingLabel", + "selectFieldsToEdit", "showAll", "swapUpload", "toggleBlock", @@ -541,6 +553,15 @@ "aboutToDelete": { "type": "string" }, + "aboutToDeleteCount_many": { + "type": "string" + }, + "aboutToDeleteCount_one": { + "type": "string" + }, + "aboutToDeleteCount_other": { + "type": "string" + }, "addBelow": { "type": "string" }, @@ -622,6 +643,9 @@ "delete": { "type": "string" }, + "deletedCountSuccessfully": { + "type": "string" + }, "deletedSuccessfully": { "type": "string" }, @@ -646,6 +670,15 @@ "editing": { "type": "string" }, + "editingLabel_many": { + "type": "string" + }, + "editingLabel_one": { + "type": "string" + }, + "editingLabel_other": { + "type": "string" + }, "email": { "type": "string" }, @@ -760,9 +793,15 @@ "searchBy": { "type": "string" }, + "selectAll": { + "type": "string" + }, "selectValue": { "type": "string" }, + "selectedCount": { + "type": "string" + }, "sorryNotFound": { "type": "string" }, @@ -802,6 +841,9 @@ "updatedAt": { "type": "string" }, + "updatedCountSuccessfully": { + "type": "string" + }, "updatedSuccessfully": { "type": "string" }, @@ -823,6 +865,9 @@ }, "required": [ "aboutToDelete", + "aboutToDeleteCount_many", + "aboutToDeleteCount_one", + "aboutToDeleteCount_other", "addBelow", "addFilter", "adminTheme", @@ -849,6 +894,7 @@ "dark", "dashboard", "delete", + "deletedCountSuccessfully", "deletedSuccessfully", "deleting", "descending", @@ -856,6 +902,9 @@ "duplicateWithoutSaving", "editLabel", "editing", + "editingLabel_many", + "editingLabel_one", + "editingLabel_other", "email", "emailAddress", "enterAValue", @@ -893,7 +942,9 @@ "save", "saving", "searchBy", + "selectAll", "selectValue", + "selectedCount", "sorryNotFound", "sort", "stayOnThisPage", @@ -907,6 +958,7 @@ "unsavedChangesDuplicate", "untitled", "updatedAt", + "updatedCountSuccessfully", "updatedSuccessfully", "user", "users", @@ -1101,6 +1153,9 @@ "type": "object", "additionalProperties": false, "properties": { + "aboutToPublishSelection": { + "type": "string" + }, "aboutToRestore": { "type": "string" }, @@ -1113,6 +1168,9 @@ "aboutToUnpublish": { "type": "string" }, + "aboutToUnpublishSelection": { + "type": "string" + }, "autosave": { "type": "string" }, @@ -1128,6 +1186,9 @@ "compareVersion": { "type": "string" }, + "confirmPublish": { + "type": "string" + }, "confirmRevertToSaved": { "type": "string" }, @@ -1164,6 +1225,9 @@ "publishChanges": { "type": "string" }, + "publish": { + "type": "string" + }, "published": { "type": "string" }, @@ -1247,15 +1311,18 @@ } }, "required": [ + "aboutToPublishSelection", "aboutToRestore", "aboutToRestoreGlobal", "aboutToRevertToPublished", "aboutToUnpublish", + "aboutToUnpublishSelection", "autosave", "autosavedSuccessfully", "autosavedVersion", "changed", "compareVersion", + "confirmPublish", "confirmRevertToSaved", "confirmUnpublish", "confirmVersionRestoration", @@ -1268,6 +1335,7 @@ "preview", "problemRestoringVersion", "publishChanges", + "publish", "published", "restoreThisVersion", "restoredSuccessfully", diff --git a/src/translations/ua.json b/src/translations/ua.json index d41b52c95d..46692927ba 100644 --- a/src/translations/ua.json +++ b/src/translations/ua.json @@ -82,6 +82,8 @@ "problemUploadingFile": "Виникла помилка під час завантаження файлу.", "tokenInvalidOrExpired": "Токен або не дійсний, або його строк дії закінчився.", "unPublishingDocument": "Підчас відміни публікації даного документа, виникла помилка.", + "unableToDeleteCount": "Не вдалося видалити {{count}} із {{total}} {{label}}.", + "unableToUpdateCount": "Не вдалося оновити {{count}} із {{total}} {{label}}.", "unauthorized": "Немає доступу, ви повинні увійти, щоб виконати цей запит.", "unknown": "Виникла невідома помилка.", "unspecific": "Виникла помилка.", @@ -123,6 +125,7 @@ "saveChanges": "Зберегти зміни", "searchForBlock": "Знайти блок", "selectExistingLabel": "Вибрати існуючий {{label}}", + "selectFieldsToEdit": "Виберіть поля для редагування", "showAll": "Показати все", "swapUpload": "Замінити завантаження", "textToDisplay": "Текст для відображення", @@ -131,6 +134,9 @@ }, "general": { "aboutToDelete": "Ви бажаєте видалити {{label}} <1>{{title}}. Ви впевнені?", + "aboutToDeleteCount_many": "Ви збираєтеся видалити {{count}} {{label}}", + "aboutToDeleteCount_one": "Ви збираєтеся видалити {{count}} {{label}}", + "aboutToDeleteCount_other": "Ви збираєтеся видалити {{count}} {{label}}", "addBelow": "Добавити нижче", "addFilter": "Добавити фільтр", "adminTheme": "Тема адмінки", @@ -158,6 +164,7 @@ "dark": "Темна", "dashboard": "Головна", "delete": "Видалити", + "deletedCountSuccessfully": "Успішно видалено {{count}} {{label}}.", "deletedSuccessfully": "Успішно видалено.", "deleting": "Видалення...", "descending": "В порядку спадання", @@ -166,6 +173,9 @@ "edit": "Редагувати", "editLabel": "Редагувати {{label}}", "editing": "Редагування", + "editingLabel_many": "Редагування {{count}} {{label}}", + "editingLabel_one": "Редагування {{count}} {{label}}", + "editingLabel_other": "Редагування {{count}} {{label}}", "email": "Email", "emailAddress": "Email адреса", "enterAValue": "Введіть значення", @@ -204,7 +214,9 @@ "save": "Зберегти", "saving": "Збереження...", "searchBy": "Шукати по {{label}}", + "selectAll": "Вибрати всі {{count}} {{label}}", "selectValue": "Вибрати значення", + "selectedCount": "Вибрано {{count}} {{label}}", "sorryNotFound": "Вибачте - немає нічого, що відповідало б Вашому запиту.", "sort": "Сортувати", "stayOnThisPage": "Залишитись на цій сторінці", @@ -218,6 +230,7 @@ "unsavedChangesDuplicate": "Ви маєте не збережені зміни. Чи бажаєте ви продовжити дублювання?", "untitled": "Без назви", "updatedAt": "Змінено", + "updatedCountSuccessfully": "Успішно оновлено {{count}} {{label}}.", "updatedSuccessfully": "Успішно відредаговано.", "updating": "оновлення", "uploading": "завантаження", @@ -226,20 +239,21 @@ "welcome": "Вітаю" }, "operators": { + "contains": "містить", "equals": "дорівнює", - "isNotEqualTo": "не дорівнює", - "isIn": "є в", - "isNotIn": "не в", "exists": "існує", "isGreaterThan": "більше ніж", + "isGreaterThanOrEqualTo": "більше або дорівнює", + "isIn": "є в", "isLessThan": "менше ніж", "isLessThanOrEqualTo": "менше або дорівнює", - "isGreaterThanOrEqualTo": "більше або дорівнює", - "near": "поруч", "isLike": "схоже", - "contains": "містить" + "isNotEqualTo": "не дорівнює", + "isNotIn": "не в", + "near": "поруч" }, "upload": { + "dragAndDrop": "Перемістіть файл", "dragAndDropHere": "або перемістіть сюди файл", "fileName": "Назва файлу", "fileSize": "Розмір файлу", @@ -248,7 +262,6 @@ "moreInfo": "Більше інформації", "selectCollectionToBrowse": "Виберіть колекцію для перегляду", "selectFile": "Виберіть файл", - "dragAndDrop": "Перемістіть файл", "sizes": "Розміри", "width": "Ширина" }, @@ -272,15 +285,18 @@ "validUploadID": "Це поле не є дійсним ID завантаження." }, "version": { + "aboutToPublishSelection": "Ви збираєтеся опублікувати всі {{label}} у добірці. Ти впевнений?", "aboutToRestore": "Ви збираєтесь відновити цей документ {{label}} до стану, в якому він знаходився {{versionDate}}.", "aboutToRestoreGlobal": "Ви збираєтесь відновити глобальний запис {{label}} до стану, в якому він знаходився {{versionDate}}.", "aboutToRevertToPublished": "Ви збираєтесь вернути зміни цього документа до його опублікованого стану. Ви впевнені?", "aboutToUnpublish": "Ви збираєтесь відмінити публікацю цього документа. Ви впевнені?", + "aboutToUnpublishSelection": "Ви збираєтеся скасувати публікацію всіх {{label}} у вибраному. Ти впевнений?", "autosave": "Автозбереження", "autosavedSuccessfully": "Автозбереження успішно виконано.", "autosavedVersion": "Автозбережена версія", "changed": "Змінено", "compareVersion": "Порівняти версію з:", + "confirmPublish": "Підтвердити публікацію", "confirmRevertToSaved": "Підтвердити повернення до збереженого", "confirmUnpublish": "Підвтердити відміну публікації", "confirmVersionRestoration": "Підтвердити відновлення версії", @@ -292,6 +308,7 @@ "noRowsFound": "Не знайдено {{label}}", "preview": "Попередній перегляд", "problemRestoringVersion": "Виникла проблема з відновленням цієї версії", + "publish": "Опублікувати", "publishChanges": "Опублікувати зміни", "published": "Опубліковано", "restoreThisVersion": "Відновити цю версію", @@ -302,8 +319,8 @@ "saveDraft": "Зберегти чернетку", "selectLocales": "Виберіть переклад для відображення", "selectVersionToCompare": "Виберіть версію для порівняння", - "showingVersionsFor": "Показані версії для:", "showLocales": "Показати переклади:", + "showingVersionsFor": "Показані версії для:", "status": "Статус", "type": "Тип", "unpublish": "Відмінити публікацію", diff --git a/src/translations/vi.json b/src/translations/vi.json index 34767bf748..9221d208ac 100644 --- a/src/translations/vi.json +++ b/src/translations/vi.json @@ -60,8 +60,8 @@ "accountAlreadyActivated": "Lỗi - Tài khoản này đã được kích hoạt.", "autosaving": "Lỗi - Đã xảy ra vấn đề khi tự động sao lưu bản tài liệu này.", "correctInvalidFields": "Lỗi - Xin hãy sửa lại những fields không hợp lệ.", - "deletingTitle": "Lỗi - Đã xảy ra vấn đề khi xóa {{title}}. Hãy kiểm tra kết nối mạng và thử lại.", "deletingFile": "Lỗi - Đã xảy ra vấn đề khi xóa file này.", + "deletingTitle": "Lỗi - Đã xảy ra vấn đề khi xóa {{title}}. Hãy kiểm tra kết nối mạng và thử lại.", "emailOrPasswordIncorrect": "Lỗi - Email hoặc mật khẩu không chính xác.", "followingFieldsInvalid_many": "Lỗi - Những fields sau không hợp lệ:", "followingFieldsInvalid_one": "Lỗi - Field sau không hợp lệ:", @@ -82,6 +82,8 @@ "problemUploadingFile": "Lỗi - Đã xảy ra vấn để khi tải lên file sau.", "tokenInvalidOrExpired": "Lỗi - Token không hợp lệ hoặc đã hết hạn.", "unPublishingDocument": "Lỗi - Đã xảy ra vấn để khi ẩn bản tài liệu.", + "unableToDeleteCount": "Không thể xóa {{count}} trong số {{total}} {{label}}.", + "unableToUpdateCount": "Không thể cập nhật {{count}} trên {{total}} {{label}}.", "unauthorized": "Lỗi - Bạn cần phải đăng nhập trước khi gửi request sau.", "unknown": "Lỗi - Không xác định (unknown error).", "unspecific": "Lỗi - Đã xảy ra (unspecific error).", @@ -90,47 +92,59 @@ "verificationTokenInvalid": "Lỗi - Token dùng để xác thực không hợp lệ." }, "fields": { - "block": "block", - "blocks": "blocks", "addLabel": "Thêm: {{label}}", "addLink": "Thêm liên kết", "addNew": "Thêm mới", "addNewLabel": "Thêm mới: {{label}}", "addRelationship": "Thêm mối quan hệ (relationship)", "addUpload": "Thêm tải lên (upload)", + "block": "block", "blockType": "Block Type", + "blocks": "blocks", + "chooseBetweenCustomTextOrDocument": "Chọn giữa nhập URL văn bản tùy chỉnh hoặc liên kết đến tài liệu khác.", + "chooseDocumentToLink": "Chọn một tài liệu để liên kết đến", "chooseFromExisting": "Chọn từ vật phẩm có sẵn", "chooseLabel": "Chọn: {{label}}", "collapseAll": "Ẩn toàn bộ", + "customURL": "URL tùy chỉnh", "editLabelData": "Chỉnh sửa nội dung của: {{label}}", "editLink": "Chỉnh sửa liên kết", "editRelationship": "Chỉnh sửa mối quan hệ", + "enterURL": "Nhập một URL", + "internalLink": "Liên kết nội bộ", "itemsAndMore": "{{items}} và {{count}} món nữa", "labelRelationship": "Mối quan hệ của {{label}} (Relationship)", "latitude": "Vĩ độ", + "linkType": "Loại liên kết", "linkedTo": "Được nối với <0>{{label}}", "longitude": "Kinh độ", "newLabel": "Tạo {{label}} mới", + "openInNewTab": "Mở ra trong trang mới", "passwordsDoNotMatch": "Mật khẩu không trùng.", "relatedDocument": "bản tài liệu liên quan", "relationTo": "Có quan hệ với", - "removeReelationship": "Xóa mối quan hệ", "removeUpload": "Xóa bản tải lên", "saveChanges": "Luu thay đổi", "searchForBlock": "Tìm block", "selectExistingLabel": "Chọn một {{label}} có sẵn", + "selectFieldsToEdit": "Chọn các trường để chỉnh sửa", "showAll": "Hiển thị toàn bộ", "swapRelationship": "Hoán đổi quan hệ", "swapUpload": "Đổi bản tải lên", + "textToDisplay": "Văn bản để hiển thị", "toggleBlock": "Bật/tắt block", "uploadNewLabel": "Tải lên bản mới: {{label}}" }, "general": { "aboutToDelete": "Chuẩn bị xóa {{label}} <1>{{title}}. Bạn có muốn tiếp tục không?", + "aboutToDeleteCount_many": "Bạn sắp xóa {{count}} {{label}}", + "aboutToDeleteCount_one": "Bạn sắp xóa {{count}} {{label}}", + "aboutToDeleteCount_other": "Bạn sắp xóa {{count}} {{label}}", "addBelow": "Thêm bên dưới", "addFilter": "Thêm bộ lọc", "adminTheme": "Phông nền của trang Admin", "and": "Và", + "ascending": "Sắp xếp theo thứ tự tăng dần", "automatic": "Tự động", "backToDashboard": "Quay lại bảng điều khiển", "cancel": "Ngừng thao tác", @@ -153,6 +167,7 @@ "dark": "Nền tối", "dashboard": "Bảng điều khiển", "delete": "Xóa", + "deletedCountSuccessfully": "Đã xóa thành công {{count}} {{label}}.", "deletedSuccessfully": "Đã xoá thành công.", "deleting": "Đang xóa...", "descending": "Xếp theo thứ tự giảm dần", @@ -161,6 +176,9 @@ "edit": "Chỉnh sửa", "editLabel": "Chỉnh sửa: {{label}}", "editing": "Đang chỉnh sửa", + "editingLabel_many": "Đang chỉnh sửa {{count}} {{label}}", + "editingLabel_one": "Đang chỉnh sửa {{count}} {{label}}", + "editingLabel_other": "Đang chỉnh sửa {{count}} {{label}}", "email": "Email", "emailAddress": "Địa chỉ Email", "enterAValue": "Nhập một giá trị", @@ -199,7 +217,9 @@ "save": "Luu", "saving": "Đang lưu...", "searchBy": "Tìm với {{label}}", + "selectAll": "Chọn tất cả {{count}} {{label}}", "selectValue": "Chọn một giá trị", + "selectedCount": "Đã chọn {{count}} {{nhãn}}", "sorryNotFound": "Xin lỗi, không có kết quả nào tương ứng với request của bạn.", "sort": "Sắp xếp", "stayOnThisPage": "Ở lại trang này", @@ -213,6 +233,7 @@ "unsavedChangesDuplicate": "Bạn chưa lưu các thay đổi. Bạn có muốn tiếp tục tạo bản sao?", "untitled": "Không tiêu đề", "updatedAt": "Được cập nhật vào lúc", + "updatedCountSuccessfully": "Đã cập nhật thành công {{count}} {{label}}.", "updatedSuccessfully": "Cập nhật thành công.", "updating": "Đang cập nhật", "uploading": "Đang tải lên", @@ -221,20 +242,21 @@ "welcome": "Xin chào" }, "operators": { + "contains": "chứa", "equals": "bằng", - "isNotEqualTo": "không bằng", - "isIn": "đang ở", - "isNotIn": "không có trong", "exists": "tồn tại", "isGreaterThan": "lớn hơn", + "isGreaterThanOrEqualTo": "lớn hơn hoặc bằng", + "isIn": "đang ở", "isLessThan": "nhỏ hơn", "isLessThanOrEqualTo": "nhỏ hơn hoặc bằng", - "isGreaterThanOrEqualTo": "lớn hơn hoặc bằng", - "near": "gần", "isLike": "giống như", - "contains": "chứa" + "isNotEqualTo": "không bằng", + "isNotIn": "không có trong", + "near": "gần" }, "upload": { + "dragAndDrop": "Kéo và thả một tập tin", "dragAndDropHere": "hoặc kéo và thả file vào đây", "fileName": "Tên file", "fileSize": "Dung lượng file", @@ -243,7 +265,6 @@ "moreInfo": "Hiển thị nhiều hơn", "selectCollectionToBrowse": "Chọn một Collection để tìm", "selectFile": "Chọn một file", - "dragAndDrop": "Kéo và thả một tập tin", "sizes": "Các độ phân giải", "width": "Chiều rộng" }, @@ -267,15 +288,18 @@ "validUploadID": "'Field này không chứa ID tải lên hợp lệ.'" }, "version": { + "aboutToPublishSelection": "Bạn sắp xuất bản tất cả {{nhãn}} trong lựa chọn. Bạn có chắc không?", "aboutToRestore": "Bạn chuẩn bị khôi phục lại {{label}} về trạng thái mà bản tài liệu này sở hữu lúc {{versionDate}}.", "aboutToRestoreGlobal": "Bạn chuẩn bị khôi phục lại bản toàn thể (global) của {{label}} về trạng thái mà bản tài liệu này sở hữu lúc {{versionDate}}.", "aboutToRevertToPublished": "Bạn chuẩn bị khiến bản nháp này quay về trạng thái khi được xuất bản. Bạn có muốn tiếp tục không?", "aboutToUnpublish": "Bạn chuẩn bị ẩn bản tài liệu này. Bạn chắc chứ?", + "aboutToUnpublishSelection": "Bạn sắp hủy xuất bản tất cả {{nhãn}} trong lựa chọn. Bạn có chắc không?", "autosave": "Tự động lưu dữ liệu", "autosavedSuccessfully": "Đã tự động lưu thành công.", "autosavedVersion": "Các phiên bản từ việc tự động lưu dữ liệu", "changed": "Đã đổi", "compareVersion": "So sánh phiên bản này với:", + "confirmPublish": "xác nhận xuất bản", "confirmRevertToSaved": "Xác nhận, quay về trạng thái đã lưu", "confirmUnpublish": "Xác nhận, ẩn tài liệu", "confirmVersionRestoration": "Xác nhận, khôi phục về phiên bản sau", @@ -287,6 +311,7 @@ "noRowsFound": "Không tìm thấy: {{label}}", "preview": "Bản xem trước", "problemRestoringVersion": "Đã xảy ra vấn đề khi khôi phục phiên bản này", + "publish": "Công bố", "publishChanges": "Xuất bản tài liệu", "published": "Đã xuất bản", "restoreThisVersion": "Khôi phục về phiên bản này", @@ -298,6 +323,7 @@ "selectLocales": "Chọn mã khu vực để hiện thị", "selectVersionToCompare": "Chọn phiên bản để so sánh", "showLocales": "Hiển thị mã khu vực:", + "showingVersionsFor": "Hiển thị các phiên bản cho:", "status": "Trạng thái", "type": "Loại", "unpublish": "Ẩn tài liệu", @@ -306,6 +332,7 @@ "versionCount_many": "{{count}} phiên bản được tìm thấy", "versionCount_none": "Không có phiên bản nào được tìm thấy", "versionCount_one": "{{count}} phiên bản được tìm thấy", + "versionCount_other": "Đã tìm thấy {{count}} phiên bản", "versionCreatedOn": "Phiên bản {{version}} được tạo vào lúc:", "versionID": "ID của phiên bản", "versions": "Những phiên bản", diff --git a/src/translations/zh.json b/src/translations/zh.json index 3f3e5265c2..c2c93f1d6f 100644 --- a/src/translations/zh.json +++ b/src/translations/zh.json @@ -82,6 +82,8 @@ "problemUploadingFile": "上传文件时出现了问题。", "tokenInvalidOrExpired": "令牌无效或已过期。", "unPublishingDocument": "取消发布此文件时出现了问题。", + "unableToDeleteCount": "无法从 {{total}} {{label}} 中删除 {{count}}。", + "unableToUpdateCount": "无法更新 {{count}} 个,共 {{total}} 个 {{label}}。", "unauthorized": "未经授权,您必须登录才能提出这个请求。", "unknown": "发生了一个未知的错误。", "unspecific": "发生了一个错误。", @@ -126,6 +128,7 @@ "saveChanges": "保存更改", "searchForBlock": "搜索一个区块", "selectExistingLabel": "选择现有的{{label}}", + "selectFieldsToEdit": "选择要编辑的字段", "showAll": "显示全部", "swapRelationship": "交换关系", "swapUpload": "交换上传", @@ -135,6 +138,9 @@ }, "general": { "aboutToDelete": "您即将删除{{label}} <1>{{title}}。您确定要继续吗?", + "aboutToDeleteCount_many": "您即将删除 {{count}} {{label}}", + "aboutToDeleteCount_one": "您即将删除 {{count}} {{label}}", + "aboutToDeleteCount_other": "您即将删除 {{count}} {{label}}", "addBelow": "添加到下面", "addFilter": "添加过滤器", "adminTheme": "管理页面主题", @@ -162,6 +168,7 @@ "dark": "深色", "dashboard": "仪表板", "delete": "删除", + "deletedCountSuccessfully": "已成功删除 {{count}} {{label}}。", "deletedSuccessfully": "已成功删除。", "deleting": "删除中...", "descending": "降序", @@ -170,6 +177,9 @@ "edit": "编辑", "editLabel": "编辑{{label}}", "editing": "编辑中", + "editingLabel_many": "编辑 {{count}} {{label}}", + "editingLabel_one": "编辑 {{count}} {{label}}", + "editingLabel_other": "编辑 {{count}} {{label}}", "email": "电子邮件", "emailAddress": "电子邮件地址", "enterAValue": "输入一个值", @@ -208,7 +218,9 @@ "save": "保存", "saving": "保存中...", "searchBy": "搜索{{label}}", + "selectAll": "选择所有 {{count}} {{label}}", "selectValue": "选择一个值", + "selectedCount": "已选择 {{count}} {{label}}", "sorryNotFound": "对不起,没有与您的请求相对应的东西。", "sort": "排序", "stayOnThisPage": "停留在此页面", @@ -222,26 +234,28 @@ "unsavedChangesDuplicate": "您有未保存的修改。您确定要继续重复吗?", "untitled": "无标题", "updatedAt": "更新于", + "updatedCountSuccessfully": "已成功更新 {{count}} {{label}}。", "updatedSuccessfully": "更新成功。", "user": "用户", "users": "用户", "welcome": "欢迎" }, "operators": { + "contains": "包含", "equals": "等于", - "isNotEqualTo": "不等于", - "isIn": "在", - "isNotIn": "不在", "exists": "存在", "isGreaterThan": "大于", + "isGreaterThanOrEqualTo": "大于等于", + "isIn": "在", "isLessThan": "小于", "isLessThanOrEqualTo": "小于或等于", - "isGreaterThanOrEqualTo": "大于等于", - "near": "附近", "isLike": "就像", - "contains": "包含" + "isNotEqualTo": "不等于", + "isNotIn": "不在", + "near": "附近" }, "upload": { + "dragAndDrop": "拖放一个文件", "dragAndDropHere": "或在这里拖放一个文件", "fileName": "文件名", "fileSize": "文件大小", @@ -250,7 +264,6 @@ "moreInfo": "更多信息", "selectCollectionToBrowse": "选择一个要浏览的集合", "selectFile": "选择一个文件", - "dragAndDrop": "拖放一个文件", "sizes": "尺寸", "width": "宽度" }, @@ -274,15 +287,18 @@ "validUploadID": "该字段不是有效的上传ID。" }, "version": { + "aboutToPublishSelection": "您即将发布所选内容中的所有 {{label}}。 你确定吗?", "aboutToRestore": "您将把这个{{label}}文档恢复到{{versionDate}}时的状态", "aboutToRestoreGlobal": "您要将全局的{{label}}恢复到{{versionDate}}时的状态", "aboutToRevertToPublished": "您将要把这个文档的内容还原到它的发布状态。您确定吗?", "aboutToUnpublish": "你即将取消发布这个文档。你确定吗?", + "aboutToUnpublishSelection": "您即将取消发布所选内容中的所有 {{label}}。 你确定吗?", "autosave": "自动保存", "autosavedSuccessfully": "自动保存成功。", "autosavedVersion": "自动保存的版本", "changed": "已更改", "compareVersion": "对比版本:", + "confirmPublish": "确认发布", "confirmRevertToSaved": "确认恢复到保存状态", "confirmUnpublish": "确认取消发布", "confirmVersionRestoration": "确认版本恢复", @@ -294,6 +310,7 @@ "noRowsFound": "没有发现{{label}}", "preview": "预览", "problemRestoringVersion": "恢复这个版本时发生了问题", + "publish": "发布", "publishChanges": "发布修改", "published": "已发布", "restoreThisVersion": "恢复此版本", @@ -304,8 +321,8 @@ "saveDraft": "保存草稿", "selectLocales": "选择要显示的语言", "selectVersionToCompare": "选择要比较的版本", - "showingVersionsFor": "显示版本为:", "showLocales": "显示语言:", + "showingVersionsFor": "显示版本为:", "status": "状态", "type": "类型", "unpublish": "取消发布", diff --git a/src/uploads/deleteAssociatedFiles.ts b/src/uploads/deleteAssociatedFiles.ts new file mode 100644 index 0000000000..b15222b167 --- /dev/null +++ b/src/uploads/deleteAssociatedFiles.ts @@ -0,0 +1,56 @@ +import path from 'path'; +import fs from 'fs'; +import type { TFunction } from 'i18next'; +import fileExists from './fileExists'; +import { ErrorDeletingFile } from '../errors'; +import type { FileData, FileToSave } from './types'; +import type { SanitizedConfig } from '../config/types'; +import type { SanitizedCollectionConfig } from '../collections/config/types'; + +type Args = { + config: SanitizedConfig + collectionConfig: SanitizedCollectionConfig + files?: FileToSave[] + doc: Record + t: TFunction + overrideDelete: boolean +} + +export const deleteAssociatedFiles: (args: Args) => Promise = async ({ + config, + collectionConfig, + files = [], + doc, + t, + overrideDelete, +}) => { + if (!collectionConfig.upload) return; + if (overrideDelete || files.length > 0) { + const { staticDir } = collectionConfig.upload; + const staticPath = path.resolve(config.paths.configDir, staticDir); + + const fileToDelete = `${staticPath}/${doc.filename}`; + + if (await fileExists(fileToDelete)) { + fs.unlink(fileToDelete, (err) => { + if (err) { + throw new ErrorDeletingFile(t); + } + }); + } + + if (doc.sizes) { + Object.values(doc.sizes) + .forEach(async (size: FileData) => { + const sizeToDelete = `${staticPath}/${size.filename}`; + if (await fileExists(sizeToDelete)) { + fs.unlink(sizeToDelete, (err) => { + if (err) { + throw new ErrorDeletingFile(t); + } + }); + } + }); + } + } +}; diff --git a/src/uploads/getBaseFields.ts b/src/uploads/getBaseFields.ts index 035db45730..0813952b8b 100644 --- a/src/uploads/getBaseFields.ts +++ b/src/uploads/getBaseFields.ts @@ -74,6 +74,7 @@ const getBaseUploadFields = ({ config, collection }: Options): Field[] => { admin: { readOnly: true, hidden: true, + disableBulkEdit: true, }, }; diff --git a/src/uploads/unlinkTempFiles.ts b/src/uploads/unlinkTempFiles.ts new file mode 100644 index 0000000000..8a1444cdc0 --- /dev/null +++ b/src/uploads/unlinkTempFiles.ts @@ -0,0 +1,33 @@ +import fs from 'fs'; +import { promisify } from 'util'; +import { mapAsync } from '../utilities/mapAsync'; +import type { PayloadRequest } from '../express/types'; +import type { SanitizedConfig } from '../config/types'; +import type { SanitizedCollectionConfig } from '../collections/config/types'; + +const unlinkFile = promisify(fs.unlink); + +type Args = { + req: PayloadRequest + config: SanitizedConfig + collectionConfig: SanitizedCollectionConfig +} +/** + * Remove temp files if enabled, as express-fileupload does not do this automatically + */ +export const unlinkTempFiles: (args: Args) => Promise = async ({ + req, + config, + collectionConfig, +}) => { + if (config.upload?.useTempFiles && collectionConfig.upload) { + const { files } = req; + const fileArray = Array.isArray(files) ? files : [files]; + await mapAsync(fileArray, async ({ file }) => { + // Still need this check because this will not be populated if using local API + if (file.tempFilePath) { + await unlinkFile(file.tempFilePath); + } + }); + } +}; diff --git a/src/versions/baseFields.ts b/src/versions/baseFields.ts index 42ca66e498..2200a4acba 100644 --- a/src/versions/baseFields.ts +++ b/src/versions/baseFields.ts @@ -22,6 +22,7 @@ const baseVersionFields: Field[] = [ options: statuses, defaultValue: 'draft', admin: { + disableBulkEdit: true, components: { Field: () => null, }, diff --git a/src/versions/drafts/queryDrafts.ts b/src/versions/drafts/queryDrafts.ts index 64b4691f16..72b87cc4b7 100644 --- a/src/versions/drafts/queryDrafts.ts +++ b/src/versions/drafts/queryDrafts.ts @@ -18,7 +18,7 @@ type Args = { accessResult: AccessResult collection: Collection locale: string - paginationOptions: PaginateOptions + paginationOptions?: PaginateOptions payload: Payload where: Where } @@ -69,25 +69,32 @@ export const queryDrafts = async ({ allowDiskUse: true, }); - const aggregatePaginateOptions = { - ...paginationOptions, - useFacet: payload.mongoOptions?.useFacet, - sort: Object.entries(paginationOptions.sort).reduce((sort, [incomingSortKey, order]) => { - let key = incomingSortKey; + let result; - if (!['createdAt', 'updatedAt', '_id'].includes(incomingSortKey)) { - key = `version.${incomingSortKey}` - } + if (paginationOptions) { + const aggregatePaginateOptions = { + ...paginationOptions, + useFacet: payload.mongoOptions?.useFacet, + sort: Object.entries(paginationOptions.sort) + .reduce((sort, [incomingSortKey, order]) => { + let key = incomingSortKey; - return { - ...sort, - [key]: order === 'asc' ? 1 : -1, - }; - }, {}) + if (!['createdAt', 'updatedAt', '_id'].includes(incomingSortKey)) { + key = `version.${incomingSortKey}`; + } + + return { + ...sort, + [key]: order === 'asc' ? 1 : -1, + }; + }, {}), + }; + + result = await VersionModel.aggregatePaginate(aggregate, aggregatePaginateOptions); + } else { + result = aggregate.exec(); } - const result = await VersionModel.aggregatePaginate(aggregate, aggregatePaginateOptions); - return { ...result, docs: result.docs.map((doc) => ({ diff --git a/test/access-control/int.spec.ts b/test/access-control/int.spec.ts index 6599acace7..ce3673aa8b 100644 --- a/test/access-control/int.spec.ts +++ b/test/access-control/int.spec.ts @@ -66,6 +66,41 @@ describe('Access Control', () => { expect(updatedDoc.partiallyHiddenArray[0].value).toEqual('private_value'); }); + it('should not affect hidden fields when patching data - update many', async () => { + const docsMany = await payload.create({ + collection: hiddenFieldsSlug, + data: { + partiallyHiddenArray: [{ + name: 'public_name', + value: 'private_value', + }], + partiallyHiddenGroup: { + name: 'public_name', + value: 'private_value', + }, + }, + }); + + await payload.update({ + collection: hiddenFieldsSlug, + where: { + id: { equals: docsMany.id }, + }, + data: { + title: 'Doc Title', + }, + }); + + const updatedMany = await payload.findByID({ + collection: hiddenFieldsSlug, + id: docsMany.id, + showHiddenFields: true, + }); + + expect(updatedMany.partiallyHiddenGroup.value).toEqual('private_value'); + expect(updatedMany.partiallyHiddenArray[0].value).toEqual('private_value'); + }); + it('should be able to restrict access based upon siblingData', async () => { const { id } = await payload.create({ collection: siblingDataSlug, @@ -210,6 +245,44 @@ describe('Access Control', () => { expect(doc).toMatchObject({ id: post1.id }); }); + + it('should allow overrideAccess: false - update many', async () => { + const req = async () => payload.update({ + collection: slug, + where: { + id: { equals: post1.id }, + }, + data: { restrictedField: restricted.id }, + overrideAccess: false, // this should respect access control + }); + + await expect(req).rejects.toThrow(Forbidden); + }); + + it('should allow overrideAccess: true - update many', async () => { + const doc = await payload.update({ + collection: slug, + where: { + id: { equals: post1.id }, + }, + data: { restrictedField: restricted.id }, + overrideAccess: true, // this should override access control + }); + + expect(doc.docs[0]).toMatchObject({ id: post1.id }); + }); + + it('should allow overrideAccess by default - update many', async () => { + const doc = await payload.update({ + collection: slug, + where: { + id: { equals: post1.id }, + }, + data: { restrictedField: restricted.id }, + }); + + expect(doc.docs[0]).toMatchObject({ id: post1.id }); + }); }); describe('Collections', () => { @@ -246,6 +319,44 @@ describe('Access Control', () => { expect(doc).toMatchObject({ id: restricted.id, name: updatedName }); }); + + it('should allow overrideAccess: false - update many', async () => { + const req = async () => payload.update({ + collection: restrictedSlug, + where: { + id: { equals: restricted.id }, + }, + data: { name: updatedName }, + overrideAccess: false, // this should respect access control + }); + + await expect(req).rejects.toThrow(Forbidden); + }); + + it('should allow overrideAccess: true - update many', async () => { + const doc = await payload.update({ + collection: restrictedSlug, + where: { + id: { equals: restricted.id }, + }, + data: { name: updatedName }, + overrideAccess: true, // this should override access control + }); + + expect(doc.docs[0]).toMatchObject({ id: restricted.id, name: updatedName }); + }); + + it('should allow overrideAccess by default - update many', async () => { + const doc = await payload.update({ + collection: restrictedSlug, + where: { + id: { equals: restricted.id }, + }, + data: { name: updatedName }, + }); + + expect(doc.docs[0]).toMatchObject({ id: restricted.id, name: updatedName }); + }); }); }); }); diff --git a/test/admin/e2e.spec.ts b/test/admin/e2e.spec.ts index 69ecdf4020..979c73f51f 100644 --- a/test/admin/e2e.spec.ts +++ b/test/admin/e2e.spec.ts @@ -162,6 +162,53 @@ describe('admin', () => { expect(page.url()).toContain(url.list); }); + test('should bulk delete', async () => { + createPost(); + createPost(); + createPost(); + + await page.goto(url.list); + + await page.locator('.select-all__input').click(); + + await page.locator('.delete-documents__toggle').click(); + + await page.locator('#confirm-delete').click(); + + await expect(page.locator('.Toastify__toast--success')).toHaveText('Deleted 3 Posts en successfully.'); + await expect(page.locator('.collection-list__no-results')).toBeVisible(); + }); + + test('should bulk update', async () => { + createPost(); + createPost(); + createPost(); + + const bulkTitle = 'Bulk update title'; + await page.goto(url.list); + + await page.locator('.select-all__input').click(); + await page.locator('.edit-many__toggle').click(); + await page.locator('.field-select .rs__control').click(); + const options = page.locator('.rs__option'); + const titleOption = await options.locator('text=Title en'); + + await expect(titleOption).toHaveText('Title en'); + + await titleOption.click(); + const titleInput = await page.locator('#field-title'); + + await expect(titleInput).toBeVisible(); + + await titleInput.fill(bulkTitle); + + await page.locator('.form-submit button[type="submit"]').click(); + await expect(page.locator('.Toastify__toast--success')).toContainText('Updated 3 Posts en successfully.'); + await expect(page.locator('.row-1 .cell-title')).toContainText(bulkTitle); + await expect(page.locator('.row-2 .cell-title')).toContainText(bulkTitle); + await expect(page.locator('.row-3 .cell-title')).toContainText(bulkTitle); + }); + test('should save globals', async () => { await page.goto(url.global(globalSlug)); @@ -230,11 +277,12 @@ describe('admin', () => { test('toggle columns', async () => { const columnCountLocator = 'table >> thead >> tr >> th'; await createPost(); + await page.locator('.list-controls__toggle-columns').click(); await wait(500); // Wait for column toggle UI, should probably use waitForSelector const numberOfColumns = await page.locator(columnCountLocator).count(); - await expect(await page.locator('table >> thead >> tr >> th:first-child')).toHaveText('ID'); + await expect(await page.locator('table >> thead >> tr >> th:nth-child(2)')).toHaveText('ID'); const idButton = await page.locator('.column-selector >> text=ID'); @@ -242,19 +290,19 @@ describe('admin', () => { await idButton.click(); await wait(100); await expect(await page.locator(columnCountLocator)).toHaveCount(numberOfColumns - 1); - await expect(await page.locator('table >> thead >> tr >> th:first-child')).toHaveText('Number'); + await expect(await page.locator('table >> thead >> tr >> th:nth-child(2)')).toHaveText('Number'); // Add back ID column await idButton.click(); await wait(100); await expect(await page.locator(columnCountLocator)).toHaveCount(numberOfColumns); - await expect(await page.locator('table >> thead >> tr >> th:first-child')).toHaveText('ID'); + await expect(await page.locator('table >> thead >> tr >> th:nth-child(2)')).toHaveText('ID'); }); - test('first cell is a link', async () => { + test('2nd cell is a link', async () => { const { id } = await createPost(); - const firstCell = await page.locator(`${tableRowLocator} td`).first().locator('a'); - await expect(firstCell).toHaveAttribute('href', `/admin/collections/posts/${id}`); + const linkCell = await page.locator(`${tableRowLocator} td`).nth(1).locator('a'); + await expect(linkCell).toHaveAttribute('href', `/admin/collections/posts/${id}`); // open the column controls await page.locator('.list-controls__toggle-columns').click(); @@ -264,8 +312,8 @@ describe('admin', () => { page.locator('.column-selector >> text=ID').click(); await wait(200); - // recheck that the first cell is still a link - await expect(firstCell).toHaveAttribute('href', `/admin/collections/posts/${id}`); + // recheck that the 2nd cell is still a link + await expect(linkCell).toHaveAttribute('href', `/admin/collections/posts/${id}`); }); test('filter rows', async () => { @@ -303,8 +351,7 @@ describe('admin', () => { await wait(1000); await expect(page.locator(tableRowLocator)).toHaveCount(1); - const firstId = await page.locator(tableRowLocator).first().locator('td').first() - .innerText(); + const firstId = await page.locator(tableRowLocator).first().locator('.cell-id').innerText(); expect(firstId).toEqual(id); // Remove filter @@ -339,15 +386,17 @@ describe('admin', () => { // ensure the "number" column is now first await expect(await page.locator('.list-controls .column-selector .column-selector__column').first()).toHaveText('Number'); - await expect(await page.locator('table >> thead >> tr >> th').first()).toHaveText('Number'); + await expect(await page.locator('table thead tr th').nth(1)).toHaveText('Number'); + // await expect(await page.locator('table >> thead >> tr >> th').first()).toHaveText('Number'); // reload to ensure the preferred order was stored in the database await page.reload(); await expect(await page.locator('.list-controls .column-selector .column-selector__column').first()).toHaveText('Number'); - await expect(await page.locator('table >> thead >> tr >> th').first()).toHaveText('Number'); + await expect(await page.locator('table thead tr th').nth(1)).toHaveText('Number'); }); test('should render drawer columns in order', async () => { + await createPost(); await page.goto(url.create); // Open the drawer @@ -356,13 +405,13 @@ describe('admin', () => { await expect(listDrawer).toBeVisible(); const collectionSelector = await page.locator('[id^=list-drawer_1_] .list-drawer__select-collection.react-select'); - const columnSelector = await page.locator('[id^=list-drawer_1_] .list-controls__toggle-columns'); // select the "Post en" collection await collectionSelector.click(); await page.locator('[id^=list-drawer_1_] .list-drawer__select-collection.react-select .rs__option >> text="Post en"').click(); // open the column controls + const columnSelector = await page.locator('[id^=list-drawer_1_] .list-controls__toggle-columns'); await columnSelector.click(); await wait(500); // Wait for column toggle UI, should probably use waitForSelector (same as above) @@ -417,6 +466,46 @@ describe('admin', () => { }); }); + describe('multi-select', () => { + beforeEach(async () => { + await mapAsync([...Array(3)], async () => { + await createPost(); + }); + }); + + test('should select multiple rows', async () => { + const selectAll = page.locator('.select-all'); + await page.locator('.row-1 .select-row button').click(); + + const indeterminateSelectAll = selectAll.locator('.icon--line'); + expect(indeterminateSelectAll).toBeDefined(); + + await selectAll.locator('button').click(); + const emptySelectAll = selectAll.locator('.icon'); + await expect(emptySelectAll).toHaveCount(0); + + await selectAll.locator('button').click(); + const checkSelectAll = selectAll.locator('.icon .icon--check'); + expect(checkSelectAll).toBeDefined(); + }); + + test('should delete many', async () => { + // delete should not appear without selection + await expect(page.locator('#confirm-delete')).toHaveCount(0); + // select one row + await page.locator('.row-1 .select-row button').click(); + + // delete button should be present + await expect(page.locator('#confirm-delete')).toHaveCount(1); + + await page.locator('.row-2 .select-row button').click(); + + await page.locator('.delete-documents__toggle').click(); + await page.locator('#confirm-delete').click(); + await expect(await page.locator('.select-row')).toHaveCount(1); + }); + }); + describe('pagination', () => { beforeAll(async () => { await mapAsync([...Array(11)], async () => { diff --git a/test/collections-rest/config.ts b/test/collections-rest/config.ts index ab20a7bd96..cb60c5382c 100644 --- a/test/collections-rest/config.ts +++ b/test/collections-rest/config.ts @@ -32,6 +32,7 @@ export const relationSlug = 'relation'; export const pointSlug = 'point'; export const customIdSlug = 'custom-id'; export const customIdNumberSlug = 'custom-id-number'; +export const errorOnHookSlug = 'error-on-hooks'; export default buildConfig({ collections: [ @@ -124,6 +125,36 @@ export default buildConfig({ }, ], }, + { + slug: errorOnHookSlug, + access: openAccess, + hooks: { + beforeChange: [({ originalDoc }) => { + if (originalDoc?.errorBeforeChange) { + throw new Error('Error Before Change Thrown'); + } + }], + afterDelete: [({ doc }) => { + if (doc?.errorAfterDelete) { + throw new Error('Error After Delete Thrown'); + } + }], + }, + fields: [ + { + name: 'text', + type: 'text', + }, + { + name: 'errorBeforeChange', + type: 'checkbox', + }, + { + name: 'errorAfterDelete', + type: 'checkbox', + }, + ], + }, ], onInit: async (payload) => { await payload.create({ diff --git a/test/collections-rest/int.spec.ts b/test/collections-rest/int.spec.ts index 3a92083d87..119f7b3fe6 100644 --- a/test/collections-rest/int.spec.ts +++ b/test/collections-rest/int.spec.ts @@ -2,10 +2,10 @@ import mongoose from 'mongoose'; import { randomBytes } from 'crypto'; import { initPayloadTest } from '../helpers/configHelpers'; import type { Relation } from './config'; -import config, { customIdNumberSlug, customIdSlug, slug, relationSlug, pointSlug } from './config'; +import config, { customIdNumberSlug, customIdSlug, slug, relationSlug, pointSlug, errorOnHookSlug } from './config'; import payload from '../../src'; import { RESTClient } from '../helpers/rest'; -import type { Post } from './payload-types'; +import type { ErrorOnHook, Post } from './payload-types'; import { mapAsync } from '../../src/utilities/mapAsync'; let client: RESTClient; @@ -13,7 +13,7 @@ let client: RESTClient; describe('collections-rest', () => { beforeAll(async () => { const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } }); - client = new RESTClient(config, { serverURL, defaultSlug: slug }); + client = new RESTClient(await config, { serverURL, defaultSlug: slug }); }); afterAll(async () => { @@ -62,6 +62,100 @@ describe('collections-rest', () => { expect(updated.description).toEqual(description); // Check was not modified }); + it('should bulk update', async () => { + await mapAsync([...Array(11)], async (_, i) => { + await createPost({ description: `desc ${i}` }); + }); + + const description = 'updated'; + const { status, docs } = await client.updateMany({ + query: { title: { equals: 'title' } }, + data: { description }, + }); + + expect(status).toEqual(200); + expect(docs[0].title).toEqual('title'); // Check was not modified + expect(docs[0].description).toEqual(description); + expect(docs.pop().description).toEqual(description); + }); + + it('should return formatted errors for bulk updates', async () => { + const text = 'bulk-update-test-errors'; + const errorDoc = await payload.create({ + collection: errorOnHookSlug, + data: { + text, + errorBeforeChange: true, + }, + }); + const successDoc = await payload.create({ + collection: errorOnHookSlug, + data: { + text, + errorBeforeChange: false, + }, + }); + + const update = 'update'; + + const result = await client.updateMany({ + slug: errorOnHookSlug, + query: { text: { equals: text } }, + data: { text: update }, + }); + + expect(result.status).toEqual(400); + expect(result.docs).toHaveLength(1); + expect(result.docs[0].id).toEqual(successDoc.id); + expect(result.errors).toHaveLength(1); + expect(result.errors[0].message).toBeDefined(); + expect(result.errors[0].id).toEqual(errorDoc.id); + expect(result.docs[0].text).toEqual(update); + }); + + it('should bulk delete', async () => { + const count = 11; + await mapAsync([...Array(count)], async (_, i) => { + await createPost({ description: `desc ${i}` }); + }); + + const { status, docs } = await client.deleteMany({ + query: { title: { eq: 'title' } }, + }); + + expect(status).toEqual(200); + expect(docs[0].title).toEqual('title'); // Check was not modified + expect(docs).toHaveLength(count); + }); + + it('should return formatted errors for bulk deletes', async () => { + await payload.create({ + collection: errorOnHookSlug, + data: { + text: 'test', + errorAfterDelete: true, + }, + }); + await payload.create({ + collection: errorOnHookSlug, + data: { + text: 'test', + errorAfterDelete: false, + }, + }); + + const result = await client.deleteMany({ + slug: errorOnHookSlug, + query: { text: { equals: 'test' } }, + }); + + expect(result.status).toEqual(400); + expect(result.docs).toHaveLength(1); + expect(result.errors).toHaveLength(1); + expect(result.errors[0].message).toBeDefined(); + expect(result.errors[0].id).toBeDefined(); + }); + describe('Custom ID', () => { describe('string', () => { it('should create', async () => { @@ -697,7 +791,7 @@ async function createPosts(count: number) { } async function clearDocs(): Promise { - const allDocs = await payload.find({ collection: slug, limit: 100 }); + const allDocs = await payload.find({ collection: slug, limit: 100 }); const ids = allDocs.docs.map((doc) => doc.id); await mapAsync(ids, async (id) => { await payload.delete({ collection: slug, id }); diff --git a/test/collections-rest/payload-types.ts b/test/collections-rest/payload-types.ts index eb3d753ff0..0a748b2b85 100644 --- a/test/collections-rest/payload-types.ts +++ b/test/collections-rest/payload-types.ts @@ -5,11 +5,19 @@ * and re-run `payload generate:types` to regenerate this file. */ -export interface Config {} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "posts". - */ +export interface Config { + collections: { + posts: Post; + point: Point; + relation: Relation; + dummy: Dummy; + 'custom-id': CustomId; + 'custom-id-number': CustomIdNumber; + 'error-on-hooks': ErrorOnHook; + users: User; + }; + globals: {}; +} export interface Post { id: string; title?: string; @@ -50,30 +58,18 @@ export interface Post { createdAt: string; updatedAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "relation". - */ export interface Relation { id: string; name?: string; createdAt: string; updatedAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "dummy". - */ export interface Dummy { id: string; name?: string; createdAt: string; updatedAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "point". - */ export interface Point { id: string; /** @@ -84,30 +80,26 @@ export interface Point { createdAt: string; updatedAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "custom-id". - */ export interface CustomId { id: string; name?: string; createdAt: string; updatedAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "custom-id-number". - */ export interface CustomIdNumber { id: number; name?: string; createdAt: string; updatedAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "users". - */ +export interface ErrorOnHook { + id: string; + text?: string; + errorBeforeChange?: boolean; + errorAfterDelete?: boolean; + createdAt: string; + updatedAt: string; +} export interface User { id: string; email?: string; @@ -117,4 +109,5 @@ export interface User { lockUntil?: string; createdAt: string; updatedAt: string; + password?: string; } diff --git a/test/helpers/rest.ts b/test/helpers/rest.ts index 9b936c12bb..36867a93b8 100644 --- a/test/helpers/rest.ts +++ b/test/helpers/rest.ts @@ -52,12 +52,26 @@ type UpdateArgs = { auth?: boolean; query?: any; }; + +type UpdateManyArgs = { + slug?: string; + data: Partial; + auth?: boolean; + query: any; +}; + type DeleteArgs = { slug?: string; id: string; auth?: boolean; }; +type DeleteManyArgs = { + slug?: string; + auth?: boolean; + query: any; +}; + type FindGlobalArgs = { slug?: string; auth?: boolean; @@ -75,6 +89,12 @@ type DocResponse = { errors?: { name: string, message: string, data: any }[] }; +type DocsResponse = { + status: number; + docs: T[]; + errors?: { name: string, message: string, data: any, id: string | number}[] +}; + const headers = { 'Content-Type': 'application/json', Authorization: '', @@ -184,6 +204,45 @@ export class RESTClient { return { status, doc: json.doc, errors: json.errors }; } + async updateMany(args: UpdateManyArgs): Promise> { + const { slug, data, query } = args; + const formattedQs = qs.stringify({ + ...(query ? { where: query } : {}), + }, { + addQueryPrefix: true, + }); + if (args?.auth) { + headers.Authorization = `JWT ${this.token}`; + } + const response = await fetch(`${this.serverURL}/api/${slug || this.defaultSlug}${formattedQs}`, { + body: JSON.stringify(data), + headers, + method: 'PATCH', + }); + const { status } = response; + const json = await response.json(); + return { status, docs: json.docs, errors: json.errors }; + } + + async deleteMany(args: DeleteManyArgs): Promise> { + const { slug, query } = args; + const formattedQs = qs.stringify({ + ...(query ? { where: query } : {}), + }, { + addQueryPrefix: true, + }); + if (args?.auth) { + headers.Authorization = `JWT ${this.token}`; + } + const response = await fetch(`${this.serverURL}/api/${slug || this.defaultSlug}${formattedQs}`, { + headers, + method: 'DELETE', + }); + const { status } = response; + const json = await response.json(); + return { status, docs: json.docs, errors: json.errors }; + } + async findByID(args: FindByIDArgs): Promise> { const options = { headers: { diff --git a/test/uploads/e2e.spec.ts b/test/uploads/e2e.spec.ts index 5b751b8911..06fce105aa 100644 --- a/test/uploads/e2e.spec.ts +++ b/test/uploads/e2e.spec.ts @@ -65,10 +65,10 @@ describe('uploads', () => { test('should show upload filename in upload collection list', async () => { await page.goto(mediaURL.list); - const audioUpload = page.locator('.thumbnail-card__label').nth(0); + const audioUpload = page.locator('tr.row-1 .cell-filename'); await expect(audioUpload).toHaveText('audio.mp3'); - const imageUpload = page.locator('.thumbnail-card__label').nth(1); + const imageUpload = page.locator('tr.row-2 .cell-filename'); await expect(imageUpload).toHaveText('image.png'); }); @@ -118,13 +118,13 @@ describe('uploads', () => { await page.locator('.upload__toggler.list-drawer__toggler').click(); const listDrawer = await page.locator('[id^=list-drawer_1_]'); await expect(listDrawer).toBeVisible(); - await wait(200); // cards are loading + await wait(200); // list is loading // ensure the only card is the audio file - const cards = await listDrawer.locator('.upload-gallery .thumbnail-card'); - expect(await cards.count()).toEqual(1); - const card = cards.nth(0); - await expect(card).toHaveText('audio.mp3'); + const rows = await listDrawer.locator('table tbody tr'); + expect(await rows.count()).toEqual(1); + const filename = rows.locator('.cell-filename'); + await expect(filename).toHaveText('audio.mp3'); // upload an image and try to select it await listDrawer.locator('button.list-drawer__create-new-button.doc-drawer__toggler').click(); diff --git a/test/uploads/int.spec.ts b/test/uploads/int.spec.ts index 1187180f54..ec52be6532 100644 --- a/test/uploads/int.spec.ts +++ b/test/uploads/int.spec.ts @@ -181,6 +181,39 @@ describe('Collections - Uploads', () => { expect(await fileExists(path.join(__dirname, './media', mediaDoc.sizes.icon.filename))).toBe(true); }); + it('update - update many', async () => { + // Create image + const filePath = path.resolve(__dirname, './image.png'); + const file = await getFileByPath(filePath); + file.name = 'renamed.png'; + + const mediaDoc = await payload.create({ + collection: mediaSlug, + data: {}, + file, + }); + + const formData = new FormData(); + formData.append('file', fs.createReadStream(path.join(__dirname, './small.png'))); + + const { status } = await client.updateMany({ + // id: mediaDoc.id, + query: { + id: { equals: mediaDoc.id }, + }, + file: true, + data: formData, + auth: true, + headers: {}, + }); + + expect(status).toBe(200); + + // Check that previously existing files were removed + expect(await fileExists(path.join(__dirname, './media', mediaDoc.filename))).toBe(true); + expect(await fileExists(path.join(__dirname, './media', mediaDoc.sizes.icon.filename))).toBe(true); + }); + it('should remove existing media on re-upload', async () => { // Create temp file const filePath = path.resolve(__dirname, './temp.png'); @@ -213,6 +246,40 @@ describe('Collections - Uploads', () => { expect(await fileExists(path.join(__dirname, './media', mediaDoc.filename))).toBe(false); }); + it('should remove existing media on re-upload - update many', async () => { + // Create temp file + const filePath = path.resolve(__dirname, './temp.png'); + const file = await getFileByPath(filePath); + file.name = 'temp.png'; + + const mediaDoc = await payload.create({ + collection: mediaSlug, + data: {}, + file, + }); + + // Check that the temp file was created + expect(await fileExists(path.join(__dirname, './media', mediaDoc.filename))).toBe(true); + + // Replace the temp file with a new one + const newFilePath = path.resolve(__dirname, './temp-renamed.png'); + const newFile = await getFileByPath(newFilePath); + newFile.name = 'temp-renamed.png'; + + const updatedMediaDoc = await payload.update({ + collection: mediaSlug, + where: { + id: { equals: mediaDoc.id }, + }, + file: newFile, + data: {}, + }); + + // Check that the replacement file was created and the old one was removed + expect(await fileExists(path.join(__dirname, './media', updatedMediaDoc.docs[0].filename))).toBe(true); + expect(await fileExists(path.join(__dirname, './media', mediaDoc.filename))).toBe(false); + }); + it('should remove extra sizes on update', async () => { const filePath = path.resolve(__dirname, './image.png'); const file = await getFileByPath(filePath); @@ -235,6 +302,30 @@ describe('Collections - Uploads', () => { expect(doc.sizes.tablet.width).toBeNull(); }); + it('should remove extra sizes on update - update many', async () => { + const filePath = path.resolve(__dirname, './image.png'); + const file = await getFileByPath(filePath); + const small = await getFileByPath(path.resolve(__dirname, './small.png')); + + const { id } = await payload.create({ + collection: mediaSlug, + data: {}, + file, + }); + + const doc = await payload.update({ + collection: mediaSlug, + where: { + id: { equals: id }, + }, + data: {}, + file: small, + }); + + expect(doc.docs[0].sizes.icon).toBeDefined(); + expect(doc.docs[0].sizes.tablet.width).toBeNull(); + }); + it('should allow update removing a relationship', async () => { const filePath = path.resolve(__dirname, './image.png'); const file = await getFileByPath(filePath); @@ -264,6 +355,37 @@ describe('Collections - Uploads', () => { expect(doc.image).toBeNull(); }); + it('should allow update removing a relationship - update many', async () => { + const filePath = path.resolve(__dirname, './image.png'); + const file = await getFileByPath(filePath); + file.name = 'renamed.png'; + + const { id } = await payload.create({ + collection: mediaSlug, + data: {}, + file, + }); + + const related = await payload.create({ + collection: relationSlug, + data: { + image: id, + }, + }); + + const doc = await payload.update({ + collection: relationSlug, + where: { + id: { equals: related.id }, + }, + data: { + image: null, + }, + }); + + expect(doc.docs[0].image).toBeNull(); + }); + it('delete', async () => { const formData = new FormData(); formData.append('file', fs.createReadStream(path.join(__dirname, './image.png'))); @@ -283,6 +405,30 @@ describe('Collections - Uploads', () => { expect(await fileExists(path.join(__dirname, doc.filename))).toBe(false); }); + + it('delete - update many', async () => { + const formData = new FormData(); + formData.append('file', fs.createReadStream(path.join(__dirname, './image.png'))); + + const { doc } = await client.create({ + file: true, + data: formData, + auth: true, + headers: {}, + }); + + const { errors } = await client.deleteMany({ + slug: mediaSlug, + query: { + id: { equals: doc.id }, + }, + auth: true, + }); + + expect(errors).toHaveLength(0); + + expect(await fileExists(path.join(__dirname, doc.filename))).toBe(false); + }); }); async function fileExists(fileName: string): Promise { diff --git a/test/versions/collections/Autosave.ts b/test/versions/collections/Autosave.ts index 61e9b307bc..6d667d4f74 100644 --- a/test/versions/collections/Autosave.ts +++ b/test/versions/collections/Autosave.ts @@ -1,7 +1,8 @@ import type { CollectionConfig } from '../../../src/collections/config/types'; +import { autosaveSlug } from '../shared'; const AutosavePosts: CollectionConfig = { - slug: 'autosave-posts', + slug: autosaveSlug, labels: { singular: 'Autosave Post', plural: 'Autosave Posts', diff --git a/test/versions/collections/Drafts.ts b/test/versions/collections/Drafts.ts index d893cd2784..d200850765 100644 --- a/test/versions/collections/Drafts.ts +++ b/test/versions/collections/Drafts.ts @@ -1,10 +1,11 @@ import type { CollectionConfig } from '../../../src/collections/config/types'; +import { draftSlug } from '../shared'; const DraftPosts: CollectionConfig = { - slug: 'draft-posts', + slug: draftSlug, admin: { useAsTitle: 'title', - defaultColumns: ['title', 'description', 'createdAt'], + defaultColumns: ['title', 'description', 'createdAt', '_status'], preview: () => 'https://payloadcms.com', }, versions: { diff --git a/test/versions/collections/Versions.ts b/test/versions/collections/Versions.ts index 7e4bd3c4c0..bef471d2d3 100644 --- a/test/versions/collections/Versions.ts +++ b/test/versions/collections/Versions.ts @@ -1,7 +1,8 @@ import type { CollectionConfig } from '../../../src/collections/config/types'; +import { versionSlug } from '../shared'; const VersionPosts: CollectionConfig = { - slug: 'version-posts', + slug: versionSlug, admin: { useAsTitle: 'title', defaultColumns: ['title', 'description', 'createdAt'], diff --git a/test/versions/config.ts b/test/versions/config.ts index 9e0b4383d3..b96dded104 100644 --- a/test/versions/config.ts +++ b/test/versions/config.ts @@ -5,6 +5,7 @@ import AutosaveGlobal from './globals/Autosave'; import { devUser } from '../credentials'; import DraftGlobal from './globals/Draft'; import VersionPosts from './collections/Versions'; +import { draftSlug } from './shared'; export default buildConfig({ collections: [ @@ -28,5 +29,46 @@ export default buildConfig({ password: devUser.password, }, }); + + const { id: draftID } = await payload.create({ + collection: draftSlug, + draft: true, + data: { + id: 1, + title: 'draft title', + description: 'draft description', + radio: 'test', + }, + }); + + await payload.create({ + collection: draftSlug, + draft: false, + data: { + id: 2, + title: 'published title', + description: 'published description', + radio: 'test', + _status: 'published', + }, + }); + + await payload.update({ + collection: draftSlug, + id: draftID, + draft: true, + data: { + title: 'draft title 2', + }, + }); + + await payload.update({ + collection: draftSlug, + id: draftID, + draft: true, + data: { + title: 'draft title 3', + }, + }); }, }); diff --git a/test/versions/e2e.spec.ts b/test/versions/e2e.spec.ts new file mode 100644 index 0000000000..90974ce627 --- /dev/null +++ b/test/versions/e2e.spec.ts @@ -0,0 +1,116 @@ +/** + * TODO: Versions, 3 separate collections + * - drafts + * - save draft before publishing + * - publish immediately + * - validation should be skipped when creating a draft + * + * - autosave + * - versions (no drafts) + * - version control shown + * - assert version counts increment + * - navigate to versions + * - versions view accurately shows number of versions + * - compare + * - iterable + * - nested + * - relationship + * - select w/ i18n options (label: { en: 'example', ... }) + * - tabs + * - text + * - richtext + * - restore version + * - specify locales to show + */ + + +import type { Page } from '@playwright/test'; +import { expect, test } from '@playwright/test'; +import { initPayloadE2E } from '../helpers/configHelpers'; +import { AdminUrlUtil } from '../helpers/adminUrlUtil'; +import { login } from '../helpers'; +import { draftSlug } from './shared'; + +const { beforeAll, describe } = test; + +describe('versions', () => { + let page: Page; + let serverURL: string; + + beforeAll(async ({ browser }) => { + const config = await initPayloadE2E(__dirname); + serverURL = config.serverURL; + + const context = await browser.newContext(); + page = await context.newPage(); + + await login({ page, serverURL }); + }); + + describe('draft collections', () => { + let url: AdminUrlUtil; + beforeAll(() => { + url = new AdminUrlUtil(serverURL, draftSlug); + }); + + test('should bulk publish', async () => { + await page.goto(url.list); + + await page.locator('.select-all__input').click(); + + await page.locator('.publish-many__toggle').click(); + + await page.locator('#confirm-publish').click(); + + await expect(page.locator('.row-1 .cell-_status')).toContainText('Published'); + await expect(page.locator('.row-2 .cell-_status')).toContainText('Published'); + }); + + test('should bulk unpublish', async () => { + await page.goto(url.list); + + await page.locator('.select-all__input').click(); + + await page.locator('.unpublish-many__toggle').click(); + + await page.locator('#confirm-unpublish').click(); + + await expect(page.locator('.row-1 .cell-_status')).toContainText('Draft'); + await expect(page.locator('.row-2 .cell-_status')).toContainText('Draft'); + }); + + test('should publish while editing many', async () => { + const description = 'published document'; + await page.goto(url.list); + await page.locator('.select-all__input').click(); + await page.locator('.edit-many__toggle').click(); + await page.locator('.field-select .rs__control').click(); + const options = page.locator('.rs__option'); + const field = await options.locator('text=description'); + await field.click(); + await page.locator('#field-description').fill(description); + await page.locator('.form-submit .edit-many__publish').click(); + + await expect(page.locator('.Toastify__toast--success')).toContainText('Updated 2 Draft Posts successfully.'); + await expect(page.locator('.row-1 .cell-_status')).toContainText('Published'); + await expect(page.locator('.row-2 .cell-_status')).toContainText('Published'); + }); + + test('should save as draft while editing many', async () => { + const description = 'draft document'; + await page.goto(url.list); + await page.locator('.select-all__input').click(); + await page.locator('.edit-many__toggle').click(); + await page.locator('.field-select .rs__control').click(); + const options = page.locator('.rs__option'); + const field = await options.locator('text=description'); + await field.click(); + await page.locator('#field-description').fill(description); + await page.locator('.form-submit .edit-many__draft').click(); + + await expect(page.locator('.Toastify__toast--success')).toContainText('Updated 2 Draft Posts successfully.'); + await expect(page.locator('.row-1 .cell-_status')).toContainText('Draft'); + await expect(page.locator('.row-2 .cell-_status')).toContainText('Draft'); + }); + }); +}); diff --git a/test/versions/e2e.todo-spec.ts b/test/versions/e2e.todo-spec.ts deleted file mode 100644 index da5a9fb080..0000000000 --- a/test/versions/e2e.todo-spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * TODO: Versions, 3 separate collections - * - drafts - * - save draft before publishing - * - publish immediately - * - validation should be skipped when creating a draft - * - * - autosave - * - versions (no drafts) - * - version control shown - * - assert version counts increment - * - navigate to versions - * - versions view accurately shows number of versions - * - compare - * - iterable - * - nested - * - relationship - * - select w/ i18n options (label: { en: 'example', ... }) - * - tabs - * - text - * - richtext - * - restore version - * - specify locales to show - */ diff --git a/test/versions/globals/Autosave.ts b/test/versions/globals/Autosave.ts index 82ae6dbba9..9e76309de2 100644 --- a/test/versions/globals/Autosave.ts +++ b/test/versions/globals/Autosave.ts @@ -1,7 +1,8 @@ import { GlobalConfig } from '../../../src/globals/config/types'; +import { autoSaveGlobalSlug } from '../shared'; const AutosaveGlobal: GlobalConfig = { - slug: 'autosave-global', + slug: autoSaveGlobalSlug, label: 'Autosave Global', preview: () => 'https://payloadcms.com', versions: { diff --git a/test/versions/globals/Draft.ts b/test/versions/globals/Draft.ts index 5a5a694907..a14864252b 100644 --- a/test/versions/globals/Draft.ts +++ b/test/versions/globals/Draft.ts @@ -1,7 +1,8 @@ import { GlobalConfig } from '../../../src/globals/config/types'; +import { draftGlobalSlug } from '../shared'; const DraftGlobal: GlobalConfig = { - slug: 'draft-global', + slug: draftGlobalSlug, label: 'Draft Global', preview: () => 'https://payloadcms.com', versions: { diff --git a/test/versions/shared.ts b/test/versions/shared.ts new file mode 100644 index 0000000000..99ee455552 --- /dev/null +++ b/test/versions/shared.ts @@ -0,0 +1,6 @@ +export const draftSlug = 'draft-posts'; +export const autosaveSlug = 'autosave-posts'; +export const versionSlug = 'version-posts'; + +export const autoSaveGlobalSlug = 'autosave-global'; +export const draftGlobalSlug = 'draft-global'; diff --git a/yarn.lock b/yarn.lock index a40ca945d5..6ed3f2d2ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -69,32 +69,32 @@ "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/client-cognito-identity@3.279.0": - version "3.279.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.279.0.tgz#c7fc1e920d8466e65934f2d272396185e2ab4d1d" - integrity sha512-MQQdgc3CGDumKutKWlNcDFgNXe2/Tr9TqDGh1EbtXODdxrsT/xQPMghlGCsrTxnPk16zj6OIlS/4h9bTbk6CVg== +"@aws-sdk/client-cognito-identity@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.282.0.tgz#6476c67067873ba695581c83976fecdae1fd3618" + integrity sha512-OU9Wy50u31Mog4xmj9o+lLOb/y+yuQBTFwEVYApJtCkPsI2e3DtZFt36IcAy04fcjNUaSD3u6SGgfYo2vDQ2zA== dependencies: "@aws-crypto/sha256-browser" "3.0.0" "@aws-crypto/sha256-js" "3.0.0" - "@aws-sdk/client-sts" "3.279.0" - "@aws-sdk/config-resolver" "3.272.0" - "@aws-sdk/credential-provider-node" "3.279.0" - "@aws-sdk/fetch-http-handler" "3.272.0" + "@aws-sdk/client-sts" "3.282.0" + "@aws-sdk/config-resolver" "3.282.0" + "@aws-sdk/credential-provider-node" "3.282.0" + "@aws-sdk/fetch-http-handler" "3.282.0" "@aws-sdk/hash-node" "3.272.0" "@aws-sdk/invalid-dependency" "3.272.0" - "@aws-sdk/middleware-content-length" "3.272.0" - "@aws-sdk/middleware-endpoint" "3.272.0" - "@aws-sdk/middleware-host-header" "3.278.0" + "@aws-sdk/middleware-content-length" "3.282.0" + "@aws-sdk/middleware-endpoint" "3.282.0" + "@aws-sdk/middleware-host-header" "3.282.0" "@aws-sdk/middleware-logger" "3.272.0" - "@aws-sdk/middleware-recursion-detection" "3.272.0" - "@aws-sdk/middleware-retry" "3.272.0" + "@aws-sdk/middleware-recursion-detection" "3.282.0" + "@aws-sdk/middleware-retry" "3.282.0" "@aws-sdk/middleware-serde" "3.272.0" - "@aws-sdk/middleware-signing" "3.272.0" + "@aws-sdk/middleware-signing" "3.282.0" "@aws-sdk/middleware-stack" "3.272.0" - "@aws-sdk/middleware-user-agent" "3.272.0" + "@aws-sdk/middleware-user-agent" "3.282.0" "@aws-sdk/node-config-provider" "3.272.0" - "@aws-sdk/node-http-handler" "3.272.0" - "@aws-sdk/protocol-http" "3.272.0" + "@aws-sdk/node-http-handler" "3.282.0" + "@aws-sdk/protocol-http" "3.282.0" "@aws-sdk/smithy-client" "3.279.0" "@aws-sdk/types" "3.272.0" "@aws-sdk/url-parser" "3.272.0" @@ -102,37 +102,37 @@ "@aws-sdk/util-body-length-browser" "3.188.0" "@aws-sdk/util-body-length-node" "3.208.0" "@aws-sdk/util-defaults-mode-browser" "3.279.0" - "@aws-sdk/util-defaults-mode-node" "3.279.0" + "@aws-sdk/util-defaults-mode-node" "3.282.0" "@aws-sdk/util-endpoints" "3.272.0" "@aws-sdk/util-retry" "3.272.0" - "@aws-sdk/util-user-agent-browser" "3.272.0" - "@aws-sdk/util-user-agent-node" "3.272.0" + "@aws-sdk/util-user-agent-browser" "3.282.0" + "@aws-sdk/util-user-agent-node" "3.282.0" "@aws-sdk/util-utf8" "3.254.0" tslib "^2.3.1" -"@aws-sdk/client-sso-oidc@3.279.0": - version "3.279.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.279.0.tgz#358c67172d066968f4439884cdf40299b6822c4e" - integrity sha512-tC9xKGo3z/HQbJDMvaUrnBSSRX7sOX2YUA2OpJ3T1TTfylLTO70OKjg1G4OMFNiPpJsHonwD7Iud+rnMnUKI0g== +"@aws-sdk/client-sso-oidc@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.282.0.tgz#538259969e472e4497f01c8b6fe6fafd59db4147" + integrity sha512-upC4yBZllAXg5OVIuS8Lu9MI1aqfAObl2BBixj9fIYbDanQ02s0b1IwfZqlOqNNkGzMko1AWyiOSyOdVgyJ+xg== dependencies: "@aws-crypto/sha256-browser" "3.0.0" "@aws-crypto/sha256-js" "3.0.0" - "@aws-sdk/config-resolver" "3.272.0" - "@aws-sdk/fetch-http-handler" "3.272.0" + "@aws-sdk/config-resolver" "3.282.0" + "@aws-sdk/fetch-http-handler" "3.282.0" "@aws-sdk/hash-node" "3.272.0" "@aws-sdk/invalid-dependency" "3.272.0" - "@aws-sdk/middleware-content-length" "3.272.0" - "@aws-sdk/middleware-endpoint" "3.272.0" - "@aws-sdk/middleware-host-header" "3.278.0" + "@aws-sdk/middleware-content-length" "3.282.0" + "@aws-sdk/middleware-endpoint" "3.282.0" + "@aws-sdk/middleware-host-header" "3.282.0" "@aws-sdk/middleware-logger" "3.272.0" - "@aws-sdk/middleware-recursion-detection" "3.272.0" - "@aws-sdk/middleware-retry" "3.272.0" + "@aws-sdk/middleware-recursion-detection" "3.282.0" + "@aws-sdk/middleware-retry" "3.282.0" "@aws-sdk/middleware-serde" "3.272.0" "@aws-sdk/middleware-stack" "3.272.0" - "@aws-sdk/middleware-user-agent" "3.272.0" + "@aws-sdk/middleware-user-agent" "3.282.0" "@aws-sdk/node-config-provider" "3.272.0" - "@aws-sdk/node-http-handler" "3.272.0" - "@aws-sdk/protocol-http" "3.272.0" + "@aws-sdk/node-http-handler" "3.282.0" + "@aws-sdk/protocol-http" "3.282.0" "@aws-sdk/smithy-client" "3.279.0" "@aws-sdk/types" "3.272.0" "@aws-sdk/url-parser" "3.272.0" @@ -140,37 +140,37 @@ "@aws-sdk/util-body-length-browser" "3.188.0" "@aws-sdk/util-body-length-node" "3.208.0" "@aws-sdk/util-defaults-mode-browser" "3.279.0" - "@aws-sdk/util-defaults-mode-node" "3.279.0" + "@aws-sdk/util-defaults-mode-node" "3.282.0" "@aws-sdk/util-endpoints" "3.272.0" "@aws-sdk/util-retry" "3.272.0" - "@aws-sdk/util-user-agent-browser" "3.272.0" - "@aws-sdk/util-user-agent-node" "3.272.0" + "@aws-sdk/util-user-agent-browser" "3.282.0" + "@aws-sdk/util-user-agent-node" "3.282.0" "@aws-sdk/util-utf8" "3.254.0" tslib "^2.3.1" -"@aws-sdk/client-sso@3.279.0": - version "3.279.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.279.0.tgz#92c7a8af6859093e7adbf87add9aa3e4a155aa39" - integrity sha512-599Y5wOrkpjD6p0BTs0X4+Ge9O7jlAPJ2ttI9lfhT2/UEZyqoJHajJs1pMJV75oeZklPOBi8G9jnZcMVJgRpvQ== +"@aws-sdk/client-sso@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.282.0.tgz#9d31cf2eacd6d022213d40ad976ae3a00f99838f" + integrity sha512-VzdCCaxlDyU+7wvLDWh+uACQ6RPfaKLQ3yJ2UY0B0SkH4R0E4GLDJ2OJzqS5eyyOsnq1rxfY75S4WYzj8E2cvg== dependencies: "@aws-crypto/sha256-browser" "3.0.0" "@aws-crypto/sha256-js" "3.0.0" - "@aws-sdk/config-resolver" "3.272.0" - "@aws-sdk/fetch-http-handler" "3.272.0" + "@aws-sdk/config-resolver" "3.282.0" + "@aws-sdk/fetch-http-handler" "3.282.0" "@aws-sdk/hash-node" "3.272.0" "@aws-sdk/invalid-dependency" "3.272.0" - "@aws-sdk/middleware-content-length" "3.272.0" - "@aws-sdk/middleware-endpoint" "3.272.0" - "@aws-sdk/middleware-host-header" "3.278.0" + "@aws-sdk/middleware-content-length" "3.282.0" + "@aws-sdk/middleware-endpoint" "3.282.0" + "@aws-sdk/middleware-host-header" "3.282.0" "@aws-sdk/middleware-logger" "3.272.0" - "@aws-sdk/middleware-recursion-detection" "3.272.0" - "@aws-sdk/middleware-retry" "3.272.0" + "@aws-sdk/middleware-recursion-detection" "3.282.0" + "@aws-sdk/middleware-retry" "3.282.0" "@aws-sdk/middleware-serde" "3.272.0" "@aws-sdk/middleware-stack" "3.272.0" - "@aws-sdk/middleware-user-agent" "3.272.0" + "@aws-sdk/middleware-user-agent" "3.282.0" "@aws-sdk/node-config-provider" "3.272.0" - "@aws-sdk/node-http-handler" "3.272.0" - "@aws-sdk/protocol-http" "3.272.0" + "@aws-sdk/node-http-handler" "3.282.0" + "@aws-sdk/protocol-http" "3.282.0" "@aws-sdk/smithy-client" "3.279.0" "@aws-sdk/types" "3.272.0" "@aws-sdk/url-parser" "3.272.0" @@ -178,40 +178,40 @@ "@aws-sdk/util-body-length-browser" "3.188.0" "@aws-sdk/util-body-length-node" "3.208.0" "@aws-sdk/util-defaults-mode-browser" "3.279.0" - "@aws-sdk/util-defaults-mode-node" "3.279.0" + "@aws-sdk/util-defaults-mode-node" "3.282.0" "@aws-sdk/util-endpoints" "3.272.0" "@aws-sdk/util-retry" "3.272.0" - "@aws-sdk/util-user-agent-browser" "3.272.0" - "@aws-sdk/util-user-agent-node" "3.272.0" + "@aws-sdk/util-user-agent-browser" "3.282.0" + "@aws-sdk/util-user-agent-node" "3.282.0" "@aws-sdk/util-utf8" "3.254.0" tslib "^2.3.1" -"@aws-sdk/client-sts@3.279.0": - version "3.279.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.279.0.tgz#87b0a75f47e2b0a20624cc88f5c117546331571e" - integrity sha512-y/cI5Gg5WWqmSSDQftCT26wOLu0HSuPY1u6Q4Q97FBIfRC3hYztjYdUDHuTu6qPPT+tdsnWOUy2tr3qamVSQ4g== +"@aws-sdk/client-sts@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.282.0.tgz#1c4355a5d6a8e6af03e752c3273a59c57aaf1715" + integrity sha512-JZybEaST0rloS9drlX/0yJAnKHuV7DlS1n1WZxgaM2DY704ydlGiviiPQvC/q/dItsX4017gscC0blGJcUjK1g== dependencies: "@aws-crypto/sha256-browser" "3.0.0" "@aws-crypto/sha256-js" "3.0.0" - "@aws-sdk/config-resolver" "3.272.0" - "@aws-sdk/credential-provider-node" "3.279.0" - "@aws-sdk/fetch-http-handler" "3.272.0" + "@aws-sdk/config-resolver" "3.282.0" + "@aws-sdk/credential-provider-node" "3.282.0" + "@aws-sdk/fetch-http-handler" "3.282.0" "@aws-sdk/hash-node" "3.272.0" "@aws-sdk/invalid-dependency" "3.272.0" - "@aws-sdk/middleware-content-length" "3.272.0" - "@aws-sdk/middleware-endpoint" "3.272.0" - "@aws-sdk/middleware-host-header" "3.278.0" + "@aws-sdk/middleware-content-length" "3.282.0" + "@aws-sdk/middleware-endpoint" "3.282.0" + "@aws-sdk/middleware-host-header" "3.282.0" "@aws-sdk/middleware-logger" "3.272.0" - "@aws-sdk/middleware-recursion-detection" "3.272.0" - "@aws-sdk/middleware-retry" "3.272.0" - "@aws-sdk/middleware-sdk-sts" "3.272.0" + "@aws-sdk/middleware-recursion-detection" "3.282.0" + "@aws-sdk/middleware-retry" "3.282.0" + "@aws-sdk/middleware-sdk-sts" "3.282.0" "@aws-sdk/middleware-serde" "3.272.0" - "@aws-sdk/middleware-signing" "3.272.0" + "@aws-sdk/middleware-signing" "3.282.0" "@aws-sdk/middleware-stack" "3.272.0" - "@aws-sdk/middleware-user-agent" "3.272.0" + "@aws-sdk/middleware-user-agent" "3.282.0" "@aws-sdk/node-config-provider" "3.272.0" - "@aws-sdk/node-http-handler" "3.272.0" - "@aws-sdk/protocol-http" "3.272.0" + "@aws-sdk/node-http-handler" "3.282.0" + "@aws-sdk/protocol-http" "3.282.0" "@aws-sdk/smithy-client" "3.279.0" "@aws-sdk/types" "3.272.0" "@aws-sdk/url-parser" "3.272.0" @@ -219,32 +219,32 @@ "@aws-sdk/util-body-length-browser" "3.188.0" "@aws-sdk/util-body-length-node" "3.208.0" "@aws-sdk/util-defaults-mode-browser" "3.279.0" - "@aws-sdk/util-defaults-mode-node" "3.279.0" + "@aws-sdk/util-defaults-mode-node" "3.282.0" "@aws-sdk/util-endpoints" "3.272.0" "@aws-sdk/util-retry" "3.272.0" - "@aws-sdk/util-user-agent-browser" "3.272.0" - "@aws-sdk/util-user-agent-node" "3.272.0" + "@aws-sdk/util-user-agent-browser" "3.282.0" + "@aws-sdk/util-user-agent-node" "3.282.0" "@aws-sdk/util-utf8" "3.254.0" fast-xml-parser "4.1.2" tslib "^2.3.1" -"@aws-sdk/config-resolver@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/config-resolver/-/config-resolver-3.272.0.tgz#207af3c70b05c4d93c60fa60201c93dff78802ba" - integrity sha512-Dr4CffRVNsOp3LRNdpvcH6XuSgXOSLblWliCy/5I86cNl567KVMxujVx6uPrdTXYs2h1rt3MNl6jQGnAiJeTbw== +"@aws-sdk/config-resolver@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/config-resolver/-/config-resolver-3.282.0.tgz#b76f3b7daedc2dfca261445f0d222b3d15d693e5" + integrity sha512-30qFLh2N4NXQ2EAook7NIFeu1K/nlrRLrdVb2BtGFi/F3cZnz+sy9o0XmL6x+sO9TznWjdNxD1RKQdqoAwGnCQ== dependencies: - "@aws-sdk/signature-v4" "3.272.0" + "@aws-sdk/signature-v4" "3.282.0" "@aws-sdk/types" "3.272.0" "@aws-sdk/util-config-provider" "3.208.0" "@aws-sdk/util-middleware" "3.272.0" tslib "^2.3.1" -"@aws-sdk/credential-provider-cognito-identity@3.279.0": - version "3.279.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.279.0.tgz#941876f7ca56d72eebb051ee51c52e52fada4e2e" - integrity sha512-RhpKmIM1RfrmbtTXiDK3qyx9aJcZlvHpP18Axbjokld7R6YEdMpv8piB8v19kd6zl0e9xPNrX9At6UOFsXpeJw== +"@aws-sdk/credential-provider-cognito-identity@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.282.0.tgz#4a6153fc2da525e1c01ee2d07cc6626f40c22248" + integrity sha512-GsLOt6GzckLQbMzgXOblKcRtXyMu3NcP0vFkYpy4r9oEzoxqPhy1yUpRNLeDv7r2qoa8naN81F5FwPwd17PrKg== dependencies: - "@aws-sdk/client-cognito-identity" "3.279.0" + "@aws-sdk/client-cognito-identity" "3.282.0" "@aws-sdk/property-provider" "3.272.0" "@aws-sdk/types" "3.272.0" tslib "^2.3.1" @@ -269,31 +269,31 @@ "@aws-sdk/url-parser" "3.272.0" tslib "^2.3.1" -"@aws-sdk/credential-provider-ini@3.279.0": - version "3.279.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.279.0.tgz#22e477be3b9642a7509aad64d69bd7624cdfafd1" - integrity sha512-FCpr3/khMTb2pWLMC138wU7HzcpkrjejrBNfCJSUu7Emxm039UMLjT2JObnRKxidKlz58oYBRayVIbBYRWREcg== +"@aws-sdk/credential-provider-ini@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.282.0.tgz#60bc1d0fb3cf7053335f42f95f01601f5fdcf4bc" + integrity sha512-2GKduXORcUgOigF1jZF7A1Wh4W/aJt3ynh7xb1vfx020nHx6YDljrEGpzgH6pOVzl7ZhgthpojicCuy2UumkMA== dependencies: "@aws-sdk/credential-provider-env" "3.272.0" "@aws-sdk/credential-provider-imds" "3.272.0" "@aws-sdk/credential-provider-process" "3.272.0" - "@aws-sdk/credential-provider-sso" "3.279.0" + "@aws-sdk/credential-provider-sso" "3.282.0" "@aws-sdk/credential-provider-web-identity" "3.272.0" "@aws-sdk/property-provider" "3.272.0" "@aws-sdk/shared-ini-file-loader" "3.272.0" "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/credential-provider-node@3.279.0": - version "3.279.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.279.0.tgz#46e6d477f2a94878f3635598a371d34df6e6ace3" - integrity sha512-7D8ETopCt3H+x2BEPMEhzc4dcNtSK7umnRdgfiTGRjcQSVPh5Whq/tDIAtNj2D+PmLgu3e+Hk7jrXkaMCDlxMw== +"@aws-sdk/credential-provider-node@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.282.0.tgz#90b71f75ae25b8e654b15271b14b0af736a2b2b3" + integrity sha512-qyHipZW0ep8STY+SO+Me8ObQ1Ee/aaZTmAK0Os/gB+EsiZhIE+mi6zRcScwdnpgJPLRYMEe4p/Cr6DOrA0G0GQ== dependencies: "@aws-sdk/credential-provider-env" "3.272.0" "@aws-sdk/credential-provider-imds" "3.272.0" - "@aws-sdk/credential-provider-ini" "3.279.0" + "@aws-sdk/credential-provider-ini" "3.282.0" "@aws-sdk/credential-provider-process" "3.272.0" - "@aws-sdk/credential-provider-sso" "3.279.0" + "@aws-sdk/credential-provider-sso" "3.282.0" "@aws-sdk/credential-provider-web-identity" "3.272.0" "@aws-sdk/property-provider" "3.272.0" "@aws-sdk/shared-ini-file-loader" "3.272.0" @@ -310,15 +310,15 @@ "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/credential-provider-sso@3.279.0": - version "3.279.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.279.0.tgz#e0eb3928a86c3978f77dd218ed84c306ebdfb44c" - integrity sha512-u8ZHz9Sv7sAv2vllyzcdgcO1pC5pa2UuoYKfz9J8hqLHc8VJYtYQ6WJvi4GoA55VDPk87pyVYz9t6ATntsukaw== +"@aws-sdk/credential-provider-sso@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.282.0.tgz#a922821d9e0fa892af131c3774f1ecbd62545cd2" + integrity sha512-c4nibry7u0hkYRMi7+cWzdwYXfDDG+j3VYFxk2oOvU1VIJRyE6oeJqVaz3jgYLX9brHyrLJjuFCIJCUV/WXgIA== dependencies: - "@aws-sdk/client-sso" "3.279.0" + "@aws-sdk/client-sso" "3.282.0" "@aws-sdk/property-provider" "3.272.0" "@aws-sdk/shared-ini-file-loader" "3.272.0" - "@aws-sdk/token-providers" "3.279.0" + "@aws-sdk/token-providers" "3.282.0" "@aws-sdk/types" "3.272.0" tslib "^2.3.1" @@ -332,32 +332,32 @@ tslib "^2.3.1" "@aws-sdk/credential-providers@^3.186.0": - version "3.279.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-providers/-/credential-providers-3.279.0.tgz#1c703e7638eabe4ccda071badbcdc48e14c0d601" - integrity sha512-iZI7hrP7oEP2zzStuiEWklVQY3HqIX02PxQiGMgW9/6Bb+2xBpZykIkBBmqaOKlNF/us1TJS6WDOylL1Z1EcIw== + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-providers/-/credential-providers-3.282.0.tgz#c236588fd3fb8283df26267be93da3917fb8cea6" + integrity sha512-/Pau2Ht15j26ibTSTaJHbx6wA3suNT0Qgu+++6ZUoVCeHL5ZN/otcoebsR/lOZTw8Fji7K5kl8TW41UNAE8s2w== dependencies: - "@aws-sdk/client-cognito-identity" "3.279.0" - "@aws-sdk/client-sso" "3.279.0" - "@aws-sdk/client-sts" "3.279.0" - "@aws-sdk/credential-provider-cognito-identity" "3.279.0" + "@aws-sdk/client-cognito-identity" "3.282.0" + "@aws-sdk/client-sso" "3.282.0" + "@aws-sdk/client-sts" "3.282.0" + "@aws-sdk/credential-provider-cognito-identity" "3.282.0" "@aws-sdk/credential-provider-env" "3.272.0" "@aws-sdk/credential-provider-imds" "3.272.0" - "@aws-sdk/credential-provider-ini" "3.279.0" - "@aws-sdk/credential-provider-node" "3.279.0" + "@aws-sdk/credential-provider-ini" "3.282.0" + "@aws-sdk/credential-provider-node" "3.282.0" "@aws-sdk/credential-provider-process" "3.272.0" - "@aws-sdk/credential-provider-sso" "3.279.0" + "@aws-sdk/credential-provider-sso" "3.282.0" "@aws-sdk/credential-provider-web-identity" "3.272.0" "@aws-sdk/property-provider" "3.272.0" "@aws-sdk/shared-ini-file-loader" "3.272.0" "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/fetch-http-handler@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.272.0.tgz#52ec2ba4ea25738a91db466a617bd7cc2bd6d2e9" - integrity sha512-1Qhm9e0RbS1Xf4CZqUbQyUMkDLd7GrsRXWIvm9b86/vgeV8/WnjO3CMue9D51nYgcyQORhYXv6uVjAYCWbUExA== +"@aws-sdk/fetch-http-handler@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.282.0.tgz#aee6e441013880553b15db7ce66cbebba2e26f6b" + integrity sha512-RTd53UzKtUucIEdVLGGgtlbVwp0QkOt3ZfHuA/A1lOH7meChSh1kz7B5z3p4HQDpXO+MQ1Y6Ble9Vg2fh1zwJQ== dependencies: - "@aws-sdk/protocol-http" "3.272.0" + "@aws-sdk/protocol-http" "3.282.0" "@aws-sdk/querystring-builder" "3.272.0" "@aws-sdk/types" "3.272.0" "@aws-sdk/util-base64" "3.208.0" @@ -388,35 +388,35 @@ dependencies: tslib "^2.3.1" -"@aws-sdk/middleware-content-length@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-content-length/-/middleware-content-length-3.272.0.tgz#400532904c505d3478ddf5c8fe1d703692ea87e8" - integrity sha512-sAbDZSTNmLX+UTGwlUHJBWy0QGQkiClpHwVFXACon+aG0ySLNeRKEVYs6NCPYldw4cj6hveLUn50cX44ukHErw== +"@aws-sdk/middleware-content-length@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-content-length/-/middleware-content-length-3.282.0.tgz#aa05051b33e94b0db46ede2e9839b601503e081a" + integrity sha512-SDgMLRRTMr9LlHSNk4bXUXynYnkT4oNMqE+FxhjsdbT8hK36eS4AadM58R7nPwgjR3EuWRW4ZRRawLWatpWspA== dependencies: - "@aws-sdk/protocol-http" "3.272.0" + "@aws-sdk/protocol-http" "3.282.0" "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/middleware-endpoint@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.272.0.tgz#3d10dff07eeb6239b39b2e2762b11d97f19e4a56" - integrity sha512-Dk3JVjj7SxxoUKv3xGiOeBksvPtFhTDrVW75XJ98Ymv8gJH5L1sq4hIeJAHRKogGiRFq2J73mnZSlM9FVXEylg== +"@aws-sdk/middleware-endpoint@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.282.0.tgz#c69615330932db1292206926752cac84428fde47" + integrity sha512-8U9Mv/Sbdo1KI6/ip7IIUdBl5pgmalFbfkYAyO+AtmkEvawI9ipdWFs5HB0Dwd1BGVup5choY72Ik/7sCAAFTQ== dependencies: "@aws-sdk/middleware-serde" "3.272.0" - "@aws-sdk/protocol-http" "3.272.0" - "@aws-sdk/signature-v4" "3.272.0" + "@aws-sdk/protocol-http" "3.282.0" + "@aws-sdk/signature-v4" "3.282.0" "@aws-sdk/types" "3.272.0" "@aws-sdk/url-parser" "3.272.0" "@aws-sdk/util-config-provider" "3.208.0" "@aws-sdk/util-middleware" "3.272.0" tslib "^2.3.1" -"@aws-sdk/middleware-host-header@3.278.0": - version "3.278.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.278.0.tgz#d941a952d3f26453a4fff5939951e4bf99d7ce65" - integrity sha512-oTkF3exy89KE8NgSeXFwD+0H0GRKL2qUw92t3caEj7+4KzU/0m3t7NtKlq2NLRtTJhZ/izYRpV536oogLzGm3g== +"@aws-sdk/middleware-host-header@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.282.0.tgz#3df90724f9a97b1bf8151faf7534ac7f7fa2c5e9" + integrity sha512-90dfYow4zh4tCatTOnqB3nE/dIAucQLZnMqwN/WBPu0fUqjymzpsNkPchqWBPnSWdNE8w3PiKMqqD9rjYwqw4Q== dependencies: - "@aws-sdk/protocol-http" "3.272.0" + "@aws-sdk/protocol-http" "3.282.0" "@aws-sdk/types" "3.272.0" tslib "^2.3.1" @@ -428,21 +428,21 @@ "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/middleware-recursion-detection@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.272.0.tgz#1e6ddc66a11fa2bfd2a59607d2ac5603be6d1072" - integrity sha512-Gp/eKWeUWVNiiBdmUM2qLkBv+VLSJKoWAO+aKmyxxwjjmWhE0FrfA1NQ1a3g+NGMhRbAfQdaYswRAKsul70ISg== +"@aws-sdk/middleware-recursion-detection@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.282.0.tgz#7766d7dc95fa59e8fdfe2dc8cc5af647063eaa0f" + integrity sha512-cSLq/daEaTEucbP/TgAXIOcpwLu7Bfw3VGzH1U56ngDjI4KWvUheF16JiB6OqKQXduPBPsdZ9dVmkDVKddmCRw== dependencies: - "@aws-sdk/protocol-http" "3.272.0" + "@aws-sdk/protocol-http" "3.282.0" "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/middleware-retry@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-retry/-/middleware-retry-3.272.0.tgz#a38adcb9eb478246de3f3398bb8fd0a7682462eb" - integrity sha512-pCGvHM7C76VbO/dFerH+Vwf7tGv7j+e+eGrvhQ35mRghCtfIou/WMfTZlD1TNee93crrAQQVZKjtW3dMB3WCzg== +"@aws-sdk/middleware-retry@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-retry/-/middleware-retry-3.282.0.tgz#0ddc73f9a41d7990bac2b8221452beb244cf88c5" + integrity sha512-3+0M1GP9o480IdqHVZbkhTgge63uKhDFlS6cQznpNGj0eIuQPhXRnlEz2/rma0INUqFm6+7qJ5yzHR4WQbfHpw== dependencies: - "@aws-sdk/protocol-http" "3.272.0" + "@aws-sdk/protocol-http" "3.282.0" "@aws-sdk/service-error-classification" "3.272.0" "@aws-sdk/types" "3.272.0" "@aws-sdk/util-middleware" "3.272.0" @@ -450,15 +450,15 @@ tslib "^2.3.1" uuid "^8.3.2" -"@aws-sdk/middleware-sdk-sts@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.272.0.tgz#aa437331f958e3af3b4bec7951256d0f34a8d431" - integrity sha512-VvYPg7LrDIjUOWueSzo2wBzcNG7dw+cmzV6zAKaLxf0RC5jeAP4hE0OzDiiZfDrjNghEzgq/V+0NO+LewqYL9Q== +"@aws-sdk/middleware-sdk-sts@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.282.0.tgz#f8a52a0ef2b5e0bc7c3df697d0f24f85ea4f12c9" + integrity sha512-Qe20mtJcF6lxt7280FhTFD2IpBDn39MEXmbm/zIkXR2/cAmvji8YhcxhNrq1l7XiuMM6SokBDC/f3dlF1oOC6g== dependencies: - "@aws-sdk/middleware-signing" "3.272.0" + "@aws-sdk/middleware-signing" "3.282.0" "@aws-sdk/property-provider" "3.272.0" - "@aws-sdk/protocol-http" "3.272.0" - "@aws-sdk/signature-v4" "3.272.0" + "@aws-sdk/protocol-http" "3.282.0" + "@aws-sdk/signature-v4" "3.282.0" "@aws-sdk/types" "3.272.0" tslib "^2.3.1" @@ -470,14 +470,14 @@ "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/middleware-signing@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.272.0.tgz#ce632b547d5a091b4bda9d65cb4745445ab5d237" - integrity sha512-4LChFK4VAR91X+dupqM8fQqYhFGE0G4Bf9rQlVTgGSbi2KUOmpqXzH0/WKE228nKuEhmH8+Qd2VPSAE2JcyAUA== +"@aws-sdk/middleware-signing@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.282.0.tgz#10551814e112300bfae906e00f9045ddad9fa05f" + integrity sha512-eE5qMDcqqxZPdSwybUEph/knrA2j2cHjW+B2ddROw3Ojg0XLjep5hOhithAudgBREQhYF9pdsBr6mUMynUIrKw== dependencies: "@aws-sdk/property-provider" "3.272.0" - "@aws-sdk/protocol-http" "3.272.0" - "@aws-sdk/signature-v4" "3.272.0" + "@aws-sdk/protocol-http" "3.282.0" + "@aws-sdk/signature-v4" "3.282.0" "@aws-sdk/types" "3.272.0" "@aws-sdk/util-middleware" "3.272.0" tslib "^2.3.1" @@ -489,12 +489,12 @@ dependencies: tslib "^2.3.1" -"@aws-sdk/middleware-user-agent@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.272.0.tgz#ea49970c9dbbe4e8fce21763e2ff0d7acab057c2" - integrity sha512-Qy7/0fsDJxY5l0bEk7WKDfqb4Os/sCAgFR2zEvrhDtbkhYPf72ysvg/nRUTncmCbo8tOok4SJii2myk8KMfjjw== +"@aws-sdk/middleware-user-agent@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.282.0.tgz#6f6f3ed06bbf90c871516e1cdbce4cb98b90da2e" + integrity sha512-P1ealsSrUALo0w0Qu5nBKsNQwsmqIfsoNtFWpaznjIcXE5rRMlZL69zb0KnGbQCBfEXsgaMOWjeGT8I3/XbOHQ== dependencies: - "@aws-sdk/protocol-http" "3.272.0" + "@aws-sdk/protocol-http" "3.282.0" "@aws-sdk/types" "3.272.0" tslib "^2.3.1" @@ -508,13 +508,13 @@ "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/node-http-handler@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/node-http-handler/-/node-http-handler-3.272.0.tgz#732c7010310da292d4a6c30f915078e1792d029e" - integrity sha512-VrW9PjhhngeyYp4yGYPe5S0vgZH6NwU3Po9xAgayUeE37Inr7LS1YteFMHdpgsUUeNXnh7d06CXqHo1XjtqOKA== +"@aws-sdk/node-http-handler@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/node-http-handler/-/node-http-handler-3.282.0.tgz#dde64a0977d98dc862770fc99b5127ff48726a9e" + integrity sha512-LIA4lsSKA/l1kTR5ERkJG2gARveB7Y40MR6yDwtIuhXeVu7Xo9m4BJFanCYIbyc093W0T53x438bwoBR+R+/fw== dependencies: "@aws-sdk/abort-controller" "3.272.0" - "@aws-sdk/protocol-http" "3.272.0" + "@aws-sdk/protocol-http" "3.282.0" "@aws-sdk/querystring-builder" "3.272.0" "@aws-sdk/types" "3.272.0" tslib "^2.3.1" @@ -527,10 +527,10 @@ "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/protocol-http@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/protocol-http/-/protocol-http-3.272.0.tgz#11090fed5d1e20f9f8e97b479e1d6fb2247686f6" - integrity sha512-4JQ54v5Yn08jspNDeHo45CaSn1CvTJqS1Ywgr79eU6jBExtguOWv6LNtwVSBD9X37v88iqaxt8iu1Z3pZZAJeg== +"@aws-sdk/protocol-http@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/protocol-http/-/protocol-http-3.282.0.tgz#ed6b345fad824bea27bd78dcc3f6b54c55118d70" + integrity sha512-aOPv5DhsbG06WKfeh2g0H8RGnaeI8pLhaA+Mq1BvzXcghhlDu+FM9K/GjC/f1lWk1UNryfevOR7SdQm95ciHQg== dependencies: "@aws-sdk/types" "3.272.0" tslib "^2.3.1" @@ -565,10 +565,10 @@ "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/signature-v4@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4/-/signature-v4-3.272.0.tgz#751895d68c1d1122f1e9a0148146dbdf9db023ae" - integrity sha512-pWxnHG1NqJWMwlhJ6NHNiUikOL00DHROmxah6krJPMPq4I3am2KY2Rs/8ouWhnEXKaHAv4EQhSALJ+7Mq5S4/A== +"@aws-sdk/signature-v4@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4/-/signature-v4-3.282.0.tgz#5ce58267b8225fadbf5134e616e02fae567cfc0a" + integrity sha512-rnSL3UyF/No7+O2EMtN1sTCiqL1a+odbfnfo3wCSl8DH5PEYINt2kZgVEvT1Fgaffk1pUggBBOZoR+arPIIDJA== dependencies: "@aws-sdk/is-array-buffer" "3.201.0" "@aws-sdk/types" "3.272.0" @@ -587,12 +587,12 @@ "@aws-sdk/types" "3.272.0" tslib "^2.3.1" -"@aws-sdk/token-providers@3.279.0": - version "3.279.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.279.0.tgz#721194bc67100dbf8b563857bf71900de6735261" - integrity sha512-bsUlZSizTXZ8Pehdatcioi8VphxYn7fRjo5L083peOvBjoL+9WSGyP74PLrFLNwA35QxddwOqgnpQZoE1heuPQ== +"@aws-sdk/token-providers@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.282.0.tgz#a3983a121e430f1dce043aeb3251dc6a0887e009" + integrity sha512-Qk/D6i+Hpc0fp/2SRHbfJeKPgUIugzsmye3NL0OV1bqd1Y40dW5LT4u67VcZHwqxzYDKe6Eo+7NHJu7qfvwhog== dependencies: - "@aws-sdk/client-sso-oidc" "3.279.0" + "@aws-sdk/client-sso-oidc" "3.282.0" "@aws-sdk/property-provider" "3.272.0" "@aws-sdk/shared-ini-file-loader" "3.272.0" "@aws-sdk/types" "3.272.0" @@ -661,12 +661,12 @@ bowser "^2.11.0" tslib "^2.3.1" -"@aws-sdk/util-defaults-mode-node@3.279.0": - version "3.279.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.279.0.tgz#e05c043898e937282c45c1b3bcefab10e569783e" - integrity sha512-A2NB10xReWC+GSnOivKGZ9rnljIZdEP8WMCQQEnA6DJNI19AUFF/O9QJ9y+cHGLKEms7jH86Y99wShdpzAK+Jw== +"@aws-sdk/util-defaults-mode-node@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.282.0.tgz#827c6d7c7b6de1493873a789be4d4916ae3163b2" + integrity sha512-D1BlFoA7ZMeK2diDUWFx1xBFrSaJuBZMRBuWbnbT9AnRYNCsASZ8DRU1KkZ8LuFQIwmZz94P9q683emYnZBhiw== dependencies: - "@aws-sdk/config-resolver" "3.272.0" + "@aws-sdk/config-resolver" "3.282.0" "@aws-sdk/credential-provider-imds" "3.272.0" "@aws-sdk/node-config-provider" "3.272.0" "@aws-sdk/property-provider" "3.272.0" @@ -717,19 +717,19 @@ dependencies: tslib "^2.3.1" -"@aws-sdk/util-user-agent-browser@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.272.0.tgz#9ff8834d38b2178d72cc5c63ba3e089cc1b9a9ae" - integrity sha512-Lp5QX5bH6uuwBlIdr7w7OAcAI50ttyskb++yUr9i+SPvj6RI2dsfIBaK4mDg1qUdM5LeUdvIyqwj3XHjFKAAvA== +"@aws-sdk/util-user-agent-browser@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.282.0.tgz#00998e8bbab30baa45c38701907b80338abe55cc" + integrity sha512-Z639oyTa5fZfyi4Xr64+eiAwBCxfpe9Op4Vhnr1z/RwonQM/qywydv6Ttpeq1q5uQ0nG4wTkOMpfh39g+VqIgw== dependencies: "@aws-sdk/types" "3.272.0" bowser "^2.11.0" tslib "^2.3.1" -"@aws-sdk/util-user-agent-node@3.272.0": - version "3.272.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.272.0.tgz#8e8c85d8c3ac4471a309589d91094be14a4260df" - integrity sha512-ljK+R3l+Q1LIHrcR+Knhk0rmcSkfFadZ8V+crEGpABf/QUQRg7NkZMsoe814tfBO5F7tMxo8wwwSdaVNNHtoRA== +"@aws-sdk/util-user-agent-node@3.282.0": + version "3.282.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.282.0.tgz#1e8c59b32f7567a07e222ecebb4bcf91398b01f2" + integrity sha512-GSOdWNmzEd554wR9HBrgeYptKBOybveVwUkd6ws+YTdCOz4xD5Gga+I5JomKkcMEUVdBrJnYVUtq7ZsJy2f11w== dependencies: "@aws-sdk/node-config-provider" "3.272.0" "@aws-sdk/types" "3.272.0" @@ -2455,9 +2455,9 @@ form-data "^3.0.0" "@types/node@*": - version "18.14.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.2.tgz#c076ed1d7b6095078ad3cf21dfeea951842778b1" - integrity sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA== + version "18.14.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.4.tgz#0e64ec0b35a772e1e3d849f9a0ff61782d0cb647" + integrity sha512-VhCw7I7qO2X49+jaKcAUwi3rR+hbxT5VcYF493+Z5kMLI0DL568b7JI4IDJaxWFH0D/xwmGJNoXisyX+w7GH/g== "@types/nodemailer@^6.4.7": version "6.4.7" @@ -2601,9 +2601,9 @@ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-datepicker@^4.8.0": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@types/react-datepicker/-/react-datepicker-4.8.0.tgz#0221bd38725b7db64cd08a89f49a93d816c2f691" - integrity sha512-20uzZsIf4moPAjjHDfPvH8UaOHZBxrkiQZoLS3wgKq8Xhp+95gdercLEdoA7/I8nR9R5Jz2qQkdMIM+Lq4AS1A== + version "4.10.0" + resolved "https://registry.yarnpkg.com/@types/react-datepicker/-/react-datepicker-4.10.0.tgz#fcb0e6a7787491bf2f37fbda2b537062608a0056" + integrity sha512-Cq+ks20vBIU6XN67TbkCHu8M7V46Y6vJrKE2n+8q/GfueJyWWTIKeC3Z7cz/d+qxGDq/VCrqA929R0U4lNuztg== dependencies: "@popperjs/core" "^2.9.2" "@types/react" "*" @@ -5104,9 +5104,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.284: - version "1.4.313" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.313.tgz#ff95f01926ab748c65beb23fc55f2f178e7a24a9" - integrity sha512-QckB9OVqr2oybjIrbMI99uF+b9+iTja5weFe0ePbqLb5BHqXOJUO1SG6kDj/1WtWPRIBr51N153AEq8m7HuIaA== + version "1.4.316" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.316.tgz#7f2cfda199ae1a32a69b3eb9046b8b0640086064" + integrity sha512-9urqVpdeJYAwVL/sBjsX2pSlgYI/b4nOqC6UaNa0xlq1VUbXLRhERWlxcQ4FWfUOQQxSSxN/taFtapEcwg5tVA== emittery@^0.13.1: version "0.13.1" @@ -5575,9 +5575,9 @@ esprima@^4.0.0, esprima@^4.0.1: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.0.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.2.tgz#c6d3fee05dd665808e2ad870631f221f5617b1d1" - integrity sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng== + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" @@ -6853,12 +6853,12 @@ is-arguments@^1.1.1: has-tostringtag "^1.0.0" is-array-buffer@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" - integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== dependencies: call-bind "^1.0.2" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" is-typed-array "^1.1.10" is-arrayish@^0.2.1: @@ -8603,9 +8603,9 @@ nodemailer@^6.9.0: integrity sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA== nodemon@^2.0.20: - version "2.0.20" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.20.tgz#e3537de768a492e8d74da5c5813cb0c7486fc701" - integrity sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw== + version "2.0.21" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.21.tgz#267edff25578da91075d6aa54346ef77ecb7b302" + integrity sha512-djN/n2549DUtY33S7o1djRCd7dEm0kBnj9c7S9XVXqRUbuggN1MZH/Nqa+5RFQr63Fbefq37nFXAE9VU86yL1A== dependencies: chokidar "^3.5.2" debug "^3.2.7" @@ -10377,9 +10377,9 @@ relateurl@^0.2.7: integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== release-it@^15.6.0: - version "15.6.1" - resolved "https://registry.yarnpkg.com/release-it/-/release-it-15.6.1.tgz#843e4ab66110bf2460d14536fb7ce36804f2a078" - integrity sha512-oPJKP2yxMcA/TaGFnS5uB0spDSPiDXXd1yGKSSMQ2xutyiSs8X+1UahlJCF/xHnp01QwUqRv0fpsV9MFr2GKmQ== + version "15.7.0" + resolved "https://registry.yarnpkg.com/release-it/-/release-it-15.7.0.tgz#9ea011208710736b5173e3691b48b6f719622c65" + integrity sha512-/ajllmzQNuxXCrPySwx+MiR/nBHp8H+lTn42QFw9YlFPJQpLFwgMjCOFTB4hhuavi9jMbDpWcTPFy31QJgzNcw== dependencies: "@iarna/toml" "2.2.5" "@octokit/rest" "19.0.7"