payload-plugin-newsletter 0.16.3 → 0.16.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/dist/collections.cjs +148 -115
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +148 -115
- package/dist/collections.js.map +1 -1
- package/dist/index.cjs +152 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +152 -119
- package/dist/index.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/plugin-api-key-recommendations.md +117 -0
package/dist/collections.js
CHANGED
|
@@ -269,9 +269,9 @@ var init_broadcast2 = __esm({
|
|
|
269
269
|
async update(id, data) {
|
|
270
270
|
try {
|
|
271
271
|
const existing = await this.get(id);
|
|
272
|
-
if (!this.canEditInStatus(existing.
|
|
272
|
+
if (!this.canEditInStatus(existing.sendStatus)) {
|
|
273
273
|
throw new BroadcastProviderError(
|
|
274
|
-
`Cannot update broadcast in status: ${existing.
|
|
274
|
+
`Cannot update broadcast in status: ${existing.sendStatus}`,
|
|
275
275
|
"INVALID_STATUS" /* INVALID_STATUS */,
|
|
276
276
|
this.name
|
|
277
277
|
);
|
|
@@ -314,9 +314,9 @@ var init_broadcast2 = __esm({
|
|
|
314
314
|
async delete(id) {
|
|
315
315
|
try {
|
|
316
316
|
const existing = await this.get(id);
|
|
317
|
-
if (!this.canEditInStatus(existing.
|
|
317
|
+
if (!this.canEditInStatus(existing.sendStatus)) {
|
|
318
318
|
throw new BroadcastProviderError(
|
|
319
|
-
`Cannot delete broadcast in status: ${existing.
|
|
319
|
+
`Cannot delete broadcast in status: ${existing.sendStatus}`,
|
|
320
320
|
"INVALID_STATUS" /* INVALID_STATUS */,
|
|
321
321
|
this.name
|
|
322
322
|
);
|
|
@@ -475,7 +475,7 @@ var init_broadcast2 = __esm({
|
|
|
475
475
|
subject: broadcast.subject,
|
|
476
476
|
preheader: broadcast.preheader,
|
|
477
477
|
content: broadcast.body,
|
|
478
|
-
|
|
478
|
+
sendStatus: this.mapBroadcastStatus(broadcast.status),
|
|
479
479
|
trackOpens: broadcast.track_opens,
|
|
480
480
|
trackClicks: broadcast.track_clicks,
|
|
481
481
|
replyTo: broadcast.reply_to,
|
|
@@ -1178,6 +1178,19 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1178
1178
|
const customizations = pluginConfig.customizations?.broadcasts;
|
|
1179
1179
|
return {
|
|
1180
1180
|
slug: "broadcasts",
|
|
1181
|
+
access: {
|
|
1182
|
+
read: () => true,
|
|
1183
|
+
// Public read access
|
|
1184
|
+
create: ({ req: { user } }) => {
|
|
1185
|
+
return Boolean(user);
|
|
1186
|
+
},
|
|
1187
|
+
update: ({ req: { user } }) => {
|
|
1188
|
+
return Boolean(user);
|
|
1189
|
+
},
|
|
1190
|
+
delete: ({ req: { user } }) => {
|
|
1191
|
+
return Boolean(user);
|
|
1192
|
+
}
|
|
1193
|
+
},
|
|
1181
1194
|
versions: {
|
|
1182
1195
|
drafts: {
|
|
1183
1196
|
autosave: true,
|
|
@@ -1191,7 +1204,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1191
1204
|
admin: {
|
|
1192
1205
|
useAsTitle: "subject",
|
|
1193
1206
|
description: "Individual email campaigns sent to subscribers",
|
|
1194
|
-
defaultColumns: ["subject", "_status", "
|
|
1207
|
+
defaultColumns: ["subject", "_status", "sendStatus", "sentAt", "recipientCount"]
|
|
1195
1208
|
},
|
|
1196
1209
|
fields: [
|
|
1197
1210
|
{
|
|
@@ -1251,8 +1264,9 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1251
1264
|
]
|
|
1252
1265
|
},
|
|
1253
1266
|
{
|
|
1254
|
-
name: "
|
|
1267
|
+
name: "sendStatus",
|
|
1255
1268
|
type: "select",
|
|
1269
|
+
label: "Send Status",
|
|
1256
1270
|
required: true,
|
|
1257
1271
|
defaultValue: "draft" /* DRAFT */,
|
|
1258
1272
|
options: [
|
|
@@ -1266,6 +1280,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1266
1280
|
],
|
|
1267
1281
|
admin: {
|
|
1268
1282
|
readOnly: true,
|
|
1283
|
+
description: "The status of the email send operation",
|
|
1269
1284
|
components: {
|
|
1270
1285
|
Cell: "payload-plugin-newsletter/components#StatusBadge"
|
|
1271
1286
|
}
|
|
@@ -1322,7 +1337,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1322
1337
|
type: "group",
|
|
1323
1338
|
admin: {
|
|
1324
1339
|
readOnly: true,
|
|
1325
|
-
condition: (data) => data?.
|
|
1340
|
+
condition: (data) => data?.sendStatus === "sent" /* SENT */
|
|
1326
1341
|
},
|
|
1327
1342
|
fields: [
|
|
1328
1343
|
{
|
|
@@ -1381,7 +1396,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1381
1396
|
name: "scheduledAt",
|
|
1382
1397
|
type: "date",
|
|
1383
1398
|
admin: {
|
|
1384
|
-
condition: (data) => data?.
|
|
1399
|
+
condition: (data) => data?.sendStatus === "scheduled" /* SCHEDULED */,
|
|
1385
1400
|
date: {
|
|
1386
1401
|
displayFormat: "MMM d, yyyy h:mm a"
|
|
1387
1402
|
}
|
|
@@ -1407,58 +1422,130 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1407
1422
|
}
|
|
1408
1423
|
],
|
|
1409
1424
|
hooks: {
|
|
1410
|
-
// Sync with provider on create
|
|
1425
|
+
// Sync with provider on create and update
|
|
1411
1426
|
afterChange: [
|
|
1412
|
-
async ({ doc, operation, req }) => {
|
|
1413
|
-
if (!hasProviders
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1427
|
+
async ({ doc, operation, req, previousDoc }) => {
|
|
1428
|
+
if (!hasProviders) return doc;
|
|
1429
|
+
if (operation === "create") {
|
|
1430
|
+
try {
|
|
1431
|
+
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
1432
|
+
if (!providerConfig || !providerConfig.token) {
|
|
1433
|
+
req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
|
|
1434
|
+
return doc;
|
|
1435
|
+
}
|
|
1436
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
1437
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1438
|
+
const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content);
|
|
1439
|
+
const providerBroadcast = await provider.create({
|
|
1440
|
+
name: doc.subject,
|
|
1441
|
+
// Use subject as name since we removed the name field
|
|
1442
|
+
subject: doc.subject,
|
|
1443
|
+
preheader: doc.contentSection?.preheader,
|
|
1444
|
+
content: htmlContent,
|
|
1445
|
+
trackOpens: doc.settings?.trackOpens,
|
|
1446
|
+
trackClicks: doc.settings?.trackClicks,
|
|
1447
|
+
replyTo: doc.settings?.replyTo || providerConfig.replyTo,
|
|
1448
|
+
audienceIds: doc.audienceIds?.map((a) => a.audienceId)
|
|
1449
|
+
});
|
|
1450
|
+
await req.payload.update({
|
|
1451
|
+
collection: "broadcasts",
|
|
1452
|
+
id: doc.id,
|
|
1453
|
+
data: {
|
|
1454
|
+
providerId: providerBroadcast.id,
|
|
1455
|
+
providerData: providerBroadcast.providerData
|
|
1456
|
+
},
|
|
1457
|
+
req
|
|
1458
|
+
});
|
|
1459
|
+
return {
|
|
1460
|
+
...doc,
|
|
1438
1461
|
providerId: providerBroadcast.id,
|
|
1439
1462
|
providerData: providerBroadcast.providerData
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1463
|
+
};
|
|
1464
|
+
} catch (error) {
|
|
1465
|
+
if (error instanceof Error) {
|
|
1466
|
+
req.payload.logger.error("Failed to create broadcast in provider:", {
|
|
1467
|
+
message: error.message,
|
|
1468
|
+
stack: error.stack,
|
|
1469
|
+
name: error.name,
|
|
1470
|
+
// If it's a BroadcastProviderError, it might have additional details
|
|
1471
|
+
...error.details
|
|
1472
|
+
});
|
|
1473
|
+
} else {
|
|
1474
|
+
req.payload.logger.error("Failed to create broadcast in provider:", error);
|
|
1475
|
+
}
|
|
1476
|
+
return doc;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
if (operation === "update" && doc.providerId) {
|
|
1480
|
+
req.payload.logger.info("Broadcast afterChange update hook triggered", {
|
|
1481
|
+
operation,
|
|
1482
|
+
hasProviderId: !!doc.providerId,
|
|
1483
|
+
sendStatus: doc.sendStatus,
|
|
1484
|
+
publishStatus: doc._status
|
|
1442
1485
|
});
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1486
|
+
try {
|
|
1487
|
+
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
1488
|
+
if (!providerConfig || !providerConfig.token) {
|
|
1489
|
+
req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
|
|
1490
|
+
return doc;
|
|
1491
|
+
}
|
|
1492
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
1493
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1494
|
+
const capabilities = provider.getCapabilities();
|
|
1495
|
+
const sendStatus = doc.sendStatus || "draft" /* DRAFT */;
|
|
1496
|
+
if (!capabilities.editableStatuses.includes(sendStatus)) {
|
|
1497
|
+
req.payload.logger.info(`Skipping sync for broadcast in status: ${sendStatus}`);
|
|
1498
|
+
return doc;
|
|
1499
|
+
}
|
|
1500
|
+
const contentChanged = doc.subject !== previousDoc?.subject || doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader || JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content) || doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens || doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks || doc.settings?.replyTo !== previousDoc?.settings?.replyTo || JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds);
|
|
1501
|
+
if (contentChanged) {
|
|
1502
|
+
const updates = {};
|
|
1503
|
+
if (doc.subject !== previousDoc?.subject) {
|
|
1504
|
+
updates.name = doc.subject;
|
|
1505
|
+
updates.subject = doc.subject;
|
|
1506
|
+
}
|
|
1507
|
+
if (doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader) {
|
|
1508
|
+
updates.preheader = doc.contentSection?.preheader;
|
|
1509
|
+
}
|
|
1510
|
+
if (JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content)) {
|
|
1511
|
+
updates.content = await convertToEmailSafeHtml(doc.contentSection?.content);
|
|
1512
|
+
}
|
|
1513
|
+
if (doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens) {
|
|
1514
|
+
updates.trackOpens = doc.settings.trackOpens;
|
|
1515
|
+
}
|
|
1516
|
+
if (doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks) {
|
|
1517
|
+
updates.trackClicks = doc.settings.trackClicks;
|
|
1518
|
+
}
|
|
1519
|
+
if (doc.settings?.replyTo !== previousDoc?.settings?.replyTo) {
|
|
1520
|
+
updates.replyTo = doc.settings.replyTo || providerConfig.replyTo;
|
|
1521
|
+
}
|
|
1522
|
+
if (JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds)) {
|
|
1523
|
+
updates.audienceIds = doc.audienceIds?.map((a) => a.audienceId);
|
|
1524
|
+
}
|
|
1525
|
+
req.payload.logger.info("Syncing broadcast updates to provider", {
|
|
1526
|
+
providerId: doc.providerId,
|
|
1527
|
+
updates
|
|
1528
|
+
});
|
|
1529
|
+
await provider.update(doc.providerId, updates);
|
|
1530
|
+
req.payload.logger.info(`Broadcast ${doc.id} synced to provider successfully`);
|
|
1531
|
+
} else {
|
|
1532
|
+
req.payload.logger.info("No content changes to sync to provider");
|
|
1533
|
+
}
|
|
1534
|
+
} catch (error) {
|
|
1535
|
+
if (error instanceof Error) {
|
|
1536
|
+
req.payload.logger.error("Failed to sync broadcast update to provider:", {
|
|
1537
|
+
message: error.message,
|
|
1538
|
+
stack: error.stack,
|
|
1539
|
+
name: error.name,
|
|
1540
|
+
// If it's a BroadcastProviderError, it might have additional details
|
|
1541
|
+
...error.details
|
|
1542
|
+
});
|
|
1543
|
+
} else {
|
|
1544
|
+
req.payload.logger.error("Failed to sync broadcast update to provider:", error);
|
|
1545
|
+
}
|
|
1459
1546
|
}
|
|
1460
|
-
return doc;
|
|
1461
1547
|
}
|
|
1548
|
+
return doc;
|
|
1462
1549
|
},
|
|
1463
1550
|
// Hook to send when published
|
|
1464
1551
|
async ({ doc, operation, previousDoc, req }) => {
|
|
@@ -1466,7 +1553,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1466
1553
|
const wasUnpublished = !previousDoc?._status || previousDoc._status === "draft";
|
|
1467
1554
|
const isNowPublished = doc._status === "published";
|
|
1468
1555
|
if (wasUnpublished && isNowPublished && doc.providerId) {
|
|
1469
|
-
if (doc.
|
|
1556
|
+
if (doc.sendStatus === "sent" /* SENT */ || doc.sendStatus === "sending" /* SENDING */) {
|
|
1470
1557
|
return doc;
|
|
1471
1558
|
}
|
|
1472
1559
|
try {
|
|
@@ -1485,7 +1572,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1485
1572
|
collection: "broadcasts",
|
|
1486
1573
|
id: doc.id,
|
|
1487
1574
|
data: {
|
|
1488
|
-
|
|
1575
|
+
sendStatus: "sending" /* SENDING */,
|
|
1489
1576
|
sentAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1490
1577
|
},
|
|
1491
1578
|
req
|
|
@@ -1507,7 +1594,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1507
1594
|
collection: "broadcasts",
|
|
1508
1595
|
id: doc.id,
|
|
1509
1596
|
data: {
|
|
1510
|
-
|
|
1597
|
+
sendStatus: "failed" /* FAILED */
|
|
1511
1598
|
},
|
|
1512
1599
|
req
|
|
1513
1600
|
});
|
|
@@ -1516,62 +1603,8 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1516
1603
|
return doc;
|
|
1517
1604
|
}
|
|
1518
1605
|
],
|
|
1519
|
-
//
|
|
1520
|
-
beforeChange: [
|
|
1521
|
-
async ({ data, originalDoc, operation, req }) => {
|
|
1522
|
-
if (!hasProviders || !originalDoc?.providerId || operation !== "update") return data;
|
|
1523
|
-
try {
|
|
1524
|
-
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
1525
|
-
if (!providerConfig || !providerConfig.token) {
|
|
1526
|
-
req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
|
|
1527
|
-
return data;
|
|
1528
|
-
}
|
|
1529
|
-
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
1530
|
-
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1531
|
-
const capabilities = provider.getCapabilities();
|
|
1532
|
-
if (!capabilities.editableStatuses.includes(originalDoc.status)) {
|
|
1533
|
-
return data;
|
|
1534
|
-
}
|
|
1535
|
-
const updates = {};
|
|
1536
|
-
if (data.subject !== originalDoc.subject) {
|
|
1537
|
-
updates.name = data.subject;
|
|
1538
|
-
updates.subject = data.subject;
|
|
1539
|
-
}
|
|
1540
|
-
if (data.contentSection?.preheader !== originalDoc.contentSection?.preheader) updates.preheader = data.contentSection?.preheader;
|
|
1541
|
-
if (data.contentSection?.content !== originalDoc.contentSection?.content) {
|
|
1542
|
-
updates.content = await convertToEmailSafeHtml(data.contentSection?.content);
|
|
1543
|
-
}
|
|
1544
|
-
if (data.settings?.trackOpens !== originalDoc.settings?.trackOpens) {
|
|
1545
|
-
updates.trackOpens = data.settings.trackOpens;
|
|
1546
|
-
}
|
|
1547
|
-
if (data.settings?.trackClicks !== originalDoc.settings?.trackClicks) {
|
|
1548
|
-
updates.trackClicks = data.settings.trackClicks;
|
|
1549
|
-
}
|
|
1550
|
-
if (data.settings?.replyTo !== originalDoc.settings?.replyTo) {
|
|
1551
|
-
updates.replyTo = data.settings.replyTo || providerConfig.replyTo;
|
|
1552
|
-
}
|
|
1553
|
-
if (JSON.stringify(data.audienceIds) !== JSON.stringify(originalDoc.audienceIds)) {
|
|
1554
|
-
updates.audienceIds = data.audienceIds?.map((a) => a.audienceId);
|
|
1555
|
-
}
|
|
1556
|
-
if (Object.keys(updates).length > 0) {
|
|
1557
|
-
await provider.update(originalDoc.providerId, updates);
|
|
1558
|
-
}
|
|
1559
|
-
} catch (error) {
|
|
1560
|
-
if (error instanceof Error) {
|
|
1561
|
-
req.payload.logger.error("Failed to update broadcast in provider:", {
|
|
1562
|
-
message: error.message,
|
|
1563
|
-
stack: error.stack,
|
|
1564
|
-
name: error.name,
|
|
1565
|
-
// If it's a BroadcastProviderError, it might have additional details
|
|
1566
|
-
...error.details
|
|
1567
|
-
});
|
|
1568
|
-
} else {
|
|
1569
|
-
req.payload.logger.error("Failed to update broadcast in provider:", error);
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
return data;
|
|
1573
|
-
}
|
|
1574
|
-
],
|
|
1606
|
+
// beforeChange hooks can be added here if needed
|
|
1607
|
+
beforeChange: [],
|
|
1575
1608
|
// Handle deletion
|
|
1576
1609
|
afterDelete: [
|
|
1577
1610
|
async ({ doc, req }) => {
|
|
@@ -1585,7 +1618,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1585
1618
|
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
1586
1619
|
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1587
1620
|
const capabilities = provider.getCapabilities();
|
|
1588
|
-
if (capabilities.editableStatuses.includes(doc.
|
|
1621
|
+
if (capabilities.editableStatuses.includes(doc.sendStatus)) {
|
|
1589
1622
|
await provider.delete(doc.providerId);
|
|
1590
1623
|
}
|
|
1591
1624
|
} catch (error) {
|