Merge pull request #19058 from strapi/v5/extend-locale-attr-to-all-ct

feat: extend i18n attributes to every content type
This commit is contained in:
Marc Roig 2024-01-12 11:22:53 +01:00 committed by GitHub
commit d39441e3c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 162 additions and 32 deletions

View File

@ -181,7 +181,10 @@ describe('Search query', () => {
expect(Array.isArray(res.body.results)).toBe(true);
expect(res.body.results.length).toBe(data.beds.length);
expect(res.body.results.map(omit(CREATOR_FIELDS))).toEqual(expect.arrayContaining(data.beds));
// TODO V5: Filter out i18n fields if content type is not localized
expect(res.body.results.map(omit([...CREATOR_FIELDS, 'localizations']))).toEqual(
expect.arrayContaining(data.beds)
);
});
test('search with special characters', async () => {

View File

@ -745,6 +745,7 @@ describe('Core API - Validate', () => {
const allDocumentFields = [
// TODO: Document id should not be in attributes
'documentId',
'locale',
'name',
'name_non_searchable',
'misc',

View File

@ -2,7 +2,7 @@ import { LoadedStrapi } from '@strapi/types';
import { createTestSetup, destroyTestSetup } from '../../../utils/builder-helper';
import { testInTransaction } from '../../../utils/index';
import resources from './resources/index';
import { ARTICLE_UID, findArticlesDb } from './utils';
import { ARTICLE_UID, findArticlesDb, AUTHOR_UID } from './utils';
describe('Document Service', () => {
let testUtils;
@ -118,5 +118,22 @@ describe('Document Service', () => {
});
})
);
it(
'ignores locale parameter on non-localized content type',
testInTransaction(async () => {
const author = await strapi.documents(AUTHOR_UID).create({
// Should be ignored on non-localized content types
locale: 'fr',
data: { name: 'Author' },
});
// verify that the returned document was updated
expect(author).toMatchObject({
name: 'Author',
locale: null, // should be null, as it is not a localized content type
});
})
);
});
});

View File

@ -1,7 +1,7 @@
import { LoadedStrapi } from '@strapi/types';
import { createTestSetup, destroyTestSetup } from '../../../utils/builder-helper';
import resources from './resources/index';
import { ARTICLE_UID, findArticleDb } from './utils';
import { ARTICLE_UID, findArticleDb, AUTHOR_UID, findAuthorDb } from './utils';
describe('Document Service', () => {
let testUtils;
@ -55,6 +55,17 @@ describe('Document Service', () => {
expect(article).toMatchObject(articleDb);
});
it('ignores locale parameter on non-localized content type', async () => {
const authorDb = await findAuthorDb({ name: 'Author1-Draft' });
// Locale should be ignored on non-localized content types
const author = await strapi.documents(AUTHOR_UID).findOne(authorDb.documentId, {
locale: 'en',
});
expect(author).toMatchObject(authorDb);
});
it.todo('ignores pagination parameters');
});
});

View File

@ -0,0 +1,16 @@
[
{
"id": 1,
"documentId": "Author1",
"name": "Author1-Draft",
"publishedAt": null,
"locale": null
},
{
"id": 2,
"documentId": "Author2",
"name": "Author2-Draft",
"publishedAt": null,
"locale": null
}
]

View File

@ -5,5 +5,6 @@ module.exports = {
// Make sure this is sorted by order to create them
'api::category.category': require('./category.json'),
'api::article.article': require('./article.json'),
'api::author.author': require('./author.json'),
},
};

View File

@ -0,0 +1,26 @@
'use strict';
module.exports = {
kind: 'collectionType',
collectionName: 'authors',
singularName: 'author',
pluralName: 'authors',
displayName: 'Author',
description: '',
draftAndPublish: true,
pluginOptions: {
i18n: {
localized: false,
},
},
attributes: {
name: {
type: 'string',
pluginOptions: {
i18n: {
localized: false,
},
},
},
},
};

View File

