Removed posts and contents
This commit is contained in:
parent
8a819de41e
commit
a5b636a858
|
@ -1,230 +0,0 @@
|
||||||
import { Where } from "payload/types";
|
|
||||||
import { sectionBlock } from "../../blocks/sectionBlock";
|
|
||||||
import { transcriptBlock } from "../../blocks/transcriptBlock";
|
|
||||||
import { CollectionGroups, Collections, FileTypes } from "../../constants";
|
|
||||||
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
|
||||||
import { fileField } from "../../fields/fileField/fileField";
|
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
|
||||||
import { rowField } from "../../fields/rowField/rowField";
|
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
|
||||||
import { tagsField } from "../../fields/tagsField/tagsField";
|
|
||||||
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
|
||||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
|
||||||
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
|
||||||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
|
||||||
import { isDefined } from "../../utils/asserts";
|
|
||||||
import { createEditor } from "../../utils/editor";
|
|
||||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
|
||||||
import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint";
|
|
||||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
|
||||||
import { importRelationsFromStrapi } from "./endpoints/importRelationsFromStrapi";
|
|
||||||
|
|
||||||
const fields = {
|
|
||||||
slug: "slug",
|
|
||||||
thumbnail: "thumbnail",
|
|
||||||
translations: "translations",
|
|
||||||
pretitle: "pretitle",
|
|
||||||
title: "title",
|
|
||||||
subtitle: "subtitle",
|
|
||||||
summary: "summary",
|
|
||||||
textContent: "textContent",
|
|
||||||
textTranscribers: "textTranscribers",
|
|
||||||
textTranslators: "textTranslators",
|
|
||||||
textProofreaders: "textProofreaders",
|
|
||||||
textNotes: "textNotes",
|
|
||||||
video: "video",
|
|
||||||
videoNotes: "videoNotes",
|
|
||||||
audio: "audio",
|
|
||||||
audioNotes: "audioNotes",
|
|
||||||
status: "status",
|
|
||||||
updatedBy: "updatedBy",
|
|
||||||
previousContents: "previousContents",
|
|
||||||
nextContents: "nextContents",
|
|
||||||
folders: "folders",
|
|
||||||
libraryItems: "libraryItems",
|
|
||||||
tags: "tags",
|
|
||||||
} as const satisfies Record<string, string>;
|
|
||||||
|
|
||||||
export const Contents = buildVersionedCollectionConfig({
|
|
||||||
slug: Collections.Contents,
|
|
||||||
labels: {
|
|
||||||
singular: "Content",
|
|
||||||
plural: "Contents",
|
|
||||||
},
|
|
||||||
defaultSort: fields.slug,
|
|
||||||
admin: {
|
|
||||||
useAsTitle: fields.slug,
|
|
||||||
description:
|
|
||||||
"All the contents (textual, audio, and video) from the Library or other online sources.",
|
|
||||||
defaultColumns: [fields.thumbnail, fields.slug, fields.translations, fields.status],
|
|
||||||
group: CollectionGroups.Collections,
|
|
||||||
hooks: {
|
|
||||||
beforeDuplicate: beforeDuplicatePiping([
|
|
||||||
beforeDuplicateUnpublish,
|
|
||||||
beforeDuplicateAddCopyTo(fields.slug),
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
endpoints: [importFromStrapi, importRelationsFromStrapi, getBySlugEndpoint],
|
|
||||||
fields: [
|
|
||||||
rowField([
|
|
||||||
slugField({ name: fields.slug }),
|
|
||||||
imageField({
|
|
||||||
name: fields.thumbnail,
|
|
||||||
relationTo: Collections.ContentsThumbnails,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
tagsField({ name: fields.tags }),
|
|
||||||
translatedFields({
|
|
||||||
name: fields.translations,
|
|
||||||
admin: { useAsTitle: fields.title, hasSourceLanguage: true },
|
|
||||||
required: true,
|
|
||||||
minRows: 1,
|
|
||||||
fields: [
|
|
||||||
rowField([
|
|
||||||
{ name: fields.pretitle, type: "text" },
|
|
||||||
{ name: fields.title, type: "text", required: true },
|
|
||||||
{ name: fields.subtitle, type: "text" },
|
|
||||||
]),
|
|
||||||
{
|
|
||||||
name: fields.summary,
|
|
||||||
type: "richText",
|
|
||||||
editor: createEditor({ inlines: true, lists: true, links: true }),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "tabs",
|
|
||||||
admin: {
|
|
||||||
condition: (_, siblingData) =>
|
|
||||||
isDefined(siblingData.language) && isDefined(siblingData.sourceLanguage),
|
|
||||||
},
|
|
||||||
tabs: [
|
|
||||||
{
|
|
||||||
label: "Text",
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: fields.textContent,
|
|
||||||
type: "richText",
|
|
||||||
label: false,
|
|
||||||
editor: createEditor({
|
|
||||||
blocks: [sectionBlock, transcriptBlock],
|
|
||||||
images: true,
|
|
||||||
inlines: true,
|
|
||||||
lists: true,
|
|
||||||
links: true,
|
|
||||||
relations: true,
|
|
||||||
alignment: true,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
rowField([
|
|
||||||
{
|
|
||||||
name: fields.textTranscribers,
|
|
||||||
label: "Transcribers",
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.Recorders,
|
|
||||||
hasMany: true,
|
|
||||||
admin: {
|
|
||||||
condition: (_, siblingData) =>
|
|
||||||
siblingData.language === siblingData.sourceLanguage,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.textTranslators,
|
|
||||||
label: "Translators",
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.Recorders,
|
|
||||||
hasMany: true,
|
|
||||||
admin: {
|
|
||||||
condition: (_, siblingData) =>
|
|
||||||
siblingData.language !== siblingData.sourceLanguage,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.textProofreaders,
|
|
||||||
label: "Proofreaders",
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.Recorders,
|
|
||||||
hasMany: true,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
{
|
|
||||||
name: fields.textNotes,
|
|
||||||
label: "Notes",
|
|
||||||
type: "richText",
|
|
||||||
editor: createEditor({ inlines: true, lists: true, links: true }),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Video",
|
|
||||||
fields: [
|
|
||||||
rowField([
|
|
||||||
fileField({
|
|
||||||
name: fields.video,
|
|
||||||
relationTo: FileTypes.ContentVideo,
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: fields.videoNotes,
|
|
||||||
label: "Notes",
|
|
||||||
type: "richText",
|
|
||||||
editor: createEditor({ inlines: true, lists: true, links: true }),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Audio",
|
|
||||||
fields: [
|
|
||||||
rowField([
|
|
||||||
fileField({
|
|
||||||
name: fields.audio,
|
|
||||||
relationTo: FileTypes.ContentAudio,
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: fields.audioNotes,
|
|
||||||
label: "Notes",
|
|
||||||
type: "richText",
|
|
||||||
editor: createEditor({ inlines: true, lists: true, links: true }),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
rowField([
|
|
||||||
backPropagationField({
|
|
||||||
name: fields.folders,
|
|
||||||
relationTo: Collections.Folders,
|
|
||||||
hasMany: true,
|
|
||||||
where: ({ id }) => ({
|
|
||||||
and: [
|
|
||||||
{ "files.value": { equals: id } },
|
|
||||||
{ "files.relationTo": { equals: Collections.Contents } },
|
|
||||||
] as Where[],
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
backPropagationField({
|
|
||||||
name: fields.libraryItems,
|
|
||||||
hasMany: true,
|
|
||||||
relationTo: Collections.LibraryItems,
|
|
||||||
where: ({ id }) => ({ "contents.content": { equals: id } }),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
rowField([
|
|
||||||
backPropagationField({
|
|
||||||
name: fields.previousContents,
|
|
||||||
relationTo: Collections.Contents,
|
|
||||||
hasMany: true,
|
|
||||||
where: ({ id }) => ({ [fields.nextContents]: { equals: id } }),
|
|
||||||
}),
|
|
||||||
|
|
||||||
{
|
|
||||||
name: fields.nextContents,
|
|
||||||
type: "relationship",
|
|
||||||
hasMany: true,
|
|
||||||
relationTo: Collections.Contents,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
});
|
|
|
@ -1,50 +0,0 @@
|
||||||
import { Collections } from "../../../constants";
|
|
||||||
import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint";
|
|
||||||
import { EndpointContent } from "../../../sdk";
|
|
||||||
import { Content } from "../../../types/collections";
|
|
||||||
import { isPayloadArrayType, isPayloadType, isValidPayloadImage } from "../../../utils/asserts";
|
|
||||||
import { convertTagsToGroups } from "../../../utils/tags";
|
|
||||||
|
|
||||||
export const getBySlugEndpoint = createGetByEndpoint(
|
|
||||||
Collections.Contents,
|
|
||||||
"slug",
|
|
||||||
({ thumbnail, slug, translations, tags }: Content): EndpointContent => ({
|
|
||||||
slug,
|
|
||||||
...(isValidPayloadImage(thumbnail) ? { thumbnail } : {}),
|
|
||||||
tagGroups: convertTagsToGroups(tags),
|
|
||||||
translations: translations.map((translation) => {
|
|
||||||
const { language, sourceLanguage, title, subtitle, pretitle, summary } = translation;
|
|
||||||
const text = handleTextContent(translation);
|
|
||||||
|
|
||||||
return {
|
|
||||||
language: isPayloadType(language) ? language.id : language,
|
|
||||||
sourceLanguage: isPayloadType(sourceLanguage) ? sourceLanguage.id : sourceLanguage,
|
|
||||||
...(pretitle ? { pretitle } : {}),
|
|
||||||
title,
|
|
||||||
...(subtitle ? { subtitle } : {}),
|
|
||||||
...(summary ? { summary } : {}),
|
|
||||||
format: { ...(text ? { text } : {}) },
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
const handleTextContent = ({
|
|
||||||
textContent,
|
|
||||||
textNotes,
|
|
||||||
textProofreaders,
|
|
||||||
textTranscribers,
|
|
||||||
textTranslators,
|
|
||||||
}: Content["translations"][number]): EndpointContent["translations"][number]["format"]["text"] => {
|
|
||||||
if (!textContent) return undefined;
|
|
||||||
|
|
||||||
return {
|
|
||||||
content: textContent,
|
|
||||||
toc: [],
|
|
||||||
translators: isPayloadArrayType(textTranslators) ? textTranslators.map(({ id }) => id) : [],
|
|
||||||
transcribers: isPayloadArrayType(textTranscribers) ? textTranscribers.map(({ id }) => id) : [],
|
|
||||||
proofreaders: isPayloadArrayType(textProofreaders) ? textProofreaders.map(({ id }) => id) : [],
|
|
||||||
...(textNotes ? { notes: textNotes } : {}),
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,123 +0,0 @@
|
||||||
import type { MarkOptional } from "ts-essentials";
|
|
||||||
import { Collections } from "../../../constants";
|
|
||||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
|
||||||
import { Content } from "../../../types/collections";
|
|
||||||
import { StrapiImage, StrapiLanguage, StrapiRecorders } from "../../../types/strapi";
|
|
||||||
import { isNotEmpty, isUndefined } from "../../../utils/asserts";
|
|
||||||
import {
|
|
||||||
findCategory,
|
|
||||||
findContentType,
|
|
||||||
findRecorder,
|
|
||||||
uploadStrapiImage,
|
|
||||||
} from "../../../utils/localApi";
|
|
||||||
import { plainTextToLexical } from "../../../utils/string";
|
|
||||||
|
|
||||||
type StrapiContent = {
|
|
||||||
slug: string;
|
|
||||||
categories: { data?: { attributes: { slug: string } }[] };
|
|
||||||
type: { data?: { attributes: { slug: string } } };
|
|
||||||
thumbnail: StrapiImage;
|
|
||||||
translations: {
|
|
||||||
title: string;
|
|
||||||
subtitle?: string;
|
|
||||||
pre_title?: string;
|
|
||||||
description?: string;
|
|
||||||
language: StrapiLanguage;
|
|
||||||
text_set?: {
|
|
||||||
text: string;
|
|
||||||
notes?: string;
|
|
||||||
source_language: StrapiLanguage;
|
|
||||||
transcribers: StrapiRecorders;
|
|
||||||
translators: StrapiRecorders;
|
|
||||||
proofreaders: StrapiRecorders;
|
|
||||||
};
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const importFromStrapi = createStrapiImportEndpoint<StrapiContent>({
|
|
||||||
strapi: {
|
|
||||||
collection: "contents",
|
|
||||||
params: {
|
|
||||||
populate: [
|
|
||||||
"type",
|
|
||||||
"categories",
|
|
||||||
"thumbnail",
|
|
||||||
"translations",
|
|
||||||
"translations.language",
|
|
||||||
"translations.text_set",
|
|
||||||
"translations.text_set.source_language",
|
|
||||||
"translations.text_set.transcribers",
|
|
||||||
"translations.text_set.translators",
|
|
||||||
"translations.text_set.proofreaders",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
payload: {
|
|
||||||
collection: Collections.Contents,
|
|
||||||
convert: async ({ slug, categories, type, thumbnail, translations }) => {
|
|
||||||
const thumbnailId = await uploadStrapiImage({
|
|
||||||
collection: Collections.ContentsThumbnails,
|
|
||||||
image: thumbnail,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleTranslation = async ({
|
|
||||||
language,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
pre_title,
|
|
||||||
subtitle,
|
|
||||||
text_set,
|
|
||||||
}: StrapiContent["translations"][number]) => {
|
|
||||||
if (isUndefined(language.data))
|
|
||||||
throw new Error("A language is required for a content translation");
|
|
||||||
if (isUndefined(text_set)) throw new Error("Only content with text_set are supported");
|
|
||||||
if (isUndefined(text_set.source_language.data))
|
|
||||||
throw new Error("A language is required for a content translation text_set");
|
|
||||||
return {
|
|
||||||
language: language.data.attributes.code,
|
|
||||||
sourceLanguage: text_set.source_language.data.attributes.code,
|
|
||||||
title,
|
|
||||||
pretitle: pre_title,
|
|
||||||
subtitle,
|
|
||||||
summary: isNotEmpty(description) ? plainTextToLexical(description) : undefined,
|
|
||||||
textContent: plainTextToLexical(text_set.text),
|
|
||||||
textNotes: isNotEmpty(text_set.notes) ? plainTextToLexical(text_set.notes) : undefined,
|
|
||||||
textTranscribers:
|
|
||||||
text_set.transcribers.data &&
|
|
||||||
(await Promise.all(
|
|
||||||
text_set.transcribers.data?.map(async (recorder) =>
|
|
||||||
findRecorder(recorder.attributes.username)
|
|
||||||
)
|
|
||||||
)),
|
|
||||||
textTranslators:
|
|
||||||
text_set.translators.data &&
|
|
||||||
(await Promise.all(
|
|
||||||
text_set.translators.data?.map(async (recorder) =>
|
|
||||||
findRecorder(recorder.attributes.username)
|
|
||||||
)
|
|
||||||
)),
|
|
||||||
textProofreaders:
|
|
||||||
text_set.proofreaders.data &&
|
|
||||||
(await Promise.all(
|
|
||||||
text_set.proofreaders.data?.map(async (recorder) =>
|
|
||||||
findRecorder(recorder.attributes.username)
|
|
||||||
)
|
|
||||||
)),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const data: MarkOptional<Content, "createdAt" | "id" | "updatedAt" | "updatedBy"> = {
|
|
||||||
slug,
|
|
||||||
categories:
|
|
||||||
categories.data &&
|
|
||||||
(await Promise.all(
|
|
||||||
categories.data.map(async (category) => await findCategory(category.attributes.slug))
|
|
||||||
)),
|
|
||||||
type: type.data && (await findContentType(type.data?.attributes.slug)),
|
|
||||||
thumbnail: thumbnailId,
|
|
||||||
translations: await Promise.all(translations.map(handleTranslation)),
|
|
||||||
};
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,38 +0,0 @@
|
||||||
import payload from "payload";
|
|
||||||
import { Collections } from "../../../constants";
|
|
||||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
|
||||||
import { findContent } from "../../../utils/localApi";
|
|
||||||
|
|
||||||
type StrapiContent = {
|
|
||||||
slug: string;
|
|
||||||
next_contents: { data: { attributes: { slug: string } }[] };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const importRelationsFromStrapi = createStrapiImportEndpoint<StrapiContent>({
|
|
||||||
strapi: {
|
|
||||||
collection: "contents",
|
|
||||||
params: {
|
|
||||||
populate: ["next_contents"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
payload: {
|
|
||||||
path: "/strapi/related-content",
|
|
||||||
collection: Collections.Contents,
|
|
||||||
import: async ({ slug, next_contents }, user) => {
|
|
||||||
if (next_contents.data.length === 0) return;
|
|
||||||
const currentContent = await findContent(slug);
|
|
||||||
const nextContents: string[] = [];
|
|
||||||
for (const nextContent of next_contents.data) {
|
|
||||||
const result = await findContent(nextContent.attributes.slug);
|
|
||||||
nextContents.push(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
payload.update({
|
|
||||||
collection: Collections.Contents,
|
|
||||||
id: currentContent,
|
|
||||||
data: { nextContents },
|
|
||||||
user,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,50 +0,0 @@
|
||||||
import { Collections } from "../../constants";
|
|
||||||
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
|
||||||
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
|
|
||||||
|
|
||||||
const fields = {
|
|
||||||
filename: "filename",
|
|
||||||
mimeType: "mimeType",
|
|
||||||
filesize: "filesize",
|
|
||||||
contents: "contents",
|
|
||||||
updatedAt: "updatedAt",
|
|
||||||
} as const satisfies Record<string, string>;
|
|
||||||
|
|
||||||
export const ContentsThumbnails = buildImageCollectionConfig({
|
|
||||||
slug: Collections.ContentsThumbnails,
|
|
||||||
labels: {
|
|
||||||
singular: "Contents Thumbnail",
|
|
||||||
plural: "Contents Thumbnails",
|
|
||||||
},
|
|
||||||
admin: { defaultColumns: [fields.filename, fields.contents, fields.updatedAt] },
|
|
||||||
upload: {
|
|
||||||
imageSizes: [
|
|
||||||
{
|
|
||||||
name: "og",
|
|
||||||
height: 750,
|
|
||||||
width: 1125,
|
|
||||||
formatOptions: {
|
|
||||||
format: "jpg",
|
|
||||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "medium",
|
|
||||||
height: 1000,
|
|
||||||
width: 1500,
|
|
||||||
formatOptions: {
|
|
||||||
format: "webp",
|
|
||||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
fields: [
|
|
||||||
backPropagationField({
|
|
||||||
name: fields.contents,
|
|
||||||
hasMany: true,
|
|
||||||
relationTo: Collections.Contents,
|
|
||||||
where: ({ id }) => ({ thumbnail: { equals: id } }),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
|
@ -73,7 +73,7 @@ export const Folders = buildCollectionConfig({
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: fields.sectionsSubfolders,
|
name: fields.sectionsSubfolders,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Collections.Folders,
|
relationTo: Collections.Folders,
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
|
@ -83,7 +83,7 @@ export const Folders = buildCollectionConfig({
|
||||||
{
|
{
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
name: fields.files,
|
name: fields.files,
|
||||||
relationTo: [Collections.LibraryItems, Collections.Contents, Collections.Pages],
|
relationTo: [Collections.LibraryItems, Collections.Pages],
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
LibraryItemsTextualBindingTypes,
|
LibraryItemsTextualBindingTypes,
|
||||||
LibraryItemsTextualPageOrders,
|
LibraryItemsTextualPageOrders,
|
||||||
LibraryItemsTypes,
|
LibraryItemsTypes,
|
||||||
|
PageType,
|
||||||
} from "../../constants";
|
} from "../../constants";
|
||||||
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
||||||
import { componentField } from "../../fields/componentField/componentField";
|
import { componentField } from "../../fields/componentField/componentField";
|
||||||
|
@ -734,8 +735,12 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
||||||
{
|
{
|
||||||
name: fields.contentsContent,
|
name: fields.contentsContent,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Collections.Contents,
|
relationTo: Collections.Pages,
|
||||||
|
admin: {
|
||||||
|
allowCreate: false,
|
||||||
|
},
|
||||||
required: true,
|
required: true,
|
||||||
|
filterOptions: { type: { equals: PageType.Content } },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "row",
|
type: "row",
|
||||||
|
|
|
@ -56,7 +56,7 @@ export const Pages = buildVersionedCollectionConfig({
|
||||||
BeforeListTable: [
|
BeforeListTable: [
|
||||||
() =>
|
() =>
|
||||||
QuickFilters({
|
QuickFilters({
|
||||||
slug: Collections.Posts,
|
slug: Collections.Pages,
|
||||||
filterGroups: [publishStatusFilters],
|
filterGroups: [publishStatusFilters],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,173 +0,0 @@
|
||||||
import { sectionBlock } from "../../blocks/sectionBlock";
|
|
||||||
import { transcriptBlock } from "../../blocks/transcriptBlock";
|
|
||||||
import { QuickFilters, publishStatusFilters } from "../../components/QuickFilters";
|
|
||||||
import { CollectionGroups, Collections, KeysTypes } from "../../constants";
|
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
|
||||||
import { keysField } from "../../fields/keysField/keysField";
|
|
||||||
import { rowField } from "../../fields/rowField/rowField";
|
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
|
||||||
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
|
||||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
|
||||||
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
|
||||||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
|
||||||
import { isDefined, isUndefined } from "../../utils/asserts";
|
|
||||||
import { createEditor } from "../../utils/editor";
|
|
||||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
|
||||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
|
||||||
|
|
||||||
const fields = {
|
|
||||||
slug: "slug",
|
|
||||||
hidden: "hidden",
|
|
||||||
thumbnail: "thumbnail",
|
|
||||||
categories: "categories",
|
|
||||||
authors: "authors",
|
|
||||||
publishedDate: "publishedDate",
|
|
||||||
translations: "translations",
|
|
||||||
sourceLanguage: "sourceLanguage",
|
|
||||||
title: "title",
|
|
||||||
summary: "summary",
|
|
||||||
content: "content",
|
|
||||||
translators: "translators",
|
|
||||||
proofreaders: "proofreaders",
|
|
||||||
} as const satisfies Record<string, string>;
|
|
||||||
|
|
||||||
export const Posts = buildVersionedCollectionConfig({
|
|
||||||
slug: Collections.Posts,
|
|
||||||
labels: {
|
|
||||||
singular: "Post",
|
|
||||||
plural: "Posts",
|
|
||||||
},
|
|
||||||
defaultSort: fields.slug,
|
|
||||||
admin: {
|
|
||||||
useAsTitle: fields.slug,
|
|
||||||
description:
|
|
||||||
"News articles written by our Recorders! Here you will find announcements about \
|
|
||||||
new merch/items releases, guides, theories, unboxings, showcases...",
|
|
||||||
defaultColumns: [fields.thumbnail, fields.slug, fields.categories],
|
|
||||||
group: CollectionGroups.Collections,
|
|
||||||
components: {
|
|
||||||
BeforeListTable: [
|
|
||||||
() =>
|
|
||||||
QuickFilters({
|
|
||||||
slug: Collections.Posts,
|
|
||||||
filterGroups: [publishStatusFilters],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
hooks: {
|
|
||||||
beforeDuplicate: beforeDuplicatePiping([
|
|
||||||
beforeDuplicateUnpublish,
|
|
||||||
beforeDuplicateAddCopyTo(fields.slug),
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
preview: (doc) => `https://accords-library.com/news/${doc.slug}`,
|
|
||||||
},
|
|
||||||
endpoints: [importFromStrapi],
|
|
||||||
fields: [
|
|
||||||
rowField([
|
|
||||||
slugField({ name: fields.slug }),
|
|
||||||
imageField({
|
|
||||||
name: fields.thumbnail,
|
|
||||||
relationTo: Collections.PostsThumbnails,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
rowField([
|
|
||||||
{
|
|
||||||
name: fields.authors,
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.Recorders,
|
|
||||||
required: true,
|
|
||||||
minRows: 1,
|
|
||||||
hasMany: true,
|
|
||||||
},
|
|
||||||
keysField({ name: fields.categories, relationTo: KeysTypes.Categories, hasMany: true }),
|
|
||||||
]),
|
|
||||||
translatedFields({
|
|
||||||
name: fields.translations,
|
|
||||||
admin: { useAsTitle: fields.title, hasSourceLanguage: true },
|
|
||||||
required: true,
|
|
||||||
minRows: 1,
|
|
||||||
fields: [
|
|
||||||
{ name: fields.title, type: "text", required: true },
|
|
||||||
{
|
|
||||||
name: fields.summary,
|
|
||||||
type: "richText",
|
|
||||||
editor: createEditor({ inlines: true, lists: true, links: true }),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.content,
|
|
||||||
type: "richText",
|
|
||||||
editor: createEditor({
|
|
||||||
images: true,
|
|
||||||
inlines: true,
|
|
||||||
alignment: true,
|
|
||||||
blocks: [sectionBlock, transcriptBlock],
|
|
||||||
links: true,
|
|
||||||
lists: true,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
rowField([
|
|
||||||
{
|
|
||||||
name: fields.translators,
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.Recorders,
|
|
||||||
hasMany: true,
|
|
||||||
hooks: {
|
|
||||||
beforeChange: [
|
|
||||||
({ siblingData }) => {
|
|
||||||
if (siblingData.language === siblingData.sourceLanguage) {
|
|
||||||
delete siblingData.translators;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
condition: (_, siblingData) => {
|
|
||||||
if (isUndefined(siblingData.language) || isUndefined(siblingData.sourceLanguage)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return siblingData.language !== siblingData.sourceLanguage;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
validate: (translators, { siblingData }) => {
|
|
||||||
if (isUndefined(siblingData.language) || isUndefined(siblingData.sourceLanguage)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (siblingData.language === siblingData.sourceLanguage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (isDefined(translators) && translators.length > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return "This field is required when the language is different from the source language.";
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.proofreaders,
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.Recorders,
|
|
||||||
hasMany: true,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: fields.publishedDate,
|
|
||||||
type: "date",
|
|
||||||
defaultValue: new Date().toISOString(),
|
|
||||||
admin: {
|
|
||||||
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.hidden,
|
|
||||||
type: "checkbox",
|
|
||||||
required: false,
|
|
||||||
defaultValue: false,
|
|
||||||
admin: {
|
|
||||||
description: "If enabled, the post won't appear in the 'News' section",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
|
@ -1,115 +0,0 @@
|
||||||
import { DateTime } from "luxon";
|
|
||||||
import type { MarkOptional } from "ts-essentials";
|
|
||||||
import { Collections } from "../../../constants";
|
|
||||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
|
||||||
import type { Post } from "../../../types/collections";
|
|
||||||
import { StrapiImage, StrapiLanguage, StrapiRecorders } from "../../../types/strapi";
|
|
||||||
import { isDefined, isUndefined } from "../../../utils/asserts";
|
|
||||||
import { findCategory, findRecorder, uploadStrapiImage } from "../../../utils/localApi";
|
|
||||||
import { plainTextToLexical } from "../../../utils/string";
|
|
||||||
|
|
||||||
type StrapiPost = {
|
|
||||||
slug: string;
|
|
||||||
categories: { data: { attributes: { slug: string } }[] };
|
|
||||||
authors: StrapiRecorders;
|
|
||||||
hidden: boolean;
|
|
||||||
thumbnail: StrapiImage;
|
|
||||||
translations: {
|
|
||||||
title: string;
|
|
||||||
excerpt?: string;
|
|
||||||
body: string;
|
|
||||||
translators: StrapiRecorders;
|
|
||||||
proofreaders: StrapiRecorders;
|
|
||||||
language: StrapiLanguage;
|
|
||||||
source_language: StrapiLanguage;
|
|
||||||
}[];
|
|
||||||
date: {
|
|
||||||
day: number;
|
|
||||||
month: number;
|
|
||||||
year: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const importFromStrapi = createStrapiImportEndpoint<StrapiPost>({
|
|
||||||
strapi: {
|
|
||||||
collection: "posts",
|
|
||||||
params: {
|
|
||||||
populate: [
|
|
||||||
"date",
|
|
||||||
"authors",
|
|
||||||
"thumbnail",
|
|
||||||
"categories",
|
|
||||||
"translations",
|
|
||||||
"translations.language",
|
|
||||||
"translations.translators",
|
|
||||||
"translations.proofreaders",
|
|
||||||
"translations.source_language",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
payload: {
|
|
||||||
collection: Collections.Posts,
|
|
||||||
convert: async ({
|
|
||||||
slug,
|
|
||||||
date: { day, month, year },
|
|
||||||
hidden,
|
|
||||||
authors,
|
|
||||||
thumbnail,
|
|
||||||
categories,
|
|
||||||
translations,
|
|
||||||
}) => {
|
|
||||||
const thumbnailId = await uploadStrapiImage({
|
|
||||||
collection: Collections.PostsThumbnails,
|
|
||||||
image: thumbnail,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleTranslation = async ({
|
|
||||||
language,
|
|
||||||
title,
|
|
||||||
body,
|
|
||||||
excerpt,
|
|
||||||
proofreaders,
|
|
||||||
source_language,
|
|
||||||
translators,
|
|
||||||
}: StrapiPost["translations"][number]): Promise<Post["translations"][number]> => {
|
|
||||||
if (isUndefined(language.data))
|
|
||||||
throw new Error("A language is required for a post translation");
|
|
||||||
if (isUndefined(source_language.data))
|
|
||||||
throw new Error("A source_language is required for a post translation");
|
|
||||||
return {
|
|
||||||
language: language.data.attributes.code,
|
|
||||||
sourceLanguage: source_language.data.attributes.code,
|
|
||||||
title,
|
|
||||||
content: plainTextToLexical(body),
|
|
||||||
summary: isDefined(excerpt) ? plainTextToLexical(excerpt) : undefined,
|
|
||||||
translators:
|
|
||||||
translators.data &&
|
|
||||||
(await Promise.all(
|
|
||||||
translators.data?.map(async (recorder) => findRecorder(recorder.attributes.username))
|
|
||||||
)),
|
|
||||||
proofreaders:
|
|
||||||
proofreaders.data &&
|
|
||||||
(await Promise.all(
|
|
||||||
proofreaders.data?.map(async (recorder) => findRecorder(recorder.attributes.username))
|
|
||||||
)),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const data: MarkOptional<Post, "createdAt" | "id" | "updatedAt" | "updatedBy"> = {
|
|
||||||
slug,
|
|
||||||
publishedDate:
|
|
||||||
DateTime.fromObject({ day, month, year }).toISO() ?? new Date().toISOString(),
|
|
||||||
categories: await Promise.all(
|
|
||||||
categories.data.map((category) => findCategory(category.attributes.slug))
|
|
||||||
),
|
|
||||||
translations: await Promise.all(translations.map(handleTranslation)),
|
|
||||||
authors: await Promise.all(
|
|
||||||
authors.data?.map((author) => findRecorder(author.attributes.username)) ?? []
|
|
||||||
),
|
|
||||||
thumbnail: thumbnailId,
|
|
||||||
hidden,
|
|
||||||
};
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,50 +0,0 @@
|
||||||
import { Collections } from "../../constants";
|
|
||||||
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
|
||||||
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
|
|
||||||
|
|
||||||
const fields = {
|
|
||||||
filename: "filename",
|
|
||||||
mimeType: "mimeType",
|
|
||||||
filesize: "filesize",
|
|
||||||
posts: "posts",
|
|
||||||
updatedAt: "updatedAt",
|
|
||||||
} as const satisfies Record<string, string>;
|
|
||||||
|
|
||||||
export const PostsThumbnails = buildImageCollectionConfig({
|
|
||||||
slug: Collections.PostsThumbnails,
|
|
||||||
labels: {
|
|
||||||
singular: "Post Thumbnail",
|
|
||||||
plural: "Post Thumbnails",
|
|
||||||
},
|
|
||||||
admin: { defaultColumns: [fields.filename, fields.posts, fields.updatedAt] },
|
|
||||||
upload: {
|
|
||||||
imageSizes: [
|
|
||||||
{
|
|
||||||
name: "og",
|
|
||||||
height: 750,
|
|
||||||
width: 1125,
|
|
||||||
formatOptions: {
|
|
||||||
format: "jpg",
|
|
||||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "medium",
|
|
||||||
height: 1000,
|
|
||||||
width: 1500,
|
|
||||||
formatOptions: {
|
|
||||||
format: "webp",
|
|
||||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
fields: [
|
|
||||||
backPropagationField({
|
|
||||||
name: fields.posts,
|
|
||||||
hasMany: true,
|
|
||||||
relationTo: Collections.Posts,
|
|
||||||
where: ({ id }) => ({ thumbnail: { equals: id } }),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
|
@ -5,8 +5,6 @@ import type { CueBlock, LineBlock, SectionBlock, TranscriptBlock } from "./types
|
||||||
export enum Collections {
|
export enum Collections {
|
||||||
ChronologyEras = "chronology-eras",
|
ChronologyEras = "chronology-eras",
|
||||||
ChronologyItems = "chronology-items",
|
ChronologyItems = "chronology-items",
|
||||||
Contents = "contents",
|
|
||||||
ContentsThumbnails = "contents-thumbnails",
|
|
||||||
Currencies = "currencies",
|
Currencies = "currencies",
|
||||||
Files = "files",
|
Files = "files",
|
||||||
Keys = "keys",
|
Keys = "keys",
|
||||||
|
@ -16,10 +14,8 @@ export enum Collections {
|
||||||
LibraryItemsScans = "library-items-scans",
|
LibraryItemsScans = "library-items-scans",
|
||||||
LibraryItemsGallery = "library-items-gallery",
|
LibraryItemsGallery = "library-items-gallery",
|
||||||
Notes = "notes",
|
Notes = "notes",
|
||||||
Posts = "posts",
|
|
||||||
Pages = "pages",
|
Pages = "pages",
|
||||||
PagesThumbnails = "pages-thumbnails",
|
PagesThumbnails = "pages-thumbnails",
|
||||||
PostsThumbnails = "posts-thumbnails",
|
|
||||||
Recorders = "recorders",
|
Recorders = "recorders",
|
||||||
RecordersThumbnails = "recorders-thumbnails",
|
RecordersThumbnails = "recorders-thumbnails",
|
||||||
VideosChannels = "videos-channels",
|
VideosChannels = "videos-channels",
|
||||||
|
|
|
@ -4,8 +4,6 @@ import path from "path";
|
||||||
import { buildConfig } from "payload/config";
|
import { buildConfig } from "payload/config";
|
||||||
import { ChronologyEras } from "./collections/ChronologyEras/ChronologyEras";
|
import { ChronologyEras } from "./collections/ChronologyEras/ChronologyEras";
|
||||||
import { ChronologyItems } from "./collections/ChronologyItems/ChronologyItems";
|
import { ChronologyItems } from "./collections/ChronologyItems/ChronologyItems";
|
||||||
import { Contents } from "./collections/Contents/Contents";
|
|
||||||
import { ContentsThumbnails } from "./collections/ContentsThumbnails/ContentsThumbnails";
|
|
||||||
import { Currencies } from "./collections/Currencies/Currencies";
|
import { Currencies } from "./collections/Currencies/Currencies";
|
||||||
import { Files } from "./collections/Files/Files";
|
import { Files } from "./collections/Files/Files";
|
||||||
import { Folders } from "./collections/Folders/Folders";
|
import { Folders } from "./collections/Folders/Folders";
|
||||||
|
@ -19,8 +17,6 @@ import { LibraryItemsScans } from "./collections/LibraryItemsScans/LibraryItemsS
|
||||||
import { LibraryItemsThumbnails } from "./collections/LibraryItemsThumbnails/LibraryItemsThumbnails";
|
import { LibraryItemsThumbnails } from "./collections/LibraryItemsThumbnails/LibraryItemsThumbnails";
|
||||||
import { Notes } from "./collections/Notes/Notes";
|
import { Notes } from "./collections/Notes/Notes";
|
||||||
import { Pages } from "./collections/Pages/Pages";
|
import { Pages } from "./collections/Pages/Pages";
|
||||||
import { Posts } from "./collections/Posts/Posts";
|
|
||||||
import { PostsThumbnails } from "./collections/PostsThumbnails/PostsThumbnails";
|
|
||||||
import { Recorders } from "./collections/Recorders/Recorders";
|
import { Recorders } from "./collections/Recorders/Recorders";
|
||||||
import { RecordersThumbnails } from "./collections/RecordersThumbnails/RecordersThumbnails";
|
import { RecordersThumbnails } from "./collections/RecordersThumbnails/RecordersThumbnails";
|
||||||
import { Tags } from "./collections/Tags/Tags";
|
import { Tags } from "./collections/Tags/Tags";
|
||||||
|
@ -53,20 +49,16 @@ export default buildConfig({
|
||||||
Folders,
|
Folders,
|
||||||
FoldersThumbnails,
|
FoldersThumbnails,
|
||||||
LibraryItems,
|
LibraryItems,
|
||||||
Contents,
|
|
||||||
Posts,
|
|
||||||
Pages,
|
Pages,
|
||||||
ChronologyItems,
|
ChronologyItems,
|
||||||
ChronologyEras,
|
ChronologyEras,
|
||||||
Weapons,
|
Weapons,
|
||||||
WeaponsGroups,
|
WeaponsGroups,
|
||||||
WeaponsThumbnails,
|
WeaponsThumbnails,
|
||||||
ContentsThumbnails,
|
|
||||||
LibraryItemsThumbnails,
|
LibraryItemsThumbnails,
|
||||||
LibraryItemsScans,
|
LibraryItemsScans,
|
||||||
LibraryItemsGallery,
|
LibraryItemsGallery,
|
||||||
RecordersThumbnails,
|
RecordersThumbnails,
|
||||||
PostsThumbnails,
|
|
||||||
Files,
|
Files,
|
||||||
Notes,
|
Notes,
|
||||||
Videos,
|
Videos,
|
||||||
|
|
|
@ -33,26 +33,6 @@ export const findRecorder = async (name: string): Promise<string> => {
|
||||||
return recorder.docs[0].id;
|
return recorder.docs[0].id;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const findContentType = async (name: string): Promise<string> => {
|
|
||||||
const key = await payload.find({
|
|
||||||
collection: Collections.Keys,
|
|
||||||
where: { name: { equals: name }, type: { equals: KeysTypes.Contents } },
|
|
||||||
depth: 0,
|
|
||||||
});
|
|
||||||
if (!key.docs[0]) throw new Error(`Content type ${name} wasn't found`);
|
|
||||||
return key.docs[0].id;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const findContent = async (slug: string): Promise<string> => {
|
|
||||||
const content = await payload.find({
|
|
||||||
collection: Collections.Contents,
|
|
||||||
where: { slug: { equals: slug } },
|
|
||||||
depth: 0,
|
|
||||||
});
|
|
||||||
if (!content.docs[0]) throw new Error(`Content ${slug} wasn't found`);
|
|
||||||
return content.docs[0].id;
|
|
||||||
};
|
|
||||||
|
|
||||||
type UploadStrapiImage = {
|
type UploadStrapiImage = {
|
||||||
image: StrapiImage;
|
image: StrapiImage;
|
||||||
collection: Collections;
|
collection: Collections;
|
||||||
|
|
Loading…
Reference in New Issue