mirror of
				https://github.com/strapi/strapi.git
				synced 2025-11-04 03:43:34 +00:00 
			
		
		
		
	chore(i18n): convert CM List view components to TS (#18838)
This commit is contained in:
		
							parent
							
								
									306b218011
								
							
						
					
					
						commit
						a1c8cbb8a8
					
				
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -1,41 +0,0 @@
 | 
				
			|||||||
import * as React from 'react';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { Typography } from '@strapi/design-system';
 | 
					 | 
				
			||||||
import { useIntl } from 'react-intl';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import useHasI18n from '../../../hooks/useHasI18n';
 | 
					 | 
				
			||||||
import { getTranslation } from '../../../utils/getTranslation';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const Emphasis = (chunks: React.ReactNode) => {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Typography fontWeight="semiBold" textColor="danger500">
 | 
					 | 
				
			||||||
      {chunks}
 | 
					 | 
				
			||||||
    </Typography>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const DeleteModalAdditionalInfos = () => {
 | 
					 | 
				
			||||||
  const hasI18nEnabled = useHasI18n();
 | 
					 | 
				
			||||||
  const { formatMessage } = useIntl();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!hasI18nEnabled) {
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Typography textColor="danger500">
 | 
					 | 
				
			||||||
      {formatMessage(
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: getTranslation('Settings.list.actions.deleteAdditionalInfos'),
 | 
					 | 
				
			||||||
          defaultMessage:
 | 
					 | 
				
			||||||
            'This will delete the active locale versions <em>(from Internationalization)</em>',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          em: Emphasis,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      )}
 | 
					 | 
				
			||||||
    </Typography>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default DeleteModalAdditionalInfos;
 | 
					 | 
				
			||||||
@ -1,41 +0,0 @@
 | 
				
			|||||||
import * as React from 'react';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { Typography } from '@strapi/design-system';
 | 
					 | 
				
			||||||
import { useIntl } from 'react-intl';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import useHasI18n from '../../../hooks/useHasI18n';
 | 
					 | 
				
			||||||
import { getTranslation } from '../../../utils/getTranslation';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const Emphasis = (chunks: React.ReactNode) => {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Typography fontWeight="semiBold" textColor="danger500">
 | 
					 | 
				
			||||||
      {chunks}
 | 
					 | 
				
			||||||
    </Typography>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const PublishModalAdditionalInfos = () => {
 | 
					 | 
				
			||||||
  const hasI18nEnabled = useHasI18n();
 | 
					 | 
				
			||||||
  const { formatMessage } = useIntl();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!hasI18nEnabled) {
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Typography textColor="danger500">
 | 
					 | 
				
			||||||
      {formatMessage(
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: getTranslation('Settings.list.actions.publishAdditionalInfos'),
 | 
					 | 
				
			||||||
          defaultMessage:
 | 
					 | 
				
			||||||
            'This will publish the active locale versions <em>(from Internationalization)</em>',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          em: Emphasis,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      )}
 | 
					 | 
				
			||||||
    </Typography>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default PublishModalAdditionalInfos;
 | 
					 | 
				
			||||||
@ -1,41 +0,0 @@
 | 
				
			|||||||
import * as React from 'react';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { Typography } from '@strapi/design-system';
 | 
					 | 
				
			||||||
import { useIntl } from 'react-intl';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import useHasI18n from '../../../hooks/useHasI18n';
 | 
					 | 
				
			||||||
import { getTranslation } from '../../../utils/getTranslation';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const Emphasis = (chunks: React.ReactNode) => {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Typography fontWeight="semiBold" textColor="danger500">
 | 
					 | 
				
			||||||
      {chunks}
 | 
					 | 
				
			||||||
    </Typography>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const UnpublishModalAdditionalInfos = () => {
 | 
					 | 
				
			||||||
  const hasI18nEnabled = useHasI18n();
 | 
					 | 
				
			||||||
  const { formatMessage } = useIntl();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!hasI18nEnabled) {
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Typography textColor="danger500">
 | 
					 | 
				
			||||||
      {formatMessage(
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: getTranslation('Settings.list.actions.unpublishAdditionalInfos'),
 | 
					 | 
				
			||||||
          defaultMessage:
 | 
					 | 
				
			||||||
            'This will unpublish the active locale versions <em>(from Internationalization)</em>',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          em: Emphasis,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      )}
 | 
					 | 
				
			||||||
    </Typography>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default UnpublishModalAdditionalInfos;
 | 
					 | 
				
			||||||
@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					import * as React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Typography } from '@strapi/design-system';
 | 
				
			||||||
 | 
					import { useIntl } from 'react-intl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useContentTypeHasI18n } from '../hooks/useContentTypeHasI18n';
 | 
				
			||||||
 | 
					import { getTranslation } from '../utils/getTranslation';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Emphasis = (chunks: React.ReactNode) => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Typography fontWeight="semiBold" textColor="danger500">
 | 
				
			||||||
 | 
					      {chunks}
 | 
				
			||||||
 | 
					    </Typography>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DeleteModalAdditionalInfo = () => {
 | 
				
			||||||
 | 
					  const hasI18nEnabled = useContentTypeHasI18n();
 | 
				
			||||||
 | 
					  const { formatMessage } = useIntl();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!hasI18nEnabled) {
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Typography textColor="danger500">
 | 
				
			||||||
 | 
					      {formatMessage(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: getTranslation('Settings.list.actions.deleteAdditionalInfos'),
 | 
				
			||||||
 | 
					          defaultMessage:
 | 
				
			||||||
 | 
					            'This will delete the active locale versions <em>(from Internationalization)</em>',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          em: Emphasis,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </Typography>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const PublishModalAdditionalInfo = () => {
 | 
				
			||||||
 | 
					  const hasI18nEnabled = useContentTypeHasI18n();
 | 
				
			||||||
 | 
					  const { formatMessage } = useIntl();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!hasI18nEnabled) {
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Typography textColor="danger500">
 | 
				
			||||||
 | 
					      {formatMessage(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: getTranslation('Settings.list.actions.publishAdditionalInfos'),
 | 
				
			||||||
 | 
					          defaultMessage:
 | 
				
			||||||
 | 
					            'This will publish the active locale versions <em>(from Internationalization)</em>',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          em: Emphasis,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </Typography>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const UnpublishModalAdditionalInfo = () => {
 | 
				
			||||||
 | 
					  const hasI18nEnabled = useContentTypeHasI18n();
 | 
				
			||||||
 | 
					  const { formatMessage } = useIntl();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!hasI18nEnabled) {
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Typography textColor="danger500">
 | 
				
			||||||
 | 
					      {formatMessage(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: getTranslation('Settings.list.actions.unpublishAdditionalInfos'),
 | 
				
			||||||
 | 
					          defaultMessage:
 | 
				
			||||||
 | 
					            'This will unpublish the active locale versions <em>(from Internationalization)</em>',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          em: Emphasis,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </Typography>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { DeleteModalAdditionalInfo, PublishModalAdditionalInfo, UnpublishModalAdditionalInfo };
 | 
				
			||||||
							
								
								
									
										84
									
								
								packages/plugins/i18n/admin/src/components/LocalePicker.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								packages/plugins/i18n/admin/src/components/LocalePicker.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					import { useState } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { SingleSelect, SingleSelectOption, SingleSelectProps } from '@strapi/design-system';
 | 
				
			||||||
 | 
					import { useQueryParams } from '@strapi/helper-plugin';
 | 
				
			||||||
 | 
					import { useIntl } from 'react-intl';
 | 
				
			||||||
 | 
					import { useDispatch } from 'react-redux';
 | 
				
			||||||
 | 
					import { useRouteMatch } from 'react-router-dom';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useContentTypeHasI18n } from '../hooks/useContentTypeHasI18n';
 | 
				
			||||||
 | 
					import { useContentTypePermissions } from '../hooks/useContentTypePermissions';
 | 
				
			||||||
 | 
					import { useTypedSelector } from '../store/hooks';
 | 
				
			||||||
 | 
					import { getTranslation } from '../utils/getTranslation';
 | 
				
			||||||
 | 
					import { getInitialLocale } from '../utils/locales';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const LocalePicker = () => {
 | 
				
			||||||
 | 
					  const { formatMessage } = useIntl();
 | 
				
			||||||
 | 
					  const dispatch = useDispatch();
 | 
				
			||||||
 | 
					  const locales = useTypedSelector((state) => state.i18n_locales.locales);
 | 
				
			||||||
 | 
					  const [{ query }, setQuery] = useQueryParams<{
 | 
				
			||||||
 | 
					    page: number;
 | 
				
			||||||
 | 
					    plugins: { i18n: { locale: string } };
 | 
				
			||||||
 | 
					  }>();
 | 
				
			||||||
 | 
					  const match = useRouteMatch<{ slug: string }>('/content-manager/collectionType/:slug');
 | 
				
			||||||
 | 
					  const isContentTypeLocalized = useContentTypeHasI18n();
 | 
				
			||||||
 | 
					  const { createPermissions, readPermissions } = useContentTypePermissions(match?.params.slug);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const initialLocale = getInitialLocale(query, locales);
 | 
				
			||||||
 | 
					  const [selected, setSelected] = useState(initialLocale?.code || '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!isContentTypeLocalized) {
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!locales || locales.length === 0) {
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const displayedLocales = locales.filter((locale) => {
 | 
				
			||||||
 | 
					    const canCreate = createPermissions.some(({ properties }) =>
 | 
				
			||||||
 | 
					      (properties?.locales ?? []).includes(locale.code)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    const canRead = readPermissions.some(({ properties }) =>
 | 
				
			||||||
 | 
					      (properties?.locales ?? []).includes(locale.code)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return canCreate || canRead;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // @ts-expect-error – This can be removed in V2 of the DS.
 | 
				
			||||||
 | 
					  const handleChange: SingleSelectProps['onChange'] = (code: string) => {
 | 
				
			||||||
 | 
					    if (code === selected) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setSelected(code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dispatch({ type: 'ContentManager/RBACManager/RESET_PERMISSIONS' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setQuery({
 | 
				
			||||||
 | 
					      page: 1,
 | 
				
			||||||
 | 
					      plugins: { ...query.plugins, i18n: { locale: code } },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <SingleSelect
 | 
				
			||||||
 | 
					      size="S"
 | 
				
			||||||
 | 
					      aria-label={formatMessage({
 | 
				
			||||||
 | 
					        id: getTranslation('actions.select-locale'),
 | 
				
			||||||
 | 
					        defaultMessage: 'Select locale',
 | 
				
			||||||
 | 
					      })}
 | 
				
			||||||
 | 
					      value={selected}
 | 
				
			||||||
 | 
					      onChange={handleChange}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      {displayedLocales.map((locale) => (
 | 
				
			||||||
 | 
					        <SingleSelectOption key={locale.id} value={locale.code}>
 | 
				
			||||||
 | 
					          {locale.name}
 | 
				
			||||||
 | 
					        </SingleSelectOption>
 | 
				
			||||||
 | 
					      ))}
 | 
				
			||||||
 | 
					    </SingleSelect>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { LocalePicker };
 | 
				
			||||||
@ -1,93 +0,0 @@
 | 
				
			|||||||
import { useState } from 'react';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { Option, Select } from '@strapi/design-system';
 | 
					 | 
				
			||||||
import { useQueryParams } from '@strapi/helper-plugin';
 | 
					 | 
				
			||||||
import get from 'lodash/get';
 | 
					 | 
				
			||||||
import { useIntl } from 'react-intl';
 | 
					 | 
				
			||||||
import { useDispatch } from 'react-redux';
 | 
					 | 
				
			||||||
import { useRouteMatch } from 'react-router-dom';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { useContentTypePermissions } from '../../hooks/useContentTypePermissions';
 | 
					 | 
				
			||||||
import useHasI18n from '../../hooks/useHasI18n';
 | 
					 | 
				
			||||||
import { useTypedSelector } from '../../store/hooks';
 | 
					 | 
				
			||||||
import getInitialLocale from '../../utils/getInitialLocale';
 | 
					 | 
				
			||||||
import { getTranslation } from '../../utils/getTranslation';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const LocalePicker = () => {
 | 
					 | 
				
			||||||
  const { formatMessage } = useIntl();
 | 
					 | 
				
			||||||
  const dispatch = useDispatch();
 | 
					 | 
				
			||||||
  const locales = useTypedSelector((state) => state.i18n_locales.locales);
 | 
					 | 
				
			||||||
  const [{ query }, setQuery] = useQueryParams<any>();
 | 
					 | 
				
			||||||
  const {
 | 
					 | 
				
			||||||
    params: { slug },
 | 
					 | 
				
			||||||
  } = useRouteMatch('/content-manager/collectionType/:slug') as any;
 | 
					 | 
				
			||||||
  const isFieldLocalized = useHasI18n();
 | 
					 | 
				
			||||||
  const { createPermissions, readPermissions } = useContentTypePermissions(slug);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const initialLocale = getInitialLocale(query, locales);
 | 
					 | 
				
			||||||
  const [selected, setSelected] = useState(initialLocale?.code || '');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!isFieldLocalized) {
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!locales || locales.length === 0) {
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const displayedLocales = locales.filter((locale: any) => {
 | 
					 | 
				
			||||||
    const canCreate = createPermissions.find(({ properties }: any) => {
 | 
					 | 
				
			||||||
      return get(properties, 'locales', []).includes(locale.code);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    const canRead = readPermissions.find(({ properties }: any) =>
 | 
					 | 
				
			||||||
      get(properties, 'locales', []).includes(locale.code)
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return canCreate || canRead;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const handleClick = (code: string) => {
 | 
					 | 
				
			||||||
    if (code === selected) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setSelected(code);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * if the selected value is set at the same time as the dispatcher
 | 
					 | 
				
			||||||
     * is run, react might not have enough time to re-render the Select
 | 
					 | 
				
			||||||
     * component, which leads to the `source` ref, which is passed to
 | 
					 | 
				
			||||||
     * Popout, not being defined.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * By pushing the dispatcher to the end of the current execution
 | 
					 | 
				
			||||||
     * context, we can guarantee the rendering can finish before.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    setTimeout(() => {
 | 
					 | 
				
			||||||
      dispatch({ type: 'ContentManager/RBACManager/RESET_PERMISSIONS' });
 | 
					 | 
				
			||||||
      setQuery({
 | 
					 | 
				
			||||||
        page: 1,
 | 
					 | 
				
			||||||
        plugins: { ...query.plugins, i18n: { locale: code } },
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Select
 | 
					 | 
				
			||||||
      size="S"
 | 
					 | 
				
			||||||
      aria-label={formatMessage({
 | 
					 | 
				
			||||||
        id: getTranslation('actions.select-locale'),
 | 
					 | 
				
			||||||
        defaultMessage: '',
 | 
					 | 
				
			||||||
      })}
 | 
					 | 
				
			||||||
      value={selected}
 | 
					 | 
				
			||||||
      onChange={handleClick as any}
 | 
					 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
      {displayedLocales.map((locale) => (
 | 
					 | 
				
			||||||
        <Option key={locale.id} id={`menu-item${locale.name || locale.code}`} value={locale.code}>
 | 
					 | 
				
			||||||
          {locale.name}
 | 
					 | 
				
			||||||
        </Option>
 | 
					 | 
				
			||||||
      ))}
 | 
					 | 
				
			||||||
    </Select>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default LocalePicker;
 | 
					 | 
				
			||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import get from 'lodash/get';
 | 
					import get from 'lodash/get';
 | 
				
			||||||
import { parse, stringify } from 'qs';
 | 
					import { parse, stringify } from 'qs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import getDefaultLocale from '../../utils/getDefaultLocale';
 | 
					import { getDefaultLocale } from '../../utils/locales';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const addLocaleToLinksSearch = (
 | 
					const addLocaleToLinksSearch = (
 | 
				
			||||||
  links: any[],
 | 
					  links: any[],
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export const useContentTypeHasI18n = jest.fn().mockReturnValue(true);
 | 
				
			||||||
@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					import { useTypedSelector } from '../store/hooks';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useContentTypeHasI18n = (): boolean => {
 | 
				
			||||||
 | 
					  const pluginOptions = useTypedSelector(
 | 
				
			||||||
 | 
					    // @ts-expect-error – we've not typed the CM ListView yet.
 | 
				
			||||||
 | 
					    (state) => state['content-manager_listView'].contentType.pluginOptions
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return pluginOptions?.i18n?.localized ?? false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { useContentTypeHasI18n };
 | 
				
			||||||
@ -1,24 +1,37 @@
 | 
				
			|||||||
 | 
					import { useMemo } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { createSelector } from '@reduxjs/toolkit';
 | 
					import { createSelector } from '@reduxjs/toolkit';
 | 
				
			||||||
 | 
					import { Permission } from '@strapi/helper-plugin';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { useTypedSelector } from '../store/hooks';
 | 
					import { useTypedSelector } from '../store/hooks';
 | 
				
			||||||
import { RootState } from '../store/reducers';
 | 
					import { RootState } from '../store/reducers';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const selectContentTypePermissions = createSelector(
 | 
					const makeSelectContentTypePermissions = () =>
 | 
				
			||||||
  (state: RootState) => state.rbacProvider.collectionTypesRelatedPermissions,
 | 
					  // @ts-expect-error – I have no idea why this fails like this.
 | 
				
			||||||
  (_, slug: string) => slug,
 | 
					  createSelector(
 | 
				
			||||||
  (state, slug) => {
 | 
					    (state: RootState) => state.rbacProvider.collectionTypesRelatedPermissions,
 | 
				
			||||||
    // @ts-expect-error – Selectors are weird, why don't they work with TS?
 | 
					    (_, slug: string) => slug,
 | 
				
			||||||
    const currentCTRelatedPermissions = state[slug];
 | 
					    (state: RootState['rbacProvider']['collectionTypesRelatedPermissions'], slug: string) => {
 | 
				
			||||||
    const readPermissions =
 | 
					      const currentCTRelatedPermissions = slug ? state[slug] : {};
 | 
				
			||||||
      currentCTRelatedPermissions['plugin::content-manager.explorer.read'] || [];
 | 
					 | 
				
			||||||
    const createPermissions =
 | 
					 | 
				
			||||||
      currentCTRelatedPermissions['plugin::content-manager.explorer.create'] || [];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return { createPermissions, readPermissions };
 | 
					      if (!currentCTRelatedPermissions) {
 | 
				
			||||||
  }
 | 
					        return { createPermissions: [], readPermissions: [] };
 | 
				
			||||||
);
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const useContentTypePermissions = (slug: string) =>
 | 
					      const readPermissions =
 | 
				
			||||||
  useTypedSelector((state) => selectContentTypePermissions(state, slug));
 | 
					        currentCTRelatedPermissions['plugin::content-manager.explorer.read'] || [];
 | 
				
			||||||
 | 
					      const createPermissions =
 | 
				
			||||||
 | 
					        currentCTRelatedPermissions['plugin::content-manager.explorer.create'] || [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return { createPermissions, readPermissions };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useContentTypePermissions = (
 | 
				
			||||||
 | 
					  slug?: string
 | 
				
			||||||
 | 
					): { createPermissions: Permission[]; readPermissions: Permission[] } => {
 | 
				
			||||||
 | 
					  const selectContentTypePermissions = useMemo(makeSelectContentTypePermissions, []);
 | 
				
			||||||
 | 
					  return useTypedSelector((state) => selectContentTypePermissions(state, slug));
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { useContentTypePermissions };
 | 
					export { useContentTypePermissions };
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +0,0 @@
 | 
				
			|||||||
import get from 'lodash/get';
 | 
					 | 
				
			||||||
import { useSelector } from 'react-redux';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const selectContentManagerListViewPluginOptions = (state: any) =>
 | 
					 | 
				
			||||||
  state['content-manager_listView'].contentType.pluginOptions;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const useHasI18n = () => {
 | 
					 | 
				
			||||||
  const pluginOptions = useSelector(selectContentManagerListViewPluginOptions);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return get(pluginOptions, 'i18n.localized', false);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default useHasI18n;
 | 
					 | 
				
			||||||
@ -4,11 +4,13 @@ import * as yup from 'yup';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import CheckboxConfirmation from './components/CheckboxConfirmation';
 | 
					import CheckboxConfirmation from './components/CheckboxConfirmation';
 | 
				
			||||||
import { CMEditViewInjectedComponents } from './components/CMEditViewInjectedComponents';
 | 
					import { CMEditViewInjectedComponents } from './components/CMEditViewInjectedComponents';
 | 
				
			||||||
import DeleteModalAdditionalInfos from './components/CMListViewInjectedComponents/DeleteModalAdditionalInfos';
 | 
					import {
 | 
				
			||||||
import PublishModalAdditionalInfos from './components/CMListViewInjectedComponents/PublishModalAdditionalInfos';
 | 
					  DeleteModalAdditionalInfo,
 | 
				
			||||||
import UnpublishModalAdditionalInfos from './components/CMListViewInjectedComponents/UnpublishModalAdditionalInfos';
 | 
					  PublishModalAdditionalInfo,
 | 
				
			||||||
 | 
					  UnpublishModalAdditionalInfo,
 | 
				
			||||||
 | 
					} from './components/CMListViewModalsAdditionalInformation';
 | 
				
			||||||
import Initializer from './components/Initializer';
 | 
					import Initializer from './components/Initializer';
 | 
				
			||||||
import LocalePicker from './components/LocalePicker';
 | 
					import { LocalePicker } from './components/LocalePicker';
 | 
				
			||||||
import { PERMISSIONS } from './constants';
 | 
					import { PERMISSIONS } from './constants';
 | 
				
			||||||
import addColumnToTableHook from './contentManagerHooks/addColumnToTable';
 | 
					import addColumnToTableHook from './contentManagerHooks/addColumnToTable';
 | 
				
			||||||
import addLocaleToCollectionTypesLinksHook from './contentManagerHooks/addLocaleToCollectionTypesLinks';
 | 
					import addLocaleToCollectionTypesLinksHook from './contentManagerHooks/addLocaleToCollectionTypesLinks';
 | 
				
			||||||
@ -81,17 +83,17 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    app.injectContentManagerComponent('listView', 'deleteModalAdditionalInfos', {
 | 
					    app.injectContentManagerComponent('listView', 'deleteModalAdditionalInfos', {
 | 
				
			||||||
      name: 'i18n-delete-bullets-in-modal',
 | 
					      name: 'i18n-delete-bullets-in-modal',
 | 
				
			||||||
      Component: DeleteModalAdditionalInfos,
 | 
					      Component: DeleteModalAdditionalInfo,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app.injectContentManagerComponent('listView', 'publishModalAdditionalInfos', {
 | 
					    app.injectContentManagerComponent('listView', 'publishModalAdditionalInfos', {
 | 
				
			||||||
      name: 'i18n-publish-bullets-in-modal',
 | 
					      name: 'i18n-publish-bullets-in-modal',
 | 
				
			||||||
      Component: PublishModalAdditionalInfos,
 | 
					      Component: PublishModalAdditionalInfo,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app.injectContentManagerComponent('listView', 'unpublishModalAdditionalInfos', {
 | 
					    app.injectContentManagerComponent('listView', 'unpublishModalAdditionalInfos', {
 | 
				
			||||||
      name: 'i18n-unpublish-bullets-in-modal',
 | 
					      name: 'i18n-unpublish-bullets-in-modal',
 | 
				
			||||||
      Component: UnpublishModalAdditionalInfos,
 | 
					      Component: UnpublishModalAdditionalInfo,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const ctbPlugin = app.getPlugin('content-type-builder');
 | 
					    const ctbPlugin = app.getPlugin('content-type-builder');
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { createSelector, Dispatch, Selector } from '@reduxjs/toolkit';
 | 
					import { Dispatch } from '@reduxjs/toolkit';
 | 
				
			||||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
 | 
					import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Action, RootState } from './reducers';
 | 
					import { Action, RootState } from './reducers';
 | 
				
			||||||
@ -8,9 +8,4 @@ type AppDispatch = Dispatch<Action>;
 | 
				
			|||||||
const useTypedDispatch: () => AppDispatch = useDispatch;
 | 
					const useTypedDispatch: () => AppDispatch = useDispatch;
 | 
				
			||||||
const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector;
 | 
					const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createTypedSelector = <TResult, TSelector extends Selector<RootState, TResult>>(
 | 
					export { useTypedSelector, useTypedDispatch };
 | 
				
			||||||
  selector: TSelector
 | 
					 | 
				
			||||||
  // @ts-expect-error – TODO: this is needed to avoid TS2742. But it's not quite right.
 | 
					 | 
				
			||||||
): ReturnType<TSelector> => createSelector((state: RootState) => state, selector);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export { useTypedSelector, createTypedSelector, useTypedDispatch };
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,60 +0,0 @@
 | 
				
			|||||||
import get from 'lodash/get';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const hasLocalePermission = (permissions: any, localeCode: any) => {
 | 
					 | 
				
			||||||
  if (permissions) {
 | 
					 | 
				
			||||||
    const hasPermission = permissions.some((permission: any) =>
 | 
					 | 
				
			||||||
      get(permission, 'properties.locales', []).includes(localeCode)
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (hasPermission) {
 | 
					 | 
				
			||||||
      return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return false;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const getFirstLocale = (permissions: any) => {
 | 
					 | 
				
			||||||
  if (permissions && permissions.length > 0) {
 | 
					 | 
				
			||||||
    const firstAuthorizedNonDefaultLocale = get(permissions, [0, 'properties', 'locales', 0], null);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (firstAuthorizedNonDefaultLocale) {
 | 
					 | 
				
			||||||
      return firstAuthorizedNonDefaultLocale;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return null;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Entry point of the module
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
const getDefaultLocale = (ctPermissions: any, locales: any = []) => {
 | 
					 | 
				
			||||||
  const defaultLocale = locales.find((locale: any) => locale.isDefault);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!defaultLocale) {
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const readPermissions = ctPermissions['plugin::content-manager.explorer.read'];
 | 
					 | 
				
			||||||
  const createPermissions = ctPermissions['plugin::content-manager.explorer.create'];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (hasLocalePermission(readPermissions, defaultLocale.code)) {
 | 
					 | 
				
			||||||
    return defaultLocale.code;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (hasLocalePermission(createPermissions, defaultLocale.code)) {
 | 
					 | 
				
			||||||
    return defaultLocale.code;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // When the default locale is not authorized, we return the first authorized locale
 | 
					 | 
				
			||||||
  const firstAuthorizedForReadNonDefaultLocale = getFirstLocale(readPermissions);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (firstAuthorizedForReadNonDefaultLocale) {
 | 
					 | 
				
			||||||
    return firstAuthorizedForReadNonDefaultLocale;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return getFirstLocale(createPermissions);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default getDefaultLocale;
 | 
					 | 
				
			||||||
@ -1,14 +0,0 @@
 | 
				
			|||||||
import getLocaleFromQuery from './getLocaleFromQuery';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const getInitialLocale = (query: any, locales: any = []) => {
 | 
					 | 
				
			||||||
  const localeFromQuery = getLocaleFromQuery(query);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (localeFromQuery) {
 | 
					 | 
				
			||||||
    return locales.find((locale: any) => locale.code === localeFromQuery);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Returns the default locale when nothing is in the query
 | 
					 | 
				
			||||||
  return locales.find((locale: any) => locale.isDefault);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default getInitialLocale;
 | 
					 | 
				
			||||||
@ -1,7 +0,0 @@
 | 
				
			|||||||
import get from 'lodash/get';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const getLocaleFromQuery = (query: any) => {
 | 
					 | 
				
			||||||
  return get(query, 'plugins.i18n.locale', undefined);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default getLocaleFromQuery;
 | 
					 | 
				
			||||||
							
								
								
									
										76
									
								
								packages/plugins/i18n/admin/src/utils/locales.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								packages/plugins/i18n/admin/src/utils/locales.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					import { Locale, RootState } from '../store/reducers';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface PotentialQueryWithLocale {
 | 
				
			||||||
 | 
					  plugins?: { i18n?: { locale?: string; [key: string]: unknown }; [key: string]: unknown };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Returns the locale from the passed query.
 | 
				
			||||||
 | 
					 * If a default value is passed, it will return it if the locale does not exist.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function getLocaleFromQuery(query: PotentialQueryWithLocale): string | undefined;
 | 
				
			||||||
 | 
					function getLocaleFromQuery(query: PotentialQueryWithLocale, defaultValue: string): string;
 | 
				
			||||||
 | 
					function getLocaleFromQuery(
 | 
				
			||||||
 | 
					  query: PotentialQueryWithLocale,
 | 
				
			||||||
 | 
					  defaultValue?: string
 | 
				
			||||||
 | 
					): string | undefined {
 | 
				
			||||||
 | 
					  const locale = query?.plugins?.i18n?.locale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!locale && defaultValue) {
 | 
				
			||||||
 | 
					    return defaultValue;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return locale;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Returns the initial locale from the query falling back to the default locale
 | 
				
			||||||
 | 
					 * listed in the collection of locales provided.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const getInitialLocale = (
 | 
				
			||||||
 | 
					  query: PotentialQueryWithLocale,
 | 
				
			||||||
 | 
					  locales: Locale[] = []
 | 
				
			||||||
 | 
					): Locale | undefined => {
 | 
				
			||||||
 | 
					  const localeFromQuery = getLocaleFromQuery(query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (localeFromQuery) {
 | 
				
			||||||
 | 
					    return locales.find((locale) => locale.code === localeFromQuery);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Returns the default locale when nothing is in the query
 | 
				
			||||||
 | 
					  return locales.find((locale) => locale.isDefault);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getDefaultLocale = (
 | 
				
			||||||
 | 
					  ctPermissions: RootState['rbacProvider']['collectionTypesRelatedPermissions'][string],
 | 
				
			||||||
 | 
					  locales: Locale[] = []
 | 
				
			||||||
 | 
					) => {
 | 
				
			||||||
 | 
					  const defaultLocale = locales.find((locale) => locale.isDefault);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!defaultLocale) {
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const readPermissions = ctPermissions['plugin::content-manager.explorer.read'] ?? [];
 | 
				
			||||||
 | 
					  const createPermissions = ctPermissions['plugin::content-manager.explorer.create'] ?? [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (
 | 
				
			||||||
 | 
					    readPermissions.some(({ properties }) =>
 | 
				
			||||||
 | 
					      (properties?.locales ?? []).includes(defaultLocale.code)
 | 
				
			||||||
 | 
					    ) ||
 | 
				
			||||||
 | 
					    createPermissions.some(({ properties }) =>
 | 
				
			||||||
 | 
					      (properties?.locales ?? []).includes(defaultLocale.code)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    return defaultLocale.code;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // When the default locale is not authorized, we return the first authorized locale
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    (readPermissions[0]?.properties?.locales?.[0] ||
 | 
				
			||||||
 | 
					      createPermissions[0]?.properties?.locales?.[0]) ??
 | 
				
			||||||
 | 
					    null
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { getLocaleFromQuery, getInitialLocale, getDefaultLocale };
 | 
				
			||||||
@ -1,337 +0,0 @@
 | 
				
			|||||||
import getDefaultLocale from '../getDefaultLocale';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('getDefaultLocale', () => {
 | 
					 | 
				
			||||||
  it('gives fr-FR when it s the default locale and that it has read access to it', () => {
 | 
					 | 
				
			||||||
    const locales = [
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 1,
 | 
					 | 
				
			||||||
        name: 'English',
 | 
					 | 
				
			||||||
        code: 'en',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        isDefault: false,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 2,
 | 
					 | 
				
			||||||
        name: 'French (France) (fr-FR)',
 | 
					 | 
				
			||||||
        code: 'fr-FR',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T15:03:06.992Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-17T13:01:03.569Z',
 | 
					 | 
				
			||||||
        isDefault: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const ctPermissions = {
 | 
					 | 
				
			||||||
      'plugin::content-manager.explorer.create': [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: 1325,
 | 
					 | 
				
			||||||
          action: 'plugin::content-manager.explorer.create',
 | 
					 | 
				
			||||||
          subject: 'api::address.address',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            fields: [
 | 
					 | 
				
			||||||
              'postal_coder',
 | 
					 | 
				
			||||||
              'categories',
 | 
					 | 
				
			||||||
              'cover',
 | 
					 | 
				
			||||||
              'images',
 | 
					 | 
				
			||||||
              'city',
 | 
					 | 
				
			||||||
              'likes',
 | 
					 | 
				
			||||||
              'json',
 | 
					 | 
				
			||||||
              'slug',
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            locales: [],
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          conditions: [],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      'plugin::content-manager.explorer.read': [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: 1326,
 | 
					 | 
				
			||||||
          action: 'plugin::content-manager.explorer.read',
 | 
					 | 
				
			||||||
          subject: 'api::address.address',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            fields: [],
 | 
					 | 
				
			||||||
            locales: ['en', 'fr-FR'],
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          conditions: [],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const expected = 'fr-FR';
 | 
					 | 
				
			||||||
    const actual = getDefaultLocale(ctPermissions, locales);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    expect(actual).toEqual(expected);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('gives fr-FR when it s the default locale and that it has create access to it', () => {
 | 
					 | 
				
			||||||
    const locales = [
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 1,
 | 
					 | 
				
			||||||
        name: 'English',
 | 
					 | 
				
			||||||
        code: 'en',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        isDefault: false,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 2,
 | 
					 | 
				
			||||||
        name: 'French (France) (fr-FR)',
 | 
					 | 
				
			||||||
        code: 'fr-FR',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T15:03:06.992Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-17T13:01:03.569Z',
 | 
					 | 
				
			||||||
        isDefault: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const ctPermissions = {
 | 
					 | 
				
			||||||
      'plugin::content-manager.explorer.create': [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: 1325,
 | 
					 | 
				
			||||||
          action: 'plugin::content-manager.explorer.create',
 | 
					 | 
				
			||||||
          subject: 'api::address.address',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            fields: [
 | 
					 | 
				
			||||||
              'postal_coder',
 | 
					 | 
				
			||||||
              'categories',
 | 
					 | 
				
			||||||
              'cover',
 | 
					 | 
				
			||||||
              'images',
 | 
					 | 
				
			||||||
              'city',
 | 
					 | 
				
			||||||
              'likes',
 | 
					 | 
				
			||||||
              'json',
 | 
					 | 
				
			||||||
              'slug',
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            locales: ['fr-FR'],
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          conditions: [],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      'plugin::content-manager.explorer.read': [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: 1326,
 | 
					 | 
				
			||||||
          action: 'plugin::content-manager.explorer.read',
 | 
					 | 
				
			||||||
          subject: 'api::address.address',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            fields: [],
 | 
					 | 
				
			||||||
            locales: ['en'],
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          conditions: [],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const expected = 'fr-FR';
 | 
					 | 
				
			||||||
    const actual = getDefaultLocale(ctPermissions, locales);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    expect(actual).toEqual(expected);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('gives gives the first locale with read permission ("en") when the locale is allowed', () => {
 | 
					 | 
				
			||||||
    const locales = [
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 1,
 | 
					 | 
				
			||||||
        name: 'English',
 | 
					 | 
				
			||||||
        code: 'en',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        isDefault: false,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 2,
 | 
					 | 
				
			||||||
        name: 'French (France) (fr-FR)',
 | 
					 | 
				
			||||||
        code: 'fr-FR',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T15:03:06.992Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-17T13:01:03.569Z',
 | 
					 | 
				
			||||||
        isDefault: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 3,
 | 
					 | 
				
			||||||
        name: 'Another lang',
 | 
					 | 
				
			||||||
        code: 'de',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T15:03:06.992Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-17T13:01:03.569Z',
 | 
					 | 
				
			||||||
        isDefault: false,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const ctPermissions = {
 | 
					 | 
				
			||||||
      'plugin::content-manager.explorer.create': [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: 1325,
 | 
					 | 
				
			||||||
          action: 'plugin::content-manager.explorer.create',
 | 
					 | 
				
			||||||
          subject: 'api::address.address',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            fields: [
 | 
					 | 
				
			||||||
              'postal_coder',
 | 
					 | 
				
			||||||
              'categories',
 | 
					 | 
				
			||||||
              'cover',
 | 
					 | 
				
			||||||
              'images',
 | 
					 | 
				
			||||||
              'city',
 | 
					 | 
				
			||||||
              'likes',
 | 
					 | 
				
			||||||
              'json',
 | 
					 | 
				
			||||||
              'slug',
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            locales: [],
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          conditions: [],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      'plugin::content-manager.explorer.read': [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: 1326,
 | 
					 | 
				
			||||||
          action: 'plugin::content-manager.explorer.read',
 | 
					 | 
				
			||||||
          subject: 'api::address.address',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            fields: [],
 | 
					 | 
				
			||||||
            locales: ['en', 'de'],
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          conditions: [],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const expected = 'en';
 | 
					 | 
				
			||||||
    const actual = getDefaultLocale(ctPermissions, locales);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    expect(actual).toEqual(expected);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('gives gives the first locale with create permission ("en") when the locale is allowed', () => {
 | 
					 | 
				
			||||||
    const locales = [
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 1,
 | 
					 | 
				
			||||||
        name: 'English',
 | 
					 | 
				
			||||||
        code: 'en',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        isDefault: false,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 2,
 | 
					 | 
				
			||||||
        name: 'French (France) (fr-FR)',
 | 
					 | 
				
			||||||
        code: 'fr-FR',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T15:03:06.992Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-17T13:01:03.569Z',
 | 
					 | 
				
			||||||
        isDefault: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 3,
 | 
					 | 
				
			||||||
        name: 'Another lang',
 | 
					 | 
				
			||||||
        code: 'de',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T15:03:06.992Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-17T13:01:03.569Z',
 | 
					 | 
				
			||||||
        isDefault: false,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const ctPermissions = {
 | 
					 | 
				
			||||||
      'plugin::content-manager.explorer.create': [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: 1325,
 | 
					 | 
				
			||||||
          action: 'plugin::content-manager.explorer.create',
 | 
					 | 
				
			||||||
          subject: 'api::address.address',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            fields: [
 | 
					 | 
				
			||||||
              'postal_coder',
 | 
					 | 
				
			||||||
              'categories',
 | 
					 | 
				
			||||||
              'cover',
 | 
					 | 
				
			||||||
              'images',
 | 
					 | 
				
			||||||
              'city',
 | 
					 | 
				
			||||||
              'likes',
 | 
					 | 
				
			||||||
              'json',
 | 
					 | 
				
			||||||
              'slug',
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            locales: ['en', 'de'],
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          conditions: [],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      'plugin::content-manager.explorer.read': [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: 1326,
 | 
					 | 
				
			||||||
          action: 'plugin::content-manager.explorer.read',
 | 
					 | 
				
			||||||
          subject: 'api::address.address',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            fields: [],
 | 
					 | 
				
			||||||
            locales: [],
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          conditions: [],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const expected = 'en';
 | 
					 | 
				
			||||||
    const actual = getDefaultLocale(ctPermissions, locales);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    expect(actual).toEqual(expected);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('gives null when the user has no permission on any locale', () => {
 | 
					 | 
				
			||||||
    const locales = [
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 1,
 | 
					 | 
				
			||||||
        name: 'English',
 | 
					 | 
				
			||||||
        code: 'en',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        isDefault: false,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 2,
 | 
					 | 
				
			||||||
        name: 'French (France) (fr-FR)',
 | 
					 | 
				
			||||||
        code: 'fr-FR',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T15:03:06.992Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-17T13:01:03.569Z',
 | 
					 | 
				
			||||||
        isDefault: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 3,
 | 
					 | 
				
			||||||
        name: 'Another lang',
 | 
					 | 
				
			||||||
        code: 'de',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T15:03:06.992Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-17T13:01:03.569Z',
 | 
					 | 
				
			||||||
        isDefault: false,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const ctPermissions = {
 | 
					 | 
				
			||||||
      'plugin::content-manager.explorer.create': [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: 1325,
 | 
					 | 
				
			||||||
          action: 'plugin::content-manager.explorer.create',
 | 
					 | 
				
			||||||
          subject: 'api::address.address',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            fields: [
 | 
					 | 
				
			||||||
              'postal_coder',
 | 
					 | 
				
			||||||
              'categories',
 | 
					 | 
				
			||||||
              'cover',
 | 
					 | 
				
			||||||
              'images',
 | 
					 | 
				
			||||||
              'city',
 | 
					 | 
				
			||||||
              'likes',
 | 
					 | 
				
			||||||
              'json',
 | 
					 | 
				
			||||||
              'slug',
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            locales: [],
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          conditions: [],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      'plugin::content-manager.explorer.read': [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: 1326,
 | 
					 | 
				
			||||||
          action: 'plugin::content-manager.explorer.read',
 | 
					 | 
				
			||||||
          subject: 'api::address.address',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            fields: [],
 | 
					 | 
				
			||||||
            locales: [],
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          conditions: [],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const expected = null;
 | 
					 | 
				
			||||||
    const actual = getDefaultLocale(ctPermissions, locales);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    expect(actual).toEqual(expected);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
@ -1,106 +0,0 @@
 | 
				
			|||||||
import getInitialLocale from '../getInitialLocale';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('getInitialLocale', () => {
 | 
					 | 
				
			||||||
  it('gives "fr-FR" when the query.plugins.locale is "fr-FR"', () => {
 | 
					 | 
				
			||||||
    const query = {
 | 
					 | 
				
			||||||
      page: '1',
 | 
					 | 
				
			||||||
      pageSize: '10',
 | 
					 | 
				
			||||||
      sort: 'Name:ASC',
 | 
					 | 
				
			||||||
      plugins: {
 | 
					 | 
				
			||||||
        i18n: { locale: 'fr-FR' },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const locales = [
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 1,
 | 
					 | 
				
			||||||
        name: 'English',
 | 
					 | 
				
			||||||
        code: 'en',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        isDefault: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 2,
 | 
					 | 
				
			||||||
        name: 'French (France) (fr-FR)',
 | 
					 | 
				
			||||||
        code: 'fr-FR',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T15:03:06.992Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-09T15:03:06.996Z',
 | 
					 | 
				
			||||||
        isDefault: false,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const expected = {
 | 
					 | 
				
			||||||
      id: 2,
 | 
					 | 
				
			||||||
      name: 'French (France) (fr-FR)',
 | 
					 | 
				
			||||||
      code: 'fr-FR',
 | 
					 | 
				
			||||||
      createdAt: '2021-03-09T15:03:06.992Z',
 | 
					 | 
				
			||||||
      updatedAt: '2021-03-09T15:03:06.996Z',
 | 
					 | 
				
			||||||
      isDefault: false,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    const actual = getInitialLocale(query, locales);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    expect(actual).toEqual(expected);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('gives the default locale ("en") when there s no locale in the query', () => {
 | 
					 | 
				
			||||||
    const query = {
 | 
					 | 
				
			||||||
      page: '1',
 | 
					 | 
				
			||||||
      pageSize: '10',
 | 
					 | 
				
			||||||
      sort: 'Name:ASC',
 | 
					 | 
				
			||||||
      plugins: {
 | 
					 | 
				
			||||||
        something: 'great',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const locales = [
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 2,
 | 
					 | 
				
			||||||
        name: 'French (France) (fr-FR)',
 | 
					 | 
				
			||||||
        code: 'fr-FR',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T15:03:06.992Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-09T15:03:06.996Z',
 | 
					 | 
				
			||||||
        isDefault: false,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        id: 1,
 | 
					 | 
				
			||||||
        name: 'English',
 | 
					 | 
				
			||||||
        code: 'en',
 | 
					 | 
				
			||||||
        createdAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        updatedAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
        isDefault: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const expected = {
 | 
					 | 
				
			||||||
      id: 1,
 | 
					 | 
				
			||||||
      name: 'English',
 | 
					 | 
				
			||||||
      code: 'en',
 | 
					 | 
				
			||||||
      createdAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
      updatedAt: '2021-03-09T14:57:03.016Z',
 | 
					 | 
				
			||||||
      isDefault: true,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const actual = getInitialLocale(query, locales);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    expect(actual).toEqual(expected);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('gives "undefined" when theres no locale. IMPORTANT: such case should not exist since at least one locale is created on the backend when plug-in i18n', () => {
 | 
					 | 
				
			||||||
    const query = {
 | 
					 | 
				
			||||||
      page: '1',
 | 
					 | 
				
			||||||
      pageSize: '10',
 | 
					 | 
				
			||||||
      sort: 'Name:ASC',
 | 
					 | 
				
			||||||
      plugins: {
 | 
					 | 
				
			||||||
        something: 'great',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const locales: any = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const expected = undefined;
 | 
					 | 
				
			||||||
    const actual = getInitialLocale(query, locales);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    expect(actual).toEqual(expected);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
							
								
								
									
										502
									
								
								packages/plugins/i18n/admin/src/utils/tests/locales.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										502
									
								
								packages/plugins/i18n/admin/src/utils/tests/locales.test.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,502 @@
 | 
				
			|||||||
 | 
					import { Locale } from '../../store/reducers';
 | 
				
			||||||
 | 
					import { getInitialLocale, getLocaleFromQuery, getDefaultLocale } from '../locales';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('locales', () => {
 | 
				
			||||||
 | 
					  describe('getLocaleFromQuery', () => {
 | 
				
			||||||
 | 
					    it('returns the locale from the query', () => {
 | 
				
			||||||
 | 
					      const query = {
 | 
				
			||||||
 | 
					        plugins: {
 | 
				
			||||||
 | 
					          i18n: {
 | 
				
			||||||
 | 
					            locale: 'en-GB',
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(getLocaleFromQuery(query)).toBe('en-GB');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("should return undefined if the locale doesn't exist", () => {
 | 
				
			||||||
 | 
					      expect(
 | 
				
			||||||
 | 
					        getLocaleFromQuery({
 | 
				
			||||||
 | 
					          plugins: {
 | 
				
			||||||
 | 
					            i18n: {},
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      ).toBe(undefined);
 | 
				
			||||||
 | 
					      expect(
 | 
				
			||||||
 | 
					        getLocaleFromQuery({
 | 
				
			||||||
 | 
					          plugins: {},
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      ).toBe(undefined);
 | 
				
			||||||
 | 
					      expect(getLocaleFromQuery({})).toBe(undefined);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should return the default value if the locale does not exist', () => {
 | 
				
			||||||
 | 
					      expect(
 | 
				
			||||||
 | 
					        getLocaleFromQuery(
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            plugins: {
 | 
				
			||||||
 | 
					              i18n: {},
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          'en-GB'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      ).toBe('en-GB');
 | 
				
			||||||
 | 
					      expect(
 | 
				
			||||||
 | 
					        getLocaleFromQuery(
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            plugins: {},
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          'en-GB'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      ).toBe('en-GB');
 | 
				
			||||||
 | 
					      expect(getLocaleFromQuery({}, 'en-GB')).toBe('en-GB');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('getInitialLocale', () => {
 | 
				
			||||||
 | 
					    it('gives "fr-FR" when the query.plugins.locale is "fr-FR"', () => {
 | 
				
			||||||
 | 
					      const query = {
 | 
				
			||||||
 | 
					        page: '1',
 | 
				
			||||||
 | 
					        pageSize: '10',
 | 
				
			||||||
 | 
					        sort: 'Name:ASC',
 | 
				
			||||||
 | 
					        plugins: {
 | 
				
			||||||
 | 
					          i18n: { locale: 'en-GB' },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const locales: Locale[] = [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 1,
 | 
				
			||||||
 | 
					          name: 'English',
 | 
				
			||||||
 | 
					          code: 'en-GB',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          isDefault: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 2,
 | 
				
			||||||
 | 
					          name: 'French (France) (fr-FR)',
 | 
				
			||||||
 | 
					          code: 'fr-FR',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T15:03:06.992Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-09T15:03:06.996Z',
 | 
				
			||||||
 | 
					          isDefault: false,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const actual = getInitialLocale(query, locales);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(actual).toMatchInlineSnapshot(`
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "code": "en-GB",
 | 
				
			||||||
 | 
					          "createdAt": "2021-03-09T14:57:03.016Z",
 | 
				
			||||||
 | 
					          "id": 1,
 | 
				
			||||||
 | 
					          "isDefault": true,
 | 
				
			||||||
 | 
					          "name": "English",
 | 
				
			||||||
 | 
					          "updatedAt": "2021-03-09T14:57:03.016Z",
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      `);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('gives the default locale ("en") when there s no locale in the query', () => {
 | 
				
			||||||
 | 
					      const query = {
 | 
				
			||||||
 | 
					        page: '1',
 | 
				
			||||||
 | 
					        pageSize: '10',
 | 
				
			||||||
 | 
					        sort: 'Name:ASC',
 | 
				
			||||||
 | 
					        plugins: {
 | 
				
			||||||
 | 
					          something: 'great',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const locales: Locale[] = [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 2,
 | 
				
			||||||
 | 
					          name: 'French (France) (fr-FR)',
 | 
				
			||||||
 | 
					          code: 'fr-FR',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T15:03:06.992Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-09T15:03:06.996Z',
 | 
				
			||||||
 | 
					          isDefault: false,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 1,
 | 
				
			||||||
 | 
					          name: 'English',
 | 
				
			||||||
 | 
					          code: 'en-GB',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          isDefault: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const actual = getInitialLocale(query, locales);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(actual).toMatchInlineSnapshot(`
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "code": "en-GB",
 | 
				
			||||||
 | 
					          "createdAt": "2021-03-09T14:57:03.016Z",
 | 
				
			||||||
 | 
					          "id": 1,
 | 
				
			||||||
 | 
					          "isDefault": true,
 | 
				
			||||||
 | 
					          "name": "English",
 | 
				
			||||||
 | 
					          "updatedAt": "2021-03-09T14:57:03.016Z",
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      `);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('gives "undefined" when theres no locale', () => {
 | 
				
			||||||
 | 
					      /**
 | 
				
			||||||
 | 
					       * @note this case should not exist since at least one locale
 | 
				
			||||||
 | 
					       * is created on the backend when plug-in i18n. But you can
 | 
				
			||||||
 | 
					       * never trust the server.
 | 
				
			||||||
 | 
					       */
 | 
				
			||||||
 | 
					      const query = {
 | 
				
			||||||
 | 
					        page: '1',
 | 
				
			||||||
 | 
					        pageSize: '10',
 | 
				
			||||||
 | 
					        sort: 'Name:ASC',
 | 
				
			||||||
 | 
					        plugins: {
 | 
				
			||||||
 | 
					          something: 'great',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const locales: Locale[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const actual = getInitialLocale(query, locales);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(actual).toMatchInlineSnapshot(`undefined`);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('getDefaultLocale', () => {
 | 
				
			||||||
 | 
					    it('gives fr-FR when it s the default locale and that it has read access to it', () => {
 | 
				
			||||||
 | 
					      const locales = [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 1,
 | 
				
			||||||
 | 
					          name: 'English',
 | 
				
			||||||
 | 
					          code: 'en',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          isDefault: false,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 2,
 | 
				
			||||||
 | 
					          name: 'French (France) (fr-FR)',
 | 
				
			||||||
 | 
					          code: 'fr-FR',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T15:03:06.992Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-17T13:01:03.569Z',
 | 
				
			||||||
 | 
					          isDefault: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const ctPermissions = {
 | 
				
			||||||
 | 
					        'plugin::content-manager.explorer.create': [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            id: 1325,
 | 
				
			||||||
 | 
					            action: 'plugin::content-manager.explorer.create',
 | 
				
			||||||
 | 
					            subject: 'api::address.address',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              fields: [
 | 
				
			||||||
 | 
					                'postal_coder',
 | 
				
			||||||
 | 
					                'categories',
 | 
				
			||||||
 | 
					                'cover',
 | 
				
			||||||
 | 
					                'images',
 | 
				
			||||||
 | 
					                'city',
 | 
				
			||||||
 | 
					                'likes',
 | 
				
			||||||
 | 
					                'json',
 | 
				
			||||||
 | 
					                'slug',
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					              locales: [],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            conditions: [],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        'plugin::content-manager.explorer.read': [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            id: 1326,
 | 
				
			||||||
 | 
					            action: 'plugin::content-manager.explorer.read',
 | 
				
			||||||
 | 
					            subject: 'api::address.address',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              fields: [],
 | 
				
			||||||
 | 
					              locales: ['en', 'fr-FR'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            conditions: [],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const expected = 'fr-FR';
 | 
				
			||||||
 | 
					      const actual = getDefaultLocale(ctPermissions, locales);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(actual).toEqual(expected);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('gives fr-FR when it s the default locale and that it has create access to it', () => {
 | 
				
			||||||
 | 
					      const locales = [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 1,
 | 
				
			||||||
 | 
					          name: 'English',
 | 
				
			||||||
 | 
					          code: 'en',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          isDefault: false,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 2,
 | 
				
			||||||
 | 
					          name: 'French (France) (fr-FR)',
 | 
				
			||||||
 | 
					          code: 'fr-FR',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T15:03:06.992Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-17T13:01:03.569Z',
 | 
				
			||||||
 | 
					          isDefault: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const ctPermissions = {
 | 
				
			||||||
 | 
					        'plugin::content-manager.explorer.create': [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            id: 1325,
 | 
				
			||||||
 | 
					            action: 'plugin::content-manager.explorer.create',
 | 
				
			||||||
 | 
					            subject: 'api::address.address',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              fields: [
 | 
				
			||||||
 | 
					                'postal_coder',
 | 
				
			||||||
 | 
					                'categories',
 | 
				
			||||||
 | 
					                'cover',
 | 
				
			||||||
 | 
					                'images',
 | 
				
			||||||
 | 
					                'city',
 | 
				
			||||||
 | 
					                'likes',
 | 
				
			||||||
 | 
					                'json',
 | 
				
			||||||
 | 
					                'slug',
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					              locales: ['fr-FR'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            conditions: [],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        'plugin::content-manager.explorer.read': [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            id: 1326,
 | 
				
			||||||
 | 
					            action: 'plugin::content-manager.explorer.read',
 | 
				
			||||||
 | 
					            subject: 'api::address.address',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              fields: [],
 | 
				
			||||||
 | 
					              locales: ['en'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            conditions: [],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const expected = 'fr-FR';
 | 
				
			||||||
 | 
					      const actual = getDefaultLocale(ctPermissions, locales);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(actual).toEqual(expected);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('gives gives the first locale with read permission ("en") when the locale is allowed', () => {
 | 
				
			||||||
 | 
					      const locales = [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 1,
 | 
				
			||||||
 | 
					          name: 'English',
 | 
				
			||||||
 | 
					          code: 'en',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          isDefault: false,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 2,
 | 
				
			||||||
 | 
					          name: 'French (France) (fr-FR)',
 | 
				
			||||||
 | 
					          code: 'fr-FR',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T15:03:06.992Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-17T13:01:03.569Z',
 | 
				
			||||||
 | 
					          isDefault: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 3,
 | 
				
			||||||
 | 
					          name: 'Another lang',
 | 
				
			||||||
 | 
					          code: 'de',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T15:03:06.992Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-17T13:01:03.569Z',
 | 
				
			||||||
 | 
					          isDefault: false,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const ctPermissions = {
 | 
				
			||||||
 | 
					        'plugin::content-manager.explorer.create': [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            id: 1325,
 | 
				
			||||||
 | 
					            action: 'plugin::content-manager.explorer.create',
 | 
				
			||||||
 | 
					            subject: 'api::address.address',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              fields: [
 | 
				
			||||||
 | 
					                'postal_coder',
 | 
				
			||||||
 | 
					                'categories',
 | 
				
			||||||
 | 
					                'cover',
 | 
				
			||||||
 | 
					                'images',
 | 
				
			||||||
 | 
					                'city',
 | 
				
			||||||
 | 
					                'likes',
 | 
				
			||||||
 | 
					                'json',
 | 
				
			||||||
 | 
					                'slug',
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					              locales: [],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            conditions: [],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        'plugin::content-manager.explorer.read': [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            id: 1326,
 | 
				
			||||||
 | 
					            action: 'plugin::content-manager.explorer.read',
 | 
				
			||||||
 | 
					            subject: 'api::address.address',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              fields: [],
 | 
				
			||||||
 | 
					              locales: ['en', 'de'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            conditions: [],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const expected = 'en';
 | 
				
			||||||
 | 
					      const actual = getDefaultLocale(ctPermissions, locales);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(actual).toEqual(expected);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('gives gives the first locale with create permission ("en") when the locale is allowed', () => {
 | 
				
			||||||
 | 
					      const locales = [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 1,
 | 
				
			||||||
 | 
					          name: 'English',
 | 
				
			||||||
 | 
					          code: 'en',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          isDefault: false,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 2,
 | 
				
			||||||
 | 
					          name: 'French (France) (fr-FR)',
 | 
				
			||||||
 | 
					          code: 'fr-FR',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T15:03:06.992Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-17T13:01:03.569Z',
 | 
				
			||||||
 | 
					          isDefault: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 3,
 | 
				
			||||||
 | 
					          name: 'Another lang',
 | 
				
			||||||
 | 
					          code: 'de',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T15:03:06.992Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-17T13:01:03.569Z',
 | 
				
			||||||
 | 
					          isDefault: false,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const ctPermissions = {
 | 
				
			||||||
 | 
					        'plugin::content-manager.explorer.create': [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            id: 1325,
 | 
				
			||||||
 | 
					            action: 'plugin::content-manager.explorer.create',
 | 
				
			||||||
 | 
					            subject: 'api::address.address',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              fields: [
 | 
				
			||||||
 | 
					                'postal_coder',
 | 
				
			||||||
 | 
					                'categories',
 | 
				
			||||||
 | 
					                'cover',
 | 
				
			||||||
 | 
					                'images',
 | 
				
			||||||
 | 
					                'city',
 | 
				
			||||||
 | 
					                'likes',
 | 
				
			||||||
 | 
					                'json',
 | 
				
			||||||
 | 
					                'slug',
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					              locales: ['en', 'de'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            conditions: [],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        'plugin::content-manager.explorer.read': [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            id: 1326,
 | 
				
			||||||
 | 
					            action: 'plugin::content-manager.explorer.read',
 | 
				
			||||||
 | 
					            subject: 'api::address.address',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              fields: [],
 | 
				
			||||||
 | 
					              locales: [],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            conditions: [],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const expected = 'en';
 | 
				
			||||||
 | 
					      const actual = getDefaultLocale(ctPermissions, locales);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(actual).toEqual(expected);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('gives null when the user has no permission on any locale', () => {
 | 
				
			||||||
 | 
					      const locales = [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 1,
 | 
				
			||||||
 | 
					          name: 'English',
 | 
				
			||||||
 | 
					          code: 'en',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-09T14:57:03.016Z',
 | 
				
			||||||
 | 
					          isDefault: false,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 2,
 | 
				
			||||||
 | 
					          name: 'French (France) (fr-FR)',
 | 
				
			||||||
 | 
					          code: 'fr-FR',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T15:03:06.992Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-17T13:01:03.569Z',
 | 
				
			||||||
 | 
					          isDefault: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 3,
 | 
				
			||||||
 | 
					          name: 'Another lang',
 | 
				
			||||||
 | 
					          code: 'de',
 | 
				
			||||||
 | 
					          createdAt: '2021-03-09T15:03:06.992Z',
 | 
				
			||||||
 | 
					          updatedAt: '2021-03-17T13:01:03.569Z',
 | 
				
			||||||
 | 
					          isDefault: false,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const ctPermissions = {
 | 
				
			||||||
 | 
					        'plugin::content-manager.explorer.create': [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            id: 1325,
 | 
				
			||||||
 | 
					            action: 'plugin::content-manager.explorer.create',
 | 
				
			||||||
 | 
					            subject: 'api::address.address',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              fields: [
 | 
				
			||||||
 | 
					                'postal_coder',
 | 
				
			||||||
 | 
					                'categories',
 | 
				
			||||||
 | 
					                'cover',
 | 
				
			||||||
 | 
					                'images',
 | 
				
			||||||
 | 
					                'city',
 | 
				
			||||||
 | 
					                'likes',
 | 
				
			||||||
 | 
					                'json',
 | 
				
			||||||
 | 
					                'slug',
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					              locales: [],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            conditions: [],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        'plugin::content-manager.explorer.read': [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            id: 1326,
 | 
				
			||||||
 | 
					            action: 'plugin::content-manager.explorer.read',
 | 
				
			||||||
 | 
					            subject: 'api::address.address',
 | 
				
			||||||
 | 
					            properties: {
 | 
				
			||||||
 | 
					              fields: [],
 | 
				
			||||||
 | 
					              locales: [],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            conditions: [],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const expected = null;
 | 
				
			||||||
 | 
					      const actual = getDefaultLocale(ctPermissions, locales);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(actual).toEqual(expected);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -219,7 +219,7 @@ const getNestedPopulateOfNonLocalizedAttributes = (modelUID: any) => {
 | 
				
			|||||||
  return attributesToPopulate;
 | 
					  return attributesToPopulate;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default () => ({
 | 
					const contentTypes = () => ({
 | 
				
			||||||
  isLocalizedContentType,
 | 
					  isLocalizedContentType,
 | 
				
			||||||
  getValidLocale,
 | 
					  getValidLocale,
 | 
				
			||||||
  getNewLocalizationsFrom,
 | 
					  getNewLocalizationsFrom,
 | 
				
			||||||
@ -230,3 +230,8 @@ export default () => ({
 | 
				
			|||||||
  fillNonLocalizedAttributes,
 | 
					  fillNonLocalizedAttributes,
 | 
				
			||||||
  getNestedPopulateOfNonLocalizedAttributes,
 | 
					  getNestedPopulateOfNonLocalizedAttributes,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ContentTypesService = typeof contentTypes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default contentTypes;
 | 
				
			||||||
 | 
					export { ContentTypesService };
 | 
				
			||||||
 | 
				
			|||||||
@ -277,9 +277,14 @@ const addGraphqlLocalizationAction = (contentType: any) => {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default () => ({
 | 
					const coreApi = () => ({
 | 
				
			||||||
  addCreateLocalizationAction,
 | 
					  addCreateLocalizationAction,
 | 
				
			||||||
  addGraphqlLocalizationAction,
 | 
					  addGraphqlLocalizationAction,
 | 
				
			||||||
  createSanitizer,
 | 
					  createSanitizer,
 | 
				
			||||||
  createCreateLocalizationHandler,
 | 
					  createCreateLocalizationHandler,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CoreApiService = typeof coreApi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default coreApi;
 | 
				
			||||||
 | 
					export { CoreApiService };
 | 
				
			||||||
 | 
				
			|||||||
@ -196,7 +196,12 @@ const decorator = (service: any) => ({
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default () => ({
 | 
					const entityServiceDecorator = () => ({
 | 
				
			||||||
  decorator,
 | 
					  decorator,
 | 
				
			||||||
  wrapParams,
 | 
					  wrapParams,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type EntityServiceDecoratorService = typeof entityServiceDecorator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default entityServiceDecorator;
 | 
				
			||||||
 | 
					export type { EntityServiceDecoratorService };
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,11 @@ import { isoLocales } from '../constants';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const getIsoLocales = () => isoLocales;
 | 
					const getIsoLocales = () => isoLocales;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default () => ({
 | 
					const isoLocalesService = () => ({
 | 
				
			||||||
  getIsoLocales,
 | 
					  getIsoLocales,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ISOLocalesService = typeof isoLocalesService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default isoLocalesService;
 | 
				
			||||||
 | 
					export type { ISOLocalesService };
 | 
				
			||||||
 | 
				
			|||||||
@ -79,7 +79,7 @@ const deleteAllLocalizedEntriesFor = async ({ locale }: any) => {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default () => ({
 | 
					const locales = () => ({
 | 
				
			||||||
  find,
 | 
					  find,
 | 
				
			||||||
  findById,
 | 
					  findById,
 | 
				
			||||||
  findByCode,
 | 
					  findByCode,
 | 
				
			||||||
@ -92,3 +92,8 @@ export default () => ({
 | 
				
			|||||||
  delete: deleteFn,
 | 
					  delete: deleteFn,
 | 
				
			||||||
  initDefaultLocale,
 | 
					  initDefaultLocale,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type LocaleService = typeof locales;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default locales;
 | 
				
			||||||
 | 
					export type { LocaleService };
 | 
				
			||||||
 | 
				
			|||||||
@ -82,8 +82,13 @@ const syncNonLocalizedAttributes = async (entry: any, { model }: any) => {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default () => ({
 | 
					const localizations = () => ({
 | 
				
			||||||
  assignDefaultLocaleToEntries,
 | 
					  assignDefaultLocaleToEntries,
 | 
				
			||||||
  syncLocalizations,
 | 
					  syncLocalizations,
 | 
				
			||||||
  syncNonLocalizedAttributes,
 | 
					  syncNonLocalizedAttributes,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type LocalizationsService = typeof localizations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default localizations;
 | 
				
			||||||
 | 
					export type { LocalizationsService };
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,12 @@ const sendDidUpdateI18nLocalesEvent = async () => {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default () => ({
 | 
					const metrics = () => ({
 | 
				
			||||||
  sendDidInitializeEvent,
 | 
					  sendDidInitializeEvent,
 | 
				
			||||||
  sendDidUpdateI18nLocalesEvent,
 | 
					  sendDidUpdateI18nLocalesEvent,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MetricsService = typeof metrics;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default metrics;
 | 
				
			||||||
 | 
					export type { MetricsService };
 | 
				
			||||||
 | 
				
			|||||||
@ -2,8 +2,13 @@ import i18nActionsService from './permissions/actions';
 | 
				
			|||||||
import sectionsBuilderService from './permissions/sections-builder';
 | 
					import sectionsBuilderService from './permissions/sections-builder';
 | 
				
			||||||
import engineService from './permissions/engine';
 | 
					import engineService from './permissions/engine';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default () => ({
 | 
					const permissions = () => ({
 | 
				
			||||||
  actions: i18nActionsService,
 | 
					  actions: i18nActionsService,
 | 
				
			||||||
  sectionsBuilder: sectionsBuilderService,
 | 
					  sectionsBuilder: sectionsBuilderService,
 | 
				
			||||||
  engine: engineService,
 | 
					  engine: engineService,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type PermissionsService = typeof permissions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default permissions;
 | 
				
			||||||
 | 
					export type { PermissionsService };
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,21 @@
 | 
				
			|||||||
import locales from '../services/locales';
 | 
					import type { LocaleService } from '../services/locales';
 | 
				
			||||||
import permissions from '../services/permissions';
 | 
					import type { PermissionsService } from '../services/permissions';
 | 
				
			||||||
import contentTypes from '../services/content-types';
 | 
					import type { ContentTypesService } from '../services/content-types';
 | 
				
			||||||
import metrics from '../services/metrics';
 | 
					import type { MetricsService } from '../services/metrics';
 | 
				
			||||||
import entityServiceDecorator from '../services/entity-service-decorator';
 | 
					import type { EntityServiceDecoratorService } from '../services/entity-service-decorator';
 | 
				
			||||||
import coreAPI from '../services/core-api';
 | 
					import type { CoreApiService } from '../services/core-api';
 | 
				
			||||||
import ISOLocales from '../services/iso-locales';
 | 
					import type { ISOLocalesService } from '../services/iso-locales';
 | 
				
			||||||
import localizations from '../services/localizations';
 | 
					import type { LocalizationsService } from '../services/localizations';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type S = {
 | 
					type S = {
 | 
				
			||||||
  permissions: typeof permissions;
 | 
					  permissions: PermissionsService;
 | 
				
			||||||
  metrics: typeof metrics;
 | 
					  metrics: MetricsService;
 | 
				
			||||||
  locales: typeof locales;
 | 
					  locales: LocaleService;
 | 
				
			||||||
  localizations: typeof localizations;
 | 
					  localizations: LocalizationsService;
 | 
				
			||||||
  ['iso-locales']: typeof ISOLocales;
 | 
					  ['iso-locales']: ISOLocalesService;
 | 
				
			||||||
  ['content-types']: typeof contentTypes;
 | 
					  ['content-types']: ContentTypesService;
 | 
				
			||||||
  ['entity-service-decorator']: typeof entityServiceDecorator;
 | 
					  ['entity-service-decorator']: EntityServiceDecoratorService;
 | 
				
			||||||
  ['core-api']: typeof coreAPI;
 | 
					  ['core-api']: CoreApiService;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getCoreStore = () => {
 | 
					const getCoreStore = () => {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user