payload-plugin-scheduler
Version:
Scheduled posts plugin for PayloadCMS
117 lines (116 loc) • 5.41 kB
JavaScript
import { getPublishDateFieldNameFromFields } from '../../util.js';
/**
* Wrapper around the default `RelationshipField` that ensures "related" documents are published before the primary document
*/ export const SafeRelationship = (props)=>{
const validate = async (value, options)=>{
// first run user validate fn
if (props.validate) {
const validateRes = await props.validate(value, options);
if (validateRes !== true) {
return validateRes;
}
}
const { data: rawData, req } = options;
const data = rawData;
const payload = req.payload;
const { config } = payload;
const currentCollection = options.collectionSlug ? config.collections.find((collection)=>collection.slug === options.collectionSlug) : undefined;
const currentPublishDateFieldName = currentCollection ? getPublishDateFieldNameFromFields(currentCollection.fields) : getPublishDateFieldNameFromFields([]);
// abort if the field is empty
if (!value || Array.isArray(value) && value.length === 0) {
return true;
}
// abort if the current document is an unscheduled draft
if (!currentPublishDateFieldName || data?._status === 'draft' && !data?.[currentPublishDateFieldName]) {
return true;
}
// cast relationTo to an array
const relationsTo = Array.isArray(props.relationTo) ? props.relationTo : [
props.relationTo
];
// cast value to an array
const values = Array.isArray(value) ? value : [
value
];
// build a list of collections we need to check draft status for
// format: { [collectionSlug]: <titleFieldKey> } so we can generate a pretty error message later
const relatedDraftCollections = {};
relationsTo.forEach((name)=>{
const collection = config.collections.find((collection)=>collection.slug === name);
const useAsTitle = collection?.admin.useAsTitle;
const publishDateFieldName = getPublishDateFieldNameFromFields(collection?.fields || []);
if (collection?.versions?.drafts && useAsTitle && publishDateFieldName) {
relatedDraftCollections[collection.slug] = {
publishDateFieldName,
titleFieldName: useAsTitle
};
}
});
// compile an array of related draft documents to check
const relatedDocs = await values.reduce(async (accPromise, v)=>{
const acc = await accPromise;
// naively assume that we're dealing with a simple (not polymorphic) relationship
// https://payloadcms.com/docs/fields/relationship#how-the-data-is-saved
let collection = props.relationTo;
let id = v;
// handle polymorphic relationships
if (typeof v === 'object') {
collection = v.relationTo;
id = v.value;
}
// ignore related docs w/o drafts
if (!relatedDraftCollections[collection]) {
return acc;
}
try {
const doc = await payload.findByID({
id,
collection: collection,
req
});
// Only add the document if it's in draft status
if (doc && doc._status === 'draft') {
acc.push({
...doc,
collection
});
}
} catch (error) {
payload.logger.error(error, `[SafeRelationship] ${collection}:${id}`);
}
return acc;
}, Promise.resolve([]));
const invalidRelatedDocs = [];
// store a reference to the publish date for the current document
let publishDate;
if (data?._status === 'published' || !data?._status) {
// for published docs, or docs w/o drafts, we assume they are currently being published
publishDate = new Date();
} else if (data?._status === 'draft' && data?.[currentPublishDateFieldName]) {
// for scheduled documents, use the set publish_date
publishDate = new Date(data[currentPublishDateFieldName]);
}
// loop over the related documents and find any stragglers
relatedDocs.forEach((rd)=>{
const relatedCollection = relatedDraftCollections[rd.collection];
const docPubDate = rd[relatedCollection.publishDateFieldName] ? new Date(rd[relatedCollection.publishDateFieldName]) : null // this check is necessary otherwise new Date(null) => 1/1/70
;
if (!docPubDate || docPubDate >= publishDate) {
invalidRelatedDocs.push({
collection: rd.collection,
title: rd[relatedCollection.titleFieldName]
});
}
});
if (invalidRelatedDocs.length > 0) {
return `The following docs must be published before this one: ${invalidRelatedDocs.map(({ collection, title })=>`${title} (${collection})`).join(', ')}`;
}
return true;
};
return {
type: 'relationship',
...props,
validate
};
};
//# sourceMappingURL=index.js.map