@ -4,6 +4,7 @@ module.exports = {
'content-types': {
'api::category.category': require('./category'),
'api::article.article': require('./article'),
'api::author.author': require('./author'),
},
components: {
'article.comp': require('./comp'),

View File

@ -618,6 +618,38 @@ export interface ApiArticleArticle extends Schema.CollectionType {
};
}
export interface ApiAuthorAuthor extends Schema.CollectionType {
collectionName: 'authors';
info: {
singularName: 'author';
pluralName: 'authors';
displayName: 'Author';
description: '';
};
pluginOptions: {
i18n: {
localized: false;
};
};
attributes: {
name: Attribute.String &
Attribute.SetPluginOptions<{
i18n: {
localized: false;
};
}>;
createdAt: Attribute.DateTime;
updatedAt: Attribute.DateTime;
publishedAt: Attribute.DateTime;
createdBy: Attribute.Relation<'api::author.author', 'oneToOne', 'admin::user'> &
Attribute.Private;
updatedBy: Attribute.Relation<'api::author.author', 'oneToOne', 'admin::user'> &
Attribute.Private;
localizations: Attribute.Relation<'api::author.author', 'oneToMany', 'api::author.author'>;
locale: Attribute.String;
};
}
export interface ApiCategoryCategory extends Schema.CollectionType {
collectionName: 'categories';
info: {
@ -674,6 +706,7 @@ declare module '@strapi/types' {
'plugin::users-permissions.role': PluginUsersPermissionsRole;
'plugin::users-permissions.user': PluginUsersPermissionsUser;
'api::article.article': ApiArticleArticle;
'api::author.author': ApiAuthorAuthor;
'api::category.category': ApiCategoryCategory;
}
}

View File

@ -1,5 +1,8 @@
import { Attribute } from '@strapi/strapi';
export const AUTHOR_UID = 'api::author.author';
export type Author = Attribute.GetAll<typeof AUTHOR_UID> & { documentId: string; id: number };
export const ARTICLE_UID = 'api::article.article';
export type Article = Attribute.GetAll<typeof ARTICLE_UID> & { documentId: string; id: number };
@ -14,3 +17,15 @@ export const findArticlesDb = async (where: any) => {
export const findPublishedArticlesDb = async (documentId) => {
return findArticlesDb({ documentId, publishedAt: { $notNull: true } });
};
export const findAuthorDb = async (where: any) => {
return (await strapi.query(AUTHOR_UID).findOne({ where })) as Author | undefined;
};
export const findAuthorsDb = async (where: any) => {
return (await strapi.query(AUTHOR_UID).findMany({ where })) as Author[];
};
export const findPublishedAuthorsDb = async (documentId) => {
return findAuthorsDb({ documentId, publishedAt: { $notNull: true } });
};

View File

@ -276,10 +276,8 @@ export const CMReleasesContainer = () => {
/**
* - Impossible to add entry to release before it exists
* - Content types without draft and publish cannot add entries to release
* TODO v5: All contentTypes will have draft and publish enabled
*/
if (isCreatingEntry || !contentType?.options?.draftAndPublish) {
if (isCreatingEntry) {
return null;
}

View File

@ -1,13 +1,19 @@
// TODO: Move to i18n
import { Documents } from '@strapi/types';
import { Documents, Common } from '@strapi/types';
type Middleware = Documents.Middleware.Middleware<any, any>;
const isLocalizedContentType = (uid: Common.UID.Schema) => {
const model = strapi.getModel(uid);
return strapi.plugin('i18n').service('content-types').isLocalizedContentType(model);
};
export const defaultLocale: Middleware = async (ctx, next) => {
if (!isLocalizedContentType(ctx.uid)) return next(ctx);
if (!ctx.params) ctx.params = {};
// Default to en (TODO: Load default locale from db in i18n)
if (!ctx.params.locale) {
// Default to en (TODO: Load default locale from db in i18n)
ctx.params.locale = 'en';
}
@ -18,6 +24,7 @@ export const defaultLocale: Middleware = async (ctx, next) => {
* Add locale lookup query to the params
*/
export const localeToLookup: Middleware = async (ctx, next) => {
if (!isLocalizedContentType(ctx.uid)) return next(ctx);
if (!ctx.params) ctx.params = {};
const lookup = ctx.params.lookup || {};
@ -34,6 +41,7 @@ export const localeToLookup: Middleware = async (ctx, next) => {
* Translate locale status parameter into the data that will be saved
*/
export const localeToData: Middleware = async (ctx, next) => {
if (!isLocalizedContentType(ctx.uid)) return next(ctx);
if (!ctx.params) ctx.params = {};
const data = ctx.params.data || {};

View File

@ -9,7 +9,7 @@ import enableContentType from './migrations/content-type/enable';
import disableContentType from './migrations/content-type/disable';
export default ({ strapi }: { strapi: Strapi }) => {
extendLocalizedContentTypes(strapi);
extendContentTypes(strapi);
addContentManagerLocaleMiddleware(strapi);
addContentTypeSyncHooks(strapi);
};
@ -46,37 +46,35 @@ const addContentTypeSyncHooks = (strapi: Strapi) => {
};
/**
* Adds locale and localization fields to localized content types
* Adds locale and localization fields to all content types
* Even if content type is not localized, it will have these fields
* @param {Strapi} strapi
*/
const extendLocalizedContentTypes = (strapi: Strapi) => {
const contentTypeService = getService('content-types');
const extendContentTypes = (strapi: Strapi) => {
const coreApiService = getService('core-api');
Object.values(strapi.contentTypes).forEach((contentType) => {
if (contentTypeService.isLocalizedContentType(contentType)) {
const { attributes } = contentType;
const { attributes } = contentType;
_.set(attributes, 'localizations', {
writable: true,
private: false,
configurable: false,
visible: false,
type: 'relation',
relation: 'oneToMany',
target: contentType.uid,
});
_.set(attributes, 'localizations', {
writable: true,
private: false,
configurable: false,
visible: false,
type: 'relation',
relation: 'oneToMany',
target: contentType.uid,
});
_.set(attributes, 'locale', {
writable: true,
private: false,
configurable: false,
visible: false,
type: 'string',
});
_.set(attributes, 'locale', {
writable: true,
private: false,
configurable: false,
visible: false,
type: 'string',
});
coreApiService.addCreateLocalizationAction(contentType);
}
coreApiService.addCreateLocalizationAction(contentType);
});
if (strapi.plugin('graphql')) {

View File

@ -115,6 +115,8 @@ const userSchemaAdditions = () => {
'publishedAt',
'strapi_stage',
'strapi_assignee',
'locale',
'localizations',
];
return currentSchema.filter((key) => !(ignoreDiffs.includes(key) || defaultSchema.includes(key)));